Project 3: Convergent Design
@Shaw Chen @Ethan @Maggie @Yinghou Wang
Functional Design
Concepts:
Concept: Preference [ T ]
- Purpose: customize app experience and/or interface
- Operating Principle: after an interval t, update a set of features f based on a set of responses r
- State
- Owner: One User
- interval: One Time
- expiry: One Date
- features: Set String
- preference: features → String
- Actions
- createPreference(owner: User, feature: String, preference: String, out: Tuple String)
- updatePreference(owner: User, feature: String, preference: String, out: Tuple String)
- changeInterval( i: Time, out*: Time*)
- isIntervalOver(i: Time, out: Boolean)
- updateExpiry(e: Date, i: Time, out: Date)
Concept: Delay [ T ]
- Purpose: perform operation on content after a specified amount of time
- Operating Principle: after a specified time t, execute a function f on a piece of content of type T
- State
- content: Set T
- behavior: content → One function
- activation: content → One Date
- Actions
- createDelay (content: set T, behavior: function, activation: Date, out d: Delay )
- deleteDelay (content: T)
- checkDelaybyDate (activation: Date, out d: Set Delay)
- checkDelaybyContent (content: T, out d: Delay)
- activateDelay(d: Delay)
- TURNED THIS INTO SYNC BC OF TYPES, IN REAL PRACTICE CAN MAKE THE DELETE + REVEAL/SEND MORE CONSISTENT TO AVOID TYPECHECKING
Concept: Letter[ T ] (Initially as Message, but we found Letter is more appropriate)
- **Purpose: **share content with others
- Operating Principle: if e is enabled*,** then a piece of content c of type T will be shared with a set of recipients r*
- State
- from: One User
- to: Set Contact
- content: One T
- responseEnabled: One Boolean
- Actions
- createLetter (from: User, to: Set User, content: T, responseEnabled: Bool, out m: Message)
- getLetterBySender (sender: User, out m: Set Message)
- getLetterByContent (content: T, out m: Message)
- updateLetterContent (m: Message, content: T)
- updateLetterResponse (m: Message, enable: Bool)
- sendLetter(m: Message)
- deleteLetter (m: Message)
- removeLetterReceiver (m: Message, receiver: User)
- addLetterReceiver (m: Message, receiver: User)
Concept: Diary[ T ]
- purpose: private content
- operating principle: create a piece of content of type T that will not be shared until requested
- State
- entries: Set Diary
- author: entires → One User
- content: entries → One T
- revealed: entries → One Boolean (default=False)
- (diary or post : post / diary)
- Actions Ethan
- createEntry(author: User, content: T, out: Diary)
- modifyEntry( d: Diary, out: Diary)
- deleteEntry( d: Diary, out: Void)
- getEntries( author: User, out: Set Diary)
- revealEntry( d: Diary, out: Void)
- hideEntry( d: Diary, out: Void)
- isRevealed( d: Diary, out: Boolean)
Concept: Wish[ T ]
- purpose dynamic and desirable tasks to be completed
- operating principle create a wish w with a description d of type T, whose status s can be changed to fulfilled once completed
- State
- Wishes: Set T
- Owner: wishes → One User
- description: wishes → One T
- fulfilled: wishes → One Boolean
- viewer: wish → Set User |
- Actions
- createWish (owner: User, description: T) → w: Wish
- updateWishDescription (w: Wish, newDescription: T)
- deleteWish (w: Wish)
- fulfillWish (w: Wish)
- isFulfilled (w: Wish) → status: Boolean
- getUserWishes (owner: User) → wisher: Set Wish
Concept: Forum[ T ]
- purpose an environment to encourage discussion and collaboration
- operating principle based on a group of participants r, create a forum f where every participant in r can write and read about topic t
- State
- topics: Set String
- creator: topics → One User
- posts: topics → Set T
- participants: topics → Set User
- Actions
- createTopic (creator: User) → One String
- addPost (topic: String , post: T) → Post
- getUserTopics (reader: User) → Set String
- getParticipants(topic: String) → Set User
- addParticipant (topic: String, participant: User)
- removeParticipant (topic: String, participant: User)
- viewAllPosts (topic: String) → Set T
- searchPostsByAuthor (topic: String, author: User) → Set Post
Concept: Mood [ T ]
- purpose track the User’s current mental condition
- operating principle users ********can set their mood m as a state of type T that they can update anytime.
- State
- moods: Set MoodDoc
- owner: moods → One User
- viewer: moods → Set User
- status: moods → One T
- notify: moods → One Boolean
- Actions
- createMood(owner: User, mood: T) → MoodDoc
- updateMood(owner: User, newMood: T) → MoodDoc
- getMoodByUser(owner: User) → MoodDoc
- deleteMood(owner: User) → Void
- getUserMood(owner: User) → T
- addViewer(owner: User, viewer: User) → Void
- removedViewer(owner: User, viewer: User) → Void
- setNotification(owner: User, notify: Boolean) → Void
- isNotifying(owner: User) → Boolean
Synchronizations
tsx// Message + Delay: Optionally add a Delay to a message sync createMsg (user: User, content: T, to: set User, responseEnabled: Bool, delayTime: Num = 0) msg = Message.createMessage(from: User, to: Set User, content: T, responseEnabled: Bool) if delayTime === 0: Message.sendMsg(msg) else Delay.createDelay (content: msg, behavior: *function, activation: Date)*
// Message + Delay: Optionally add a Delay to a message sync createMsg (user: User, content: T, to: set User, responseEnabled: Bool, delayTime: Num = 0) msg = Message.createMessage(from: User, to: Set User, content: T, responseEnabled: Bool) if delayTime === 0: Message.sendMsg(msg) else Delay.createDelay (content: msg, behavior: *function, activation: Date)*
tsx//Wish + Message: Wishes will be sent to family members via messages. sync createWish (owner: User, description: T, contact: User) when Wish.createWish (owner: User, description: T) Message.createMessage (from: owner, to: contact, content: T, responseEnabled: false)
//Wish + Message: Wishes will be sent to family members via messages. sync createWish (owner: User, description: T, contact: User) when Wish.createWish (owner: User, description: T) Message.createMessage (from: owner, to: contact, content: T, responseEnabled: false)
tsx// Message + Forum + Delay: send a message to a forum (and optionally add delay) sync createForumPost (author: User, topic: String, content: T, delayTime: Num = 0) //if send immediate post recipients = Forum.getParticipants(topic) msg = Message.createMessage( from: author, to: recipients, content: post, responseEnabled: true, if delayTime === 0: Forum.addPost(topic: String , post: msg) else: Delay.createDelay(content: msg, behavior: *function, activation: Date*)
// Message + Forum + Delay: send a message to a forum (and optionally add delay) sync createForumPost (author: User, topic: String, content: T, delayTime: Num = 0) //if send immediate post recipients = Forum.getParticipants(topic) msg = Message.createMessage( from: author, to: recipients, content: post, responseEnabled: true, if delayTime === 0: Forum.addPost(topic: String , post: msg) else: Delay.createDelay(content: msg, behavior: *function, activation: Date*)
- Diary → Forum post: Diary entries will be posted on the forum when/if users decide to make them public.
tsx//Diary + Forum post: Diary entries will be posted on the forum when/if users decide to make them public. sync createDiary (author: ********User,******** content: ********T, public:bool, topic: String********) when Diary.createEntry(author: ********User,******** content: ********T,******** out: ********Diary********) if(public === true and topic !== undefined): Forum.addPost(topic: String , post: T)
//Diary + Forum post: Diary entries will be posted on the forum when/if users decide to make them public. sync createDiary (author: ********User,******** content: ********T, public:bool, topic: String********) when Diary.createEntry(author: ********User,******** content: ********T,******** out: ********Diary********) if(public === true and topic !== undefined): Forum.addPost(topic: String , post: T)
tsx// can likely clean this up, but based on current implementations of // our concepts, this is a practical way to do it // Delay + Message/Diary: logic to execute a delay sync activateDelay () currentDelays = Delay.checkDelaybyDate(Date.now()) for delay in currentDelays content = delay.content if(content instanceof DiaryDoc) if (delay.behavior == "show") Diary.revealEntry(content) else if (delay.behavior == "delete") Diary.deleteEntry(content) else if (content instanceof MessageDoc) if (delay.behavior == "show") Message.sendMsg(content) else if (delay.behavior == "delete") Message.deleteMsg(content)
// can likely clean this up, but based on current implementations of // our concepts, this is a practical way to do it // Delay + Message/Diary: logic to execute a delay sync activateDelay () currentDelays = Delay.checkDelaybyDate(Date.now()) for delay in currentDelays content = delay.content if(content instanceof DiaryDoc) if (delay.behavior == "show") Diary.revealEntry(content) else if (delay.behavior == "delete") Diary.deleteEntry(content) else if (content instanceof MessageDoc) if (delay.behavior == "show") Message.sendMsg(content) else if (delay.behavior == "delete") Message.deleteMsg(content)
- TimeCapsule: Delay + Diary
- Release “selected” (content w/ specific delay i.e “-1”) content
- Delete “unselected” (content w/ specific delay i.e “-1”) content
tsx// need to figure out an external time to call this function // Diary + Delay: TimeCapsule implementation sync releaseTimeCapsule (owner: User) activationFlag = new Date(0) // indicator that this delay is associated w/ timeCapsule // iterate over diary entries to reveal/delete for d in Diary.getEntries(owner) delay = Delay.checkDelayByContent(d) if (delay.activation === activationFlag) if (delay.behavior === "show") Diary.revealEntry(d) else if (delay.behavior === "delete") Diary.deleteEntry(d)
// need to figure out an external time to call this function // Diary + Delay: TimeCapsule implementation sync releaseTimeCapsule (owner: User) activationFlag = new Date(0) // indicator that this delay is associated w/ timeCapsule // iterate over diary entries to reveal/delete for d in Diary.getEntries(owner) delay = Delay.checkDelayByContent(d) if (delay.activation === activationFlag) if (delay.behavior === "show") Diary.revealEntry(d) else if (delay.behavior === "delete") Diary.deleteEntry(d)
- Mood → Message: If patients decide to set up notifications on their mood status, a message will be sent out to family members with the content as the mood.
tsx// Message + Mood: share status updates of Mood w/ others sync sendMood(owner: User) mood = Mood.getMoodByUser(owner) msg = Message.createMessage(owner, content: mood.status, to: mood.viewers, responseEnabled: false) Message.sendMsg(msg)
// Message + Mood: share status updates of Mood w/ others sync sendMood(owner: User) mood = Mood.getMoodByUser(owner) msg = Message.createMessage(owner, content: mood.status, to: mood.viewers, responseEnabled: false) Message.sendMsg(msg)
Dependency Diagram
Wireframes
User Flows
Heuristic Evaluation
Usability Criteria
- Learnability: The 'Preferences' concept supports this heuristic as it allows users to customize the app according to their needs, making it easier to understand and operate. However, the 'Delay' concept might be a bit challenging for some users to understand initially. A tutorial or guide might be helpful to improve learnability.
- Security: The 'Message' concept supports this heuristic by allowing users to enable or disable responses, giving them control over their interactions. The 'Mood' and ‘Diary’ concepts also ensures safety by providing options for users to control who can see their mood or content.
- Accessibility: The 'Preferences' concept can support this heuristic by allowing customization options that cater to users with disabilities. This concept could accommodate users with visual impairments could adjust text size or contrast settings, while those with motor issues could opt for voice commands or simplified navigation. Overall the app will ensure that elements are accessible and easy to use, beneficial for users with motor and/or mental disabilities or hinderances.
Physical Heuristics:
- Gestalt principles: The app conveys conceptual structure by placing the Preferences first, before entering the rest of the app. The app also has a menu navigation bar that categorizes each concept to a different page, giving the app structure throughout the concepts.
- Situational context: The app shows situational context by separating out the patient view and the family member view. The app also shows the situational concept through the mood concept by setting the notifications, but could further emphasize this by setting a visual display of a patient’s mood, such as showing a colored ring around their profile picture that corresponds to their mood.
Linguistic level:
- Speak a user’s language: The 'Preferences' concept supports this heuristic by allowing users to customize the app in a way that aligns with their needs and cultural customs. In addition, the language used in the app will be intentional and sensitive, aiming to break down the stigma surrounding discussions about death. This can be seen in the 'Forum' concept where users can openly discuss and share experiences, helping to normalize the conversation around end-of-life scenarios.
- Consistency: The app will consistently use the same terms and symbols across different features, reducing confusion and enhancing user experience. For example, the 'Message' and 'Diary' concepts use similar actions (sharing and revealing content), promoting consistency.
Tradeoffs
- Learnability vs Efficiency:
- While the 'Preferences' concept enhances learnability by allowing users to customize the app, it might initially reduce efficiency as users spend time setting up their preferences. However, this investment of time upfront can lead to improved efficiency in the long run as the app becomes more tailored to the user's needs.
- Security vs Efficiency:
- The 'Message' concept allows users to enable or disable responses, enhancing safety. However, this might reduce efficiency as users have to take an extra step to change these settings. Similarly, the 'Mood' concept provides users with the ability to control visibility and notifications, enhancing safety but potentially reducing efficiency.
- Consistency vs Learnability:
- While the consistent use of terms and symbols across the 'Message' and 'Diary' concepts enhances learnability, it might initially reduce efficiency as users familiarize themselves with the terms and symbols used.
Visual Design Study
Color Study
Typography Study
Project Plan
Week 1 (Nov 23 - Nov 30): Alpha Release (End-to-End Implementation)
- Day 1: Project Kickoff & Planning
- Task: Finalize the app's two core concepts, create a detailed task breakdown, and set up the development environment and the git repo.
- Assigned To: Entire team collaboratively.
- Deadline: End of Day 1.
- Day 2-4: Backend Development
- Task: Implement the necessary backend functionality for all core concepts.
- Assigned To:
- @Maggie Session, User, Friend (default concepts can be immigrated from previous work)
- @Ethan Concept Preference, Concept Diary
- @Yinghou Wang Concept Delay, Concept Letter
- @Shaw Chen Concept Wish, Concept Forum
- @Maggie Concept Mood
- Deadline: End of Day 4.
- Day 4-7: Frontend Development (Preference & Diary)
- Task: Develop the basic frontend interfaces connecting to the backend.
- Assigned To:
- Basic Functionalities: @Yinghou Wang @Shaw Chen
- Mood: @Maggie
- Diary: @Ethan
- Deadline: End of Day 7.
Week 2 (Dec 1 - Dec 7): Beta Release (Core Functionality & Preliminary Styling)
- Day 8-10: Core Functionality Completion
- Task:
- Finalize all core functionalities and ensure backend-frontend integration.
- Nail down Style Sheets (color palette, font, etc.)
- Assigned To:
- Preference & Diary: @Ethan
- Time Capsule, Letter: @Yinghou Wang
- Wishlist, Forum: @Shaw Chen
- User, Mood, Tokens: @Maggie
- Deadline: End of Day 10.
- Task:
- Day 11-12: Preliminary Styling
- Task: Apply basic styling and user interface enhancements.
- Assigned To: Entire team collaboratively.
- Deadline: End of Day 12.
Week 3 (Dec 8 - Dec 13): User Testing, Refinement & Final Polish
- Day 13-15: User Testing
- Task: Conduct user tests to gather feedback.
- Assigned To: Each conduct one user test
- Deadline: End of Day 15.
- Day 16-18: Improvement Implementation
- Task: Address feedback and refine app functionality and design.
- Assigned To: All team members.
- Deadline: End of Day 18.
- Day 19-21: Final Polishing and Submission Preparation
- Task: Finalize the app, ensure all aspects are polished, and prepare for submission.
- Assigned To: All team members.
- Deadline: End of Day 21.
Contingency Plan
In case of unforeseen issues:
- Prioritize Core Functionalities: Focus on ensuring the core functionalities are robust. Secondary features can be scaled back or documented as future enhancements.
- Resource Reallocation: If a team member falls behind, reallocate tasks among team members to balance the workload.
- Scope Adjustment: If necessary, reduce the complexity of some features to meet deadlines while maintaining app quality.
- Regular Check-ins: Increase the frequency of team meetings to address and adapt to any challenges quickly.
Note: This is a flexible plan and can be adapted based on the team's pace and any unforeseen challenges.