Assignment 3
Application pitch:
Have you ever wondered why it’s so hard to see what all your friends are reading on Goodreads? Tired of having to text your friends a link every time you see a book they might enjoy? BookClub is a social media app that connects readers and book enthusiasts. By facilitating reading as a social activity, BookClub goes beyond tracking individual reading progress and encourages users to read books together.
BookClub enables you to:
- Easily view and keep track of the books you’ve read, are currently reading, and want to read in the future using folders
- See what your friends are reading and want to read in a visually engaging format that resembles browsing at a bookstore
- Rate books and see your friends’ opinions
- Recommend books directly to specific friends
- See all the recommendations your friends have given you
- Invite friends to read books with you
In addition to viewing information and ratings for books, BookClub makes it easy for you to discover what your friends are reading (or want to read), view their opinions on books, and see what they think you might enjoy reading next. BookClub enables users to send their friends personalized reading recommendations by adding books to their Bookbags—places where each person can view the books that their friends have specifically suggested to them—and send invitations to their friends to read books with them. With BookClub, users now have a one-stop shop for discovering and enjoying books with their friends.
Functional design:
User (same as in tutorial)
Purpose: authenticate users
Principle: after a user registers with a username and password, they can authenticate as that user by providing a matching username and password
State:
registered: set User
username, password: registered -> set String
Actions:
register (username: String, password: String, out u: User)
- create user u with username, password
- add u to registered
authenticate (username: String, password: String, out u: User)
- if user u with username, password is in registered, output u
Profile [User]
Purpose: enable users to view an overview of information about other users
Principle: collection of information about a particular user
State:
profiles: User -> one Profile
name: Profile -> one String
info: Profile -> one Data
Actions:
updateName (u: User, n: String)
- update user’s display name with string n
updateInfo (u: User, i: Data)
- update user’s info with data i
Session [User] (same as in tutorial)
Purpose: authenticate user for extended period
Principle: after a session starts (and before it ends), the getUser action returns the user identified at the start
State:
active: set Session
user: active -> one User
Actions:
start (u: User, out s: Session)
- start the session for user u
getUser (s: Session, out u: User)
- get the user u for the current session
end (s: Session)
- end the current session
Friend [User]
Purpose: enable a user to define a group of other users that they want to be able to interact more closely with
Principle: after a user becomes friends with another user, they can view specific types of content and perform specific actions with them
State:
friends: User -> set Friend
Actions:
add: (u1: User, u2: User)
- add u2 to friends for u1
getFriends: (u: User, out userFriends: set User)
- get all friends for user u
Book [User]
Purpose: contain information and user-generated content related to a particular book
Principle: stores information (description, related items created by users) that is shown when a user selects a book title
State:
info: Book -> one Data
userInfo: Book -> one Data, User
userBooks: User -> set Book, User
Actions:
addInfo (b: Book, i: Data, u: User)
- add info i to book b’s userInfo; assign author of info as user u
share (b: Book, t: User, f: User)
- add b, f to userBooks for user t
Folder [Item, User]
Purpose: group items into disjoint categories
Principle: a user can assign items to a folder (maximum of one folder each); then, viewing the contents of that folder will include all the items added
State:
folder: Item -> one String
userFolders: User -> set folder
Actions:
add (i: Item, f: String, u: User)
- add i to userFolders f for user u
remove (i: Item, f: String, u: User)
- remove i from userFolders f for user u
getItems (f: String, u: User, out items: set Item)
- get all items in userFolders f for user u
Rating [Item, User]
Purpose: enable users to view others’ opinions about an item
Principle: enjoyment scores (out of 5) that users have assigned to an item
State:
ratings: Item -> set Number
userRatings: User -> set ratings
Actions:
addRating (i: Item, n: Number, u: User)
- add n to ratings for i
- add ratings for i to userRatings for user u
deleteRating (i: Item, n: Number, u: User)
- remove n from ratings for i
- remove ratings for i from userRatings for user u
Recommendation [Item, User]
Purpose: enable users to recommend items to each other and view all the items that others have recommended to them
Principle: after a user sends a recommendation to another user, the item recommended will appear in the recipient’s collection of recommendations
State:
recommendations: User -> set Items, User
Actions:
recommend (i: Item, t: User, f: User)
- add i, f to t.recommendations
getItems (u: User)
- get all items in u.recommendations
Invitation [Item, User]
Purpose: help users do activities in tandem with other people
Principle: a user can post an invitation to other users regarding a particular item, and others can accept or decline the invitation
State:
invitationsPosted: User -> set Item
invitationsReceived: User -> set Item, User
Actions:
postInvitation (i: Item)
- add i to invitationsPosted
acceptInvitation (i: Item, u: User)
- add i, u to invitationsReceived
declineInvitation (i: Item, u: User)
- remove i, u from invitationsReceived
Synchronizations
app BookClub:
include User
include Book [User.User]
include Folder [Book.Book, User.User]
include Rating [Book.Book, User.User]
include Profile [User.User]
include Session [User.User]
include Friend [User.User]
include Recommendation [Book.Book, User.User]
include Invitation [Book.Book, User.User]
sync login
- when User.authenticate occurs, trigger Session.start
sync addUserFriend
- when Friend.add occurs, trigger Profile.updateInfo
sync categorize
- when Folder.add occurs, trigger Book.addInfo and Profile.updateInfo
sync bookRating
- when Rating.addRating occurs, trigger Book.addInfo and Profile.updateInfo
sync bookRecommendation
- when Recommendation.recommend occurs, trigger Book.share and Profile.updateInfo
sync startInvitation
- when Invitation.acceptInvitation occurs, trigger Folder.add(“currently reading”)
Concept dependence diagram:
Wireframes:
UI Designs: click here
UI Prototype (includes flows): click here
Notable pages:
Homepage (includes bookshelf-browsing UI and friends' invitations):
Book page (includes invitation, rating, recommendation, and categorization options):
Bookbag page (can view all recommendations from friends):
Design tradeoffs:
Book categorization: folders versus lists
- When deciding how users should be able to categorize books as read / currently reading / to read, I had to choose between implementing categories as folders (disjoint, so each book could be in a maximum of 1 folder each) or lists (where each book could be added to multiple lists).
- I chose to go with folders in order to minimize confusion between the categories (and also because, for simplicity, I wanted to stick to these three categories rather than enabling users to create their own categories). This limited customization and flexibility, but simplified the data relationships and UI.
Specialization: distinguishing ratings from reviews
- I had the choice to either use book “reviews” as a whole concept (encompassing elements like a numerical rating, written review, upvotes) or differentiate parts like numerical/star ratings as a specialized concept.
- I chose to specialize and treat numerical ratings as a separate concept because I felt that they served a different purpose than written reviews (overviews of thoughts versus in-depth analyses), and also because it would add more clarity in implementation.
Friend requests versus adding friends
- Regarding friending, I had to choose between enabling users to send and accept friend requests or just add friends instantaneously.
- I chose to allow users to just add friends instantaneously, combining the process of requesting and becoming friends (tightening the two actions). This limited the ability for users to screen who their friends will be, but simplified the necessary app processes.