Skip to content

Commit

Permalink
Report an issue form (#1350)
Browse files Browse the repository at this point in the history
* update git ignore

* update env to be removed from gitignore

* Zendesk integration

* debug -> dev for .env

* update example

* use isPlatformiOS helper

* update readme

* remove bt specific readme instructions

* prettier

* prettier fixes
  • Loading branch information
JacobJaffe authored Aug 7, 2020
1 parent 965684e commit f03884b
Show file tree
Hide file tree
Showing 20 changed files with 360 additions and 98 deletions.
15 changes: 0 additions & 15 deletions .env.gps

This file was deleted.

8 changes: 0 additions & 8 deletions .env.gps.release

This file was deleted.

16 changes: 0 additions & 16 deletions .env.gps.staging

This file was deleted.

4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ android/app/release
safepaths.realm

# env files
.env
.env.bt*

.env.*
# private resources
pathcheck-mobile-resources

Expand Down
41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Safe Paths is designed to support a range of DCT and public health use cases. Cu
The Safe Paths app is being developed to support a variety of build 'flavors' of the application around core health and tracing functionality. Reach out to our team to discuss creating a flavor for your use-case.

### Path Check Release of COVID Safe Paths

Safe Paths is available as an app published by Path Check in the [Apple App Store](https://apps.apple.com/us/app/covid-safe-paths/id1508266966) and the [Google Play App Store](https://play.google.com/store/apps/details?id=org.pathcheck.covidsafepaths). Any authorized pubic health authority can use Safe Paths.

### Custom Builds
Expand Down Expand Up @@ -80,43 +81,45 @@ If you're looking for a first ticket - please check out the backlog for a bug or

View the [architecture diagram](docs/Private_Kit_Diagram.png) for a basic overview on the sequencing of generalized events and services that are used by Safe Paths.


## Developer Setup

First, run the appropriate setup script for your system. This will install relevant packages, walk through Android Studio configuration, etc.

**Note:** You will still need to [configure an Android Virtual Device (AVD)](https://developer.android.com/studio/run/managing-avds#createavd) after running the script.

#### Linux/MacOS
### Linux/MacOS

```
```Shell
dev_setup.sh
```

#### Windows
### Windows

```
```Shell
dev_setup.bat
```

#### Environment
### Environment

Populate the following 2 `.env` files with the relevant urls for your GAEN server:
Populate the following `.env` files. View an example file at `example.env`

```Shell
.env.dev
.env.staging
.env.release
```
.env.bt
.env.bt.release
```

**Note:** Members of the `Path-Check` org can complete this step by running `yarn set-ha` and passing in the 2-letter ha abbreviation as the first argument (i.e. `yarn set-ha pc`)
You can configure `AUTHORITIES_YAML_ROUTE` against `https://raw.githubusercontent.com/Path-Check/trusted-authorities/master/staging/authorities.1.0.1.yaml`.

`ZENDESK_URL` can be omitted in development, and the Report Issue page will throw an error when submitting.

## Running

**Note:** In some cases, these procedures can lead to the error `Failed to load bundle - Could not connect to development server`. In these cases, kill all other react-native processes and try it again.

#### Android (Windows, Linux, macOS)
### Android (Windows, Linux, macOS)

```
```Shell
yarn run-android ## for the location enabled app
```

Expand All @@ -126,13 +129,13 @@ Device storage can be cleared by long-pressing on the app icon in the simulator,

First, install the pod files:

```
```Shell
yarn install:pod ## only needs to be ran once
```

Then, run the application:

```
```Shell
yarn run-ios ## for the location enabled app
```

Expand All @@ -159,7 +162,7 @@ This project is using
[typescript](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html).

Run the complier with:
```
```Shell
yarn tsc
```

Expand Down Expand Up @@ -200,21 +203,21 @@ Tests are ran automatically through Github actions - PRs are not able to be merg

To run the static analysis tools:

```
```Shell
yarn validate
```

### Unit Test

To run the unit tests:

```
```Shell
yarn test --watch
```

[Snapshot testing](https://jestjs.io/docs/en/snapshot-testing) is used as a quick way to verify that the UI has not changed. To update the snapshots:

```
```Shell
yarn update-snapshots
```

Expand Down
6 changes: 3 additions & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'realm-android'

project.ext.envConfigFiles = [
debug: ".env.gps",
staging: ".env.gps.staging",
release: ".env.gps.release",
debug: ".env.dev",
staging: ".env.staging",
release: ".env.release",
]

apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
Expand Down
2 changes: 2 additions & 0 deletions app/Entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ShareDiagnosis from './views/onboarding/ShareDiagnosis';
import NotificationsPermissions from './views/onboarding/NotificationsPermissions';
import LocationsPermissions from './views/onboarding/LocationsPermissions';
import LanguageSelection from './views/LanguageSelection';
import ReportIssueForm from './views/ReportIssueForm';

import { Screens, Stacks } from './navigation';

Expand Down Expand Up @@ -89,6 +90,7 @@ const MoreTabStack = () => (
<Stack.Screen name={Screens.ImportFromGoogle} component={ImportScreen} />
<Stack.Screen name={Screens.ImportFromUrl} component={ImportFromUrl} />
<Stack.Screen name={Screens.ExportLocally} component={ExportLocally} />
<Stack.Screen name={Screens.ReportIssue} component={ReportIssueForm} />
</Stack.Navigator>
);

Expand Down
93 changes: 93 additions & 0 deletions app/api/zendesk/reportIssue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import env from 'react-native-config';
import { Platform } from 'react-native';
import getAppVersion from '../../helpers/getAppVersion';

interface ReportIssueProps {
email: string;
name: string;
body: string;
}

const OS_FIELD_KEY = '360033622032';
const OS_VERSION_FIELD_KEY = '360033618552';
const APP_VERSION_FIELD_KEY = '360033141172';
const APP_NAME_FIELD_KEY = '360034051891';
const ISSUE_SUBJECT = `Issue from GPS mobile application PathCheck ${
__DEV__ ? '[Dev Testing]' : ''
}`;
const ANONYMOUS = 'Anonymous';
const APP_NAME = 'PathCheck GPS';

const EMAIL_ERROR = 'Email:';

interface ErrorDescription {
description: string;
}

interface ErrorDetails {
requester?: ErrorDescription[];
base: ErrorDescription[];
}

// Errors are of the form:
// {
// "error": "RecordInvalid",
// "description": "Record validation errors",
// "details": {
// "requester": [
// {
// "description": "Requester: Email: not_really_an_email.com is not properly formatted"
// }
// ]
// }
//}
const parseErrorMessage = (zendeskError?: Record<string, unknown>): string => {
if (zendeskError?.details) {
const errorDetails = zendeskError.details as ErrorDetails;
const errorMessage = (errorDetails?.requester || errorDetails.base)
.map((error) => {
return error.description;
})
.join(',');
if (errorMessage.indexOf(EMAIL_ERROR) !== -1) {
return 'report_issue.errors.invalid_email';
}
return errorMessage.replace(':', '-'); // so localize doesn't mess with error message
}
return ''; // fallback to no error message, just an error title for our alert.
};

const reportIssue = async ({
email,
name,
body,
}: ReportIssueProps): Promise<void> => {
const requestBody = {
request: {
subject: ISSUE_SUBJECT,
requester: { name: name.trim().length > 0 ? name : ANONYMOUS, email },
comment: { body },
custom_fields: [
{
[OS_FIELD_KEY]: Platform.OS,
[OS_VERSION_FIELD_KEY]: Platform.Version,
[APP_VERSION_FIELD_KEY]: getAppVersion(),
[APP_NAME_FIELD_KEY]: APP_NAME,
},
],
},
};

const response = await fetch(env.ZENDESK_URL, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(requestBody),
});

if (!response.ok) {
const responseJson = await response.json();
throw new Error(parseErrorMessage(responseJson));
}
};

export default reportIssue;
11 changes: 7 additions & 4 deletions app/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ export const Button = ({
accessibilityRole='button'
disabled={disabled || loading}
style={[styles.button, style]}>
{loading ? (
<ActivityIndicator size={'large'} />
) : (
<Typography style={buttonTextStyle}>{label}</Typography>
<Typography style={buttonTextStyle}>{loading ? ' ' : label}</Typography>
{loading && (
<ActivityIndicator
size={'large'}
style={{ position: 'absolute' }}
color={buttonTextStyle.color}
/>
)}
</TouchableOpacity>
);
Expand Down
16 changes: 16 additions & 0 deletions app/helpers/getAppVersion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getVersion, getBuildNumber } from 'react-native-device-info';
import { isPlatformiOS } from '../Util';

const getAppVersion = (): string => {
const version = getVersion();
// Append "ALPHA" to our iOS builds that are 1.0.0, as we use
// a separate Alpha TestFlight that is always 1.0.0.
// On android we include "ALPHA" directly in the version name.
const isAlpha = version === '1.0.0';
const appVersion = `${
isAlpha && isPlatformiOS() ? 'ALPHA ' : ''
}${version} (${getBuildNumber()})`;
return appVersion;
};

export default getAppVersion;
16 changes: 14 additions & 2 deletions app/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"done": "Done",
"next": "Next",
"something_went_wrong": "Something went wrong",
"start": "Start"
"start": "Start",
"submit": "Submit",
"success": "Success"
},
"export": {
"code_input_body": "The representative from {{name}} will provide a verification code over the phone to link your data with {{name}}.",
Expand Down Expand Up @@ -182,12 +184,22 @@
"notification_header": "Would you like to receive notifications when you may have crossed paths with the virus?",
"notification_subheader": "PathCheck partners with local Health Departments to track the virus. If you are located in a jurisdiction served by one of our Health Department partners, you can receive exposure notifications from your Health Department."
},
"report_issue": {
"body": "Feedback (required)",
"email": "Email (required)",
"errors": {
"invalid_email": "The email seems to be invalid, please check again"
},
"name": "Full Name",
"success": "We received your feedback. Thank you!"
},
"screen_titles": {
"about": "About",
"delete_location_history": "Delete Location History",
"exposure_history": "Exposure History",
"legal": "Legal",
"more_info": "More Info"
"more_info": "More Info",
"report_issue": "Report an Issue"
},
"version_update": {
"alert_label": "PathCheck is outdated",
Expand Down
4 changes: 3 additions & 1 deletion app/navigation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export type Screen =
| 'AffectedUserPublishConsent'
| 'AffectedUserConfirmUpload'
| 'AffectedUserExportDone'
| 'AffectedUserComplete';
| 'AffectedUserComplete'
| 'ReportIssue';

export const Screens: { [key in Screen]: Screen } = {
ExportStart: 'ExportStart',
Expand Down Expand Up @@ -91,6 +92,7 @@ export const Screens: { [key in Screen]: Screen } = {
AffectedUserConfirmUpload: 'AffectedUserConfirmUpload',
AffectedUserExportDone: 'AffectedUserExportDone',
AffectedUserComplete: 'AffectedUserComplete',
ReportIssue: 'ReportIssue',
};

export type Stack =
Expand Down
1 change: 0 additions & 1 deletion app/styles/typography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ export const primaryTextInput: TextStyle = {
...extraBold,
fontSize: larger,
lineHeight: largest,
textAlign: 'center',
color: Colors.primaryText,
};

Expand Down
Loading

0 comments on commit f03884b

Please sign in to comment.