Skip to content

Commit

Permalink
Allow users to share the symptom history as text (#575)
Browse files Browse the repository at this point in the history
* Allow users to share the symptom history as text

Why:
----
When users add their symptoms to the log, there might be a case where
that log would be valuable to send to someone of the users choosing. We
need to allow users to pick that data from their log history and take it
with them in a text format that can be shared.

This Commit:
----
- Create a format for the symptom history to be shared, the format is of
the shape:

```
Symptom history from Oct 20, '20 to Oct 7, '20

Oct 20, '20
No symptoms were logged

Oct 19, '20
You felt well

Oct 18, '20
You did not feel well, symptoms included: Cough, Fever

(for the next 14 entries)
...
```

- Use formatter class to export a symptom history into this format
- Add a share button to the right of the header on the symptom history
screen that will open up the share menu with the symptom history in text
format to be shared
- Fix typo on symptom checker copy `learn move` => `learn more`

Co-authored-by: Devin Jameson <[email protected]>
Co-authored-by: Matt Buckley <[email protected]>

* Update style and class naming

Co-authored-by: Devin Jameson <[email protected]>
Co-authored-by: Matt Buckley <[email protected]>
  • Loading branch information
3 people authored Oct 20, 2020
1 parent 074f20a commit 5797233
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 4 deletions.
95 changes: 95 additions & 0 deletions src/SymptomHistory/SymptomHistoryFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { TFunction } from "i18next"

import { posixToDayjs } from "../utils/dateTime"
import { SymptomHistory, SymptomEntry } from "./symptomHistory"
import * as Symptom from "./symptom"

class SymptomHistoryFormatter {
private DATE_FORMAT = "MMM D, 'YY"
private t: TFunction
private symptomHistory: SymptomHistory

public static forSharing = (
t: TFunction,
symptomHistory: SymptomHistory,
): string => {
const formatter = new SymptomHistoryFormatter(t, symptomHistory)
return formatter.toShareableText()
}

private constructor(t: TFunction, symptomHistory: SymptomHistory) {
this.t = t
this.symptomHistory = symptomHistory
}

private toShareableText = (): string => {
const {
symptomHistory,
sharedHistoryHeader,
toFormattedSymptomEntry,
} = this
const NEW_EMPTY_LINE = "\n\n"

const header = sharedHistoryHeader()

const allEntriesFormatted = symptomHistory.map(toFormattedSymptomEntry)

return [header, ...allEntriesFormatted].join(NEW_EMPTY_LINE)
}

private sharedHistoryHeader = (): string => {
const { t, symptomHistory, DATE_FORMAT } = this
const fromEntry = symptomHistory[0]
const toEntry = symptomHistory[symptomHistory.length - 1]

const firstEntryDayJSDate = posixToDayjs(fromEntry.date)
const lastEntryDayJSDate = posixToDayjs(toEntry.date)

const startDate = firstEntryDayJSDate?.local().format(DATE_FORMAT) || ""
const endDate = lastEntryDayJSDate?.local().format(DATE_FORMAT) || ""

return t("symptom_history.sharing.header", { startDate, endDate })
}

private toFormattedSymptomEntry = (symptomEntry: SymptomEntry): string => {
const { symptomEntryDetails, DATE_FORMAT } = this
const NEW_LINE = "\n"

const date = symptomEntry.date
const entryDateDayJs = posixToDayjs(date)

const entryDate = entryDateDayJs?.local().format(DATE_FORMAT) || ""
const entryDetails = symptomEntryDetails(symptomEntry)

return [entryDate, entryDetails].join(NEW_LINE)
}

private symptomEntryDetails = (symptomEntry: SymptomEntry): string => {
const { t, translatedSymptoms } = this
let details = ""

if (symptomEntry.kind === "NoUserInput") {
details = t("symptom_history.sharing.no_symptoms_were_logged")
} else {
if (symptomEntry.symptoms.size > 0) {
details = t("symptom_history.sharing.you_did_not_feel_well", {
symptomList: translatedSymptoms([...symptomEntry.symptoms]),
})
} else {
details = t("symptom_history.sharing.you_felt_well")
}
}

return details
}

private translatedSymptoms = (symptoms: Symptom.Symptom[]): string => {
const { t } = this

return symptoms
.map((symptom: Symptom.Symptom) => Symptom.toTranslation(t, symptom))
.join(", ")
}
}

export default SymptomHistoryFormatter
45 changes: 44 additions & 1 deletion src/SymptomHistory/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react"
import { render } from "@testing-library/react-native"
import { fireEvent, render } from "@testing-library/react-native"
import { Share } from "react-native"
import dayjs from "dayjs"

import { SymptomHistoryContext } from "./SymptomHistoryContext"
Expand Down Expand Up @@ -58,5 +59,47 @@ describe("SymptomHistory", () => {
expect(getByText(expectedTwoDaysAgoText)).toBeDefined()
expect(getAllByText("- Cough")).toHaveLength(1)
})

it("allows the user to share their symptom history", () => {
const today = Date.parse("2020-1-3")
const oneDayAgo = Date.parse("2020-1-2")
const twoDaysAgo = Date.parse("2020-1-1")
const history: SymptomHistory = [
{
kind: "NoUserInput",
date: today,
},
{
id: "a",
kind: "UserInput",
date: oneDayAgo,
symptoms: new Set<Symptom>(),
},
{
id: "b",
kind: "UserInput",
date: twoDaysAgo,
symptoms: new Set<Symptom>(["cough", "fever"]),
},
]
const shareSpy = jest.spyOn(Share, "share")
const { getByTestId } = render(
<SymptomHistoryContext.Provider
value={factories.symptomHistoryContext.build({
symptomHistory: history,
})}
>
<SymptomHistoryScreen />
</SymptomHistoryContext.Provider>,
)

const shareButton = getByTestId("shareButton")
fireEvent.press(shareButton)

expect(shareSpy).toHaveBeenCalledWith({
message:
"Symptom history from Jan 3, '20 to Jan 1, '20\n\nJan 3, '20\nNo symptoms were logged\n\nJan 2, '20\nYou felt well\n\nJan 1, '20\nYou did not feel well, symptoms included: Cough, Fever",
})
})
})
})
39 changes: 37 additions & 2 deletions src/SymptomHistory/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React, { FunctionComponent } from "react"
import { ScrollView, StyleSheet, View } from "react-native"
import {
ScrollView,
Share,
StyleSheet,
TouchableOpacity,
View,
} from "react-native"
import { useTranslation } from "react-i18next"

import {
Expand All @@ -10,14 +16,27 @@ import { SymptomEntry } from "./symptomHistory"
import { Text, StatusBar } from "../components"
import { useStatusBarEffect } from "../navigation"
import SymptomEntryListItem from "./SymptomEntryListItem"
import SymptomHistoryFormatter from "./SymptomHistoryFormatter"

import { Typography, Colors, Spacing } from "../styles"
import { Buttons, Colors, Spacing, Typography } from "../styles"

const SymptomHistory: FunctionComponent = () => {
useStatusBarEffect("dark-content", Colors.primaryLightBackground)
const { t } = useTranslation()
const { symptomHistory } = useSymptomHistoryContext()

const handleOnPressShareHistory = async () => {
const message = SymptomHistoryFormatter.forSharing(t, symptomHistory)

try {
await Share.share({
message,
})
} catch (error) {
throw new Error(error)
}
}

return (
<View style={style.outerContainer}>
<StatusBar backgroundColor={Colors.primaryLightBackground} />
Expand All @@ -29,6 +48,7 @@ const SymptomHistory: FunctionComponent = () => {
<Text style={style.headerText}>
{t("symptom_history.symptom_history")}
</Text>

<Text style={style.subHeaderText}>
{t("symptom_history.to_protect_your_privacy", {
days: DAYS_AFTER_LOG_IS_CONSIDERED_STALE,
Expand All @@ -39,6 +59,15 @@ const SymptomHistory: FunctionComponent = () => {
return <SymptomEntryListItem key={entry.date} entry={entry} />
})}
</ScrollView>
<TouchableOpacity
style={style.shareButton}
onPress={handleOnPressShareHistory}
testID="shareButton"
>
<Text style={style.shareButtonText}>
{t("symptom_history.share_history")}
</Text>
</TouchableOpacity>
</View>
)
}
Expand All @@ -64,6 +93,12 @@ const style = StyleSheet.create({
...Typography.body1,
marginBottom: Spacing.large,
},
shareButton: {
...Buttons.fixedBottom,
},
shareButtonText: {
...Typography.buttonFixedBottom,
},
})

export default SymptomHistory
9 changes: 8 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
"covid19_self_assessment": "COVID-19 Self Assessment",
"find_out_how_to_care": "Find out how to care for yourself and others after close contact with someone positive for COVID-19",
"if_this_is": "If this is an emergency, please call {{emergencyPhoneNumber}}",
"this_is_based_on": "This is based on the latest guidance from the CDC and may be updated over time as we learn move about how the virus spreads and the illness it causes",
"this_is_based_on": "This is based on the latest guidance from the CDC and may be updated over time as we learn more about how the virus spreads and the illness it causes",
"this_is_not_intended": "This is not intended for diagnosis or treatment",
"you_are_a_resident": "You are a resident of the community supported by {{ healthAuthorityName }}"
},
Expand Down Expand Up @@ -394,6 +394,13 @@
"felt_well": "You felt well",
"no_entry": "No entry",
"no_symptoms": "No symptoms",
"share_history": "Share symptom history",
"sharing": {
"header": "Symptom history from {{startDate}} to {{endDate}}",
"no_symptoms_were_logged": "No symptoms were logged",
"you_did_not_feel_well": "You did not feel well, symptoms included: {{symptomList}}",
"you_felt_well": "You felt well"
},
"symptom_history": "Symptom History",
"to_protect_your_privacy": "To protect your privacy, entries are stored on your device and deleted automatically after {{days}} days."
},
Expand Down

0 comments on commit 5797233

Please sign in to comment.