Skip to content

yairman/4myanmar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

4myanmar

Video Demo:

Framework: flask

Environment: CS50's codespace, github

Database: sqlite3

Name: 4myanmar

Version: v3.3.1

Summary: 4myanmar (obviously a copied naming style from 4chan.org) is an imageboard where users can anonymously create account, create posts, save posts, add photos, give upvotes and downvotes in any board or whatever category that the user likes. I built this because Facebook and many social media sites are blocked in my country Myanmar. I also wanted to build this to allow information flow without the influence of any person's fame or popularity which could potentially drive misinformation even further, reduce balance in opinions, reduce freedom and negatively affect free speech. To achieve this, I didn't even add the option to describe "Anonymous" on my site to stay completely anonymous. I do store UUID and cookies to identify users' posts, saved posts, replies, upvotes and downvotes but nothing more personal information is stored.

Detailed description:

0. Any user can browse boards, create posts and replies. But they will not be able to upvote, downvote, and save posts. Users can create an account with a passkey but they will remain anonymous (not even showing the name "anonymous"). The passkey is used to create or sign in with only one button. If the user that has already created an account provides a wrong passkey, a new account is created. Only if the user provides the correct passkey will they be able to view their posts, saved posts and replies. Any user with an account can create, save, upvote, downvote, and reply to posts in any board. Long posts are automatically truncated but if the users click on "read more", the full post is shown. Users can also add photos and videos when posting or replying. Users can see saved posts and posts that they have replied to in separate pages.

1. I used sqlite3 because that's the easiest. All schemas that I used for database are in schema.sql and schema2.sql. To create a database for this application, you only need to run as follow.

sqlite3 [name].db < schema.sql
sqlite3 [name].db < schema2.sql

I used schema2.sql for the final version. I looked up online and went to the wrong site and thought sqlite3 supports boolean data type. Turns out that it doesn't but it assumes 0 as False and 1 as True as per their site. See more sqlite3 supported data types. In schema.sql, I used Bit data type but for the final version, I changed the data type of the boolean fields to INTEGER and sqlite3 can automatically recognize it as True or False.

In the database, there are 6 tables (users, posts, upvoters, downvoters, user_posts, replies). Do not be confused by posts and user_posts: user_posts is for posts that are created by the user whereas posts contain posts created by all users. But you could argue that it's not needed to create user_posts because I could get the user's posts by just passing another condition in the query like the following. But as I started out piece by piece from the scratch, only after creating many posts and done so many things did I realize that I could've done that simply. But I have no intention to fix it, maybe later in the future. (It's only a small thing to fix but I won't... just messing with you. Ha Ha...)

SELECT * FROM posts WHERE user_uuid = [user_uuid];

The table "posts" contais id, board_name, title, content, filename, filepath, timestamp, upvotes, downvotes, user_uuid, isVotedByMe, isDownvotedByMe, savedByMe columns. The last three are to function as booleans and only to disable upvotes, downvotes and save buttons or not on front-end; nothing more special. Filename is included for the sake of users' security. To show what the actual file is to at least prevent people from adding fake filenames to the post. This doesn't totally prevent milicious files especially if their names do not explicitly describe what the actual file is. I can't do anything about this right now. I could, in the future, check the files and urls by using virus total api. I used UUID to make sure that even if any two users have the same passkey, they could be identified separately. I have to, because I don't ask for the user's name. Asking for a name but saying the site is anonymous won't be logical, even if the users obviously have the option to name whatever they want. (I just didn't want any people to get a name!).

I asked chatGPT and CS50's Duck about UUID. So, the implementation is per their suggestion. user_uuid could be stored as a hash value but I think that would slow down a bit (may be I'm wrong.) Users are only asked to provide a passkey to login or create an account. One cool thing is that there's only one button to create or login to your account. I think that's cool because I've seen people confused with the most popular way which is using different forms. How did I get this? Acidentally. It's because I used UUID that the server is able to identify users if they provided the same passkey that they used before to create an account, but of course, they wouldn't and shouldn't be able to login without using the passkey they used to create an account, lol. User_uuid is given only after the user created an account by providing a passkey. Upvoters and downvoters tables record the voter's uuid and the voted post's id. Replies table is pretty much the same as posts in a lot of ways but it records the replied post's id, which is later to be used to show replies to the specific posts.

Templates include index.html, layout.html, myposts.html, myreplies.html and boards.html. I could've used the same index.html to show user's posts and replies, but the problem lies in the process of getting the board_name. In index.html, board_name is constant, but it must not be in myposts and myreplies as they are collections from different boards. So, due to these conflicts, I decided to used separate files. For saved posts, I used the same index.html file.

The busiest file is index.html. It is used for every board. You could literally die from choking by seeing this messy file. The organization of code can definitely be improved, especially on javascript part. It's just kinda a messy language. But truth be told, I'm completely a noob so just ignore it. I am not really good at Javascript. I had to ask ChatGPT and CS50's Duck frequently. But most of the time, it's about fixing syntax and less about, for example, fetching or async functions. I could achieved a lot by just a few concepts that I learned from previous weeks. Most of the functions that I wrote are for changing buttons, like upvote to undo upvote, and for changing visibility of various forms. Others are for fetching postId and actions from the user's clicks and then to pass them in json format to the backend app.py, and finally get the updated data back to the frontend. This is effective because flask needs to reload to update everything.

styles.css is int static folder. For styling, I didn't use the least bit of bootstrap. I used pure css to design everything. But there's a lot of overlaps. I used emojis and utf-8 icons to make the site more user-friendly. I used rounded corners in most places that I can. I used icons from flat Get favicons here. I used card-style posts and rounded buttons. I used some codes from chatGPT to truncate long post's content. There's no a white-mode version. In the future, I would probably add color themes that the user can change by clicking. I learned a lot about flexbox and grid from w3schools, because I suck at styling.

Uploaded files are in static/media folder. Favicons are in static. You can get favicons from favicon.io as I mentioned earlier.

Flask_session includes the session keys.

release.note contains functionalities that are fixed or added in this version. I've built multiple versions of this app. But I don't know if I have to upload them or not.

Finally, app.py is my favorite. I chose to create a dictionary of boards because it's easier and more manageable. If I want to add a new board, I could just include it into the dictionary. I could also add a feature to allow users to create new board. I could've used only a list but I thought maybe at some points, I want to do something with the full names of boards. So, I used both types. My proudest moment is when I figured out a way to dynamically create routes without registering urls to routes and without creating multiple html files. If the user visits a board, it's just index.html file with a different data set. If the user does any activity in a board, the board_name is passed along with other data. So, it's just a white lie that the user is seeing different board: the user is actually only seeing posts with different board_name. It's only an sql trick. Cool. I simplified the process of getting user_uuid, getting files from user, inserting into different tables and columns, and getting data from different tables and columns by creating functions. Second most proudest moment is when I figured out a way to record the votes of the user. I just used the same dynamic method from the board function. JS passes post_id and action(upvote/undo upvote/downvote/undo downvote) using fetch method and app.py updates vote counts accordingly in the backend.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published