diff --git a/package.json b/package.json index b6e27b997..d857a0113 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "devDependencies": { "@linaria/esbuild": "^3.0.0-beta.13", "@linaria/shaker": "^3.0.0-beta.13", - "esbuild": "^0.12.29" + "esbuild": "^0.12.29", + "prettier": "^2.2.1" } } diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 96d2d65f9..7a92afd43 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -5,11 +5,14 @@ "license": "MIT", "scripts": { "build": "preact build --no-sw --no-esm", - "serve": "sirv build --port 8080 --cors --single", - "dev": "preact watch --no-sw --no-esm", + "serve": "sirv build --port ${PORT:=8080} --cors --single", + "dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "test": "jest ./tests", "build-storybook": "build-storybook", + "build-single": "preact build --no-sw --no-esm -c preact.single-config.js --dest single && sh remove-link-stylesheet.sh", + "serve-single": "sirv single --port ${PORT:=8080} --cors --single", + "pretty": "prettier --write src", "storybook": "start-storybook -p 6006" }, "eslintConfig": { @@ -25,6 +28,7 @@ "dependencies": { "@gnu-taler/taler-util": "workspace:^0.8.3", "anastasis-core": "workspace:^0.0.1", + "base64-inline-loader": "1.1.1", "date-fns": "2.25.0", "jed": "1.1.1", "preact": "^10.5.15", diff --git a/packages/anastasis-webui/src/.babelrc b/packages/anastasis-webui/src/.babelrc index 123002210..05f4dcc81 100644 --- a/packages/anastasis-webui/src/.babelrc +++ b/packages/anastasis-webui/src/.babelrc @@ -1,5 +1,3 @@ { - "presets": [ - "preact-cli/babel" - ] + "presets": ["preact-cli/babel"] } diff --git a/packages/anastasis-webui/src/components/AsyncButton.tsx b/packages/anastasis-webui/src/components/AsyncButton.tsx index 92bef2219..33f3a7258 100644 --- a/packages/anastasis-webui/src/components/AsyncButton.tsx +++ b/packages/anastasis-webui/src/components/AsyncButton.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { ComponentChildren, h, VNode } from "preact"; // import { LoadingModal } from "../modal"; @@ -31,19 +31,26 @@ type Props = { [rest: string]: any; }; -export function AsyncButton({ onClick, disabled, children, ...rest }: Props): VNode { +export function AsyncButton({ + onClick, + disabled, + children, + ...rest +}: Props): VNode { const { isLoading, request } = useAsync(onClick); // if (isSlow) { // return ; // } - if (isLoading) { + if (isLoading) { return ; } - return - - ; + return ( + + + + ); } diff --git a/packages/anastasis-webui/src/components/Notifications.tsx b/packages/anastasis-webui/src/components/Notifications.tsx index 097ebb4de..e34550386 100644 --- a/packages/anastasis-webui/src/components/Notifications.tsx +++ b/packages/anastasis-webui/src/components/Notifications.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; @@ -27,7 +27,7 @@ export interface Notification { type: MessageType; } -export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS' +export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS"; interface Props { notifications: Notification[]; @@ -36,24 +36,39 @@ interface Props { function messageStyle(type: MessageType): string { switch (type) { - case "INFO": return "message is-info"; - case "WARN": return "message is-warning"; - case "ERROR": return "message is-danger"; - case "SUCCESS": return "message is-success"; - default: return "message" + case "INFO": + return "message is-info"; + case "WARN": + return "message is-warning"; + case "ERROR": + return "message is-danger"; + case "SUCCESS": + return "message is-success"; + default: + return "message"; } } -export function Notifications({ notifications, removeNotification }: Props): VNode { - return
- {notifications.map((n, i) =>
-
-

{n.message}

- {removeNotification &&
- {n.description &&
- {n.description} -
} -
)} -
-} \ No newline at end of file +export function Notifications({ + notifications, + removeNotification, +}: Props): VNode { + return ( +
+ {notifications.map((n, i) => ( +
+
+

{n.message}

+ {removeNotification && ( +
+ {n.description &&
{n.description}
} +
+ ))} +
+ ); +} diff --git a/packages/anastasis-webui/src/components/QR.tsx b/packages/anastasis-webui/src/components/QR.tsx index 48f1a7c12..9a05f6097 100644 --- a/packages/anastasis-webui/src/components/QR.tsx +++ b/packages/anastasis-webui/src/components/QR.tsx @@ -21,15 +21,28 @@ import qrcode from "qrcode-generator"; export function QR({ text }: { text: string }): VNode { const divRef = useRef(null); useEffect(() => { - const qr = qrcode(0, 'L'); + const qr = qrcode(0, "L"); qr.addData(text); qr.make(); - if (divRef.current) divRef.current.innerHTML = qr.createSvgTag({ - scalable: true, - }); + if (divRef.current) + divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); }); - return
-
-
; + return ( +
+
+
+ ); } diff --git a/packages/anastasis-webui/src/components/app.tsx b/packages/anastasis-webui/src/components/app.tsx index c6b4cfc14..4c6683c0c 100644 --- a/packages/anastasis-webui/src/components/app.tsx +++ b/packages/anastasis-webui/src/components/app.tsx @@ -1,6 +1,5 @@ import { FunctionalComponent, h } from "preact"; import { TranslationProvider } from "../context/translation"; - import AnastasisClient from "../pages/home"; const App: FunctionalComponent = () => { diff --git a/packages/anastasis-webui/src/components/fields/DateInput.tsx b/packages/anastasis-webui/src/components/fields/DateInput.tsx index 3148c953f..0b6a7e316 100644 --- a/packages/anastasis-webui/src/components/fields/DateInput.tsx +++ b/packages/anastasis-webui/src/components/fields/DateInput.tsx @@ -19,56 +19,66 @@ export function DateInput(props: DateInputProps): VNode { inputRef.current?.focus(); } }, [props.grabFocus]); - const [opened, setOpened] = useState(false) + const [opened, setOpened] = useState(false); const value = props.bind[0] || ""; - const [dirty, setDirty] = useState(false) - const showError = dirty && props.error + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; - const calendar = subYears(new Date(), 30) - - return
- -
-
-

- { - const text = e.currentTarget.value - setDirty(true) - props.bind[1](text); - }} - ref={inputRef} /> -

-

- { setOpened(true) }}> - - -

+ const calendar = subYears(new Date(), 30); + + return ( +
+ +
+
+

+ { + const text = e.currentTarget.value; + setDirty(true); + props.bind[1](text); + }} + ref={inputRef} + /> +

+

+ { + setOpened(true); + }} + > + + + + +

+
+

Using the format yyyy-mm-dd

+ {showError &&

{props.error}

} + setOpened(false)} + dateReceiver={(d) => { + setDirty(true); + const v = format(d, "yyyy-MM-dd"); + props.bind[1](v); + }} + />
-

Using the format yyyy-mm-dd

- {showError &&

{props.error}

} - setOpened(false)} - dateReceiver={(d) => { - setDirty(true) - const v = format(d, 'yyyy-MM-dd') - props.bind[1](v); - }} - /> -
- ; - + ); } diff --git a/packages/anastasis-webui/src/components/fields/EmailInput.tsx b/packages/anastasis-webui/src/components/fields/EmailInput.tsx index e21418fea..fe676f284 100644 --- a/packages/anastasis-webui/src/components/fields/EmailInput.tsx +++ b/packages/anastasis-webui/src/components/fields/EmailInput.tsx @@ -18,27 +18,34 @@ export function EmailInput(props: TextInputProps): VNode { } }, [props.grabFocus]); const value = props.bind[0]; - const [dirty, setDirty] = useState(false) - const showError = dirty && props.error - return (
- -
- {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} - ref={inputRef} - style={{ display: "block" }} /> + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + return ( +
+ +
+ { + setDirty(true); + props.bind[1]((e.target as HTMLInputElement).value); + }} + ref={inputRef} + style={{ display: "block" }} + /> +
+ {showError &&

{props.error}

}
- {showError &&

{props.error}

} -
); } diff --git a/packages/anastasis-webui/src/components/fields/FileInput.tsx b/packages/anastasis-webui/src/components/fields/FileInput.tsx index 8b144ea43..52d6eab4a 100644 --- a/packages/anastasis-webui/src/components/fields/FileInput.tsx +++ b/packages/anastasis-webui/src/components/fields/FileInput.tsx @@ -15,14 +15,14 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; import { TextInputProps } from "./TextInput"; -const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024 +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; export function FileInput(props: TextInputProps): VNode { const inputRef = useRef(null); @@ -34,48 +34,54 @@ export function FileInput(props: TextInputProps): VNode { const value = props.bind[0]; // const [dirty, setDirty] = useState(false) - const image = useRef(null) - const [sizeError, setSizeError] = useState(false) + const image = useRef(null); + const [sizeError, setSizeError] = useState(false); function onChange(v: string): void { // setDirty(true); props.bind[1](v); } - return
- -
- { - const f: FileList | null = e.currentTarget.files - if (!f || f.length != 1) { - return onChange("") - } - if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { - setSizeError(true) - return onChange("") - } - setSizeError(false) - return f[0].arrayBuffer().then(b => { - const b64 = btoa( - new Uint8Array(b) - .reduce((data, byte) => data + String.fromCharCode(byte), '') - ) - return onChange(`data:${f[0].type};base64,${b64}` as any) - }) - }} /> - {props.error &&

{props.error}

} - {sizeError &&

- File should be smaller than 1 MB -

} + return ( +
+ +
+ { + const f: FileList | null = e.currentTarget.files; + if (!f || f.length != 1) { + return onChange(""); + } + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true); + return onChange(""); + } + setSizeError(false); + return f[0].arrayBuffer().then((b) => { + const b64 = btoa( + new Uint8Array(b).reduce( + (data, byte) => data + String.fromCharCode(byte), + "", + ), + ); + return onChange(`data:${f[0].type};base64,${b64}` as any); + }); + }} + /> + {props.error &&

{props.error}

} + {sizeError && ( +

File should be smaller than 1 MB

+ )} +
-
+ ); } - diff --git a/packages/anastasis-webui/src/components/fields/ImageInput.tsx b/packages/anastasis-webui/src/components/fields/ImageInput.tsx index d5bf643d4..3f8cc58dd 100644 --- a/packages/anastasis-webui/src/components/fields/ImageInput.tsx +++ b/packages/anastasis-webui/src/components/fields/ImageInput.tsx @@ -15,15 +15,15 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; import emptyImage from "../../assets/empty.png"; import { TextInputProps } from "./TextInput"; -const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024 +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; export function ImageInput(props: TextInputProps): VNode { const inputRef = useRef(null); @@ -35,47 +35,59 @@ export function ImageInput(props: TextInputProps): VNode { const value = props.bind[0]; // const [dirty, setDirty] = useState(false) - const image = useRef(null) - const [sizeError, setSizeError] = useState(false) + const image = useRef(null); + const [sizeError, setSizeError] = useState(false); function onChange(v: string): void { // setDirty(true); props.bind[1](v); } - return
- -
- image.current?.click()} /> - { - const f: FileList | null = e.currentTarget.files - if (!f || f.length != 1) { - return onChange(emptyImage) - } - if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { - setSizeError(true) - return onChange(emptyImage) - } - setSizeError(false) - return f[0].arrayBuffer().then(b => { - const b64 = btoa( - new Uint8Array(b) - .reduce((data, byte) => data + String.fromCharCode(byte), '') - ) - return onChange(`data:${f[0].type};base64,${b64}` as any) - }) - }} /> - {props.error &&

{props.error}

} - {sizeError &&

- Image should be smaller than 1 MB -

} + return ( +
+ +
+ image.current?.click()} + /> + { + const f: FileList | null = e.currentTarget.files; + if (!f || f.length != 1) { + return onChange(emptyImage); + } + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true); + return onChange(emptyImage); + } + setSizeError(false); + return f[0].arrayBuffer().then((b) => { + const b64 = btoa( + new Uint8Array(b).reduce( + (data, byte) => data + String.fromCharCode(byte), + "", + ), + ); + return onChange(`data:${f[0].type};base64,${b64}` as any); + }); + }} + /> + {props.error &&

{props.error}

} + {sizeError && ( +

Image should be smaller than 1 MB

+ )} +
-
+ ); } - diff --git a/packages/anastasis-webui/src/components/fields/TextInput.tsx b/packages/anastasis-webui/src/components/fields/TextInput.tsx index c093689c5..fd0c658ed 100644 --- a/packages/anastasis-webui/src/components/fields/TextInput.tsx +++ b/packages/anastasis-webui/src/components/fields/TextInput.tsx @@ -18,25 +18,32 @@ export function TextInput(props: TextInputProps): VNode { } }, [props.grabFocus]); const value = props.bind[0]; - const [dirty, setDirty] = useState(false) - const showError = dirty && props.error - return (
- -
- {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} - ref={inputRef} - style={{ display: "block" }} /> + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + return ( +
+ +
+ { + setDirty(true); + props.bind[1]((e.target as HTMLInputElement).value); + }} + ref={inputRef} + style={{ display: "block" }} + /> +
+ {showError &&

{props.error}

}
- {showError &&

{props.error}

} -
); } diff --git a/packages/anastasis-webui/src/components/menu/LangSelector.tsx b/packages/anastasis-webui/src/components/menu/LangSelector.tsx index 0f91abd7e..fa22a29c0 100644 --- a/packages/anastasis-webui/src/components/menu/LangSelector.tsx +++ b/packages/anastasis-webui/src/components/menu/LangSelector.tsx @@ -15,59 +15,78 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; -import langIcon from '../../assets/icons/languageicon.svg'; +import langIcon from "../../assets/icons/languageicon.svg"; import { useTranslationContext } from "../../context/translation"; -import { strings as messages } from '../../i18n/strings' +import { strings as messages } from "../../i18n/strings"; type LangsNames = { - [P in keyof typeof messages]: string -} + [P in keyof typeof messages]: string; +}; const names: LangsNames = { - es: 'Español [es]', - en: 'English [en]', - fr: 'Français [fr]', - de: 'Deutsch [de]', - sv: 'Svenska [sv]', - it: 'Italiano [it]', -} + es: "Español [es]", + en: "English [en]", + fr: "Français [fr]", + de: "Deutsch [de]", + sv: "Svenska [sv]", + it: "Italiano [it]", +}; function getLangName(s: keyof LangsNames | string): string { - if (names[s]) return names[s] - return String(s) + if (names[s]) return names[s]; + return String(s); } export function LangSelector(): VNode { - const [updatingLang, setUpdatingLang] = useState(false) - const { lang, changeLanguage } = useTranslationContext() + const [updatingLang, setUpdatingLang] = useState(false); + const { lang, changeLanguage } = useTranslationContext(); - return } */}
-
Anastasis
-
+
+ Anastasis +
+
Version {process.env.__VERSION__} ({config.version})
); } - diff --git a/packages/anastasis-webui/src/components/menu/index.tsx b/packages/anastasis-webui/src/components/menu/index.tsx index fd4aab149..99d0f7646 100644 --- a/packages/anastasis-webui/src/components/menu/index.tsx +++ b/packages/anastasis-webui/src/components/menu/index.tsx @@ -85,8 +85,8 @@ export function NotificationCard({ n.type === "ERROR" ? "message is-danger" : n.type === "WARN" - ? "message is-warning" - : "message is-info" + ? "message is-warning" + : "message is-info" } >
@@ -113,7 +113,7 @@ export function NotYetReadyAppMenu({ return (
setMobileOpen(false)} > { - closeDatePicker() { this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent } /** - * Gets fired when a day gets clicked. - * @param {object} e The event thrown by the element clicked - */ + * Gets fired when a day gets clicked. + * @param {object} e The event thrown by the element clicked + */ dayClicked(e: any) { - const element = e.target; // the actual element clicked - if (element.innerHTML === '') return false; // don't continue if empty + if (element.innerHTML === "") return false; // don't continue if empty // get date from clicked element (gets attached when rendered) - const date = new Date(element.getAttribute('data-value')); + const date = new Date(element.getAttribute("data-value")); // update the state this.setState({ currentDate: date }); - this.passDateToParent(date) + this.passDateToParent(date); } /** - * returns days in month as array - * @param {number} month the month to display - * @param {number} year the year to display - */ + * returns days in month as array + * @param {number} month the month to display + * @param {number} year the year to display + */ getDaysByMonth(month: number, year: number) { - const calendar = []; const date = new Date(year, month, 1); // month to display @@ -122,15 +110,17 @@ export class DatePicker extends Component { // the calendar is 7*6 fields big, so 42 loops for (let i = 0; i < 42; i++) { - if (i >= firstDay && day !== null) day = day + 1; if (day !== null && day > lastDate) day = null; // append the calendar Array calendar.push({ - day: (day === 0 || day === null) ? null : day, // null or number - date: (day === 0 || day === null) ? null : new Date(year, month, day), // null or Date() - today: (day === now.getDate() && month === now.getMonth() && year === now.getFullYear()) // boolean + day: day === 0 || day === null ? null : day, // null or number + date: day === 0 || day === null ? null : new Date(year, month, day), // null or Date() + today: + day === now.getDate() && + month === now.getMonth() && + year === now.getFullYear(), // boolean }); } @@ -138,51 +128,48 @@ export class DatePicker extends Component { } /** - * Display previous month by updating state - */ + * Display previous month by updating state + */ displayPrevMonth() { if (this.state.displayedMonth <= 0) { this.setState({ displayedMonth: 11, - displayedYear: this.state.displayedYear - 1 + displayedYear: this.state.displayedYear - 1, }); - } - else { + } else { this.setState({ - displayedMonth: this.state.displayedMonth - 1 + displayedMonth: this.state.displayedMonth - 1, }); } } /** - * Display next month by updating state - */ + * Display next month by updating state + */ displayNextMonth() { if (this.state.displayedMonth >= 11) { this.setState({ displayedMonth: 0, - displayedYear: this.state.displayedYear + 1 + displayedYear: this.state.displayedYear + 1, }); - } - else { + } else { this.setState({ - displayedMonth: this.state.displayedMonth + 1 + displayedMonth: this.state.displayedMonth + 1, }); } } /** - * Display the selected month (gets fired when clicking on the date string) - */ + * Display the selected month (gets fired when clicking on the date string) + */ displaySelectedMonth() { if (this.state.selectYearMode) { this.toggleYearSelector(); - } - else { + } else { if (!this.state.currentDate) return false; this.setState({ displayedMonth: this.state.currentDate.getMonth(), - displayedYear: this.state.currentDate.getFullYear() + displayedYear: this.state.currentDate.getFullYear(), }); } } @@ -194,17 +181,21 @@ export class DatePicker extends Component { changeDisplayedYear(e: any) { const element = e.target; this.toggleYearSelector(); - this.setState({ displayedYear: parseInt(element.innerHTML, 10), displayedMonth: 0 }); + this.setState({ + displayedYear: parseInt(element.innerHTML, 10), + displayedMonth: 0, + }); } /** - * Pass the selected date to parent when 'OK' is clicked - */ + * Pass the selected date to parent when 'OK' is clicked + */ passSavedDateDateToParent() { - this.passDateToParent(this.state.currentDate) + this.passDateToParent(this.state.currentDate); } passDateToParent(date: Date) { - if (typeof this.props.dateReceiver === 'function') this.props.dateReceiver(date); + if (typeof this.props.dateReceiver === "function") + this.props.dateReceiver(date); this.closeDatePicker(); } @@ -233,94 +224,133 @@ export class DatePicker extends Component { currentDate: initial, displayedMonth: initial.getMonth(), displayedYear: initial.getFullYear(), - selectYearMode: false - } + selectYearMode: false, + }; } render() { - - const { currentDate, displayedMonth, displayedYear, selectYearMode } = this.state; + const { + currentDate, + displayedMonth, + displayedYear, + selectYearMode, + } = this.state; return (
-
- +
-

{currentDate.getFullYear()}

-

- {dayArr[currentDate.getDay()]}, {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()} +

+ {currentDate.getFullYear()} +

+

+ {dayArr[currentDate.getDay()]},{" "} + {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()}

- {!selectYearMode && } + {!selectYearMode && ( + + )}
+ {!selectYearMode && ( +
+
+ {["S", "M", "T", "W", "T", "F", "S"].map((day, i) => ( + {day} + ))} +
- {!selectYearMode &&
- -
- {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day,i) => {day})} -
- -
- - {/* +
+ {/* Loop through the calendar object returned by getDaysByMonth(). */} - {this.getDaysByMonth(this.state.displayedMonth, this.state.displayedYear) - .map( - day => { - let selected = false; + {this.getDaysByMonth( + this.state.displayedMonth, + this.state.displayedYear, + ).map((day) => { + let selected = false; - if (currentDate && day.date) selected = (currentDate.toLocaleDateString() === day.date.toLocaleDateString()); + if (currentDate && day.date) + selected = + currentDate.toLocaleDateString() === + day.date.toLocaleDateString(); - return ( {day.day} - ) - } - ) - } - + + ); + })} +
+ )} -
} - - {selectYearMode &&
- {(this.props.years || yearArr).map(year => ( - - {year} - - ))} - -
} - + {selectYearMode && ( +
+ {(this.props.years || yearArr).map((year) => ( + + {year} + + ))} +
+ )}
-
-
- ) + ); } } - for (let i = 2010; i <= now.getFullYear() + 10; i++) { yearArr.push(i); } diff --git a/packages/anastasis-webui/src/components/picker/DurationPicker.stories.tsx b/packages/anastasis-webui/src/components/picker/DurationPicker.stories.tsx index 275c80fa6..7f96cc15b 100644 --- a/packages/anastasis-webui/src/components/picker/DurationPicker.stories.tsx +++ b/packages/anastasis-webui/src/components/picker/DurationPicker.stories.tsx @@ -15,36 +15,41 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { h, FunctionalComponent } from 'preact'; -import { useState } from 'preact/hooks'; -import { DurationPicker as TestedComponent } from './DurationPicker'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { h, FunctionalComponent } from "preact"; +import { useState } from "preact/hooks"; +import { DurationPicker as TestedComponent } from "./DurationPicker"; export default { - title: 'Components/Picker/Duration', + title: "Components/Picker/Duration", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - goBack: { action: 'goBack' }, - } + onCreate: { action: "onCreate" }, + goBack: { action: "goBack" }, + }, }; -function createExample(Component: FunctionalComponent, props: Partial) { - const r = (args: any) => - r.args = props - return r +function createExample( + Component: FunctionalComponent, + props: Partial, +) { + const r = (args: any) => ; + r.args = props; + return r; } export const Example = createExample(TestedComponent, { - days: true, minutes: true, hours: true, seconds: true, - value: 10000000 + days: true, + minutes: true, + hours: true, + seconds: true, + value: 10000000, }); export const WithState = () => { - const [v,s] = useState(1000000) - return -} + const [v, s] = useState(1000000); + return ; +}; diff --git a/packages/anastasis-webui/src/components/picker/DurationPicker.tsx b/packages/anastasis-webui/src/components/picker/DurationPicker.tsx index 235a63e2d..8a1faf4d0 100644 --- a/packages/anastasis-webui/src/components/picker/DurationPicker.tsx +++ b/packages/anastasis-webui/src/components/picker/DurationPicker.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -30,75 +30,123 @@ export interface Props { seconds?: boolean; days?: boolean; onChange: (value: number) => void; - value: number + value: number; } // inspiration taken from https://github.com/flurmbo/react-duration-picker -export function DurationPicker({ days, hours, minutes, seconds, onChange, value }: Props): VNode { - const ss = 1000 - const ms = ss * 60 - const hs = ms * 60 - const ds = hs * 24 - const i18n = useTranslator() - - return
- {days && = ds ? () => onChange(value - ds) : undefined} - onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined} - onChange={diff => onChange(value + diff * ds)} - />} - {hours && = hs ? () => onChange(value - hs) : undefined} - onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined} - onChange={diff => onChange(value + diff * hs)} - />} - {minutes && = ms ? () => onChange(value - ms) : undefined} - onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined} - onChange={diff => onChange(value + diff * ms)} - />} - {seconds && = ss ? () => onChange(value - ss) : undefined} - onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined} - onChange={diff => onChange(value + diff * ss)} - />} -
+export function DurationPicker({ + days, + hours, + minutes, + seconds, + onChange, + value, +}: Props): VNode { + const ss = 1000; + const ms = ss * 60; + const hs = ms * 60; + const ds = hs * 24; + const i18n = useTranslator(); + + return ( +
+ {days && ( + = ds ? () => onChange(value - ds) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined} + onChange={(diff) => onChange(value + diff * ds)} + /> + )} + {hours && ( + = hs ? () => onChange(value - hs) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined} + onChange={(diff) => onChange(value + diff * hs)} + /> + )} + {minutes && ( + = ms ? () => onChange(value - ms) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined} + onChange={(diff) => onChange(value + diff * ms)} + /> + )} + {seconds && ( + = ss ? () => onChange(value - ss) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined} + onChange={(diff) => onChange(value + diff * ss)} + /> + )} +
+ ); } interface ColProps { - unit: string, - min?: number, - max: number, - value: number, + unit: string; + min?: number; + max: number; + value: number; onIncrease?: () => void; onDecrease?: () => void; onChange?: (diff: number) => void; } -function InputNumber({ initial, onChange }: { initial: number, onChange: (n: number) => void }) { - const [value, handler] = useState<{v:string}>({ - v: toTwoDigitString(initial) - }) +function InputNumber({ + initial, + onChange, +}: { + initial: number; + onChange: (n: number) => void; +}) { + const [value, handler] = useState<{ v: string }>({ + v: toTwoDigitString(initial), + }); - return onChange(parseInt(value.v, 10))} - onInput={(e) => { - e.preventDefault() - const n = Number.parseInt(e.currentTarget.value, 10); - if (isNaN(n)) return handler({v:toTwoDigitString(initial)}) - return handler({v:toTwoDigitString(n)}) - }} - style={{ width: 50, border: 'none', fontSize: 'inherit', background: 'inherit' }} /> + return ( + onChange(parseInt(value.v, 10))} + onInput={(e) => { + e.preventDefault(); + const n = Number.parseInt(e.currentTarget.value, 10); + if (isNaN(n)) return handler({ v: toTwoDigitString(initial) }); + return handler({ v: toTwoDigitString(n) }); + }} + style={{ + width: 50, + border: "none", + fontSize: "inherit", + background: "inherit", + }} + /> + ); } -function DurationColumn({ unit, min = 0, max, value, onIncrease, onDecrease, onChange }: ColProps): VNode { - - const cellHeight = 35 +function DurationColumn({ + unit, + min = 0, + max, + value, + onIncrease, + onDecrease, + onChange, +}: ColProps): VNode { + const cellHeight = 35; return (
@@ -106,49 +154,58 @@ function DurationColumn({ unit, min = 0, max, value, onIncrease, onDecrease, onC
-
- {onDecrease && } + {onDecrease && ( + + )}
- {value > min ? toTwoDigitString(value - 1) : ''} + {value > min ? toTwoDigitString(value - 1) : ""}
- {onChange ? - onChange(n - value)} /> : + {onChange ? ( + onChange(n - value)} + /> + ) : ( toTwoDigitString(value) - } + )}
{unit}
- {value < max ? toTwoDigitString(value + 1) : ''} + {value < max ? toTwoDigitString(value + 1) : ""}
- {onIncrease && } + {onIncrease && ( + + )}
-
); } - function toTwoDigitString(n: number) { if (n < 10) { return `0${n}`; } return `${n}`; -} \ No newline at end of file +} diff --git a/packages/anastasis-webui/src/context/anastasis.ts b/packages/anastasis-webui/src/context/anastasis.ts index e7f93ed43..c2e7b2a47 100644 --- a/packages/anastasis-webui/src/context/anastasis.ts +++ b/packages/anastasis-webui/src/context/anastasis.ts @@ -15,19 +15,19 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createContext, h, VNode } from 'preact'; -import { useContext } from 'preact/hooks'; -import { AnastasisReducerApi } from '../hooks/use-anastasis-reducer'; +import { createContext, h, VNode } from "preact"; +import { useContext } from "preact/hooks"; +import { AnastasisReducerApi } from "../hooks/use-anastasis-reducer"; type Type = AnastasisReducerApi | undefined; -const initial = undefined +const initial = undefined; -const Context = createContext(initial) +const Context = createContext(initial); interface Props { value: AnastasisReducerApi; @@ -36,6 +36,6 @@ interface Props { export const AnastasisProvider = ({ value, children }: Props): VNode => { return h(Context.Provider, { value, children }); -} +}; -export const useAnastasisContext = (): Type => useContext(Context); \ No newline at end of file +export const useAnastasisContext = (): Type => useContext(Context); diff --git a/packages/anastasis-webui/src/context/translation.ts b/packages/anastasis-webui/src/context/translation.ts index 5ceb5d428..a47864d75 100644 --- a/packages/anastasis-webui/src/context/translation.ts +++ b/packages/anastasis-webui/src/context/translation.ts @@ -15,13 +15,13 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createContext, h, VNode } from 'preact' -import { useContext, useEffect } from 'preact/hooks' -import { useLang } from '../hooks' +import { createContext, h, VNode } from "preact"; +import { useContext, useEffect } from "preact/hooks"; +import { useLang } from "../hooks"; import * as jedLib from "jed"; import { strings } from "../i18n/strings"; @@ -31,13 +31,13 @@ interface Type { changeLanguage: (l: string) => void; } const initial = { - lang: 'en', + lang: "en", handler: null, changeLanguage: () => { // do not change anything - } -} -const Context = createContext(initial) + }, +}; +const Context = createContext(initial); interface Props { initial?: string; @@ -45,15 +45,22 @@ interface Props { forceLang?: string; } -export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => { - const [lang, changeLanguage] = useLang(initial) +export const TranslationProvider = ({ + initial, + children, + forceLang, +}: Props): VNode => { + const [lang, changeLanguage] = useLang(initial); useEffect(() => { if (forceLang) { - changeLanguage(forceLang) + changeLanguage(forceLang); } - }) - const handler = new jedLib.Jed(strings[lang] || strings['en']); - return h(Context.Provider, { value: { lang, handler, changeLanguage }, children }); -} + }); + const handler = new jedLib.Jed(strings[lang] || strings["en"]); + return h(Context.Provider, { + value: { lang, handler, changeLanguage }, + children, + }); +}; -export const useTranslationContext = (): Type => useContext(Context); \ No newline at end of file +export const useTranslationContext = (): Type => useContext(Context); diff --git a/packages/anastasis-webui/src/declaration.d.ts b/packages/anastasis-webui/src/declaration.d.ts index 2c4b7cb3a..00b3d41d5 100644 --- a/packages/anastasis-webui/src/declaration.d.ts +++ b/packages/anastasis-webui/src/declaration.d.ts @@ -1,20 +1,20 @@ declare module "*.css" { - const mapping: Record; - export default mapping; + const mapping: Record; + export default mapping; } -declare module '*.svg' { - const content: any; - export default content; +declare module "*.svg" { + const content: any; + export default content; } -declare module '*.jpeg' { - const content: any; - export default content; +declare module "*.jpeg" { + const content: any; + export default content; } -declare module '*.png' { - const content: any; - export default content; +declare module "*.png" { + const content: any; + export default content; } -declare module 'jed' { - const x: any; - export = x; +declare module "jed" { + const x: any; + export = x; } diff --git a/packages/anastasis-webui/src/hooks/async.ts b/packages/anastasis-webui/src/hooks/async.ts index ea3ff6acf..0fc197554 100644 --- a/packages/anastasis-webui/src/hooks/async.ts +++ b/packages/anastasis-webui/src/hooks/async.ts @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { useState } from "preact/hooks"; // import { cancelPendingRequest } from "./backend"; @@ -34,36 +34,39 @@ export interface AsyncOperationApi { error: string | undefined; } -export function useAsync(fn?: (...args: any) => Promise, { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }): AsyncOperationApi { +export function useAsync( + fn?: (...args: any) => Promise, + { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }, +): AsyncOperationApi { const [data, setData] = useState(undefined); const [isLoading, setLoading] = useState(false); const [error, setError] = useState(undefined); - const [isSlow, setSlow] = useState(false) + const [isSlow, setSlow] = useState(false); const request = async (...args: any) => { if (!fn) return; setLoading(true); const handler = setTimeout(() => { - setSlow(true) - }, tooLong) + setSlow(true); + }, tooLong); try { - console.log("calling async", args) + console.log("calling async", args); const result = await fn(...args); - console.log("async back", result) + console.log("async back", result); setData(result); } catch (error) { setError(error); } setLoading(false); - setSlow(false) - clearTimeout(handler) + setSlow(false); + clearTimeout(handler); }; function cancel() { // cancelPendingRequest() setLoading(false); - setSlow(false) + setSlow(false); } return { @@ -72,6 +75,6 @@ export function useAsync(fn?: (...args: any) => Promise, { slowTolerance: data, isSlow, isLoading, - error + error, }; } diff --git a/packages/anastasis-webui/src/hooks/index.ts b/packages/anastasis-webui/src/hooks/index.ts index 15df4f154..9a1b50a11 100644 --- a/packages/anastasis-webui/src/hooks/index.ts +++ b/packages/anastasis-webui/src/hooks/index.ts @@ -15,81 +15,110 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { StateUpdater, useState } from "preact/hooks"; -export type ValueOrFunction = T | ((p: T) => T) - +export type ValueOrFunction = T | ((p: T) => T); const calculateRootPath = () => { - const rootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/' - return rootPath -} + const rootPath = + typeof window !== undefined + ? window.location.origin + window.location.pathname + : "/"; + return rootPath; +}; -export function useBackendURL(url?: string): [string, boolean, StateUpdater, () => void] { - const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath()) - const [triedToLog, setTriedToLog] = useLocalStorage('tried-login') +export function useBackendURL( + url?: string, +): [string, boolean, StateUpdater, () => void] { + const [value, setter] = useNotNullLocalStorage( + "backend-url", + url || calculateRootPath(), + ); + const [triedToLog, setTriedToLog] = useLocalStorage("tried-login"); const checkedSetter = (v: ValueOrFunction) => { - setTriedToLog('yes') - return setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, '')) - } + setTriedToLog("yes"); + return setter((p) => (v instanceof Function ? v(p) : v).replace(/\/$/, "")); + }; const resetBackend = () => { - setTriedToLog(undefined) - } - return [value, !!triedToLog, checkedSetter, resetBackend] + setTriedToLog(undefined); + }; + return [value, !!triedToLog, checkedSetter, resetBackend]; } -export function useBackendDefaultToken(): [string | undefined, StateUpdater] { - return useLocalStorage('backend-token') +export function useBackendDefaultToken(): [ + string | undefined, + StateUpdater, +] { + return useLocalStorage("backend-token"); } -export function useBackendInstanceToken(id: string): [string | undefined, StateUpdater] { - const [token, setToken] = useLocalStorage(`backend-token-${id}`) - const [defaultToken, defaultSetToken] = useBackendDefaultToken() +export function useBackendInstanceToken( + id: string, +): [string | undefined, StateUpdater] { + const [token, setToken] = useLocalStorage(`backend-token-${id}`); + const [defaultToken, defaultSetToken] = useBackendDefaultToken(); // instance named 'default' use the default token - if (id === 'default') { - return [defaultToken, defaultSetToken] + if (id === "default") { + return [defaultToken, defaultSetToken]; } - return [token, setToken] + return [token, setToken]; } export function useLang(initial?: string): [string, StateUpdater] { - const browserLang = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined; - const defaultLang = (browserLang || initial || 'en').substring(0, 2) - return useNotNullLocalStorage('lang-preference', defaultLang) + const browserLang = + typeof window !== "undefined" + ? navigator.language || (navigator as any).userLanguage + : undefined; + const defaultLang = (browserLang || initial || "en").substring(0, 2); + return useNotNullLocalStorage("lang-preference", defaultLang); } -export function useLocalStorage(key: string, initialValue?: string): [string | undefined, StateUpdater] { - const [storedValue, setStoredValue] = useState((): string | undefined => { - return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; +export function useLocalStorage( + key: string, + initialValue?: string, +): [string | undefined, StateUpdater] { + const [storedValue, setStoredValue] = useState((): + | string + | undefined => { + return typeof window !== "undefined" + ? window.localStorage.getItem(key) || initialValue + : initialValue; }); - const setValue = (value?: string | ((val?: string) => string | undefined)) => { - setStoredValue(p => { - const toStore = value instanceof Function ? value(p) : value + const setValue = ( + value?: string | ((val?: string) => string | undefined), + ) => { + setStoredValue((p) => { + const toStore = value instanceof Function ? value(p) : value; if (typeof window !== "undefined") { if (!toStore) { - window.localStorage.removeItem(key) + window.localStorage.removeItem(key); } else { window.localStorage.setItem(key, toStore); } } - return toStore - }) + return toStore; + }); }; return [storedValue, setValue]; } -export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater] { +export function useNotNullLocalStorage( + key: string, + initialValue: string, +): [string, StateUpdater] { const [storedValue, setStoredValue] = useState((): string => { - return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; + return typeof window !== "undefined" + ? window.localStorage.getItem(key) || initialValue + : initialValue; }); const setValue = (value: string | ((val: string) => string)) => { @@ -97,7 +126,7 @@ export function useNotNullLocalStorage(key: string, initialValue: string): [stri setStoredValue(valueToStore); if (typeof window !== "undefined") { if (!valueToStore) { - window.localStorage.removeItem(key) + window.localStorage.removeItem(key); } else { window.localStorage.setItem(key, valueToStore); } @@ -106,5 +135,3 @@ export function useNotNullLocalStorage(key: string, initialValue: string): [stri return [storedValue, setValue]; } - - diff --git a/packages/anastasis-webui/src/i18n/index.tsx b/packages/anastasis-webui/src/i18n/index.tsx index 63c8e1934..6e2c4e79a 100644 --- a/packages/anastasis-webui/src/i18n/index.tsx +++ b/packages/anastasis-webui/src/i18n/index.tsx @@ -27,23 +27,25 @@ import { useTranslationContext } from "../context/translation"; export function useTranslator() { const ctx = useTranslationContext(); - const jed = ctx.handler - return function str(stringSeq: TemplateStringsArray, ...values: any[]): string { + const jed = ctx.handler; + return function str( + stringSeq: TemplateStringsArray, + ...values: any[] + ): string { const s = toI18nString(stringSeq); - if (!s) return s + if (!s) return s; const tr = jed .translate(s) .ifPlural(1, s) .fetch(...values); return tr; - } + }; } - /** * Convert template strings to a msgid */ - function toI18nString(stringSeq: ReadonlyArray): string { +function toI18nString(stringSeq: ReadonlyArray): string { let s = ""; for (let i = 0; i < stringSeq.length; i++) { s += stringSeq[i]; @@ -54,7 +56,6 @@ export function useTranslator() { return s; } - interface TranslateSwitchProps { target: number; children: ComponentChildren; @@ -110,7 +111,7 @@ function getTranslatedChildren( // Text result.push(tr[i]); } else { - const childIdx = Number.parseInt(tr[i],10) - 1; + const childIdx = Number.parseInt(tr[i], 10) - 1; result.push(placeholderChildren[childIdx]); } } @@ -131,9 +132,9 @@ function getTranslatedChildren( */ export function Translate({ children }: TranslateProps): VNode { const s = stringifyChildren(children); - const ctx = useTranslationContext() + const ctx = useTranslationContext(); const translation: string = ctx.handler.ngettext(s, s, 1); - const result = getTranslatedChildren(translation, children) + const result = getTranslatedChildren(translation, children); return {result}; } @@ -154,14 +155,16 @@ export function TranslateSwitch({ children, target }: TranslateSwitchProps) { let plural: VNode | undefined; // const children = this.props.children; if (children) { - (children instanceof Array ? children : [children]).forEach((child: any) => { - if (child.type === TranslatePlural) { - plural = child; - } - if (child.type === TranslateSingular) { - singular = child; - } - }); + (children instanceof Array ? children : [children]).forEach( + (child: any) => { + if (child.type === TranslatePlural) { + plural = child; + } + if (child.type === TranslateSingular) { + singular = child; + } + }, + ); } if (!singular || !plural) { console.error("translation not found"); @@ -182,9 +185,12 @@ interface TranslationPluralProps { /** * See [[TranslateSwitch]]. */ -export function TranslatePlural({ children, target }: TranslationPluralProps): VNode { +export function TranslatePlural({ + children, + target, +}: TranslationPluralProps): VNode { const s = stringifyChildren(children); - const ctx = useTranslationContext() + const ctx = useTranslationContext(); const translation = ctx.handler.ngettext(s, s, 1); const result = getTranslatedChildren(translation, children); return {result}; @@ -193,11 +199,13 @@ export function TranslatePlural({ children, target }: TranslationPluralProps): V /** * See [[TranslateSwitch]]. */ -export function TranslateSingular({ children, target }: TranslationPluralProps): VNode { +export function TranslateSingular({ + children, + target, +}: TranslationPluralProps): VNode { const s = stringifyChildren(children); - const ctx = useTranslationContext() + const ctx = useTranslationContext(); const translation = ctx.handler.ngettext(s, s, target); const result = getTranslatedChildren(translation, children); return {result}; - } diff --git a/packages/anastasis-webui/src/i18n/strings.ts b/packages/anastasis-webui/src/i18n/strings.ts index b4f376ce0..d12e63e88 100644 --- a/packages/anastasis-webui/src/i18n/strings.ts +++ b/packages/anastasis-webui/src/i18n/strings.ts @@ -15,30 +15,30 @@ */ /*eslint quote-props: ["error", "consistent"]*/ -export const strings: {[s: string]: any} = {}; +export const strings: { [s: string]: any } = {}; -strings['de'] = { - "domain": "messages", - "locale_data": { - "messages": { +strings["de"] = { + domain: "messages", + locale_data: { + messages: { "": { - "domain": "messages", - "plural_forms": "nplurals=2; plural=(n != 1);", - "lang": "" + domain: "messages", + plural_forms: "nplurals=2; plural=(n != 1);", + lang: "", }, - } - } + }, + }, }; -strings['en'] = { - "domain": "messages", - "locale_data": { - "messages": { +strings["en"] = { + domain: "messages", + locale_data: { + messages: { "": { - "domain": "messages", - "plural_forms": "nplurals=2; plural=(n != 1);", - "lang": "" + domain: "messages", + plural_forms: "nplurals=2; plural=(n != 1);", + lang: "", }, - } - } + }, + }, }; diff --git a/packages/anastasis-webui/src/index.ts b/packages/anastasis-webui/src/index.ts index e78b9c194..4bd7b28f3 100644 --- a/packages/anastasis-webui/src/index.ts +++ b/packages/anastasis-webui/src/index.ts @@ -1,4 +1,4 @@ -import App from './components/app'; -import './scss/main.scss'; +import App from "./components/app"; +import "./scss/main.scss"; export default App; diff --git a/packages/anastasis-webui/src/manifest.json b/packages/anastasis-webui/src/manifest.json index 6b44a2b31..2752dad77 100644 --- a/packages/anastasis-webui/src/manifest.json +++ b/packages/anastasis-webui/src/manifest.json @@ -18,4 +18,4 @@ "sizes": "512x512" } ] -} \ No newline at end of file +} diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx index 08e2b4371..9b067127d 100644 --- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx @@ -15,24 +15,23 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { AddingProviderScreen as TestedComponent } from './AddingProviderScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { AddingProviderScreen as TestedComponent } from "./AddingProviderScreen"; export default { - title: 'Pages/ManageProvider', + title: "Pages/ManageProvider", component: TestedComponent, args: { order: 1, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; @@ -40,20 +39,31 @@ export const NewProvider = createExample(TestedComponent, { ...reducerStatesExample.authEditing, } as ReducerState); - export const NewProviderWithoutProviderList = createExample(TestedComponent, { ...reducerStatesExample.authEditing, - authentication_providers: {} + authentication_providers: {}, } as ReducerState); -export const NewVideoProvider = createExample(TestedComponent, { - ...reducerStatesExample.authEditing, -} as ReducerState, { providerType: 'video'}); +export const NewVideoProvider = createExample( + TestedComponent, + { + ...reducerStatesExample.authEditing, + } as ReducerState, + { providerType: "video" }, +); -export const NewSmsProvider = createExample(TestedComponent, { - ...reducerStatesExample.authEditing, -} as ReducerState, { providerType: 'sms'}); +export const NewSmsProvider = createExample( + TestedComponent, + { + ...reducerStatesExample.authEditing, + } as ReducerState, + { providerType: "sms" }, +); -export const NewIBANProvider = createExample(TestedComponent, { - ...reducerStatesExample.authEditing, -} as ReducerState, { providerType: 'iban' }); +export const NewIBANProvider = createExample( + TestedComponent, + { + ...reducerStatesExample.authEditing, + } as ReducerState, + { providerType: "iban" }, +); diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx index 7504f4d2b..96b38e92d 100644 --- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx @@ -11,185 +11,250 @@ interface Props { onCancel: () => void; } - -async function testProvider(url: string, expectedMethodType?: string): Promise { +async function testProvider( + url: string, + expectedMethodType?: string, +): Promise { try { - const response = await fetch(new URL("config", url).href) - const json = await (response.json().catch(d => ({}))) + const response = await fetch(new URL("config", url).href); + const json = await response.json().catch((d) => ({})); if (!("methods" in json) || !Array.isArray(json.methods)) { - throw Error("This provider doesn't have authentication method. Check the provider URL") + throw Error( + "This provider doesn't have authentication method. Check the provider URL", + ); } - console.log("expected", expectedMethodType) + console.log("expected", expectedMethodType); if (!expectedMethodType) { - return + return; } - let found = false + let found = false; for (let i = 0; i < json.methods.length && !found; i++) { - found = json.methods[i].type === expectedMethodType + found = json.methods[i].type === expectedMethodType; } if (!found) { - throw Error(`This provider does not support authentication method ${expectedMethodType}`) + throw Error( + `This provider does not support authentication method ${expectedMethodType}`, + ); } - return + return; } catch (e) { - console.log("error", e) - const error = e instanceof Error ? - Error(`There was an error testing this provider, try another one. ${e.message}`) : - Error(`There was an error testing this provider, try another one.`) - throw error + console.log("error", e); + const error = + e instanceof Error + ? Error( + `There was an error testing this provider, try another one. ${e.message}`, + ) + : Error(`There was an error testing this provider, try another one.`); + throw error; } - } export function AddingProviderScreen({ providerType, onCancel }: Props): VNode { const reducer = useAnastasisContext(); const [providerURL, setProviderURL] = useState(""); - const [error, setError] = useState() - const [testing, setTesting] = useState(false) - const providerLabel = providerType ? authMethods[providerType].label : undefined + const [error, setError] = useState(); + const [testing, setTesting] = useState(false); + const providerLabel = providerType + ? authMethods[providerType].label + : undefined; //FIXME: move this timeout logic into a hook const timeout = useRef(undefined); useEffect(() => { - if (timeout) window.clearTimeout(timeout.current) + if (timeout) window.clearTimeout(timeout.current); timeout.current = window.setTimeout(async () => { - const url = providerURL.endsWith('/') ? providerURL : (providerURL + '/') + const url = providerURL.endsWith("/") ? providerURL : providerURL + "/"; if (!providerURL || authProviders.includes(url)) return; try { - setTesting(true) - await testProvider(url, providerType) + setTesting(true); + await testProvider(url, providerType); // this is use as tested but everything when ok // undefined will mean that the field is not dirty - setError("") + setError(""); } catch (e) { - console.log("tuvieja", e) - if (e instanceof Error) setError(e.message) + console.log("tuvieja", e); + if (e instanceof Error) setError(e.message); } - setTesting(false) + setTesting(false); }, 200); - }, [providerURL, reducer]) - + }, [providerURL, reducer]); if (!reducer) { return
no reducer in context
; } - if (!reducer.currentReducerState || !("authentication_providers" in reducer.currentReducerState)) { - return
invalid state
+ if ( + !reducer.currentReducerState || + !("authentication_providers" in reducer.currentReducerState) + ) { + return
invalid state
; } async function addProvider(provider_url: string): Promise { - await reducer?.transition("add_provider", { provider_url }) - onCancel() + await reducer?.transition("add_provider", { provider_url }); + onCancel(); } function deleteProvider(provider_url: string): void { - reducer?.transition("delete_provider", { provider_url }) + reducer?.transition("delete_provider", { provider_url }); } - const allAuthProviders = reducer.currentReducerState.authentication_providers || {} - const authProviders = Object.keys(allAuthProviders).filter(provUrl => { + const allAuthProviders = + reducer.currentReducerState.authentication_providers || {}; + const authProviders = Object.keys(allAuthProviders).filter((provUrl) => { const p = allAuthProviders[provUrl]; if (!providerLabel) { - return p && ("currency" in p) + return p && "currency" in p; } else { - return p && ("currency" in p) && p.methods.findIndex(m => m.type === providerType) !== -1 + return ( + p && + "currency" in p && + p.methods.findIndex((m) => m.type === providerType) !== -1 + ); } - }) + }); - let errors = !providerURL ? 'Add provider URL' : undefined + let errors = !providerURL ? "Add provider URL" : undefined; let url: string | undefined; try { - url = new URL("",providerURL).href + url = new URL("", providerURL).href; } catch { - errors = 'Check the URL' + errors = "Check the URL"; } if (!!error && !errors) { - errors = error + errors = error; } if (!errors && authProviders.includes(url!)) { - errors = 'That provider is already known' + errors = "That provider is already known"; } return ( - + hideNext={errors} + >
- {!providerLabel ? -

- Add a provider url -

: -

- Add a provider url for a {providerLabel} service -

- } + {!providerLabel ? ( +

Add a provider url

+ ) : ( +

Add a provider url for a {providerLabel} service

+ )}
+ bind={[providerURL, setProviderURL]} + />
-

- Example: https://kudos.demo.anastasis.lu -

+

Example: https://kudos.demo.anastasis.lu

{testing &&

Testing

} - -
- + +
+ - +
{authProviders.length > 0 ? ( - !providerLabel ? + !providerLabel ? ( +

Current providers

+ ) : (

- Current providers -

:

Current providers for {providerLabel} service

+ ) + ) : !providerLabel ? ( +

No known providers, add one.

) : ( - !providerLabel ?

- No known providers, add one. -

:

- No known providers for {providerLabel} service -

+

No known providers for {providerLabel} service

)} - {authProviders.map(k => { - const p = allAuthProviders[k] as AuthenticationProviderStatusOk - return + {authProviders.map((k) => { + const p = allAuthProviders[k] as AuthenticationProviderStatusOk; + return ; })}
); } -function TableRow({ url, info, onDelete }: { onDelete: (s: string) => void, url: string, info: AuthenticationProviderStatusOk }) { - const [status, setStatus] = useState("checking") +function TableRow({ + url, + info, + onDelete, +}: { + onDelete: (s: string) => void; + url: string; + info: AuthenticationProviderStatusOk; +}) { + const [status, setStatus] = useState("checking"); useEffect(function () { - testProvider(url.endsWith('/') ? url.substring(0, url.length - 1) : url) - .then(function () { setStatus('responding') }) - .catch(function () { setStatus('failed to contact') }) - }) - return
-
-
{url}
-
-
Business Name
-
{info.business_name}
-
Supported methods
-
{info.methods.map(m => m.type).join(',')}
-
Maximum storage
-
{info.storage_limit_in_megabytes} Mb
-
Status
-
{status}
-
+ testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url) + .then(function () { + setStatus("responding"); + }) + .catch(function () { + setStatus("failed to contact"); + }); + }); + return ( +
+
+
{url}
+
+
+ Business Name +
+
{info.business_name}
+
+ Supported methods +
+
{info.methods.map((m) => m.type).join(",")}
+
+ Maximum storage +
+
{info.storage_limit_in_megabytes} Mb
+
+ Status +
+
{status}
+
+
+
+ +
-
- -
-
-} \ No newline at end of file + ); +} diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx index 9cdd132ef..d48e94403 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx @@ -15,76 +15,83 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { AttributeEntryScreen as TestedComponent } from './AttributeEntryScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { AttributeEntryScreen as TestedComponent } from "./AttributeEntryScreen"; export default { - title: 'Pages/PersonalInformation', + title: "Pages/PersonalInformation", component: TestedComponent, args: { order: 3, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; export const Backup = createExample(TestedComponent, { ...reducerStatesExample.backupAttributeEditing, - required_attributes: [{ - name: 'first name', - label: 'first', - type: 'string', - uuid: 'asdasdsa1', - widget: 'wid', - }, { - name: 'last name', - label: 'second', - type: 'string', - uuid: 'asdasdsa2', - widget: 'wid', - }, { - name: 'birthdate', - label: 'birthdate', - type: 'date', - uuid: 'asdasdsa3', - widget: 'calendar', - }] + required_attributes: [ + { + name: "first name", + label: "first", + type: "string", + uuid: "asdasdsa1", + widget: "wid", + }, + { + name: "last name", + label: "second", + type: "string", + uuid: "asdasdsa2", + widget: "wid", + }, + { + name: "birthdate", + label: "birthdate", + type: "date", + uuid: "asdasdsa3", + widget: "calendar", + }, + ], } as ReducerState); export const Recovery = createExample(TestedComponent, { ...reducerStatesExample.recoveryAttributeEditing, - required_attributes: [{ - name: 'first', - label: 'first', - type: 'string', - uuid: 'asdasdsa1', - widget: 'wid', - }, { - name: 'pepe', - label: 'second', - type: 'string', - uuid: 'asdasdsa2', - widget: 'wid', - }, { - name: 'pepe2', - label: 'third', - type: 'date', - uuid: 'asdasdsa3', - widget: 'calendar', - }] + required_attributes: [ + { + name: "first", + label: "first", + type: "string", + uuid: "asdasdsa1", + widget: "wid", + }, + { + name: "pepe", + label: "second", + type: "string", + uuid: "asdasdsa2", + widget: "wid", + }, + { + name: "pepe2", + label: "third", + type: "date", + uuid: "asdasdsa3", + widget: "calendar", + }, + ], } as ReducerState); export const WithNoRequiredAttribute = createExample(TestedComponent, { ...reducerStatesExample.backupAttributeEditing, - required_attributes: undefined + required_attributes: undefined, } as ReducerState); const allWidgets = [ @@ -107,23 +114,22 @@ const allWidgets = [ "anastasis_gtk_ia_tax_de", "anastasis_gtk_xx_prime", "anastasis_gtk_xx_square", -] +]; function typeForWidget(name: string): string { - if (["anastasis_gtk_xx_prime", - "anastasis_gtk_xx_square", - ].includes(name)) return "number"; - if (["anastasis_gtk_ia_birthdate"].includes(name)) return "date" + if (["anastasis_gtk_xx_prime", "anastasis_gtk_xx_square"].includes(name)) + return "number"; + if (["anastasis_gtk_ia_birthdate"].includes(name)) return "date"; return "string"; } export const WithAllPosibleWidget = createExample(TestedComponent, { ...reducerStatesExample.backupAttributeEditing, - required_attributes: allWidgets.map(w => ({ + required_attributes: allWidgets.map((w) => ({ name: w, label: `widget: ${w}`, type: typeForWidget(w), uuid: `uuid-${w}`, - widget: w - })) + widget: w, + })), } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx index 557718458..0918c2db5 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx @@ -9,24 +9,32 @@ import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, withProcessLabel } from "./index"; export function AttributeEntryScreen(): VNode { - const reducer = useAnastasisContext() - const state = reducer?.currentReducerState - const currentIdentityAttributes = state && "identity_attributes" in state ? (state.identity_attributes || {}) : {} - const [attrs, setAttrs] = useState>(currentIdentityAttributes); + const reducer = useAnastasisContext(); + const state = reducer?.currentReducerState; + const currentIdentityAttributes = + state && "identity_attributes" in state + ? state.identity_attributes || {} + : {}; + const [attrs, setAttrs] = useState>( + currentIdentityAttributes, + ); if (!reducer) { - return
no reducer in context
+ return
no reducer in context
; } - if (!reducer.currentReducerState || !("required_attributes" in reducer.currentReducerState)) { - return
invalid state
+ if ( + !reducer.currentReducerState || + !("required_attributes" in reducer.currentReducerState) + ) { + return
invalid state
; } - const reqAttr = reducer.currentReducerState.required_attributes || [] + const reqAttr = reducer.currentReducerState.required_attributes || []; let hasErrors = false; const fieldList: VNode[] = reqAttr.map((spec, i: number) => { - const value = attrs[spec.name] - const error = checkIfValid(value, spec) - hasErrors = hasErrors || error !== undefined + const value = attrs[spec.name]; + const error = checkIfValid(value, spec); + hasErrors = hasErrors || error !== undefined; return ( setAttrs({ ...attrs, [spec.name]: v })} spec={spec} errorMessage={error} - value={value} /> + value={value} + /> ); - }) + }); return ( reducer.transition("enter_user_attributes", { - identity_attributes: attrs, - })} + onNext={() => + reducer.transition("enter_user_attributes", { + identity_attributes: attrs, + }) + } > -
+
+
{fieldList}
- {fieldList} -
-

This personal information will help to locate your secret.

This stays private

The information you have entered here:

@@ -61,9 +70,12 @@ export function AttributeEntryScreen(): VNode { Will be hashed, and therefore unreadable -
  • - - The non-hashed version is not shared
  • +
  • + + + + The non-hashed version is not shared +
  • @@ -78,22 +90,22 @@ interface AttributeEntryFieldProps { spec: UserAttributeSpec; errorMessage: string | undefined; } -const possibleBirthdayYear: Array = [] +const possibleBirthdayYear: Array = []; for (let i = 0; i < 100; i++) { - possibleBirthdayYear.push(2020 - i) + possibleBirthdayYear.push(2020 - i); } function AttributeEntryField(props: AttributeEntryFieldProps): VNode { - return (
    - {props.spec.type === 'date' && + {props.spec.type === "date" && } + /> + } {props.spec.type === 'number' && } - {props.spec.type === 'string' && + {props.spec.type === "string" && ( - } + )}
    This stays private @@ -119,40 +131,43 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
    ); } -const YEAR_REGEX = /^[0-9]+-[0-9]+-[0-9]+$/ +const YEAR_REGEX = /^[0-9]+-[0-9]+-[0-9]+$/; - -function checkIfValid(value: string, spec: UserAttributeSpec): string | undefined { - const pattern = spec['validation-regex'] +function checkIfValid( + value: string, + spec: UserAttributeSpec, +): string | undefined { + const pattern = spec["validation-regex"]; if (pattern) { - const re = new RegExp(pattern) - if (!re.test(value)) return 'The value is invalid' + const re = new RegExp(pattern); + if (!re.test(value)) return "The value is invalid"; } - const logic = spec['validation-logic'] + const logic = spec["validation-logic"]; if (logic) { const func = (validators as any)[logic]; - if (func && typeof func === 'function' && !func(value)) return 'Please check the value' + if (func && typeof func === "function" && !func(value)) + return "Please check the value"; } - const optional = spec.optional + const optional = spec.optional; if (!optional && !value) { - return 'This value is required' + return "This value is required"; } if ("date" === spec.type) { if (!YEAR_REGEX.test(value)) { - return "The date doesn't follow the format" + return "The date doesn't follow the format"; } try { - const v = parse(value, 'yyyy-MM-dd', new Date()); + const v = parse(value, "yyyy-MM-dd", new Date()); if (Number.isNaN(v.getTime())) { - return "Some numeric values seems out of range for a date" + return "Some numeric values seems out of range for a date"; } if ("birthdate" === spec.name && isAfter(v, new Date())) { - return "A birthdate cannot be in the future" + return "A birthdate cannot be in the future"; } } catch (e) { - return "Could not parse the date" + return "Could not parse the date"; } } - return undefined + return undefined; } diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx index 2712522ce..8acf1c8c8 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx @@ -15,73 +15,84 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { AuthenticationEditorScreen as TestedComponent } from './AuthenticationEditorScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { AuthenticationEditorScreen as TestedComponent } from "./AuthenticationEditorScreen"; export default { - title: 'Pages/backup/AuthorizationMethod', + title: "Pages/backup/AuthorizationMethod", component: TestedComponent, args: { order: 4, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const InitialState = createExample(TestedComponent, reducerStatesExample.authEditing); +export const InitialState = createExample( + TestedComponent, + reducerStatesExample.authEditing, +); export const OneAuthMethodConfigured = createExample(TestedComponent, { ...reducerStatesExample.authEditing, - authentication_methods: [{ - type: 'question', - instructions: 'what time is it?', - challenge: 'asd', - }] + authentication_methods: [ + { + type: "question", + instructions: "what time is it?", + challenge: "asd", + }, + ], } as ReducerState); - export const SomeMoreAuthMethodConfigured = createExample(TestedComponent, { ...reducerStatesExample.authEditing, - authentication_methods: [{ - type: 'question', - instructions: 'what time is it?', - challenge: 'asd', - },{ - type: 'question', - instructions: 'what time is it?', - challenge: 'qwe', - },{ - type: 'sms', - instructions: 'what time is it?', - challenge: 'asd', - },{ - type: 'email', - instructions: 'what time is it?', - challenge: 'asd', - },{ - type: 'email', - instructions: 'what time is it?', - challenge: 'asd', - },{ - type: 'email', - instructions: 'what time is it?', - challenge: 'asd', - },{ - type: 'email', - instructions: 'what time is it?', - challenge: 'asd', - }] + authentication_methods: [ + { + type: "question", + instructions: "what time is it?", + challenge: "asd", + }, + { + type: "question", + instructions: "what time is it?", + challenge: "qwe", + }, + { + type: "sms", + instructions: "what time is it?", + challenge: "asd", + }, + { + type: "email", + instructions: "what time is it?", + challenge: "asd", + }, + { + type: "email", + instructions: "what time is it?", + challenge: "asd", + }, + { + type: "email", + instructions: "what time is it?", + challenge: "asd", + }, + { + type: "email", + instructions: "what time is it?", + challenge: "asd", + }, + ], } as ReducerState); export const NoAuthMethodProvided = createExample(TestedComponent, { ...reducerStatesExample.authEditing, authentication_providers: {}, - authentication_methods: [] + authentication_methods: [], } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index 00eb54d4d..1ef326773 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -20,7 +20,9 @@ export function AuthenticationEditorScreen(): VNode { KnownAuthMethods | undefined >(undefined); const [tooFewAuths, setTooFewAuths] = useState(false); - const [manageProvider, setManageProvider] = useState(undefined) + const [manageProvider, setManageProvider] = useState( + undefined, + ); // const [addingProvider, setAddingProvider] = useState(undefined) const reducer = useAnastasisContext(); @@ -68,11 +70,14 @@ export function AuthenticationEditorScreen(): VNode { } if (manageProvider !== undefined) { - - return setManageProvider(undefined)} - providerType={isKnownAuthMethods(manageProvider) ? manageProvider : undefined} - /> + return ( + setManageProvider(undefined)} + providerType={ + isKnownAuthMethods(manageProvider) ? manageProvider : undefined + } + /> + ); } if (selectedMethod) { @@ -100,7 +105,7 @@ export function AuthenticationEditorScreen(): VNode { description="No providers founds" label="Add a provider manually" onConfirm={() => { - setManageProvider(selectedMethod) + setManageProvider(selectedMethod); }} >

    @@ -193,7 +198,7 @@ export function AuthenticationEditorScreen(): VNode { description="No providers founds" label="Add a provider manually" onConfirm={() => { - setManageProvider("") + setManageProvider(""); }} >

    @@ -214,7 +219,10 @@ export function AuthenticationEditorScreen(): VNode { authentication method is defined by the backup provider list.

    -

    diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx index 306adacbb..0789ee6ad 100644 --- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx @@ -15,48 +15,51 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { BackupFinishedScreen as TestedComponent } from './BackupFinishedScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { BackupFinishedScreen as TestedComponent } from "./BackupFinishedScreen"; export default { - title: 'Pages/backup/Finished', + title: "Pages/backup/Finished", component: TestedComponent, args: { order: 8, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const WithoutName = createExample(TestedComponent, reducerStatesExample.backupFinished); +export const WithoutName = createExample( + TestedComponent, + reducerStatesExample.backupFinished, +); -export const WithName = createExample(TestedComponent, {...reducerStatesExample.backupFinished, - secret_name: 'super_secret', +export const WithName = createExample(TestedComponent, { + ...reducerStatesExample.backupFinished, + secret_name: "super_secret", } as ReducerState); export const WithDetails = createExample(TestedComponent, { ...reducerStatesExample.backupFinished, - secret_name: 'super_secret', + secret_name: "super_secret", success_details: { - 'http://anastasis.net': { + "http://anastasis.net": { policy_expiration: { - t_ms: 'never' + t_ms: "never", }, - policy_version: 0 + policy_version: 0, }, - 'http://taler.net': { + "http://taler.net": { policy_expiration: { - t_ms: new Date().getTime() + 60*60*24*1000 + t_ms: new Date().getTime() + 60 * 60 * 24 * 1000, }, - policy_version: 1 + policy_version: 1, }, - } + }, } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx index 7938baca4..825ec5dc0 100644 --- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx @@ -4,41 +4,62 @@ import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; export function BackupFinishedScreen(): VNode { - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return
    no reducer in context
    + return
    no reducer in context
    ; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return
    invalid state
    + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return
    invalid state
    ; } - const details = reducer.currentReducerState.success_details + const details = reducer.currentReducerState.success_details; - return ( - {reducer.currentReducerState.secret_name ?

    - Your backup of secret "{reducer.currentReducerState.secret_name}" was - successful. -

    : -

    - Your secret was successfully backed up. -

    } + return ( + + {reducer.currentReducerState.secret_name ? ( +

    + Your backup of secret{" "} + "{reducer.currentReducerState.secret_name}" was successful. +

    + ) : ( +

    Your secret was successfully backed up.

    + )} - {details &&
    -

    The backup is stored by the following providers:

    - {Object.keys(details).map((x, i) => { - const sd = details[x]; - return ( -
    - {x} -

    - version {sd.policy_version} - {sd.policy_expiration.t_ms !== 'never' ? ` expires at: ${format(sd.policy_expiration.t_ms, 'dd-MM-yyyy')}` : ' without expiration date'} -

    -
    - ); - })} -
    } -
    - -
    -
    ); + {details && ( +
    +

    The backup is stored by the following providers:

    + {Object.keys(details).map((x, i) => { + const sd = details[x]; + return ( +
    + {x} +

    + version {sd.policy_version} + {sd.policy_expiration.t_ms !== "never" + ? ` expires at: ${format( + sd.policy_expiration.t_ms, + "dd-MM-yyyy", + )}` + : " without expiration date"} +

    +
    + ); + })} +
    + )} +
    + +
    +
    + ); } diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx index 46c574cf2..56aee8763 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx @@ -19,7 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ChallengeFeedbackStatus, RecoveryStates, ReducerState } from "anastasis-core"; +import { + ChallengeFeedbackStatus, + RecoveryStates, + ReducerState, +} from "anastasis-core"; import { createExample, reducerStatesExample } from "../../utils"; import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen"; @@ -247,20 +251,20 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( "uuid-1": { state: ChallengeFeedbackStatus.Solved.toString() }, "uuid-2": { state: ChallengeFeedbackStatus.Message.toString(), - message: 'Challenge should be solved' + message: "Challenge should be solved", }, "uuid-3": { state: ChallengeFeedbackStatus.AuthIban.toString(), challenge_amount: "EUR:1", credit_iban: "DE12345789000", business_name: "Data Loss Incorporated", - wire_transfer_subject: "Anastasis 987654321" + wire_transfer_subject: "Anastasis 987654321", }, "uuid-4": { state: ChallengeFeedbackStatus.Payment.toString(), taler_pay_uri: "taler://pay/...", provider: "https://localhost:8080/", - payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", }, "uuid-5": { state: ChallengeFeedbackStatus.RateLimitExceeded.toString(), @@ -269,7 +273,7 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( "uuid-6": { state: ChallengeFeedbackStatus.Redirect.toString(), redirect_url: "https://videoconf.example.com/", - http_status: 303 + http_status: 303, }, "uuid-7": { state: ChallengeFeedbackStatus.ServerFailure.toString(), diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx index ae03dd4d4..c6de00e98 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -11,23 +11,34 @@ function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) { } switch (feedback.state) { case ChallengeFeedbackStatus.Message: - return ( -
    {feedback.message}
    - ); + return
    {feedback.message}
    ; case ChallengeFeedbackStatus.Solved: - return
    + return
    ; case ChallengeFeedbackStatus.Pending: - case ChallengeFeedbackStatus.Solved: case ChallengeFeedbackStatus.AuthIban: return null; case ChallengeFeedbackStatus.ServerFailure: return
    Server error.
    ; case ChallengeFeedbackStatus.RateLimitExceeded: - return
    There were to many failed attempts.
    ; + return ( +
    + There were to many failed attempts. +
    + ); case ChallengeFeedbackStatus.Unsupported: - return
    This client doesn't support solving this type of challenge. Use another version or contact the provider.
    ; + return ( +
    + This client doesn't support solving this type of challenge. Use + another version or contact the provider. +
    + ); case ChallengeFeedbackStatus.TruthUnknown: - return
    Provider doesn't recognize the challenge of the policy. Contact the provider for further information.
    ; + return ( +
    + Provider doesn't recognize the challenge of the policy. Contact the + provider for further information. +
    + ); case ChallengeFeedbackStatus.Redirect: default: return
    ; @@ -70,19 +81,25 @@ export function ChallengeOverviewScreen(): VNode { feedback: challengeFeedback[ch.uuid], }; } - const policiesWithInfo = policies.map((row) => { - let isPolicySolved = true; - const challenges = row - .map(({ uuid }) => { - const info = knownChallengesMap[uuid]; - const isChallengeSolved = info?.feedback?.state === "solved"; - isPolicySolved = isPolicySolved && isChallengeSolved; - return { info, uuid, isChallengeSolved }; - }) - .filter((ch) => ch.info !== undefined); + const policiesWithInfo = policies + .map((row) => { + let isPolicySolved = true; + const challenges = row + .map(({ uuid }) => { + const info = knownChallengesMap[uuid]; + const isChallengeSolved = info?.feedback?.state === "solved"; + isPolicySolved = isPolicySolved && isChallengeSolved; + return { info, uuid, isChallengeSolved }; + }) + .filter((ch) => ch.info !== undefined); - return { isPolicySolved, challenges }; - }); + return { + isPolicySolved, + challenges, + corrupted: row.length > challenges.length, + }; + }) + .filter((p) => !p.corrupted); const atLeastThereIsOnePolicySolved = policiesWithInfo.find((p) => p.isPolicySolved) !== undefined; @@ -92,19 +109,19 @@ export function ChallengeOverviewScreen(): VNode { : undefined; return ( - {!policies.length ? ( + {!policiesWithInfo.length ? (

    No policies found, try with another version of the secret

    - ) : policies.length === 1 ? ( + ) : policiesWithInfo.length === 1 ? (

    One policy found for this secret. You need to solve all the challenges in order to recover your secret.

    ) : (

    - We have found {policies.length} polices. You need to solve all the - challenges from one policy in order to recover your secret. + We have found {policiesWithInfo.length} polices. You need to solve all + the challenges from one policy in order to recover your secret.

    )} {policiesWithInfo.map((policy, policy_index) => { @@ -113,74 +130,100 @@ export function ChallengeOverviewScreen(): VNode { const method = authMethods[info.type as KnownAuthMethods]; if (!method) { - return
    -
    - unknown challenge + return ( +
    +
    + unknown challenge +
    - -
    + ); } - function ChallengeButton({ id, feedback }: { id: string; feedback?: ChallengeFeedback }): VNode { + function ChallengeButton({ + id, + feedback, + }: { + id: string; + feedback?: ChallengeFeedback; + }): VNode { function selectChallenge(): void { - if (reducer) reducer.transition("select_challenge", { uuid: id }) + if (reducer) reducer.transition("select_challenge", { uuid: id }); } if (!feedback) { - return
    - -
    + return ( +
    + +
    + ); } switch (feedback.state) { case ChallengeFeedbackStatus.ServerFailure: case ChallengeFeedbackStatus.Unsupported: case ChallengeFeedbackStatus.TruthUnknown: - case ChallengeFeedbackStatus.RateLimitExceeded: return
    + case ChallengeFeedbackStatus.RateLimitExceeded: + return
    ; case ChallengeFeedbackStatus.AuthIban: - case ChallengeFeedbackStatus.Payment: return
    - -
    - case ChallengeFeedbackStatus.Redirect: return
    - -
    - case ChallengeFeedbackStatus.Solved: return
    -
    - Solved -
    -
    - default: return
    - -
    - + case ChallengeFeedbackStatus.Payment: + return ( +
    + +
    + ); + case ChallengeFeedbackStatus.Redirect: + return ( +
    + +
    + ); + case ChallengeFeedbackStatus.Solved: + return ( +
    +
    Solved
    +
    + ); + default: + return ( +
    + +
    + ); } - // return
    - // {feedback.state !== "solved" ? ( - // - - // } - // > - // {isFree ? "Solve" : `Pay and Solve`} - // - // ) : null} - // {feedback.state === "solved" ? ( - // //
    Solved
    - //
    Solved
    - - // ) : null} - //
    } return (
    -
    ); }); @@ -210,11 +252,13 @@ export function ChallengeOverviewScreen(): VNode { const policyName = policy.challenges .map((x) => x.info.type) .join(" + "); + const opa = !atLeastThereIsOnePolicySolved ? undefined : policy.isPolicySolved - ? undefined - : "0.6"; + ? undefined + : "0.6"; + return (
    no reducer in context
    + return
    no reducer in context
    ; } - if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { - return
    invalid state
    + if ( + !reducer.currentReducerState || + reducer.currentReducerState.recovery_state === undefined + ) { + return
    invalid state
    ; } - const payments = ['']; //reducer.currentReducerState.payments ?? + const payments = [""]; //reducer.currentReducerState.payments ?? return ( - +

    Some of the providers require a payment to store the encrypted authentication information. diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx index 6bdb3515d..0948d603e 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx @@ -16,37 +16,42 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { ContinentSelectionScreen as TestedComponent } from './ContinentSelectionScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { ContinentSelectionScreen as TestedComponent } from "./ContinentSelectionScreen"; export default { - title: 'Pages/Location', + title: "Pages/Location", component: TestedComponent, args: { order: 2, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const BackupSelectContinent = createExample(TestedComponent, reducerStatesExample.backupSelectContinent); +export const BackupSelectContinent = createExample( + TestedComponent, + reducerStatesExample.backupSelectContinent, +); export const BackupSelectCountry = createExample(TestedComponent, { ...reducerStatesExample.backupSelectContinent, - selected_continent: 'Testcontinent', + selected_continent: "Testcontinent", } as ReducerState); -export const RecoverySelectContinent = createExample(TestedComponent, reducerStatesExample.recoverySelectContinent); +export const RecoverySelectContinent = createExample( + TestedComponent, + reducerStatesExample.recoverySelectContinent, +); export const RecoverySelectCountry = createExample(TestedComponent, { ...reducerStatesExample.recoverySelectContinent, - selected_continent: 'Testcontinent', + selected_continent: "Testcontinent", } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx index 3d5fcce55..4cbeb8308 100644 --- a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx @@ -16,94 +16,126 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { EditPoliciesScreen as TestedComponent } from './EditPoliciesScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { EditPoliciesScreen as TestedComponent } from "./EditPoliciesScreen"; export default { - title: 'Pages/backup/ReviewPolicies/EditPolicies', + title: "Pages/backup/ReviewPolicies/EditPolicies", args: { order: 6, }, component: TestedComponent, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const EditingAPolicy = createExample(TestedComponent, { - ...reducerStatesExample.policyReview, - policies: [{ - methods: [{ - authentication_method: 1, - provider: 'https://anastasis.demo.taler.net/' - }, { - authentication_method: 2, - provider: 'http://localhost:8086/' - }] - }, { - methods: [{ - authentication_method: 1, - provider: 'http://localhost:8086/' - }] - }], - authentication_methods: [{ - type: "email", - instructions: "Email to qwe@asd.com", - challenge: "E5VPA" - }, { - type: "totp", - instructions: "Response code for 'Anastasis'", - challenge: "E5VPA" - }, { - type: "sms", - instructions: "SMS to 6666-6666", - challenge: "" - }, { - type: "question", - instructions: "How did the chicken cross the road?", - challenge: "C5SP8" - }] -} as ReducerState, { index : 0}); - -export const CreatingAPolicy = createExample(TestedComponent, { - ...reducerStatesExample.policyReview, - policies: [{ - methods: [{ - authentication_method: 1, - provider: 'https://anastasis.demo.taler.net/' - }, { - authentication_method: 2, - provider: 'http://localhost:8086/' - }] - }, { - methods: [{ - authentication_method: 1, - provider: 'http://localhost:8086/' - }] - }], - authentication_methods: [{ - type: "email", - instructions: "Email to qwe@asd.com", - challenge: "E5VPA" - }, { - type: "totp", - instructions: "Response code for 'Anastasis'", - challenge: "E5VPA" - }, { - type: "sms", - instructions: "SMS to 6666-6666", - challenge: "" - }, { - type: "question", - instructions: "How did the chicken cross the road?", - challenge: "C5SP8" - }] -} as ReducerState, { index : 3}); +export const EditingAPolicy = createExample( + TestedComponent, + { + ...reducerStatesExample.policyReview, + policies: [ + { + methods: [ + { + authentication_method: 1, + provider: "https://anastasis.demo.taler.net/", + }, + { + authentication_method: 2, + provider: "http://localhost:8086/", + }, + ], + }, + { + methods: [ + { + authentication_method: 1, + provider: "http://localhost:8086/", + }, + ], + }, + ], + authentication_methods: [ + { + type: "email", + instructions: "Email to qwe@asd.com", + challenge: "E5VPA", + }, + { + type: "totp", + instructions: "Response code for 'Anastasis'", + challenge: "E5VPA", + }, + { + type: "sms", + instructions: "SMS to 6666-6666", + challenge: "", + }, + { + type: "question", + instructions: "How did the chicken cross the road?", + challenge: "C5SP8", + }, + ], + } as ReducerState, + { index: 0 }, +); +export const CreatingAPolicy = createExample( + TestedComponent, + { + ...reducerStatesExample.policyReview, + policies: [ + { + methods: [ + { + authentication_method: 1, + provider: "https://anastasis.demo.taler.net/", + }, + { + authentication_method: 2, + provider: "http://localhost:8086/", + }, + ], + }, + { + methods: [ + { + authentication_method: 1, + provider: "http://localhost:8086/", + }, + ], + }, + ], + authentication_methods: [ + { + type: "email", + instructions: "Email to qwe@asd.com", + challenge: "E5VPA", + }, + { + type: "totp", + instructions: "Response code for 'Anastasis'", + challenge: "E5VPA", + }, + { + type: "sms", + instructions: "SMS to 6666-6666", + challenge: "", + }, + { + type: "question", + instructions: "How did the chicken cross the road?", + challenge: "C5SP8", + }, + ], + } as ReducerState, + { index: 3 }, +); diff --git a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.tsx index 85cc96c46..198209399 100644 --- a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.tsx @@ -20,7 +20,6 @@ interface Props { index: number; cancel: () => void; confirm: (changes: MethodProvider[]) => void; - } export interface MethodProvider { @@ -28,106 +27,151 @@ export interface MethodProvider { provider: string; } -export function EditPoliciesScreen({ index: policy_index, cancel, confirm }: Props): VNode { - const [changedProvider, setChangedProvider] = useState>([]) +export function EditPoliciesScreen({ + index: policy_index, + cancel, + confirm, +}: Props): VNode { + const [changedProvider, setChangedProvider] = useState>([]); - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return

    no reducer in context
    + return
    no reducer in context
    ; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return
    invalid state
    + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return
    invalid state
    ; } - const selectableProviders: ProviderInfoByType = {} - const allProviders = Object.entries(reducer.currentReducerState.authentication_providers || {}) + const selectableProviders: ProviderInfoByType = {}; + const allProviders = Object.entries( + reducer.currentReducerState.authentication_providers || {}, + ); for (let index = 0; index < allProviders.length; index++) { - const [url, status] = allProviders[index] + const [url, status] = allProviders[index]; if ("methods" in status) { - status.methods.map(m => { - const type: KnownAuthMethods = m.type as KnownAuthMethods - const values = selectableProviders[type] || [] - const isFree = !m.usage_fee || m.usage_fee.endsWith(":0") - values.push({ url, cost: m.usage_fee, isFree }) - selectableProviders[type] = values - }) + status.methods.map((m) => { + const type: KnownAuthMethods = m.type as KnownAuthMethods; + const values = selectableProviders[type] || []; + const isFree = !m.usage_fee || m.usage_fee.endsWith(":0"); + values.push({ url, cost: m.usage_fee, isFree }); + selectableProviders[type] = values; + }); } } - const allAuthMethods = reducer.currentReducerState.authentication_methods ?? []; + const allAuthMethods = + reducer.currentReducerState.authentication_methods ?? []; const policies = reducer.currentReducerState.policies ?? []; - const policy = policies[policy_index] - - for(let method_index = 0; method_index < allAuthMethods.length; method_index++ ) { - policy?.methods.find(m => m.authentication_method === method_index)?.provider + const policy = policies[policy_index]; + + for ( + let method_index = 0; + method_index < allAuthMethods.length; + method_index++ + ) { + policy?.methods.find((m) => m.authentication_method === method_index) + ?.provider; } function sendChanges(): void { - const newMethods: MethodProvider[] = [] + const newMethods: MethodProvider[] = []; allAuthMethods.forEach((method, index) => { - const oldValue = policy?.methods.find(m => m.authentication_method === index) + const oldValue = policy?.methods.find( + (m) => m.authentication_method === index, + ); if (changedProvider[index] === undefined && oldValue !== undefined) { - newMethods.push(oldValue) + newMethods.push(oldValue); } - if (changedProvider[index] !== undefined && changedProvider[index] !== "") { + if ( + changedProvider[index] !== undefined && + changedProvider[index] !== "" + ) { newMethods.push({ authentication_method: index, - provider: changedProvider[index] - }) + provider: changedProvider[index], + }); } - }) - confirm(newMethods) + }); + confirm(newMethods); } - return -
    - {!policy ?

    - Creating a new policy #{policy_index} -

    :

    - Editing policy #{policy_index} -

    } - {allAuthMethods.map((method, index) => { - //take the url from the updated change or from the policy - const providerURL = changedProvider[index] === undefined ? - policy?.methods.find(m => m.authentication_method === index)?.provider : - changedProvider[index]; + return ( + +
    + {!policy ? ( +

    Creating a new policy #{policy_index}

    + ) : ( +

    Editing policy #{policy_index}

    + )} + {allAuthMethods.map((method, index) => { + //take the url from the updated change or from the policy + const providerURL = + changedProvider[index] === undefined + ? policy?.methods.find((m) => m.authentication_method === index) + ?.provider + : changedProvider[index]; - const type: KnownAuthMethods = method.type as KnownAuthMethods - function changeProviderTo(url: string): void { - const copy = [...changedProvider] - copy[index] = url - setChangedProvider(copy) - } - return ( -
    - - {authMethods[type]?.icon} - - - {method.instructions} - - - - changeProviderTo(e.currentTarget.value)} + value={providerURL ?? ""} + > + - ))} - + {selectableProviders[type]?.map((prov) => ( + + ))} + + - -
    - ); - })} -
    - - - - - -
    -
    -
    +
    + ); + })} +
    + + + + + +
    + + + ); } diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx index 3ddf8011e..9bebcfbc9 100644 --- a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx @@ -15,35 +15,40 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { PoliciesPayingScreen as TestedComponent } from './PoliciesPayingScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { PoliciesPayingScreen as TestedComponent } from "./PoliciesPayingScreen"; export default { - title: 'Pages/backup/__PoliciesPaying', + title: "Pages/backup/__PoliciesPaying", component: TestedComponent, args: { order: 9, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const Example = createExample(TestedComponent, reducerStatesExample.policyPay); +export const Example = createExample( + TestedComponent, + reducerStatesExample.policyPay, +); export const WithSomePaymentRequest = createExample(TestedComponent, { ...reducerStatesExample.policyPay, - policy_payment_requests: [{ - payto: 'payto://x-taler-bank/bank.taler/account-a', - provider: 'provider1' - }, { - payto: 'payto://x-taler-bank/bank.taler/account-b', - provider: 'provider2' - }] + policy_payment_requests: [ + { + payto: "payto://x-taler-bank/bank.taler/account-a", + provider: "provider1", + }, + { + payto: "payto://x-taler-bank/bank.taler/account-b", + provider: "provider2", + }, + ], } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx index a470f5155..c3568b32d 100644 --- a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx @@ -3,20 +3,23 @@ import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; export function PoliciesPayingScreen(): VNode { - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return
    no reducer in context
    + return
    no reducer in context
    ; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return
    invalid state
    + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return
    invalid state
    ; } const payments = reducer.currentReducerState.policy_payment_requests ?? []; - + return (

    - Some of the providers require a payment to store the encrypted - recovery document. + Some of the providers require a payment to store the encrypted recovery + document.

      {payments.map((x, i) => { diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx index e92a231a8..47860db29 100644 --- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx @@ -16,30 +16,32 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { RecoveryFinishedScreen as TestedComponent } from './RecoveryFinishedScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { RecoveryFinishedScreen as TestedComponent } from "./RecoveryFinishedScreen"; export default { - title: 'Pages/recovery/Finished', + title: "Pages/recovery/Finished", args: { order: 7, }, component: TestedComponent, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; export const GoodEnding = createExample(TestedComponent, { ...reducerStatesExample.recoveryFinished, - core_secret: { mime: 'text/plain', value: 'hello' } + core_secret: { mime: "text/plain", value: "hello" }, } as ReducerState); -export const BadEnding = createExample(TestedComponent, reducerStatesExample.recoveryFinished); +export const BadEnding = createExample( + TestedComponent, + reducerStatesExample.recoveryFinished, +); diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx index a61ef9efa..11ae09d4f 100644 --- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx @@ -1,39 +1,53 @@ -import { - bytesToString, - decodeCrock -} from "@gnu-taler/taler-util"; +import { bytesToString, decodeCrock } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; export function RecoveryFinishedScreen(): VNode { - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return
      no reducer in context
      + return
      no reducer in context
      ; } - if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { - return
      invalid state
      + if ( + !reducer.currentReducerState || + reducer.currentReducerState.recovery_state === undefined + ) { + return
      invalid state
      ; } - const encodedSecret = reducer.currentReducerState.core_secret + const encodedSecret = reducer.currentReducerState.core_secret; if (!encodedSecret) { - return -

      - Secret not found -

      -
      - -
      -
      + return ( + +

      Secret not found

      +
      + +
      +
      + ); } - const secret = bytesToString(decodeCrock(encodedSecret.value)) + const secret = bytesToString(decodeCrock(encodedSecret.value)); return ( -

      - Secret: {secret} -

      -
      - +

      Your secret: {secret}

      +
      +
      ); diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx index e348101ee..4a1cba6a8 100644 --- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx @@ -15,44 +15,51 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { ReviewPoliciesScreen as TestedComponent } from './ReviewPoliciesScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { ReviewPoliciesScreen as TestedComponent } from "./ReviewPoliciesScreen"; export default { - title: 'Pages/backup/ReviewPolicies', + title: "Pages/backup/ReviewPolicies", args: { order: 6, }, component: TestedComponent, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; export const HasPoliciesButMethodListIsEmpty = createExample(TestedComponent, { ...reducerStatesExample.policyReview, - policies: [{ - methods: [{ - authentication_method: 0, - provider: 'asd' - }, { - authentication_method: 1, - provider: 'asd' - }] - }, { - methods: [{ - authentication_method: 1, - provider: 'asd' - }] - }], - authentication_methods: [] + policies: [ + { + methods: [ + { + authentication_method: 0, + provider: "asd", + }, + { + authentication_method: 1, + provider: "asd", + }, + ], + }, + { + methods: [ + { + authentication_method: 1, + provider: "asd", + }, + ], + }, + ], + authentication_methods: [], } as ReducerState); export const SomePoliciesWithMethods = createExample(TestedComponent, { @@ -62,186 +69,193 @@ export const SomePoliciesWithMethods = createExample(TestedComponent, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" - } - ] + provider: "https://kudos.demo.anastasis.lu/", + }, + ], }, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 0, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" + provider: "https://anastasis.demo.taler.net/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 1, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" + provider: "https://anastasis.demo.taler.net/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] + provider: "https://anastasis.demo.taler.net/", + }, + ], }, { methods: [ { authentication_method: 2, - provider: "https://kudos.demo.anastasis.lu/" + provider: "https://kudos.demo.anastasis.lu/", }, { authentication_method: 3, - provider: "https://anastasis.demo.taler.net/" + provider: "https://anastasis.demo.taler.net/", }, { authentication_method: 4, - provider: "https://anastasis.demo.taler.net/" - } - ] - } + provider: "https://anastasis.demo.taler.net/", + }, + ], + }, + ], + authentication_methods: [ + { + type: "email", + instructions: "Email to qwe@asd.com", + challenge: "E5VPA", + }, + { + type: "sms", + instructions: "SMS to 555-555", + challenge: "", + }, + { + type: "question", + instructions: "Does P equal NP?", + challenge: "C5SP8", + }, + { + type: "totp", + instructions: "Response code for 'Anastasis'", + challenge: "E5VPA", + }, + { + type: "sms", + instructions: "SMS to 6666-6666", + challenge: "", + }, + { + type: "question", + instructions: "How did the chicken cross the road?", + challenge: "C5SP8", + }, ], - authentication_methods: [{ - type: "email", - instructions: "Email to qwe@asd.com", - challenge: "E5VPA" - }, { - type: "sms", - instructions: "SMS to 555-555", - challenge: "" - }, { - type: "question", - instructions: "Does P equal NP?", - challenge: "C5SP8" - },{ - type: "totp", - instructions: "Response code for 'Anastasis'", - challenge: "E5VPA" - }, { - type: "sms", - instructions: "SMS to 6666-6666", - challenge: "" - }, { - type: "question", - instructions: "How did the chicken cross the road?", - challenge: "C5SP8" -}] } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx index aa98b5dd9..c43f0cdea 100644 --- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx @@ -6,16 +6,20 @@ import { EditPoliciesScreen } from "./EditPoliciesScreen"; import { AnastasisClientFrame } from "./index"; export function ReviewPoliciesScreen(): VNode { - const [editingPolicy, setEditingPolicy] = useState() - const reducer = useAnastasisContext() + const [editingPolicy, setEditingPolicy] = useState(); + const reducer = useAnastasisContext(); if (!reducer) { - return
      no reducer in context
      + return
      no reducer in context
      ; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return
      invalid state
      + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return
      invalid state
      ; } - const configuredAuthMethods = reducer.currentReducerState.authentication_methods ?? []; + const configuredAuthMethods = + reducer.currentReducerState.authentication_methods ?? []; const policies = reducer.currentReducerState.policies ?? []; if (editingPolicy !== undefined) { @@ -28,58 +32,109 @@ export function ReviewPoliciesScreen(): VNode { policy_index: editingPolicy, policy: newMethods, }); - setEditingPolicy(undefined) + setEditingPolicy(undefined); }} /> - ) + ); } - const errors = policies.length < 1 ? 'Need more policies' : undefined + const errors = policies.length < 1 ? "Need more policies" : undefined; return ( - - {policies.length > 0 &&

      - Based on your configured authentication method you have created, some policies - have been configured. In order to recover your secret you have to solve all the - challenges of at least one policy. -

      } - {policies.length < 1 &&

      - No policies had been created. Go back and add more authentication methods. -

      } -
      - + + {policies.length > 0 && ( +

      + Based on your configured authentication method you have created, some + policies have been configured. In order to recover your secret you + have to solve all the challenges of at least one policy. +

      + )} + {policies.length < 1 && ( +

      + No policies had been created. Go back and add more authentication + methods. +

      + )} +
      +
      {policies.map((p, policy_index) => { const methods = p.methods - .map(x => configuredAuthMethods[x.authentication_method] && ({ ...configuredAuthMethods[x.authentication_method], provider: x.provider })) - .filter(x => !!x) + .map( + (x) => + configuredAuthMethods[x.authentication_method] && { + ...configuredAuthMethods[x.authentication_method], + provider: x.provider, + }, + ) + .filter((x) => !!x); - const policyName = methods.map(x => x.type).join(" + "); + const policyName = methods.map((x) => x.type).join(" + "); + + if (p.methods.length > methods.length) { + //there is at least one authentication method that is corrupted + return null; + } return ( -
      +

      Policy #{policy_index + 1}: {policyName}

      - {!methods.length &&

      - No auth method found -

      } + {!methods.length &&

      No auth method found

      } {methods.map((m, i) => { return ( -

      +

      {authMethods[m.type as KnownAuthMethods]?.icon} - {m.instructions} recovery provided by {m.provider} + {m.instructions} recovery provided by{" "} + {m.provider}

      ); })}
      -
      - - +
      + +
      ); diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx index db061d936..3f2c6a245 100644 --- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx @@ -15,30 +15,29 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { SecretEditorScreen as TestedComponent } from './SecretEditorScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { SecretEditorScreen as TestedComponent } from "./SecretEditorScreen"; export default { - title: 'Pages/backup/SecretInput', + title: "Pages/backup/SecretInput", component: TestedComponent, args: { order: 7, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; export const WithSecretNamePreselected = createExample(TestedComponent, { ...reducerStatesExample.secretEdition, - secret_name: 'someSecretName', + secret_name: "someSecretName", } as ReducerState); export const WithoutName = createExample(TestedComponent, { diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx index 8d02ebfbe..01ce3f0a7 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx @@ -15,37 +15,35 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { SecretSelectionScreen as TestedComponent } from './SecretSelectionScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { SecretSelectionScreen as TestedComponent } from "./SecretSelectionScreen"; export default { - title: 'Pages/recovery/SecretSelection', + title: "Pages/recovery/SecretSelection", component: TestedComponent, args: { order: 4, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; export const Example = createExample(TestedComponent, { ...reducerStatesExample.secretSelection, recovery_document: { - provider_url: 'https://kudos.demo.anastasis.lu/', - secret_name: 'secretName', + provider_url: "https://kudos.demo.anastasis.lu/", + secret_name: "secretName", version: 1, }, } as ReducerState); - export const NoRecoveryDocumentFound = createExample(TestedComponent, { ...reducerStatesExample.secretSelection, recovery_document: undefined, diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx index 398393619..4000f9bfe 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx @@ -8,18 +8,23 @@ import { AnastasisClientFrame } from "./index"; export function SecretSelectionScreen(): VNode { const [selectingVersion, setSelectingVersion] = useState(false); - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); - const [manageProvider, setManageProvider] = useState(false) - const currentVersion = (reducer?.currentReducerState - && ("recovery_document" in reducer.currentReducerState) - && reducer.currentReducerState.recovery_document?.version) || 0; + const [manageProvider, setManageProvider] = useState(false); + const currentVersion = + (reducer?.currentReducerState && + "recovery_document" in reducer.currentReducerState && + reducer.currentReducerState.recovery_document?.version) || + 0; if (!reducer) { - return
      no reducer in context
      + return
      no reducer in context
      ; } - if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { - return
      invalid state
      + if ( + !reducer.currentReducerState || + reducer.currentReducerState.recovery_state === undefined + ) { + return
      invalid state
      ; } async function doSelectVersion(p: string, n: number): Promise { @@ -33,72 +38,101 @@ export function SecretSelectionScreen(): VNode { }); } - const providerList = Object.keys(reducer.currentReducerState.authentication_providers ?? {}) - const recoveryDocument = reducer.currentReducerState.recovery_document + const providerList = Object.keys( + reducer.currentReducerState.authentication_providers ?? {}, + ); + const recoveryDocument = reducer.currentReducerState.recovery_document; if (!recoveryDocument) { - return doSelectVersion(newProv, 0)} - /> + return ( + doSelectVersion(newProv, 0)} + /> + ); } if (selectingVersion) { - return setSelectingVersion(false)} - onConfirm={doSelectVersion} - /> + return ( + setSelectingVersion(false)} + onConfirm={doSelectVersion} + /> + ); } if (manageProvider) { - return setManageProvider(false)} /> + return setManageProvider(false)} />; } return (
      -
      +

      {recoveryDocument.provider_url}

      - {currentVersion === 0 ?

      - Set to recover the latest version -

      :

      - Set to recover the version number {currentVersion} -

      } + {currentVersion === 0 ? ( +

      Set to recover the latest version

      + ) : ( +

      Set to recover the version number {currentVersion}

      + )}
      - +
      -

      Secret found, you can select another version or continue to the challenges solving

      -

      - +

      + Secret found, you can select another version or continue to the + challenges solving +

      +

      + setManageProvider(true)}> + Manage recovery providers +

      -
      ); } - -function ChooseAnotherProviderScreen({ providers, selected, onChange }: { selected: string; providers: string[]; onChange: (prov: string) => void }): VNode { +function ChooseAnotherProviderScreen({ + providers, + selected, + onChange, +}: { + selected: string; + providers: string[]; + onChange: (prov: string) => void; +}): VNode { return ( - +

      No recovery document found, try with another provider

      - onChange(e.currentTarget.value)} + value={selected} + > + + {providers.map((prov) => ( @@ -114,9 +148,23 @@ function ChooseAnotherProviderScreen({ providers, selected, onChange }: { select ); } -function SelectOtherVersionProviderScreen({ providers, provider, version, onConfirm, onCancel }: { onCancel: () => void; provider: string; version: number; providers: string[]; onConfirm: (prov: string, v: number) => Promise; }): VNode { +function SelectOtherVersionProviderScreen({ + providers, + provider, + version, + onConfirm, + onCancel, +}: { + onCancel: () => void; + provider: string; + version: number; + providers: string[]; + onConfirm: (prov: string, v: number) => Promise; +}): VNode { const [otherProvider, setOtherProvider] = useState(provider); - const [otherVersion, setOtherVersion] = useState(version > 0 ? String(version) : ""); + const [otherVersion, setOtherVersion] = useState( + version > 0 ? String(version) : "", + ); return ( @@ -125,11 +173,11 @@ function SelectOtherVersionProviderScreen({ providers, provider, version, onConf

      Provider {otherProvider}

      - {version === 0 ?

      - Set to recover the latest version -

      :

      - Set to recover the version number {version} -

      } + {version === 0 ? ( +

      Set to recover the latest version

      + ) : ( +

      Set to recover the version number {version}

      + )}

      Specify other version below or use the latest

      @@ -137,9 +185,15 @@ function SelectOtherVersionProviderScreen({ providers, provider, version, onConf
      - setOtherProvider(e.currentTarget.value)} + value={otherProvider} + > + + {providers.map((prov) => ( @@ -156,23 +210,40 @@ function SelectOtherVersionProviderScreen({ providers, provider, version, onConf label="Version" placeholder="version number to recover" grabFocus - bind={[otherVersion, setOtherVersion]} /> + bind={[otherVersion, setOtherVersion]} + />
      -
      - +
      +
      - onConfirm(otherProvider, 0)}>Use latest - onConfirm(otherProvider, parseInt(otherVersion, 10))}>Confirm + onConfirm(otherProvider, 0)} + > + Use latest + + + onConfirm(otherProvider, parseInt(otherVersion, 10)) + } + > + Confirm +
      -
      - . -
      +
      .
      -
      ); - } diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx index f1f2802ae..76d0700db 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx @@ -15,55 +15,63 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, RecoveryStates, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { SolveScreen as TestedComponent } from './SolveScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { + ChallengeFeedbackStatus, + RecoveryStates, + ReducerState, +} from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { SolveScreen as TestedComponent } from "./SolveScreen"; export default { - title: 'Pages/recovery/SolveChallenge/Solve', + title: "Pages/recovery/SolveChallenge/Solve", component: TestedComponent, args: { order: 6, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const NoInformation = createExample(TestedComponent, reducerStatesExample.challengeSolving); +export const NoInformation = createExample( + TestedComponent, + reducerStatesExample.challengeSolving, +); export const NotSupportedChallenge = createExample(TestedComponent, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'chall-type', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "chall-type", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', - + selected_challenge_uuid: "ASDASDSAD!1", } as ReducerState); export const MismatchedChallengeId = createExample(TestedComponent, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'chall-type', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "chall-type", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'no-no-no' + selected_challenge_uuid: "no-no-no", } as ReducerState); - diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index 5e17c9aa1..b87dad2ce 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -2,76 +2,126 @@ import { h, VNode } from "preact"; import { AnastasisClientFrame } from "."; import { ChallengeFeedback, - ChallengeFeedbackStatus + ChallengeFeedbackStatus, } from "../../../../anastasis-core/lib"; import { Notifications } from "../../components/Notifications"; import { useAnastasisContext } from "../../context/anastasis"; import { authMethods, KnownAuthMethods } from "./authMethod"; -export function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }): VNode { +export function SolveOverviewFeedbackDisplay(props: { + feedback?: ChallengeFeedback; +}): VNode { const { feedback } = props; if (!feedback) { return
      ; } switch (feedback.state) { case ChallengeFeedbackStatus.Message: - return (); - case ChallengeFeedbackStatus.Payment: - return - To pay you can click here - - }]} /> - case ChallengeFeedbackStatus.AuthIban: - return ; - case ChallengeFeedbackStatus.ServerFailure: - return (); - case ChallengeFeedbackStatus.RateLimitExceeded: - return (); - case ChallengeFeedbackStatus.Redirect: - return ( - Please visit this link: {feedback.redirect_url} - - }]} />); - case ChallengeFeedbackStatus.Unsupported: - return (); - case ChallengeFeedbackStatus.TruthUnknown: - return (); - default: return ( -
      -
      {JSON.stringify(feedback)}
      -
      + ); + case ChallengeFeedbackStatus.Payment: + return ( + + To pay you can click here + + ), + }, + ]} + /> + ); + case ChallengeFeedbackStatus.AuthIban: + return ( + + ); + case ChallengeFeedbackStatus.ServerFailure: + return ( + + ); + case ChallengeFeedbackStatus.RateLimitExceeded: + return ( + + ); + case ChallengeFeedbackStatus.Redirect: + return ( + + Please visit this link: {feedback.redirect_url} + + ), + }, + ]} + /> + ); + case ChallengeFeedbackStatus.Unsupported: + return ( + + ); + case ChallengeFeedbackStatus.TruthUnknown: + return ( + + ); + default: + return
      ; } } @@ -110,8 +160,16 @@ export function SolveScreen(): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -120,26 +178,36 @@ export function SolveScreen(): VNode { return (

      - The challenge selected is not supported for this UI. Please update this - version or try using another policy. + The challenge selected is not supported for this UI. Please update + this version or try using another policy.

      - {reducer && -
      - + {reducer && ( +
      +
      - } + )} ); } - const chArr = reducer.currentReducerState.recovery_information.challenges; const selectedUuid = reducer.currentReducerState.selected_challenge_uuid; - const selectedChallenge = chArr.find(ch => ch.uuid === selectedUuid) + const selectedChallenge = chArr.find((ch) => ch.uuid === selectedUuid); - const SolveDialog = !selectedChallenge || !authMethods[selectedChallenge.type as KnownAuthMethods] ? - SolveNotImplemented : - authMethods[selectedChallenge.type as KnownAuthMethods].solve ?? SolveNotImplemented + const SolveDialog = + !selectedChallenge || + !authMethods[selectedChallenge.type as KnownAuthMethods] + ? SolveNotImplemented + : authMethods[selectedChallenge.type as KnownAuthMethods].solve ?? + SolveNotImplemented; - return + return ; } diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx index 41082c128..fcddaf87a 100644 --- a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx @@ -15,24 +15,26 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../utils'; -import { StartScreen as TestedComponent } from './StartScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../utils"; +import { StartScreen as TestedComponent } from "./StartScreen"; export default { - title: 'Pages/Start', + title: "Pages/Start", component: TestedComponent, args: { order: 1, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const InitialState = createExample(TestedComponent, reducerStatesExample.initial); \ No newline at end of file +export const InitialState = createExample( + TestedComponent, + reducerStatesExample.initial, +); diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.tsx index d53df4cae..8b24ef684 100644 --- a/packages/anastasis-webui/src/pages/home/StartScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/StartScreen.tsx @@ -1,27 +1,36 @@ - import { h, VNode } from "preact"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; export function StartScreen(): VNode { - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return
      no reducer in context
      + return
      no reducer in context
      ; } return (
      -
      - - @@ -30,7 +39,6 @@ export function StartScreen(): VNode { Restore a session */}
      -
      diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx index 38b71bc36..245ad8889 100644 --- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx @@ -15,29 +15,31 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../utils'; -import { TruthsPayingScreen as TestedComponent } from './TruthsPayingScreen'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../utils"; +import { TruthsPayingScreen as TestedComponent } from "./TruthsPayingScreen"; export default { - title: 'Pages/backup/__TruthsPaying', + title: "Pages/backup/__TruthsPaying", component: TestedComponent, args: { order: 10, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -export const Example = createExample(TestedComponent, reducerStatesExample.truthsPaying); +export const Example = createExample( + TestedComponent, + reducerStatesExample.truthsPaying, +); export const WithPaytoList = createExample(TestedComponent, { ...reducerStatesExample.truthsPaying, - payments: ['payto://x-taler-bank/bank/account'] + payments: ["payto://x-taler-bank/bank/account"], } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx index 0b32e0db5..6f95fa93b 100644 --- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx @@ -3,19 +3,19 @@ import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; export function TruthsPayingScreen(): VNode { - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return
      no reducer in context
      + return
      no reducer in context
      ; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return
      invalid state
      + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return
      invalid state
      ; } const payments = reducer.currentReducerState.payments ?? []; return ( - +

      Some of the providers require a payment to store the encrypted authentication information. diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx index da87b7a8b..080a7ab31 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx @@ -15,51 +15,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/email', + title: "Pages/backup/AuthorizationMethod/AuthMethods/email", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'email' +const type: KnownAuthMethods = "email"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Email to sebasjm@email.com ', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Email to sebasjm@email.com ", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Email to sebasjm@email.com', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Email to someone@sebasjm.com', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Email to sebasjm@email.com", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Email to someone@sebasjm.com", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx index 27a0685b2..61c66c8c8 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx @@ -1,57 +1,90 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { EmailInput } from "../../../components/fields/EmailInput"; import { AnastasisClientFrame } from "../index"; import { AuthMethodSetupProps } from "./index"; -const EMAIL_PATTERN = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const EMAIL_PATTERN = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -export function AuthMethodEmailSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodEmailSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [email, setEmail] = useState(""); - const addEmailAuth = (): void => addAuthMethod({ - authentication_method: { - type: "email", - instructions: `Email to ${email}`, - challenge: encodeCrock(stringToBytes(email)), - }, - }); - const emailError = !EMAIL_PATTERN.test(email) ? 'Email address is not valid' : undefined - const errors = !email ? 'Add your email' : emailError + const addEmailAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "email", + instructions: `Email to ${email}`, + challenge: encodeCrock(stringToBytes(email)), + }, + }); + const emailError = !EMAIL_PATTERN.test(email) + ? "Email address is not valid" + : undefined; + const errors = !email ? "Add your email" : emailError; return (

      For email authentication, you need to provide an email address. When recovering your secret, you will need to enter the code you receive by - email. + email. Add the uuid from the challenge

      + bind={[email, setEmail]} + />
      - {configured.length > 0 &&
      -
      - Your emails: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      } + {configured.length > 0 && ( +
      +
      Your emails:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )}
      -
      - +
      + - +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx index 525cd2b07..6a8a2a347 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx @@ -15,66 +15,76 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/email', + title: "Pages/recovery/SolveChallenge/AuthMethods/email", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'email' +const type: KnownAuthMethods = "email"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); +); -export const PaymentFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const PaymentFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + challenge_feedback: { + "uuid-1": { + state: ChallengeFeedbackStatus.Payment, + taler_pay_uri: "taler://pay/...", + provider: "https://localhost:8080/", + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", + }, + }, + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', - challenge_feedback: { - 'uuid-1': { - state: ChallengeFeedbackStatus.Payment, - taler_pay_uri: "taler://pay/...", - provider: "https://localhost:8080/", - payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" - } - } -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx index bd4f43740..ff6c51d1c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,19 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return (

      - An email has been sent to "{selectedChallenge.instructions}". Type the - code below + An email has been sent to "{selectedChallenge.instructions}". + Type the code below. + Here we need to add the code "{selectedUuid}"

      @@ -97,9 +105,11 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx index be0a04847..c521e18fd 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx @@ -15,50 +15,66 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/IBAN', + title: "Pages/backup/AuthorizationMethod/AuthMethods/IBAN", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'iban' +const type: KnownAuthMethods = "iban"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', - remove: () => null - }] -}); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Javier', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', - remove: () => null - }] -},); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Sebastian", + remove: () => null, + }, + ], + }, +); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Javier", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Sebastian", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx index 87969ab27..dee550e5b 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx @@ -1,7 +1,7 @@ import { canonicalJson, encodeCrock, - stringToBytes + stringToBytes, } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -9,56 +9,98 @@ import { AuthMethodSetupProps } from "."; import { TextInput } from "../../../components/fields/TextInput"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodIbanSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodIbanSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [name, setName] = useState(""); const [account, setAccount] = useState(""); - const addIbanAuth = (): void => addAuthMethod({ - authentication_method: { - type: "iban", - instructions: `Wire transfer from ${account} with holder ${name}`, - challenge: encodeCrock(stringToBytes(canonicalJson({ - name, account - }))), - }, - }); - const errors = !name ? 'Add an account name' : ( - !account ? 'Add an account IBAN number' : undefined - ) + const addIbanAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "iban", + instructions: `Wire transfer from ${account} with holder ${name}`, + challenge: encodeCrock( + stringToBytes( + canonicalJson({ + name, + account, + }), + ), + ), + }, + }); + const errors = !name + ? "Add an account name" + : !account + ? "Add an account IBAN number" + : undefined; return (

      - For bank transfer authentication, you need to provide a bank - account (account holder name and IBAN). When recovering your - secret, you will be asked to pay the recovery fee via bank - transfer from the account you provided here. + For bank transfer authentication, you need to provide a bank account + (account holder name and IBAN). When recovering your secret, you will be + asked to pay the recovery fee via bank transfer from the account you + provided here.

      + bind={[name, setName]} + /> + bind={[account, setAccount]} + />
      - {configured.length > 0 &&
      -
      - Your bank accounts: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      } + {configured.length > 0 && ( +
      +
      Your bank accounts:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )}
      -
      - +
      + - +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx index df73a9214..cbbc253e9 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/Iban', + title: "Pages/recovery/SolveChallenge/AuthMethods/Iban", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'iban' +const type: KnownAuthMethods = "iban"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx index 1e4353da6..46cf0502c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,19 +79,17 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( -

      - Send a wire transfer to the address -

      - +

      Send a wire transfer to the address,

      +
      Cancel - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx index adc83d6fe..2977586ac 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx @@ -16,51 +16,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Post', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Post", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'post' +const type: KnownAuthMethods = "post"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code QWE456', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code QWE456", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code QWE456', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code ABC123', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code QWE456", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code ABC123", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx index 692421d74..6c8d36bcf 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx @@ -1,6 +1,7 @@ import { - canonicalJson, encodeCrock, - stringToBytes + canonicalJson, + encodeCrock, + stringToBytes, } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -8,7 +9,11 @@ import { AnastasisClientFrame } from ".."; import { TextInput } from "../../../components/fields/TextInput"; import { AuthMethodSetupProps } from "./index"; -export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodPostSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [fullName, setFullName] = useState(""); const [street, setStreet] = useState(""); const [city, setCity] = useState(""); @@ -32,68 +37,83 @@ export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthM }); }; - const errors = !fullName ? 'The full name is missing' : ( - !street ? 'The street is missing' : ( - !city ? 'The city is missing' : ( - !postcode ? 'The postcode is missing' : ( - !country ? 'The country is missing' : undefined - ) - ) - ) - ) + const errors = !fullName + ? "The full name is missing" + : !street + ? "The street is missing" + : !city + ? "The city is missing" + : !postcode + ? "The postcode is missing" + : !country + ? "The country is missing" + : undefined; return (

      - For postal letter authentication, you need to provide a postal - address. When recovering your secret, you will be asked to enter a - code that you will receive in a letter to that address. + For postal letter authentication, you need to provide a postal address. + When recovering your secret, you will be asked to enter a code that you + will receive in a letter to that address.

      - +
      - +
      - +
      - +
      - +
      - {configured.length > 0 &&
      -
      - Your postal code: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      -
      } -
      - + {configured.length > 0 && ( +
      +
      Your postal code:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )} +
      + - +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx index 99451090b..3b67ee884 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/post', + title: "Pages/recovery/SolveChallenge/AuthMethods/post", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'post' +const type: KnownAuthMethods = "post"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx index 7e3c45abe..ee001ebe9 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( -

      - Wait for the answer -

      +

      Wait for the answer

      Cancel - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx index 0c3ee2b77..991301cbf 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx @@ -16,51 +16,69 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Question', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Question", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'question' +const type: KnownAuthMethods = "question"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Is integer factorization polynomial? (non-quantum computer)', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: + "Is integer factorization polynomial? (non-quantum computer)", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Does P equal NP?', - remove: () => null - },{ - challenge: 'asd', - type, - instructions: 'Are continuous groups automatically differential groups?', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Does P equal NP?", + remove: () => null, + }, + { + challenge: "asd", + type, + instructions: + "Are continuous groups automatically differential groups?", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx index 780bfcb82..0a14021dd 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx @@ -1,27 +1,31 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./index"; import { AnastasisClientFrame } from "../index"; import { TextInput } from "../../../components/fields/TextInput"; -export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodQuestionSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [questionText, setQuestionText] = useState(""); const [answerText, setAnswerText] = useState(""); - const addQuestionAuth = (): void => addAuthMethod({ - authentication_method: { - type: "question", - instructions: questionText, - challenge: encodeCrock(stringToBytes(answerText)), - }, - }); + const addQuestionAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "question", + instructions: questionText, + challenge: encodeCrock(stringToBytes(answerText)), + }, + }); - const errors = !questionText ? "Add your security question" : ( - !answerText ? 'Add the answer to your question' : undefined - ) + const errors = !questionText + ? "Add your security question" + : !answerText + ? "Add the answer to your question" + : undefined; return (
      @@ -36,7 +40,8 @@ export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: A label="Security question" grabFocus placeholder="Your question" - bind={[questionText, setQuestionText]} /> + bind={[questionText, setQuestionText]} + />
      -
      - +
      + - +
      - {configured.length > 0 &&
      -
      - Your security questions: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      } + {configured.length > 0 && ( +
      +
      Your security questions:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )}
      -
      + ); } diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx index f0ec92d4d..1fa9fd6ec 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx @@ -15,186 +15,205 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/question', + title: "Pages/recovery/SolveChallenge/AuthMethods/question", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'question' +const type: KnownAuthMethods = "question"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); +); export const MessageFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Message, - message: 'Challenge should be solved' - } - } - -} as ReducerState); - -export const ServerFailureFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], - policies: [], + message: "Challenge should be solved", + }, }, - selected_challenge_uuid: 'ASDASDSAD!1', - challenge_feedback: { - 'ASDASDSAD!1': { - state: ChallengeFeedbackStatus.ServerFailure, - http_status: 500, - error_response: "Couldn't connect to mysql" - } - } - } as ReducerState); +export const ServerFailureFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "ASDASDSAD!1", + challenge_feedback: { + "ASDASDSAD!1": { + state: ChallengeFeedbackStatus.ServerFailure, + http_status: 500, + error_response: "Couldn't connect to mysql", + }, + }, + } as ReducerState, +); + export const RedirectFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Redirect, http_status: 302, - redirect_url: 'http://video.taler.net' - } - } - -} as ReducerState); - -export const MessageRateLimitExceededFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], - policies: [], + redirect_url: "http://video.taler.net", + }, }, - selected_challenge_uuid: 'ASDASDSAD!1', - challenge_feedback: { - 'ASDASDSAD!1': { - state: ChallengeFeedbackStatus.RateLimitExceeded, - } - } - } as ReducerState); +export const MessageRateLimitExceededFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "ASDASDSAD!1", + challenge_feedback: { + "ASDASDSAD!1": { + state: ChallengeFeedbackStatus.RateLimitExceeded, + }, + }, + } as ReducerState, +); + export const UnsupportedFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Unsupported, http_status: 500, - unsupported_method: 'Question' - } - } - + unsupported_method: "Question", + }, + }, } as ReducerState); export const TruthUnknownFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.TruthUnknown, - } - } - + }, + }, } as ReducerState); export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.AuthIban, challenge_amount: "EUR:1", credit_iban: "DE12345789000", @@ -210,30 +229,30 @@ export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { wire_transfer_subject: "foo", }, method: "iban", - } - } - + }, + }, } as ReducerState); export const PaymentFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Payment, - taler_pay_uri : "taler://pay/...", - provider : "https://localhost:8080/", - payment_secret : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" - } - } + taler_pay_uri: "taler://pay/...", + provider: "https://localhost:8080/", + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", + }, + }, } as ReducerState); - diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx index ee1c0028f..222789507 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( -

      - Answer the question please -

      +

      Answer the question please

      Cancel - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx index da2087ce1..3a44c7ad0 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx @@ -16,51 +16,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Sms', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Sms", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'sms' +const type: KnownAuthMethods = "sms"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-1234-2345', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "SMS to +11-1234-2345", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-1234-2345', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-5555-2345', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "SMS to +11-1234-2345", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "SMS to +11-5555-2345", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx index 9a0459d78..056b1b175 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx @@ -1,14 +1,15 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; import { AuthMethodSetupProps } from "."; import { PhoneNumberInput } from "../../../components/fields/NumberInput"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodSmsSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [mobileNumber, setMobileNumber] = useState(""); const addSmsAuth = (): void => { addAuthMethod({ @@ -23,7 +24,7 @@ export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMe useLayoutEffect(() => { inputRef.current?.focus(); }, []); - const errors = !mobileNumber ? 'Add a mobile number' : undefined + const errors = !mobileNumber ? "Add a mobile number" : undefined; return (
      @@ -37,23 +38,52 @@ export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMe label="Mobile number" placeholder="Your mobile number" grabFocus - bind={[mobileNumber, setMobileNumber]} /> + bind={[mobileNumber, setMobileNumber]} + />
      - {configured.length > 0 &&
      -
      - Your mobile numbers: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      } -
      - + {configured.length > 0 && ( +
      +
      Your mobile numbers:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )} +
      + - +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx index 76e769303..3dc3adb2b 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/sms', + title: "Pages/recovery/SolveChallenge/AuthMethods/sms", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'sms' +const type: KnownAuthMethods = "sms"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx index ce7159bd0..8ee4d600a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,18 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return (

      - An sms has been sent to "{selectedChallenge.instructions}". Type the code - below + An sms has been sent to "{selectedChallenge.instructions}". Type + the code below

      @@ -97,9 +104,11 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx index c0a52924c..bc4628828 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx @@ -16,49 +16,65 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/TOTP', + title: "Pages/backup/AuthorizationMethod/AuthMethods/TOTP", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'totp' +const type: KnownAuthMethods = "totp"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis"', - remove: () => null - }] -}); -export const WithMoreExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis1"', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis2"', - remove: () => null - }] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis"', + remove: () => null, + }, + ], + }, +); +export const WithMoreExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis1"', + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis2"', + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx index a8ac499b2..1451aadc8 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx @@ -1,7 +1,4 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useMemo, useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./index"; @@ -10,30 +7,37 @@ import { TextInput } from "../../../components/fields/TextInput"; import { QR } from "../../../components/QR"; import { base32enc, computeTOTPandCheck } from "./totp"; -export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodTotpSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [name, setName] = useState("anastasis"); const [test, setTest] = useState(""); - const digits = 8 + const digits = 8; const secretKey = useMemo(() => { - const array = new Uint8Array(32) - return window.crypto.getRandomValues(array) - }, []) + const array = new Uint8Array(32); + return window.crypto.getRandomValues(array); + }, []); const secret32 = base32enc(secretKey); - const totpURL = `otpauth://totp/${name}?digits=${digits}&secret=${secret32}` + const totpURL = `otpauth://totp/${name}?digits=${digits}&secret=${secret32}`; - const addTotpAuth = (): void => addAuthMethod({ - authentication_method: { - type: "totp", - instructions: `Enter ${digits} digits code for "${name}"`, - challenge: encodeCrock(stringToBytes(totpURL)), - }, - }); + const addTotpAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "totp", + instructions: `Enter ${digits} digits code for "${name}"`, + challenge: encodeCrock(stringToBytes(totpURL)), + }, + }); const testCodeMatches = computeTOTPandCheck(secretKey, 8, parseInt(test, 10)); - const errors = !name ? 'The TOTP name is missing' : ( - !testCodeMatches ? 'The test code doesnt match' : undefined - ); + const errors = !name + ? "The TOTP name is missing" + : !testCodeMatches + ? "The test code doesnt match" + : undefined; return (

      @@ -42,10 +46,7 @@ export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthM with your TOTP App to import the TOTP secret into your TOTP App.

      - +
      @@ -53,25 +54,51 @@ export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthM

      After scanning the code with your TOTP App, test it in the input below.

      - - {configured.length > 0 &&
      -
      - Your TOTP numbers: -
      - {configured.map((c, i) => { - return
      -

      {c.instructions}

      -
      -
      - })} -
      } + + {configured.length > 0 && ( +
      +
      Your TOTP numbers:
      +
      + {configured.map((c, i) => { + return ( +
      +

      + {c.instructions} +

      +
      + +
      +
      + ); + })} +
      +
      + )}
      -
      - +
      + - +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx index a301931b2..8743c5a73 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/totp', + title: "Pages/recovery/SolveChallenge/AuthMethods/totp", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'totp' +const type: KnownAuthMethods = "totp"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx index 30fc44f0e..98c2e51df 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( -

      - enter the totp solution -

      +

      enter the totp solution

      Cancel - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx index 52e897c60..4aad0a097 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx @@ -16,51 +16,68 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; -import logoImage from '../../../assets/logo.jpeg' +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; +import logoImage from "../../../assets/logo.jpeg"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Video', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Video", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'video' +const type: KnownAuthMethods = "video"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx index 22abe4a49..672b23500 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx @@ -1,53 +1,86 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { ImageInput } from "../../../components/fields/ImageInput"; import { AuthMethodSetupProps } from "./index"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodVideoSetup({cancel, addAuthMethod, configured}: AuthMethodSetupProps): VNode { +export function AuthMethodVideoSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [image, setImage] = useState(""); const addVideoAuth = (): void => { addAuthMethod({ authentication_method: { type: "video", - instructions: 'Join a video call', + instructions: "Join a video call", challenge: encodeCrock(stringToBytes(image)), }, - }) + }); }; return (

      - For video identification, you need to provide a passport-style - photograph. When recovering your secret, you will be asked to join a - video call. During that call, a human will use the photograph to - verify your identity. + For video identification, you need to provide a passport-style + photograph. When recovering your secret, you will be asked to join a + video call. During that call, a human will use the photograph to verify + your identity.

      -
      +
      + bind={[image, setImage]} + />
      - {configured.length > 0 &&
      + {configured.length > 0 && ( +
      +
      Your photographs:
      - Your photographs: -
      {configured.map((c, i) => { - return
      - -
      -
      + return ( +
      + +
      + +
      +
      + ); })} -
      } +
      + + )}
      -
      - - +
      + +
      diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx index 5c4976b87..7c5511c5a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/video', + title: "Pages/recovery/SolveChallenge/AuthMethods/video", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'video' +const type: KnownAuthMethods = "video"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx index 79401028a..efadb9a9a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { return (
      invalid state
      -
      - +
      +
      ); @@ -62,8 +70,7 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( -

      - You are gonna be called to check your identity -

      +

      You are gonna be called to check your identity

      Cancel - {!shouldHideConfirm && - Confirm - } + {!shouldHideConfirm && ( + + Confirm + + )}
      ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx index 8b0126ce7..b4f649488 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx @@ -1,9 +1,9 @@ import { AuthMethod } from "anastasis-core"; import { h, VNode } from "preact"; -import postalIcon from '../../../assets/icons/auth_method/postal.svg'; -import questionIcon from '../../../assets/icons/auth_method/question.svg'; -import smsIcon from '../../../assets/icons/auth_method/sms.svg'; -import videoIcon from '../../../assets/icons/auth_method/video.svg'; +import postalIcon from "../../../assets/icons/auth_method/postal.svg"; +import questionIcon from "../../../assets/icons/auth_method/question.svg"; +import smsIcon from "../../../assets/icons/auth_method/sms.svg"; +import videoIcon from "../../../assets/icons/auth_method/video.svg"; import { AuthMethodEmailSetup as EmailSetup } from "./AuthMethodEmailSetup"; import { AuthMethodEmailSolve as EmailSolve } from "./AuthMethodEmailSolve"; import { AuthMethodIbanSetup as IbanSetup } from "./AuthMethodIbanSetup"; @@ -20,8 +20,7 @@ import { AuthMethodSmsSolve as SmsSolve } from "./AuthMethodSmsSolve"; import { AuthMethodTotpSolve as TotpSolve } from "./AuthMethodTotpSolve"; import { AuthMethodVideoSolve as VideoSolve } from "./AuthMethodVideoSolve"; - -export type AuthMethodWithRemove = AuthMethod & { remove: () => void } +export type AuthMethodWithRemove = AuthMethod & { remove: () => void }; export interface AuthMethodSetupProps { method: string; @@ -43,10 +42,18 @@ interface AuthMethodConfiguration { } // export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban"; -const ALL_METHODS = ['sms', 'email', 'post', 'question', 'video' , 'totp', 'iban'] as const; -export type KnownAuthMethods = (typeof ALL_METHODS)[number]; +const ALL_METHODS = [ + "sms", + "email", + "post", + "question", + "video", + "totp", + "iban", +] as const; +export type KnownAuthMethods = typeof ALL_METHODS[number]; export function isKnownAuthMethods(value: string): value is KnownAuthMethods { - return ALL_METHODS.includes(value as KnownAuthMethods) + return ALL_METHODS.includes(value as KnownAuthMethods); } type KnowMethodConfig = { @@ -96,5 +103,5 @@ export const authMethods: KnowMethodConfig = { setup: VideoSetup, solve: VideoSolve, skip: true, - } -} \ No newline at end of file + }, +}; diff --git a/packages/anastasis-webui/src/pages/home/authMethod/totp.ts b/packages/anastasis-webui/src/pages/home/authMethod/totp.ts index 0bc3feaf8..c2288671c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/totp.ts +++ b/packages/anastasis-webui/src/pages/home/authMethod/totp.ts @@ -1,54 +1,61 @@ /* eslint-disable @typescript-eslint/camelcase */ -import jssha from 'jssha' +import jssha from "jssha"; -const SEARCH_RANGE = 16 -const timeStep = 30 +const SEARCH_RANGE = 16; +const timeStep = 30; -export function computeTOTPandCheck(secretKey: Uint8Array, digits: number, code: number): boolean { - const now = new Date().getTime() +export function computeTOTPandCheck( + secretKey: Uint8Array, + digits: number, + code: number, +): boolean { + const now = new Date().getTime(); const epoch = Math.floor(Math.round(now / 1000.0) / timeStep); for (let ms = -SEARCH_RANGE; ms < SEARCH_RANGE; ms++) { const movingFactor = (epoch + ms).toString(16).padStart(16, "0"); - const hmacSha = new jssha('SHA-1', 'HEX', { hmacKey: { value: secretKey, format: 'UINT8ARRAY' } }); + const hmacSha = new jssha("SHA-1", "HEX", { + hmacKey: { value: secretKey, format: "UINT8ARRAY" }, + }); hmacSha.update(movingFactor); - const hmac_text = hmacSha.getHMAC('UINT8ARRAY'); + const hmac_text = hmacSha.getHMAC("UINT8ARRAY"); - const offset = (hmac_text[hmac_text.length - 1] & 0xf) + const offset = hmac_text[hmac_text.length - 1] & 0xf; - const otp = (( - (hmac_text[offset + 0] << 24) + - (hmac_text[offset + 1] << 16) + - (hmac_text[offset + 2] << 8) + - (hmac_text[offset + 3]) - ) & 0x7fffffff) % Math.pow(10, digits) + const otp = + (((hmac_text[offset + 0] << 24) + + (hmac_text[offset + 1] << 16) + + (hmac_text[offset + 2] << 8) + + hmac_text[offset + 3]) & + 0x7fffffff) % + Math.pow(10, digits); - if (otp == code) return true + if (otp == code) return true; } - return false + return false; } -const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split('') +const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split(""); export function base32enc(buffer: Uint8Array): string { - let rpos = 0 - let bits = 0 - let vbit = 0 + let rpos = 0; + let bits = 0; + let vbit = 0; - let result = "" - while ((rpos < buffer.length) || (vbit > 0)) { - if ((rpos < buffer.length) && (vbit < 5)) { + let result = ""; + while (rpos < buffer.length || vbit > 0) { + if (rpos < buffer.length && vbit < 5) { bits = (bits << 8) | buffer[rpos++]; vbit += 8; } if (vbit < 5) { - bits <<= (5 - vbit); + bits <<= 5 - vbit; vbit = 5; } result += encTable__[(bits >> (vbit - 5)) & 31]; vbit -= 5; } - return result + return result; } // const array = new Uint8Array(256) diff --git a/packages/anastasis-webui/src/pages/notfound/index.tsx b/packages/anastasis-webui/src/pages/notfound/index.tsx index 4e74d1d9f..bb22429b0 100644 --- a/packages/anastasis-webui/src/pages/notfound/index.tsx +++ b/packages/anastasis-webui/src/pages/notfound/index.tsx @@ -1,16 +1,16 @@ -import { FunctionalComponent, h } from 'preact'; -import { Link } from 'preact-router/match'; +import { FunctionalComponent, h } from "preact"; +import { Link } from "preact-router/match"; const Notfound: FunctionalComponent = () => { - return ( -
      -

      Error 404

      -

      That page doesn't exist.

      - -

      Back to Home

      - -
      - ); + return ( +
      +

      Error 404

      +

      That page doesn't exist.

      + +

      Back to Home

      + +
      + ); }; export default Notfound; diff --git a/packages/anastasis-webui/src/pages/profile/index.tsx b/packages/anastasis-webui/src/pages/profile/index.tsx index 859a83ed4..bcd26370e 100644 --- a/packages/anastasis-webui/src/pages/profile/index.tsx +++ b/packages/anastasis-webui/src/pages/profile/index.tsx @@ -1,43 +1,42 @@ -import { FunctionalComponent, h } from 'preact'; -import { useEffect, useState } from 'preact/hooks'; +import { FunctionalComponent, h } from "preact"; +import { useEffect, useState } from "preact/hooks"; interface Props { - user: string; + user: string; } const Profile: FunctionalComponent = (props: Props) => { - const { user } = props; - const [time, setTime] = useState(Date.now()); - const [count, setCount] = useState(0); + const { user } = props; + const [time, setTime] = useState(Date.now()); + const [count, setCount] = useState(0); - // gets called when this route is navigated to - useEffect(() => { - const timer = window.setInterval(() => setTime(Date.now()), 1000); + // gets called when this route is navigated to + useEffect(() => { + const timer = window.setInterval(() => setTime(Date.now()), 1000); - // gets called just before navigating away from the route - return (): void => { - clearInterval(timer); - }; - }, []); - - // update the current time - const increment = (): void => { - setCount(count + 1); + // gets called just before navigating away from the route + return (): void => { + clearInterval(timer); }; + }, []); - return ( -
      -

      Profile: {user}

      -

      This is the user profile for a user named {user}.

      + // update the current time + const increment = (): void => { + setCount(count + 1); + }; -
      Current time: {new Date(time).toLocaleString()}
      + return ( +
      +

      Profile: {user}

      +

      This is the user profile for a user named {user}.

      -

      - Clicked {count}{' '} - times. -

      -
      - ); +
      Current time: {new Date(time).toLocaleString()}
      + +

      + Clicked {count} times. +

      +
      + ); }; export default Profile; diff --git a/packages/anastasis-webui/src/template.html b/packages/anastasis-webui/src/template.html index 351f1829c..2a216916e 100644 --- a/packages/anastasis-webui/src/template.html +++ b/packages/anastasis-webui/src/template.html @@ -1,15 +1,56 @@ + - - - - <% preact.title %> - - - - - <% preact.headEnd %> - - - <% preact.bodyEnd %> - + + + + <%= htmlWebpackPlugin.options.title %> + + + + + + + + <% if (htmlWebpackPlugin.options.manifest.theme_color) { %> + + <% } %> <% for (const index in htmlWebpackPlugin.files.css) { %> <% const + file = htmlWebpackPlugin.files.css[index] %> + + <% } %> + + + + + diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx index 9c01aa6ba..04cc8c921 100644 --- a/packages/anastasis-webui/src/utils/index.tsx +++ b/packages/anastasis-webui/src/utils/index.tsx @@ -1,45 +1,67 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { BackupStates, RecoveryStates, ReducerState } from 'anastasis-core'; -import { FunctionalComponent, h, VNode } from 'preact'; -import { AnastasisProvider } from '../context/anastasis'; +import { BackupStates, RecoveryStates, ReducerState } from "anastasis-core"; +import { FunctionalComponent, h, VNode } from "preact"; +import { AnastasisProvider } from "../context/anastasis"; -export function createExample(Component: FunctionalComponent, currentReducerState?: ReducerState, props?: Partial): { (args: Props): VNode } { +export function createExample( + Component: FunctionalComponent, + currentReducerState?: ReducerState, + props?: Partial, +): { (args: Props): VNode } { const r = (args: Props): VNode => { - return { null }, - dismissError: async () => { null }, - reset: () => { null }, - runTransaction: async () => { null }, - startBackup: () => { null }, - startRecover: () => { null }, - transition: async () => { null }, - }}> - - - } - r.args = props - return r + return ( + { + null; + }, + dismissError: async () => { + null; + }, + reset: () => { + null; + }, + runTransaction: async () => { + null; + }, + startBackup: () => { + null; + }, + startRecover: () => { + null; + }, + transition: async () => { + null; + }, + }} + > + + + ); + }; + r.args = props; + return r; } const base = { continents: [ { - name: "Europe" + name: "Europe", }, { - name: "India" + name: "India", }, { - name: "Asia" + name: "Asia", }, { - name: "North America" + name: "North America", }, { - name: "Testcontinent" - } + name: "Testcontinent", + }, ], countries: [ { @@ -47,33 +69,33 @@ const base = { name: "Testland", continent: "Testcontinent", continent_i18n: { - de_DE: "Testkontinent" + de_DE: "Testkontinent", }, name_i18n: { de_DE: "Testlandt", de_CH: "Testlandi", fr_FR: "Testpais", - en_UK: "Testland" + en_UK: "Testland", }, currency: "TESTKUDOS", - call_code: "+00" + call_code: "+00", }, { code: "xy", name: "Demoland", continent: "Testcontinent", continent_i18n: { - de_DE: "Testkontinent" + de_DE: "Testkontinent", }, name_i18n: { de_DE: "Demolandt", de_CH: "Demolandi", fr_FR: "Demopais", - en_UK: "Demoland" + en_UK: "Demoland", }, currency: "KUDOS", - call_code: "+01" - } + call_code: "+01", + }, ], authentication_providers: { "http://localhost:8086/": { @@ -85,18 +107,20 @@ const base = { methods: [ { type: "question", - usage_fee: "COL:0" - }, { + usage_fee: "COL:0", + }, + { type: "sms", - usage_fee: "COL:0" - }, { + usage_fee: "COL:0", + }, + { type: "email", - usage_fee: "COL:0" + usage_fee: "COL:0", }, ], salt: "WBMDD76BR1E90YQ5AHBMKPH7GW", storage_limit_in_megabytes: 16, - truth_upload_fee: "COL:0" + truth_upload_fee: "COL:0", }, "https://kudos.demo.anastasis.lu/": { http_status: 200, @@ -107,15 +131,16 @@ const base = { methods: [ { type: "question", - usage_fee: "COL:0" - }, { + usage_fee: "COL:0", + }, + { type: "email", - usage_fee: "COL:0" + usage_fee: "COL:0", }, ], salt: "WBMDD76BR1E90YQ5AHBMKPH7GW", storage_limit_in_megabytes: 16, - truth_upload_fee: "COL:0" + truth_upload_fee: "COL:0", }, "https://anastasis.demo.taler.net/": { http_status: 200, @@ -126,43 +151,45 @@ const base = { methods: [ { type: "question", - usage_fee: "COL:0" - }, { + usage_fee: "COL:0", + }, + { type: "sms", - usage_fee: "COL:0" - }, { + usage_fee: "COL:0", + }, + { type: "totp", - usage_fee: "COL:0" + usage_fee: "COL:0", }, ], salt: "WBMDD76BR1E90YQ5AHBMKPH7GW", storage_limit_in_megabytes: 16, - truth_upload_fee: "COL:0" + truth_upload_fee: "COL:0", }, "http://localhost:8087/": { code: 8414, - hint: "request to provider failed" + hint: "request to provider failed", }, "http://localhost:8088/": { code: 8414, - hint: "request to provider failed" + hint: "request to provider failed", }, "http://localhost:8089/": { code: 8414, - hint: "request to provider failed" - } + hint: "request to provider failed", + }, }, // expiration: { // d_ms: 1792525051855 // check t_ms // }, -} as Partial +} as Partial; export const reducerStatesExample = { initial: undefined, recoverySelectCountry: { ...base, - recovery_state: RecoveryStates.CountrySelecting + recovery_state: RecoveryStates.CountrySelecting, } as ReducerState, recoverySelectContinent: { ...base, @@ -190,11 +217,11 @@ export const reducerStatesExample = { } as ReducerState, recoveryAttributeEditing: { ...base, - recovery_state: RecoveryStates.UserAttributesCollecting + recovery_state: RecoveryStates.UserAttributesCollecting, } as ReducerState, backupSelectCountry: { ...base, - backup_state: BackupStates.CountrySelecting + backup_state: BackupStates.CountrySelecting, } as ReducerState, backupSelectContinent: { ...base, @@ -218,15 +245,14 @@ export const reducerStatesExample = { } as ReducerState, authEditing: { ...base, - backup_state: BackupStates.AuthenticationsEditing + backup_state: BackupStates.AuthenticationsEditing, } as ReducerState, backupAttributeEditing: { ...base, - backup_state: BackupStates.UserAttributesCollecting + backup_state: BackupStates.UserAttributesCollecting, } as ReducerState, truthsPaying: { ...base, - backup_state: BackupStates.TruthsPaying + backup_state: BackupStates.TruthsPaying, } as ReducerState, - -} +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce6296f3d..9a425713c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,12 @@ importers: '@linaria/esbuild': ^3.0.0-beta.13 '@linaria/shaker': ^3.0.0-beta.13 esbuild: ^0.12.29 + prettier: ^2.2.1 devDependencies: '@linaria/esbuild': 3.0.0-beta.13 '@linaria/shaker': 3.0.0-beta.13 esbuild: 0.12.29 + prettier: 2.2.1 packages/anastasis-core: specifiers: @@ -62,6 +64,7 @@ importers: '@typescript-eslint/eslint-plugin': ^5.3.0 '@typescript-eslint/parser': ^5.3.0 anastasis-core: workspace:^0.0.1 + base64-inline-loader: 1.1.1 bulma: ^0.9.3 bulma-checkbox: ^1.1.1 bulma-radio: ^1.1.1 @@ -86,6 +89,7 @@ importers: dependencies: '@gnu-taler/taler-util': link:../taler-util anastasis-core: link:../anastasis-core + base64-inline-loader: 1.1.1 date-fns: 2.25.0 jed: 1.1.1 preact: 10.5.15 @@ -10800,7 +10804,6 @@ packages: ajv: ^6.9.1 dependencies: ajv: 6.12.6 - dev: true /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -10809,7 +10812,6 @@ packages: fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true /ajv/7.0.3: resolution: {integrity: sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==} @@ -11871,6 +11873,17 @@ packages: pascalcase: 0.1.1 dev: true + /base64-inline-loader/1.1.1: + resolution: {integrity: sha512-v/bHvXQ8sW28t9ZwBsFGgyqZw2bpT49/dtPTtlmixoSM/s9wnOngOKFVQLRH8BfMTy6jTl5m5CdvqpZt8y5d6g==} + engines: {node: '>=6.2', npm: '>=3.8'} + peerDependencies: + webpack: ^4.x + dependencies: + file-loader: 1.1.11 + loader-utils: 1.4.0 + mime-types: 2.1.33 + dev: false + /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -11907,7 +11920,6 @@ packages: /big.js/5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - dev: true /binary-extensions/1.13.1: resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} @@ -13794,7 +13806,7 @@ packages: dependencies: globby: 11.0.4 graceful-fs: 4.2.8 - is-glob: 4.0.1 + is-glob: 4.0.3 is-path-cwd: 2.2.0 is-path-inside: 3.0.3 p-map: 4.0.0 @@ -14177,7 +14189,6 @@ packages: /emojis-list/3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - dev: true /emotion-theming/10.0.27_5f216699bc8c1f24088b3bf77b7cbbdf: resolution: {integrity: sha512-MlF1yu/gYh8u+sLUqA0YuA9JX0P4Hb69WlKc/9OLo+WCXuX6sy/KoIa+qJimgmr2dWqnypYKYPX37esjDBbhdw==} @@ -15174,7 +15185,6 @@ packages: /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-diff/1.2.0: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} @@ -15217,7 +15227,6 @@ packages: /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true /fast-levenshtein/2.0.6: resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} @@ -15296,6 +15305,16 @@ packages: flat-cache: 3.0.4 dev: true + /file-loader/1.1.11: + resolution: {integrity: sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==} + engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} + peerDependencies: + webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 + dependencies: + loader-utils: 1.4.0 + schema-utils: 0.4.7 + dev: false + /file-loader/6.2.0_webpack@4.46.0: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} @@ -18694,7 +18713,6 @@ packages: /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true /json-schema-traverse/1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -18730,7 +18748,6 @@ packages: hasBin: true dependencies: minimist: 1.2.5 - dev: true /json5/2.1.3: resolution: {integrity: sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==} @@ -18970,7 +18987,6 @@ packages: big.js: 5.2.2 emojis-list: 3.0.0 json5: 1.0.1 - dev: true /loader-utils/2.0.0: resolution: {integrity: sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==} @@ -19420,14 +19436,12 @@ packages: /mime-db/1.50.0: resolution: {integrity: sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==} engines: {node: '>= 0.6'} - dev: true /mime-types/2.1.33: resolution: {integrity: sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.50.0 - dev: true /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -19501,7 +19515,6 @@ packages: /minimist/1.2.5: resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} - dev: true /minipass-collect/1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} @@ -22013,7 +22026,6 @@ packages: /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} - dev: true /pupa/2.1.1: resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} @@ -23312,6 +23324,14 @@ packages: object-assign: 4.1.1 dev: true + /schema-utils/0.4.7: + resolution: {integrity: sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==} + engines: {node: '>= 4'} + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + dev: false + /schema-utils/1.0.0: resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} engines: {node: '>= 4'} @@ -25212,7 +25232,6 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.1.1 - dev: true /urix/0.1.0: resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=}