From da9ec5eb16298d8ca5690800eca1c15f5a6cfaa5 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 14 Oct 2022 11:40:38 -0300 Subject: [PATCH] refactored terms of service to remove duplicated code prettfied some sources --- .../src/components/CheckboxOutlined.tsx | 4 +- .../src/cta/InvoiceCreate/index.ts | 10 +- .../src/cta/InvoiceCreate/state.ts | 23 +- .../src/cta/InvoiceCreate/stories.tsx | 4 +- .../src/cta/TermsOfService/index.ts | 96 ++++++++ .../src/cta/TermsOfService/state.ts | 136 +++++++++++ .../src/cta/TermsOfService/stories.tsx | 29 +++ .../src/cta/TermsOfService/test.ts | 28 +++ .../src/cta/TermsOfService/utils.ts | 130 ++++++++++ .../src/cta/TermsOfService/views.tsx | 224 ++++++++++++++++++ .../src/cta/TermsOfServiceSection.stories.tsx | 187 --------------- .../src/cta/TermsOfServiceSection.tsx | 196 --------------- .../src/cta/Tip/index.ts | 3 +- .../src/cta/Withdraw/index.ts | 20 +- .../src/cta/Withdraw/state.ts | 185 ++++++++------- .../src/cta/Withdraw/stories.tsx | 82 +++---- .../src/cta/Withdraw/test.ts | 97 ++++---- .../src/cta/Withdraw/views.tsx | 81 +++---- .../src/cta/index.stories.ts | 2 +- .../src/hooks/useSelectedExchange.ts | 92 +++---- .../src/test-utils.ts | 6 +- .../src/utils/index.ts | 133 +---------- .../src/wallet/AddAccount/index.ts | 6 +- .../src/wallet/AddAccount/state.ts | 63 ++--- .../src/wallet/DepositPage/index.ts | 18 +- .../src/wallet/DepositPage/state.ts | 76 +++--- .../src/wallet/DepositPage/test.ts | 133 ++++++----- .../src/wallet/ExchangeAddConfirm.stories.tsx | 35 +-- .../src/wallet/ExchangeAddConfirm.tsx | 126 ++-------- .../src/wallet/ExchangeSelection/index.ts | 13 +- .../src/wallet/ExchangeSelection/state.ts | 10 +- .../src/wallet/ExchangeSelection/views.tsx | 36 +-- .../src/wallet/Settings.tsx | 2 +- .../taler-wallet-webextension/src/wxApi.ts | 45 +++- 34 files changed, 1218 insertions(+), 1113 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/index.ts create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/state.ts create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/test.ts create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/utils.ts create mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfService/views.tsx delete mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx diff --git a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx index 133c06339..79712c2f4 100644 --- a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx +++ b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx @@ -18,8 +18,8 @@ import { Outlined, StyledCheckboxLabel } from "./styled/index.js"; import { h, VNode } from "preact"; interface Props { - enabled: boolean; - onToggle: () => Promise; + enabled?: boolean; + onToggle?: () => Promise; label: VNode; name: string; } diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts index 61f286d1f..ff04a8247 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts @@ -17,9 +17,7 @@ import { AmountJson, TalerErrorDetail } from "@gnu-taler/taler-util"; import { Loading } from "../../components/Loading.js"; import { HookError } from "../../hooks/useAsyncAsHook.js"; -import { - State as SelectExchangeState -} from "../../hooks/useSelectedExchange.js"; +import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js"; import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js"; import { compose, StateViewMap } from "../../utils/index.js"; import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js"; @@ -34,12 +32,12 @@ export interface Props { onSuccess: (tx: string) => Promise; } -export type State = State.Loading +export type State = + | State.Loading | State.LoadingUriError | State.Ready | SelectExchangeState.Selecting - | SelectExchangeState.NoExchange - ; + | SelectExchangeState.NoExchange; export namespace State { export interface Loading { diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts index 4f75e982d..205a664e0 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts @@ -23,7 +23,7 @@ import { useSelectedExchange } from "../../hooks/useSelectedExchange.js"; import * as wxApi from "../../wxApi.js"; import { Props, State } from "./index.js"; -type RecursiveState = S | (() => RecursiveState) +type RecursiveState = S | (() => RecursiveState); export function useComponentState( { amount: amountStr, onClose, onSuccess }: Props, @@ -46,7 +46,7 @@ export function useComponentState( }; } - const exchangeList = hook.response.exchanges + const exchangeList = hook.response.exchanges; return () => { const [subject, setSubject] = useState(""); @@ -55,14 +55,17 @@ export function useComponentState( TalerErrorDetail | undefined >(undefined); + const selectedExchange = useSelectedExchange({ + currency: amount.currency, + defaultExchange: undefined, + list: exchangeList, + }); - const selectedExchange = useSelectedExchange({ currency: amount.currency, defaultExchange: undefined, list: exchangeList }) - - if (selectedExchange.status !== 'ready') { - return selectedExchange + if (selectedExchange.status !== "ready") { + return selectedExchange; } - const exchange = selectedExchange.selected + const exchange = selectedExchange.selected; async function accept(): Promise { try { @@ -105,9 +108,5 @@ export function useComponentState( error: undefined, operationError, }; - } - - - - + }; } diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx index 306d1b199..77885b0c1 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx @@ -38,9 +38,7 @@ export const Ready = createExample(ReadyView, { value: 1, fraction: 0, }, - doSelectExchange: { - - }, + doSelectExchange: {}, exchangeUrl: "https://exchange.taler.ar", subject: { value: "some subject", diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/index.ts b/packages/taler-wallet-webextension/src/cta/TermsOfService/index.ts new file mode 100644 index 000000000..9485f9d0a --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/index.ts @@ -0,0 +1,96 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +import { Loading } from "../../components/Loading.js"; +import { HookError } from "../../hooks/useAsyncAsHook.js"; +import { ToggleHandler } from "../../mui/handlers.js"; +import { compose, StateViewMap } from "../../utils/index.js"; +import * as wxApi from "../../wxApi.js"; +import { useComponentState } from "./state.js"; +import { TermsState } from "./utils.js"; +import { + ErrorAcceptingView, + LoadingUriView, + ShowButtonsAcceptedTosView, + ShowButtonsNonAcceptedTosView, + ShowTosContentView, +} from "./views.js"; + +export interface Props { + exchangeUrl: string; + onChange: (v: boolean) => void; + readOnly?: boolean; +} + +export type State = + | State.Loading + | State.LoadingUriError + | State.ErrorAccepting + | State.ShowContent + | State.ShowButtonsAccepted + | State.ShowButtonsNotAccepted + | State.ShowContent; + +export namespace State { + export interface Loading { + status: "loading"; + error: undefined; + } + + export interface LoadingUriError { + status: "loading-error"; + error: HookError; + } + + export interface ErrorAccepting { + status: "error-accepting"; + error: HookError; + } + + export interface BaseInfo { + error: undefined; + termsAccepted: ToggleHandler; + showingTermsOfService: ToggleHandler; + terms: TermsState; + } + export interface ShowContent extends BaseInfo { + status: "show-content"; + error: undefined; + } + export interface ShowButtonsAccepted extends BaseInfo { + status: "show-buttons-accepted"; + error: undefined; + } + export interface ShowButtonsNotAccepted extends BaseInfo { + status: "show-buttons-not-accepted"; + error: undefined; + } +} + +const viewMapping: StateViewMap = { + loading: Loading, + "loading-error": LoadingUriView, + "show-content": ShowTosContentView, + "show-buttons-accepted": ShowButtonsAcceptedTosView, + "show-buttons-not-accepted": ShowButtonsNonAcceptedTosView, + "error-accepting": ErrorAcceptingView, +}; + +export const TermsOfService = compose( + "TermsOfService", + (p: Props) => useComponentState(p, wxApi), + viewMapping, +); diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/state.ts b/packages/taler-wallet-webextension/src/cta/TermsOfService/state.ts new file mode 100644 index 000000000..4e89bc243 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/state.ts @@ -0,0 +1,136 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +import { useState } from "preact/hooks"; +import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; +import * as wxApi from "../../wxApi.js"; +import { Props, State } from "./index.js"; +import { buildTermsOfServiceState } from "./utils.js"; + +export function useComponentState( + { exchangeUrl, readOnly, onChange }: Props, + api: typeof wxApi, +): State { + const [showContent, setShowContent] = useState(false); + // const [accepted, setAccepted] = useState(false); + const [errorAccepting, setErrorAccepting] = useState( + undefined, + ); + + /** + * For the exchange selected, bring the status of the terms of service + */ + const terms = useAsyncAsHook(async () => { + const exchangeTos = await api.getExchangeTos(exchangeUrl, ["text/xml"]); + + const state = buildTermsOfServiceState(exchangeTos); + + return { state }; + }, []); + + if (!terms) { + return { + status: "loading", + error: undefined, + }; + } + if (terms.hasError) { + return { + status: "loading-error", + error: terms, + }; + } + + if (errorAccepting) { + return { + status: "error-accepting", + error: { + hasError: true, + operational: false, + message: errorAccepting.message, + }, + }; + } + + const { state } = terms.response; + + async function onUpdate(accepted: boolean): Promise { + if (!state) return; + + try { + if (accepted) { + await api.setExchangeTosAccepted(exchangeUrl, state.version); + } else { + // mark as not accepted + await api.setExchangeTosAccepted(exchangeUrl, undefined); + } + // setAccepted(accepted); + onChange(accepted); //external update + } catch (e) { + if (e instanceof Error) { + //FIXME: uncomment this and display error + // setErrorAccepting(e.message); + setErrorAccepting(e); + } + } + } + + const accepted = state.status === "accepted"; + + const base: State.BaseInfo = { + error: undefined, + showingTermsOfService: { + value: showContent, + button: { + onClick: readOnly + ? undefined + : async () => { + setShowContent(!showContent); + }, + }, + }, + terms: state, + termsAccepted: { + value: accepted, + button: { + onClick: async () => { + const newValue = !accepted; //toggle + onUpdate(newValue); + setShowContent(false); + }, + }, + }, + }; + + if (showContent) { + return { + status: "show-content", + ...base, + }; + } + //showing buttons + if (accepted) { + return { + status: "show-buttons-accepted", + ...base, + }; + } else { + return { + status: "show-buttons-not-accepted", + ...base, + }; + } +} diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/stories.tsx b/packages/taler-wallet-webextension/src/cta/TermsOfService/stories.tsx new file mode 100644 index 000000000..2479274cb --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/stories.tsx @@ -0,0 +1,29 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { createExample } from "../../test-utils.js"; +// import { ReadyView } from "./views.js"; + +export default { + title: "TermsOfService", +}; + +// export const Ready = createExample(ReadyView, {}); diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/test.ts b/packages/taler-wallet-webextension/src/cta/TermsOfService/test.ts new file mode 100644 index 000000000..eae4d4ca2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/test.ts @@ -0,0 +1,28 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { expect } from "chai"; + +describe("test description", () => { + it("should assert", () => { + expect([]).deep.equals([]); + }); +}); diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/utils.ts b/packages/taler-wallet-webextension/src/cta/TermsOfService/utils.ts new file mode 100644 index 000000000..cee6557f7 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/utils.ts @@ -0,0 +1,130 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +import { GetExchangeTosResult } from "@gnu-taler/taler-util"; + +export function buildTermsOfServiceState( + tos: GetExchangeTosResult, +): TermsState { + const content: TermsDocument | undefined = parseTermsOfServiceContent( + tos.contentType, + tos.content, + ); + + const status: TermsStatus = buildTermsOfServiceStatus( + tos.content, + tos.acceptedEtag, + tos.currentEtag, + ); + + return { content, status, version: tos.currentEtag }; +} +export function buildTermsOfServiceStatus( + content: string | undefined, + acceptedVersion: string | undefined, + currentVersion: string | undefined, +): TermsStatus { + return !content + ? "notfound" + : !acceptedVersion + ? "new" + : acceptedVersion !== currentVersion + ? "changed" + : "accepted"; +} + +function parseTermsOfServiceContent( + type: string, + text: string, +): TermsDocument | undefined { + if (type === "text/xml") { + try { + const document = new DOMParser().parseFromString(text, "text/xml"); + return { type: "xml", document }; + } catch (e) { + console.log(e); + } + } else if (type === "text/html") { + try { + const href = new URL(text); + return { type: "html", href }; + } catch (e) { + console.log(e); + } + } else if (type === "text/json") { + try { + const data = JSON.parse(text); + return { type: "json", data }; + } catch (e) { + console.log(e); + } + } else if (type === "text/pdf") { + try { + const location = new URL(text); + return { type: "pdf", location }; + } catch (e) { + console.log(e); + } + } else if (type === "text/plain") { + try { + const content = text; + return { type: "plain", content }; + } catch (e) { + console.log(e); + } + } + return undefined; +} + +export type TermsState = { + content: TermsDocument | undefined; + status: TermsStatus; + version: string; +}; + +type TermsStatus = "new" | "accepted" | "changed" | "notfound"; + +type TermsDocument = + | TermsDocumentXml + | TermsDocumentHtml + | TermsDocumentPlain + | TermsDocumentJson + | TermsDocumentPdf; + +export interface TermsDocumentXml { + type: "xml"; + document: Document; +} + +export interface TermsDocumentHtml { + type: "html"; + href: URL; +} + +export interface TermsDocumentPlain { + type: "plain"; + content: string; +} + +export interface TermsDocumentJson { + type: "json"; + data: any; +} + +export interface TermsDocumentPdf { + type: "pdf"; + location: URL; +} diff --git a/packages/taler-wallet-webextension/src/cta/TermsOfService/views.tsx b/packages/taler-wallet-webextension/src/cta/TermsOfService/views.tsx new file mode 100644 index 000000000..ed6d7dee4 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/TermsOfService/views.tsx @@ -0,0 +1,224 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +import { Fragment, h, VNode } from "preact"; +import { LoadingError } from "../../components/LoadingError.js"; +import { useTranslationContext } from "../../context/translation.js"; +import { TermsState } from "./utils.js"; +import { State } from "./index.js"; +import { CheckboxOutlined } from "../../components/CheckboxOutlined.js"; +import { + LinkSuccess, + TermsOfService, + WarningBox, + WarningText, +} from "../../components/styled/index.js"; +import { ExchangeXmlTos } from "../../components/ExchangeToS.js"; +import { ToggleHandler } from "../../mui/handlers.js"; +import { Button } from "../../mui/Button.js"; + +export function LoadingUriView({ error }: State.LoadingUriError): VNode { + const { i18n } = useTranslationContext(); + + return ( + Could not load} + error={error} + /> + ); +} + +export function ErrorAcceptingView({ error }: State.ErrorAccepting): VNode { + const { i18n } = useTranslationContext(); + + return ( + Could not load} + error={error} + /> + ); +} + +export function ShowButtonsAcceptedTosView({ + termsAccepted, + showingTermsOfService, + terms, +}: State.ShowButtonsAccepted): VNode { + const { i18n } = useTranslationContext(); + const ableToReviewTermsOfService = + showingTermsOfService.button.onClick !== undefined; + + return ( + + {ableToReviewTermsOfService && ( +
+ + Show terms of service + +
+ )} +
+ + I accept the exchange terms of service + + } + onToggle={termsAccepted.button.onClick} + /> +
+
+ ); +} + +export function ShowButtonsNonAcceptedTosView({ + termsAccepted, + showingTermsOfService, + terms, +}: State.ShowButtonsNotAccepted): VNode { + const { i18n } = useTranslationContext(); + const ableToReviewTermsOfService = + showingTermsOfService.button.onClick !== undefined; + + if (!ableToReviewTermsOfService) { + return ( + + {terms.status === "notfound" && ( +
+ + + Exchange doesn't have terms of service + + +
+ )} +
+ ); + } + + return ( + + {terms.status === "notfound" && ( +
+ + + Exchange doesn't have terms of service + + +
+ )} + {terms.status === "new" && ( +
+ +
+ )} + {terms.status === "changed" && ( +
+ +
+ )} +
+ ); +} + +export function ShowTosContentView({ + termsAccepted, + showingTermsOfService, + terms, +}: State.ShowContent): VNode { + const { i18n } = useTranslationContext(); + const ableToReviewTermsOfService = + showingTermsOfService.button.onClick !== undefined; + + return ( + + {terms.status !== "notfound" && !terms.content && ( +
+ + + The exchange reply with a empty terms of service + + +
+ )} + {terms.content && ( +
+ {terms.content.type === "xml" && ( + + + + )} + {terms.content.type === "plain" && ( +
+
{terms.content.content}
+
+ )} + {terms.content.type === "html" && ( +