Chapter 3: Convergent Design Document - V2
Pitch
WeeHive presents an innovative vision for a decentralized, data-driven agricultural platform that aims to bring stability and sweetness to our uncertain climate future. At its core, WeeHive creates a supportive network for beekeepers who generate, collaborate, and learn new knowledge to tackle the serious challenges of harsh climate conditions together.
For its social media component, WeeHive provides a location based world map for all the beekeepers to share their experiences, photos, and best practices. Both novices and experts, from different parts of the world, can connect, share, and learn from one another. To foster deeper discussions, the platform also provides a interactive forum where beekeepers can discuss specific topics, ask questions, or seek advice, by posting texts, voices, data, photos and videos.
Eventually, we aim to go beyond the bee industry, trigger a broader, smarter agriculture community with a new generation of farmer groups backed by technologies and survival wisdom in a new climate era — This small change will bring new life into a timeless and essential industry.
Functional Design
- Interactive Main Page
The Plaza page provide an combined entry point for both the Map page and the Plaza page. On top of the page it is an abstracted world map showing the current trends of interaction. By swiping left or up, the map will zoom out to the user’s current location. By swiping right or down, the interface will transform to the blogs page.
The WeeField: Location Driven Interactive Map (Core Function)
- Priority 0 Functions
- Navigation: Users can navigate the map using gestures to zoom in/out and pan.
- Surrounding: Nearby users can identify each other, but not precise position if not partner relationship
- User Markers: Display markers represent beekeepers and their hives’ location.
- User Card: When clicked the user marker, pop up a user card with their info and recent posts
- Follow, Comment and Like: users can use the user card or the announcement card as a entry point to follow the user, like or common beneath the post
- Layer: users can enable the Flower Zone layer to ****see the distribution of the flower spots, like a heat map
- Optional Functions
- Flower Calendar: Select a location, display the flower calendar of the month and zoom out for the year
- Notifications and Alerts: Display notifications and alerts as markers when identified flower zone or extreme weather condition
- Yield Estimation: Based on related (psudo for this project) data
- Check-in: Once arrived at a new place, the users will be able to create a checkpoint post show off their achievement
- Route Calculation: Using Mapbox Navigation API, users can get a route from their current location towards the dedicated location
- Priority 0 Functions
The WeePlaza: Data-Driven Online Forum
- Blog: post informative articles to the platform
- Q & A: ask and answer question
- Expert Inquiry(Optional) : ask questions to “expert” identity users
- Hive Analytics (Optional): post the question with the hive data
User page
- Register, login, logout, edit profile, delete account
- Semi-anonymous identity: New users can join the platform with a nickname of their choice, without revealing their real name.
- Honor and Rewards: active users will be award prices that can show with others
- (Optional) Hive management: register, update or delete hives under one’s account
- (Optional) Identity verification: Normal users can submit necessary documents or undergo a process to verify their identity; once approved, they unlock corresponding identity, such as “beekeeper”, “expert”, to distinguish them from normal users.
Accessibility Features
- Fieldwork Mode: Optimized for field work environment, big font and icons, big buttons, simplified layout and high contrast color theme
- Senior mode: Similar to filedwork mode, but without high contrast theme
- (Optional) Dark mode
Optional Feature List:
Here are some optional features that may exceed the time constrain. But want to share it here for the integrity of the app design.
The WeeWiki: Interactive Beekeeping Knowledge Base
- Interactive contents: Display text, 3D animation and videos to show beekeeping techniques
- Bookmark, Comment, Share: User can mark a page as their needed page, comment below a page to discuss the content, and share the page to other social media
WeeChat: Direct Messaging Page
- Two registered users can send direct message in a separate page
- Send rich text messages, including sound, video and images, and have sensor data
Theme: users can define a theme color for their UI
Voice Commands: users can control the app via voice commands, considering the limitation of web app, may be difficult to achieve
Concepts
User
Purpose: To provide an identity for user account and information
Principle: Stores credential and username
States:
registered: set User
username, password: registered -> one String
registered: set User
username, password: registered -> one String
Actions:
register (n, p: String, out u: User)
authenticate (n, p: String, out u: User)
register (n, p: String, out u: User)
authenticate (n, p: String, out u: User)
Profile [user]
Purpose: To store the user’s personal information
Principle: Each user has their initial profile when they registered. The profile can be updated. And will be deleted after user delete their account.
State:
nickname: one String
email: one String
dateJoined: one int (timestamp)
headshotUrl: one String
identity: one String
role: one int
region: set Float
liked: set Item (Post/Reply)
favorite: set Item (Post/Reply)
nickname: one String
email: one String
dateJoined: one int (timestamp)
headshotUrl: one String
identity: one String
role: one int
region: set Float
liked: set Item (Post/Reply)
favorite: set Item (Post/Reply)
Actions:
getProfile(u: User, out: p: set Profile)
p = u.profile // ps is the returned profile
updateProfile(u: User, c: String Catagory, i: Item)
u.c = i // assign the new profile line to replace the existing line
deleteProfile(u: User) // remove the profile from the database
getProfile(u: User, out: p: set Profile)
p = u.profile // ps is the returned profile
updateProfile(u: User, c: String Catagory, i: Item)
u.c = i // assign the new profile line to replace the existing line
deleteProfile(u: User) // remove the profile from the database
Session [user]
Purpose: Manage and maintain the state of a user in the system over t period.
Principle: A session begins when a user login. Session maintains state s for time t the session will expire after time t. after a session starts (and before it ends), the getUser action returns the user identified at the start: start (u, s); getUser (s, u')
State:
active: set Session
user: active -> one User
active: set Session
user: active -> one User
Actions:
start (u: User, out s: Session)
getUser (s: Session, out u: User)
end (s: Session)
start (u: User, out s: Session)
getUser (s: Session, out u: User)
end (s: Session)
Relationship [User]
Purpose: To define and manage the connection states between users
Principle: The relationship has directional states. It will take a set of User object and record their relationship states.
State:
Rel : Relationship -> set String // define the relationship as a set
u1, u2: Rel -> set User // assign relationship between u1 and u2, the u1 is the origin, u2 is the target. u1 -> u2 forms a directional relationship
relationship: Rel -> set String // the type of relationship between two users can be follower, friend to partner, to activate different functions.
Rel : Relationship -> set String // define the relationship as a set
u1, u2: Rel -> set User // assign relationship between u1 and u2, the u1 is the origin, u2 is the target. u1 -> u2 forms a directional relationship
relationship: Rel -> set String // the type of relationship between two users can be follower, friend to partner, to activate different functions.
Actions:
updateRelationship (u1: User, u2: User, isPartner)
Rel += [u1, u2, isPartner]
if isPartner == true
[u2, u1, isPartner]
getRelationship (u1, u2: User, out: set Relationship)
if not found [u1, u2], // if the relationship doesn't exists, the two users are stranger.
r = "stranger”
if [u1, u2] ! [u2, u1] // one direction relationship is follow
r = "follower"
if [u1, u2] and [u2, u1] // two direction follow is friend
r = "friend"
if [u1, u2, isPartner] // both accepted to become partner
r = "partner"
updateRelationship (u1: User, u2: User, isPartner)
Rel += [u1, u2, isPartner]
if isPartner == true
[u2, u1, isPartner]
getRelationship (u1, u2: User, out: set Relationship)
if not found [u1, u2], // if the relationship doesn't exists, the two users are stranger.
r = "stranger”
if [u1, u2] ! [u2, u1] // one direction relationship is follow
r = "follower"
if [u1, u2] and [u2, u1] // two direction follow is friend
r = "friend"
if [u1, u2, isPartner] // both accepted to become partner
r = "partner"
Map
Purpose: To provide a visual representation of geographic data and allow users to interact with it. It also serves as a base to display location based informations, such as markers.
Principle: On mobile platforms, users can use touch gestures to interact with maps. On desktop, they can use mouse. Map have multiple layers and can change themes (provided by api). Map can display markers on its geographical locations.
State:
centerPoint: set Float ((set [lng, lat]))
zoomLevel: Float
layers: set String (Collections of data visualized on the map (e.g., flower field))
markers: Symbols or icons indicating points of interest or events.
theme: Visual style (e.g., satellite, terrain, nighttime).
centerPoint: set Float ((set [lng, lat]))
zoomLevel: Float
layers: set String (Collections of data visualized on the map (e.g., flower field))
markers: Symbols or icons indicating points of interest or events.
theme: Visual style (e.g., satellite, terrain, nighttime).
Actions:
move(m:Map, c: centerPoint, movement): // Move the view center point according to the movement.
centerPoint += movement
zoom(z: zoomLevel): // adjust the level of detail visible by changing the zoomLevel
z = zoomLevel
findVisibleMarkers(c: centerPoint, z: zoomLevel, out set Markers) // find and filter the markers to be displayed on screen according to current location and zoom level. The search radius is calculated from the zoomLevel.
addMarker(m: Map, l: Location): // Place an icon or symbol at a particular location.
m + marker(u) at u.location
removeMarker(m: Map, l: Location): // Delete a specific marker from the map.
m - marker
toggleLayer(m: Map, layer): // Show or hide specific layers of data on the map.
updateTheme(m: Map, l: Location): // Alter the visual style or appearance.
move(m:Map, c: centerPoint, movement): // Move the view center point according to the movement.
centerPoint += movement
zoom(z: zoomLevel): // adjust the level of detail visible by changing the zoomLevel
z = zoomLevel
findVisibleMarkers(c: centerPoint, z: zoomLevel, out set Markers) // find and filter the markers to be displayed on screen according to current location and zoom level. The search radius is calculated from the zoomLevel.
addMarker(m: Map, l: Location): // Place an icon or symbol at a particular location.
m + marker(u) at u.location
removeMarker(m: Map, l: Location): // Delete a specific marker from the map.
m - marker
toggleLayer(m: Map, layer): // Show or hide specific layers of data on the map.
updateTheme(m: Map, l: Location): // Alter the visual style or appearance.
Location[User]
Purpose: To represent a user's current or last avaliable position for various functions such as finding nearby users or places.
Principle: location has two states, precise location and salted location. The salted location is a processed coordination that will return a vague location of the user.
State:
position: set Float ([lng, lat])
isSalted: one Boolean
visibility: Boolean (whether the user's location is visible to others)
position: set Float ([lng, lat])
isSalted: one Boolean
visibility: Boolean (whether the user's location is visible to others)
Actions:
updateLocation(u: User, newLocation: set Float): // Update the user's current position.
u.position = newLocation
toggleVisibility(u: User): // Change the visibility of user's location.
u.visibility = !u.visibility
getUsersCurrentLocation(u: User): // Fetch the current location of the user.
return u.position
saltLocation(u:User, location:Location, out salted:Location)
return salted = someEncryptMethods(location)
updateLocation(u: User, newLocation: set Float): // Update the user's current position.
u.position = newLocation
toggleVisibility(u: User): // Change the visibility of user's location.
u.visibility = !u.visibility
getUsersCurrentLocation(u: User): // Fetch the current location of the user.
return u.position
saltLocation(u:User, location:Location, out salted:Location)
return salted = someEncryptMethods(location)
Nearby[Location]
Purpose: To determine the proximity of users or places relative to a given location.
Principle: Calculate the distances between users or items on the map, and return a set of items that matches the desired radius.
State:
currentLocation: Location
isNearby: one Boolean
nearbyUsers: set User
currentLocation: Location
isNearby: one Boolean
nearbyUsers: set User
Actions:
isNearby(u1:User, u2:User, radius)
if distance(u1.location, u2.location) <= radius
return true
getNearbyUsers(currentLocation, radius: one int, out: u: set User)
if (isNearby)
u += user
isNearby(u1:User, u2:User, radius)
if distance(u1.location, u2.location) <= radius
return true
getNearbyUsers(currentLocation, radius: one int, out: u: set User)
if (isNearby)
u += user
Marker[Map]
Purpose: To display specific points of interest, events, or user locations on the map.
Principle: The Marker can be interacted and expanded into a larger information card. The Marker has different types, now including User, and POI points of interest. If the density of the marker becomes too large, they can be clustered into one marker, which will show a list by activation.
State:
location: set Float
type: one String (User/POI)
cluster: set User
location: set Float
type: one String (User/POI)
cluster: set User
Actions:
updateMarker(m: Map, l: Location, u: User, p: Post): // Refresh the marker information
fetch the latest user profile, posts and location
assign to the marker objects
updateMarker(m: Map, l: Location, u: User, p: Post): // Refresh the marker information
fetch the latest user profile, posts and location
assign to the marker objects
Favorite[User, Item]
Purpose: Save articles and answers for later review. It will also boost the mass voting system.
Principle: The user can click favorite from a list, and jump to the corresponding article. They can also delete their favorite item.
State:
item: one String
user: set String
favoriteCount: int
favoriteList: set String
item: one String
user: set String
favoriteCount: int
favoriteList: set String
Actions:
addFavorite(user:User, item:Item)
user.favoriteList += item
removeFavorite(user:User, item:Item)
user.favoriteList -= item
getFavorites(user:User, out s: set)
user.favoriteList
addFavorite(user:User, item:Item)
user.favoriteList += item
removeFavorite(user:User, item:Item)
user.favoriteList -= item
getFavorites(user:User, out s: set)
user.favoriteList
Like[User, Item]
Purpose: A mass-voting mechanism to identify good quality contents from the community
Principle: User can upvote by clicking a button next to a post/answer. Good quality contents will raise to the top of the answer list
State:
item: one String
user: set String
likeCount: one int
item: one String
user: set String
likeCount: one int
Actions:
addLike (user: User, item: Item)
item.likeCount+1
removeLike (user: User, item: Item)
item.likeCount-1
getLikeCount (item: Item, out: c int) // return the items' like Count
addLike (user: User, item: Item)
item.likeCount+1
removeLike (user: User, item: Item)
item.likeCount-1
getLikeCount (item: Item, out: c int) // return the items' like Count
Tag[User, Item]
Purpose: a general parameter to aid classification of posts, replies and users
Principle: the app will provide some necessary tags, like comment, article, to make the “post” and “reply” functioning. a tag can be assigned to multiple items. An item can have multiple tags. Required tags can’t be deleted, because it will be necessary for classification purpose. Normal tags can be deleted.
State:
name: Tag -> one String
owns: User -> set Tag
tags: Item -> set Tag
required: Tag -> one Boolean
name: Tag -> one String
owns: User -> set Tag
tags: Item -> set Tag
required: Tag -> one Boolean
Actions:
createTag (s: String, u: User, out p: Tag)
no u: User | p in u.owns // p is fresh, not owned by existing user; or "no p.~owns" or "p.~owns = {}"
p.name := s; // give p the name s
u.owns += p // add p to Tags owned by u
getTags(i: Item, out ps: set Tag)
ps := i.owns // ps is Tags that i owns
assignTag (i: Item, p: Tag)
i.Tags += p // add p to Tags for i
deleteTag (p: Tag)
// remove p from all relations
p.name = none
p.~owns := none // ~owns is owns backwards
p.~tags := none
createTag (s: String, u: User, out p: Tag)
no u: User | p in u.owns // p is fresh, not owned by existing user; or "no p.~owns" or "p.~owns = {}"
p.name := s; // give p the name s
u.owns += p // add p to Tags owned by u
getTags(i: Item, out ps: set Tag)
ps := i.owns // ps is Tags that i owns
assignTag (i: Item, p: Tag)
i.Tags += p // add p to Tags for i
deleteTag (p: Tag)
// remove p from all relations
p.name = none
p.~owns := none // ~owns is owns backwards
p.~tags := none
Post [User]
Purpose: Post is the core concept of this app. It represents a general rich text message that applicable to use as article, question and knowledge(for optional function wiki)
Principle: Posts are created using editing tools in each functions. Posts can be edited or delete by the user, but can’t be edit by other user. Posts have visibility options. By default, it is set to public, meaning all users can view. It can also be set as private, friends-only or partner-only.
State:
name: one String
time: one int (timestamp)
title: one String
content: one String
tags: set String
visibility: one String // public, private, friendsOnly, partnersOnly
type: one String // article, question
likeCount: one int // do we need to store all the liked users?
favoriteCount: one int
likedBy: set User
favoritedBy: set User
name: one String
time: one int (timestamp)
title: one String
content: one String
tags: set String
visibility: one String // public, private, friendsOnly, partnersOnly
type: one String // article, question
likeCount: one int // do we need to store all the liked users?
favoriteCount: one int
likedBy: set User
favoritedBy: set User
Actions:
createPost(user: User, type: String, content: String, out p:Post)
test authentification status
p.content := content
p.author := User
updatePost (user:User, post: Post, type: String, content: String)
test user matches author
post.content = content
deletePost (user:User, post: Post)
test user matches author, then delete post
getPost (post:Post, time:Time, type, tag, out: set Post) //all inputs are optional. If entered, it will return the post with corresponding name, timestamp or tag
getReplies (post:Post, out comment: set String)
createPost(user: User, type: String, content: String, out p:Post)
test authentification status
p.content := content
p.author := User
updatePost (user:User, post: Post, type: String, content: String)
test user matches author
post.content = content
deletePost (user:User, post: Post)
test user matches author, then delete post
getPost (post:Post, time:Time, type, tag, out: set Post) //all inputs are optional. If entered, it will return the post with corresponding name, timestamp or tag
getReplies (post:Post, out comment: set String)
Reply [User, Post]
Purpose: Reply contains two user cases: comment on a post, or answer on a question. It depends on a post to exist.
Principle: Replies can only be created after a post. It can’t independently exist. Only registered user can reply a post. Reply uses private parameter to identify types. Like Post, Reply can also be favorited or liked by users.
State:
name: one String
time: one int (timestamp)
title: one String
content: one String
tag: set String
linkedPost: one String
visibility: one String // public, private, friendsOnly, partnersOnly
type: one String // article, question
likeCount: one int // do we need to store all the liked users?
favoriteCount: one int
likedBy: set User
favoritedBy: set User
name: one String
time: one int (timestamp)
title: one String
content: one String
tag: set String
linkedPost: one String
visibility: one String // public, private, friendsOnly, partnersOnly
type: one String // article, question
likeCount: one int // do we need to store all the liked users?
favoriteCount: one int
likedBy: set User
favoritedBy: set User
Actions:
createReply(user: User, type: String, content: String, out r:Reply)
test authentification status
r.content := content
r.author := User
updateReply(user:User, reply: Reply, type: String, content: String)
test user matches author
reply.content = content
deleteReply (user:User, reply: Reply)
test user matches author, then delete post
getReply (post:Post, filter, out: set Reply)
createReply(user: User, type: String, content: String, out r:Reply)
test authentification status
r.content := content
r.author := User
updateReply(user:User, reply: Reply, type: String, content: String)
test user matches author
reply.content = content
deleteReply (user:User, reply: Reply)
test user matches author, then delete post
getReply (post:Post, filter, out: set Reply)
Concept Synchronization
ExpiringUserSession (from lecture note)
Description: a session that synchronize with user to allow expiration after login for a t period
include User
include Session [User.User]
include ExpiringResource [Session.Session]
sync register (username, password: String, out user: User)
User.register (username, password, user)
sync login (username, password: String, out user: User, out s: Session)
when User.authenticate (username, password, user)
Session.start (user, session)
ExpiringResource.allocate (session, 300)
sync logout (s: Session)
when Session.end (session)
ExpiringResource.deallocate (session)
sync authenticate (s: Session, u: User)
Session.getUser (s, u)
sync terminate (s: Session)
when ExpiringResource.expire (session)
Session.end (session)
include User
include Session [User.User]
include ExpiringResource [Session.Session]
sync register (username, password: String, out user: User)
User.register (username, password, user)
sync login (username, password: String, out user: User, out s: Session)
when User.authenticate (username, password, user)
Session.start (user, session)
ExpiringResource.allocate (session, 300)
sync logout (s: Session)
when Session.end (session)
ExpiringResource.deallocate (session)
sync authenticate (s: Session, u: User)
Session.getUser (s, u)
sync terminate (s: Session)
when ExpiringResource.expire (session)
Session.end (session)
Live Posts Update in Marker
Description: when the and user post updates, the recent posts displayed on the map marker also updates
include User
include Map
include Marker
include Posts [User]
sync updatePost(user: User, post: Posts, content: String)
when Posts.update(post, content)
Marker.updateAttachedPost(marker, post)
sync deletePost(user: User, post: Posts)
when Posts.delete(post, content)
Posts.remove(post)
Marker.updateAttachedPost(marker, post)
include User
include Map
include Marker
include Posts [User]
sync updatePost(user: User, post: Posts, content: String)
when Posts.update(post, content)
Marker.updateAttachedPost(marker, post)
sync deletePost(user: User, post: Posts)
when Posts.delete(post, content)
Posts.remove(post)
Marker.updateAttachedPost(marker, post)
User Location Sync with Marker
Description: load the user's posts and display on the map according to users' location
include User
include Map
include Marker
include Posts [User]
sync location(user: User, loc: CurrentLocation)
when (loc.update())
Marker.location = CurrentLocation
****
include User
include Map
include Marker
include Posts [User]
sync location(user: User, loc: CurrentLocation)
when (loc.update())
Marker.location = CurrentLocation
****
Posts Visibility
Description: Interlinks the relationship status between users with the accessibility of posts. Strangers can view post with restrictive accessibility. only after they become friends.
include User
include Post
include Relationship
sync visibility
when user.relationship is "friend" or "partner"
if posts.restricted == true
postDisplay += post
when user.relationship is "stranger", disallow users to view restricted posts
if posts.restricted == true
postDisplay -= post
include User
include Post
include Relationship
sync visibility
when user.relationship is "friend" or "partner"
if posts.restricted == true
postDisplay += post
when user.relationship is "stranger", disallow users to view restricted posts
if posts.restricted == true
postDisplay -= post
Concept Dependency Diagram Iterations
Original Diagram From A2
Design Iterations 1
This time, I have re-framed the concept dependency map. First I removed the boundary concepts like market and peer-review. And I reconstructed the “post” concept to make it a reusable one that suitable for contents across different functions. All the posts are rich text contents with image and videos. I added a “Type” concept to identify the content types. For example, the informative article is a post with “article” type, that identify it as not answerable content, while the “question” has “question” type, making it can be displayed as a question that requires answers.
Iteration 2
I was trying to clarify the “post” concept. I realized the behavior of post and reply are different. So it’s better to split them into two separate concepts. Meanwhile, the sub-division of concepts, like “article”, “post”, “question”, “answer”, can be achieved by tags.
Iteration 3 (Current)
I’ve been referencing a lot with the examples and TA’s suggestions. Eventually I decided to focus on core functions: location driven community. I fixed the wrong relationships and tried to structure everything with their dependencies. The outcomes seems much simpler than before, but this is to generalize the concepts and reuse the concepts with the same behaviors.
Wireframes Overview
(also attached separated views with function design)
Please also check out the Figma link~
Design Trade Offs
1. Defining "Post" Concept
Concerns: The “post” concept is one of the most common concepts in social media apps. In my app, I have multiple functions that requires user to publish something online. However, if I create a concept for each post-like feature, it will be redundancy and not necessary. I was finding a balance between modularity and maintainability.
Alternative Options:
Separate all the post-related concepts by their own feature Pros: Will be easier to modify if the need changes. Cons: Redundancy, hard to keep everything updated, complex database structure.
Merge all the post-related concepts into one general “Post” concept, and use tag to classify Pros: easy to maintain
Cons: not easy to tackle different use cases
Reduce the functionality of the app by offering general type post and comment feature.
Pros: easy to implement
Cons: limited functionality for users’ needs
Decision outcome:
Eventually, I separated the post concept into “Post” and “Reply”. The main reason is reply behaves differently from post. Reply relies on a post to exist. Then I’ll use tags to classify different kinds of contents and apply to different functions.
2. The level of user anonymity and security
Concerns: The app should establish a line between online relationship and potential offline relationship. How to prevent misuse and trolling by designing a proper user identity structure? How to make the user feel free to speech and express their true feeling?
Alternative Options:
Complete Anonymity
Pros: Users can share and communicate without fear of personal identification, which can encourage open discussions.
Cons: Complete anonymity can lead to misuse, including trolling, harassment, or the spread of misinformation. Besides, it may be difficult for users to establish long term trust.
Complete Real Name
Pros: Users might trust the platform more when they know they're interacting with real people, thus will facilitates offline interactions, which could be useful in farming scenarios.
Cons: Users might be uncomfortable sharing their real names due to privacy concerns. There can be identity theft or malicious uses. Users might self-censor from sharing their genuine opinions.
Decision outcome:
Eventually, I come with a 3-tier structure for peer relationship, the relationship will affect the content visibility.
“Stranger”
- Nicknames
- Vague location (city or region level)
- Sending 3 direct messages before the other respond
- First 10 posts visible by others by default
- Comment or like public posts
- Visibility to public profile details
“Follower”
- Nicknames
- Vague location (city or region level)
- Sending 3 direct messages before the other respond
- See all the public posts
- Comment or like public posts
- Visibility to public profile details
“Friend”
- Nicknames
- Vague location
- Sending unlimited direct messages between friends
- Follow one’s posts, comment or like public posts
- Visibility to more profile details (configurable)
“Partner”
- Real names (forceful, but not visible until request)
- Precise location (with user's consent)
- Unlimited direct messages with priority notification
- Follow one’s posts
- Exclusive access to posts with “partner” level visibility (like “close friends” in instagram)
- Collaborative features (e.g., shared calendars, to-do lists, or projects)
- Option to share personal contact details (like phone number or email, based on user's discretion)
- Special badges indicating the 'Partner' status for easier identification
3. Reduce the complexity of of user relationship concepts
**Concerns:**As mentioned above, I’m trying to define three states of user relationships, “stranger”, “friend” and “partner”. But I was doubting how to implement it in concept design.
Alternative Options:
- Make “friend”, “following”, “partner” a separated functions
- Separate relational and non-relational relationships like “following” and “friend”
- drop “partner” relationship
Design Outcome
I simplified it into a directional “relationship” concept. If user1 connects to user2, then user1 “follows” user2. If then user2 connects to user1, then they become “friend”. If both of the user agrees on becoming a “partner”, they are allowed to share more sensitive information.
4. Direct Message and Group Message Functions
Concerns: I was trying to generalize the whole “relationship” related concepts into one, but eventually I found “group” is a separate concept.
One user can belong to multiple groups, but using “relationship” can’t achieve it, which means the “relationship” behaves as directional, one to one bond, and is different from how group works. So it is better to divide the group concept with the relationship concept.
I also noticed this concept is highly dependent on the “Direct Message” concept. I’ll consider it as a optional concept to implement, since it requires a new set of workload to achieve this function. From the purpose of the app, the goal is to promote cross region social interactions driven by location.
Design Outcome:
This exercise make me realized that in this project, most of the interactions will happen on public realm. The direct and group message functions, although important, is a tier-2 functions in this case. I want to make the group and direct message functions as the optional ones.
5. The Choice of Adding the Market Function
Concerns:
- Time limit: Complex workflow that not feasible in time constrain
- Security and trust issue: a function involves financial transactions requires a high level of security.
Alternative solutions
- Consider market expansion in the future when designing concepts
- Redirect the user to professional trading provider
- Serve as a bridge, not a merchant
Decision outcome
Eventually, I decided to drop the market function for this prototype. Because I prefer to focus on the location based community functions, which is unique among competitive products.
6. Customization
Concerns:
There will be numerous parameters that can be customized in this app, what will be the ones to be customized by the user?
Under what design principle that we select the customization parameters
Alternative solution:
- Automatically decide based on time, age and scenario
- Offering limited control for the user, let the developer decide which is the best, to keep it simple
- Make the default interface suitable for most people, and give number a series of customization options.
Decision outcome:
- Using themes and display modes to pack parameter changes
- The first three modes are relatively easier, and can be achieved with the same method of dark mode and high contrast mode.
- dark/light mode
- high contrast mode
- Visibility mode
- This mode requires more work, because it is a whole set of design decisions. We need to hide complex features, tweak the layouts due to font/text size changes.
- The first three modes are relatively easier, and can be achieved with the same method of dark mode and high contrast mode.