summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer
diff options
context:
space:
mode:
authorHank <hank@mullvad.net>2022-10-17 12:02:10 +0200
committerHank <hank@mullvad.net>2022-10-20 16:36:14 +0200
commit4e490dc2329cb6209ff85379efc448f0c0080907 (patch)
treecf4d56abd3ef578d070a5fc6bf1a4f72a38bc225 /gui/src/renderer
parente9f668b853c6228102f43ca23a303b3012d81b9e (diff)
downloadmullvadvpn-4e490dc2329cb6209ff85379efc448f0c0080907.tar.xz
mullvadvpn-4e490dc2329cb6209ff85379efc448f0c0080907.zip
Make Content a separate component, add context
Diffstat (limited to 'gui/src/renderer')
-rw-r--r--gui/src/renderer/components/ProblemReport.tsx260
1 files changed, 133 insertions, 127 deletions
diff --git a/gui/src/renderer/components/ProblemReport.tsx b/gui/src/renderer/components/ProblemReport.tsx
index cb32b66364..7889e6fa1b 100644
--- a/gui/src/renderer/components/ProblemReport.tsx
+++ b/gui/src/renderer/components/ProblemReport.tsx
@@ -1,10 +1,13 @@
import {
ChangeEvent,
+ createContext,
Dispatch,
ReactNode,
SetStateAction,
useCallback,
+ useContext,
useEffect,
+ useMemo,
useRef,
useState,
} from 'react';
@@ -48,94 +51,15 @@ enum SendState {
}
export default function ProblemReport() {
- const history = useHistory();
- const { sendProblemReport, viewLog } = useAppContext();
- const { clearReportForm, saveReportForm } = useActions(support);
-
- const { email: defaultEmail, message: defaultMessage } = useSelector((state) => state.support);
-
- const { collectLog } = useCollectLog();
-
- const [sendState, setSendState] = useState(SendState.initial);
- const [email, setEmail] = useState(defaultEmail);
- const [message, setMessage] = useState(defaultMessage);
- const [disableActions, setDisableActions] = useState(false);
-
- /**
- * Save the form whenever email or message gets updated
- */
- useEffect(() => {
- saveReportForm({ email, message });
- }, [email, message]);
-
- const sendReport = useCallback(async () => {
- try {
- const reportId = await collectLog();
- await sendProblemReport(email, message, reportId);
- clearReportForm();
- setSendState(SendState.success);
- } catch (error) {
- setSendState(SendState.failed);
- }
- }, [email, message]);
-
- const onViewLog = useCallback(async () => {
- setDisableActions(true);
-
- try {
- const reportId = await collectLog();
- await viewLog(reportId);
- } catch (error) {
- // TODO: handle error
- } finally {
- setDisableActions(false);
- }
- }, []);
-
- const onSend = useCallback(async () => {
- if (sendState === SendState.initial && email.length === 0) {
- setSendState(SendState.confirm);
- } else if (
- sendState === SendState.initial ||
- sendState === SendState.confirm ||
- sendState === SendState.failed
- ) {
- try {
- setSendState(SendState.sending);
- await sendReport();
- } catch (error) {
- // No-op
- }
- }
- }, [email]);
-
- const renderContent = () => {
- switch (sendState) {
- case SendState.initial:
- case SendState.confirm:
- return (
- <Form
- email={email}
- setEmail={setEmail}
- message={message}
- setMessage={setMessage}
- onSend={onSend}
- disableActions={disableActions}
- onViewLog={onViewLog}
- />
- );
- case SendState.sending:
- return <Sending />;
- case SendState.success:
- return <Sent email={email} />;
- case SendState.failed:
- return <Failed setSendState={setSendState} onSend={onSend} />;
- default:
- return null;
- }
- };
+ return (
+ <ProblemReportContextProvider>
+ <ProblemReportComponent />
+ </ProblemReportContextProvider>
+ );
+}
- const content = renderContent();
+function ProblemReportComponent() {
+ const history = useHistory();
return (
<BackAction action={history.pop}>
@@ -152,11 +76,11 @@ export default function ProblemReport() {
</NavigationItems>
</NavigationBar>
<StyledContentContainer>
- <Header sendState={sendState} />
- {content}
+ <Header />
+ <Content />
</StyledContentContainer>
- <NoEmailDialog sendState={sendState} setSendState={setSendState} onSend={onSend} />
+ <NoEmailDialog />
<OutdatedVersionWarningDialog />
</SettingsContainer>
</Layout>
@@ -164,11 +88,9 @@ export default function ProblemReport() {
);
}
-type HeaderProps = {
- sendState: SendState;
-};
+function Header() {
+ const { sendState } = useProblemReportContext();
-function Header({ sendState }: HeaderProps) {
return (
<SettingsHeader>
<HeaderTitle>{messages.pgettext('support-view', 'Report a problem')}</HeaderTitle>
@@ -184,25 +106,44 @@ function Header({ sendState }: HeaderProps) {
);
}
-type FormProps = {
- email: string;
- setEmail: (email: string) => void;
- disableActions: boolean;
- message: string;
- setMessage: (message: string) => void;
- onSend: () => void;
- onViewLog: () => void;
-};
+function Content() {
+ const { sendState } = useProblemReportContext();
+
+ switch (sendState) {
+ case SendState.initial:
+ case SendState.confirm:
+ return <Form />;
+ case SendState.sending:
+ return <Sending />;
+ case SendState.success:
+ return <Sent />;
+ case SendState.failed:
+ return <Failed />;
+ default:
+ return null;
+ }
+}
+
+function Form() {
+ const { viewLog } = useAppContext();
+ const { email, setEmail, message, setMessage, onSend } = useProblemReportContext();
+ const { collectLog } = useCollectLog();
+
+ const [disableActions, setDisableActions] = useState(false);
+
+ const onViewLog = useCallback(async () => {
+ setDisableActions(true);
+
+ try {
+ const reportId = await collectLog();
+ await viewLog(reportId);
+ } catch (error) {
+ // TODO: handle error
+ } finally {
+ setDisableActions(false);
+ }
+ }, []);
-function Form({
- email,
- disableActions,
- message,
- onSend,
- setEmail,
- setMessage,
- onViewLog,
-}: FormProps) {
const onChangeEmail = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
}, []);
@@ -275,11 +216,9 @@ function Sending() {
);
}
-type SentProps = {
- email: string;
-};
+function Sent() {
+ const { email } = useProblemReportContext();
-function Sent({ email }: SentProps) {
const reachBackMessage: ReactNode[] =
// TRANSLATORS: The message displayed to the user after submitting the problem report, given that the user left his or her email for us to reach back.
// TRANSLATORS: Available placeholders:
@@ -307,12 +246,9 @@ function Sent({ email }: SentProps) {
);
}
-type FailedProps = {
- setSendState: Dispatch<SetStateAction<SendState>>;
- onSend: () => void;
-};
+function Failed() {
+ const { setSendState, onSend } = useProblemReportContext();
-function Failed({ setSendState, onSend }: FailedProps) {
const handleEditMessage = useCallback(() => {
setSendState(SendState.initial);
}, []);
@@ -345,13 +281,9 @@ function Failed({ setSendState, onSend }: FailedProps) {
);
}
-type NoEmailDialogProps = {
- sendState: SendState;
- onSend: () => void;
- setSendState: Dispatch<SetStateAction<SendState>>;
-};
+function NoEmailDialog() {
+ const { sendState, setSendState, onSend } = useProblemReportContext();
-function NoEmailDialog({ sendState, onSend, setSendState }: NoEmailDialogProps) {
const message = messages.pgettext(
'support-view',
'You are about to send the problem report without a way for us to get back to you. If you want an answer to your report you will have to enter an email address.',
@@ -470,3 +402,77 @@ const useCollectLog = () => {
return { collectLog };
};
+
+type ProblemReportContextType = {
+ sendState: SendState;
+ setSendState: Dispatch<SetStateAction<SendState>>;
+ email: string;
+ setEmail: Dispatch<SetStateAction<string>>;
+ message: string;
+ setMessage: Dispatch<SetStateAction<string>>;
+ onSend: () => Promise<void>;
+};
+
+const ProblemReportContext = createContext<ProblemReportContextType | undefined>(undefined);
+
+const ProblemReportContextProvider = ({ children }: { children: ReactNode }) => {
+ const { sendProblemReport } = useAppContext();
+ const { clearReportForm, saveReportForm } = useActions(support);
+
+ const { email: defaultEmail, message: defaultMessage } = useSelector((state) => state.support);
+
+ const { collectLog } = useCollectLog();
+
+ const [sendState, setSendState] = useState(SendState.initial);
+ const [email, setEmail] = useState(defaultEmail);
+ const [message, setMessage] = useState(defaultMessage);
+
+ const sendReport = useCallback(async () => {
+ try {
+ const reportId = await collectLog();
+ await sendProblemReport(email, message, reportId);
+ clearReportForm();
+ setSendState(SendState.success);
+ } catch (error) {
+ setSendState(SendState.failed);
+ }
+ }, [email, message]);
+
+ const onSend = useCallback(async () => {
+ if (sendState === SendState.initial && email.length === 0) {
+ setSendState(SendState.confirm);
+ } else if (
+ sendState === SendState.initial ||
+ sendState === SendState.confirm ||
+ sendState === SendState.failed
+ ) {
+ try {
+ setSendState(SendState.sending);
+ await sendReport();
+ } catch (error) {
+ // No-op
+ }
+ }
+ }, [email]);
+
+ /**
+ * Save the form whenever email or message gets updated
+ */
+ useEffect(() => {
+ saveReportForm({ email, message });
+ }, [email, message]);
+
+ const value: ProblemReportContextType = useMemo(
+ () => ({ sendState, setSendState, email, setEmail, message, setMessage, onSend }),
+ [sendState, setSendState, email, setEmail, message, setMessage, onSend],
+ );
+ return <ProblemReportContext.Provider value={value}>{children}</ProblemReportContext.Provider>;
+};
+
+const useProblemReportContext = () => {
+ const context = useContext(ProblemReportContext);
+ if (!context) {
+ throw new Error('useProblemReportContext must be used within a ProblemReportContextProvider');
+ }
+ return context;
+};