From b0aa10edb6c100c6247df97ea9e75d9a040c0676 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Feb 2024 19:52:07 +0100 Subject: [PATCH 1/4] feat(whistleblow): integration of globaleaks - implemented Globaleaks API flow including Proof of Work logics for submitting reports with a three input form and the possibility to copy the receipt code after successful submission - included `expo-crypto` package for Proof of Work digest function - included `expo-clipboard` for copying the receipt code - created `initialContext` object to be reused for setting the `initialGlobalSettings` on app start SBB-134 --- package.json | 3 +- src/SettingsProvider.js | 7 +- src/components/index.js | 1 + .../whistleblow/WhistleblowReportForm.tsx | 189 ++++++++++++++++++ src/components/whistleblow/index.ts | 1 + src/config/Icon.tsx | 1 + src/config/navigation/defaultStackConfig.tsx | 7 +- src/config/texts.js | 10 + src/helpers/index.js | 1 + src/helpers/whistleblow/globaleaks/index.ts | 26 +++ .../whistleblow/globaleaks/proofOfWork.ts | 43 ++++ src/helpers/whistleblow/index.ts | 1 + src/index.js | 27 +-- src/queries/index.js | 3 + src/queries/whistleblow/auth.ts | 30 +++ src/queries/whistleblow/index.ts | 2 + src/queries/whistleblow/whistleblower.ts | 17 ++ src/screens/NestedInfoScreen.tsx | 7 +- src/screens/index.js | 1 + .../whistleblow/WhistleblowFormScreen.tsx | 127 ++++++++++++ src/screens/whistleblow/index.ts | 1 + src/types/Navigation.ts | 3 +- src/types/SubQuery.ts | 1 + src/types/index.ts | 1 + src/types/whistleblow/globaleaks/index.ts | 1 + .../whistleblow/globaleaks/submission.ts | 12 ++ src/types/whistleblow/index.ts | 1 + yarn.lock | 9 +- 28 files changed, 508 insertions(+), 25 deletions(-) create mode 100644 src/components/whistleblow/WhistleblowReportForm.tsx create mode 100644 src/components/whistleblow/index.ts create mode 100644 src/helpers/whistleblow/globaleaks/index.ts create mode 100644 src/helpers/whistleblow/globaleaks/proofOfWork.ts create mode 100644 src/helpers/whistleblow/index.ts create mode 100644 src/queries/whistleblow/auth.ts create mode 100644 src/queries/whistleblow/index.ts create mode 100644 src/queries/whistleblow/whistleblower.ts create mode 100644 src/screens/whistleblow/WhistleblowFormScreen.tsx create mode 100644 src/screens/whistleblow/index.ts create mode 100644 src/types/whistleblow/globaleaks/index.ts create mode 100644 src/types/whistleblow/globaleaks/submission.ts create mode 100644 src/types/whistleblow/index.ts diff --git a/package.json b/package.json index 70d222448..4582890f9 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "expo-camera": "~15.0.16", "expo-clipboard": "~6.0.3", "expo-constants": "~16.0.2", + "expo-crypto": "~12.4.1", "expo-dev-client": "~4.0.28", "expo-device": "~6.0.2", "expo-document-picker": "~12.0.2", @@ -177,4 +178,4 @@ "node-fetch": "~2.6.7", "ws": "~8.17.1" } -} +} \ No newline at end of file diff --git a/src/SettingsProvider.js b/src/SettingsProvider.js index 6fbde7670..fc9987b66 100644 --- a/src/SettingsProvider.js +++ b/src/SettingsProvider.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React, { createContext, useState } from 'react'; -export const SettingsContext = createContext({ +export const initialContext = { globalSettings: { deprecated: {}, filter: {}, @@ -9,11 +9,14 @@ export const SettingsContext = createContext({ navigation: 'tab', sections: {}, settings: {}, + whistleblow: {}, widgets: [] }, listTypesSettings: {}, locationSettings: {} -}); +}; + +export const SettingsContext = createContext(initialContext); export const SettingsProvider = ({ initialGlobalSettings, diff --git a/src/components/index.js b/src/components/index.js index 1a70d2b65..ea5006232 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -14,6 +14,7 @@ export * from './SUE'; export * from './volunteer'; export * from './wasteCalendar'; export * from './weather'; +export * from './whistleblow'; export * from './widgets'; export * from './vouchers'; diff --git a/src/components/whistleblow/WhistleblowReportForm.tsx b/src/components/whistleblow/WhistleblowReportForm.tsx new file mode 100644 index 000000000..06bf90c1d --- /dev/null +++ b/src/components/whistleblow/WhistleblowReportForm.tsx @@ -0,0 +1,189 @@ +import { StackNavigationProp } from '@react-navigation/stack'; +import React, { useContext, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { Keyboard, StyleSheet } from 'react-native'; + +import { Button, Input, LoadingModal, RegularText, Touchable, Wrapper } from '../../components'; +import { colors, consts, texts } from '../../config'; +import { Globaleaks } from '../../helpers'; +import { SettingsContext } from '../../SettingsProvider'; + +const { EMAIL_REGEX } = consts; + +type WhistleblowReportData = { + body: string; + email: string; + file: string; + title: string; +}; + +export const WhistleblowReportForm = ({ + navigation, + setReportCode +}: { + navigation: StackNavigationProp; + setReportCode: React.Dispatch>; +}) => { + const { globalSettings } = useContext(SettingsContext); + const { whistleblow = {} } = globalSettings; + const { globaleaks: globaleaksConfig = {} } = whistleblow; + const { endpoint, form: formConfig = {} } = globaleaksConfig; + const { contextId, answers = {}, receivers, identityProvided, score } = formConfig; + + const [isLoading, setIsLoading] = useState(false); + const { + control, + formState: { errors }, + handleSubmit + } = useForm({ + defaultValues: { + body: '', + email: '', + // file: '', + title: '' + } + }); + const globaleaks = new Globaleaks(endpoint); + + // const [createGenericItem, { loading }] = useMutation(CREATE_GENERIC_ITEM); + + const onSubmit = async (whistleblowReportData: WhistleblowReportData) => { + setIsLoading(true); + Keyboard.dismiss(); + + try { + const body = { + context_id: contextId, + answers: { + [answers.email.id]: [ + { + required_status: answers.email.reportStatus, + value: whistleblowReportData.email + } + ], + [answers.title.id]: [ + { + required_status: answers.title.reportStatus, + value: whistleblowReportData.title + } + ], + [answers.body.id]: [ + { + required_status: answers.body.reportStatus, + value: whistleblowReportData.body + } + ] + }, + identity_provided: identityProvided, + receivers, + score + }; + + // Globaleaks flow + const receipt = await globaleaks.report(body); + setReportCode(receipt); + } catch (error) { + console.error(error); + } finally { + setIsLoading(false); + } + }; + + return ( + <> + + + + + + + + + + + + + {/* + + */} + + +