From a9d2a4654b414a01a53a0d79f1f90a5102564710 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 5 Nov 2021 14:56:03 -0300 Subject: feedback state rendering --- .../src/components/Notifications.tsx | 4 +- .../pages/home/ChallengeOverviewScreen.stories.tsx | 108 ++++++++++-- .../src/pages/home/ChallengeOverviewScreen.tsx | 116 +++++++++---- .../src/pages/home/ReviewPoliciesScreen.tsx | 1 - .../src/pages/home/SecretSelectionScreen.tsx | 2 +- .../src/pages/home/SolveScreen.stories.tsx | 193 ++++++++++++++++++++- .../anastasis-webui/src/pages/home/SolveScreen.tsx | 81 +++++++-- 7 files changed, 428 insertions(+), 77 deletions(-) diff --git a/packages/anastasis-webui/src/components/Notifications.tsx b/packages/anastasis-webui/src/components/Notifications.tsx index c916020d7..097ebb4de 100644 --- a/packages/anastasis-webui/src/components/Notifications.tsx +++ b/packages/anastasis-webui/src/components/Notifications.tsx @@ -46,10 +46,10 @@ function messageStyle(type: MessageType): string { export function Notifications({ notifications, removeNotification }: Props): VNode { return
- {notifications.map((n,i) =>
+ {notifications.map((n, i) =>

{n.message}

-
{n.description &&
{n.description} diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx index 48115c798..e001ed157 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ /* This file is part of GNU Taler (C) 2021 Taler Systems S.A. @@ -20,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { RecoveryStates, ReducerState } from "anastasis-core"; +import { ChallengeFeedbackStatus, RecoveryStates, ReducerState } from "anastasis-core"; import { createExample, reducerStatesExample } from "../../utils"; import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen"; @@ -176,16 +175,15 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( recovery_information: { policies: [ [ - { uuid: "1" }, - { uuid: "2" }, - { uuid: "3" }, - { uuid: "4" }, - { uuid: "5" }, - { uuid: "6" }, - { uuid: "7" }, - { uuid: "8" }, - { uuid: "9" }, - { uuid: "10" }, + { uuid: "uuid-1" }, + { uuid: "uuid-2" }, + { uuid: "uuid-3" }, + { uuid: "uuid-4" }, + { uuid: "uuid-5" }, + { uuid: "uuid-6" }, + { uuid: "uuid-7" }, + { uuid: "uuid-8" }, + { uuid: "uuid-9" }, ], ], challenges: [ @@ -193,20 +191,96 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample( cost: "USD:1", instructions: 'in state "solved"', type: "question", - uuid: "1", + uuid: "uuid-1", }, { cost: "USD:1", instructions: 'in state "message"', type: "question", - uuid: "2", + uuid: "uuid-2", + }, + { + cost: "USD:1", + instructions: 'in state "auth iban"', + type: "question", + uuid: "uuid-3", + }, + { + cost: "USD:1", + instructions: 'in state "payment "', + type: "question", + uuid: "uuid-4", + }, + { + cost: "USD:1", + instructions: 'in state "rate limit"', + type: "question", + uuid: "uuid-5", + }, + { + cost: "USD:1", + instructions: 'in state "redirect"', + type: "question", + uuid: "uuid-6", + }, + { + cost: "USD:1", + instructions: 'in state "server failure"', + type: "question", + uuid: "uuid-7", + }, + { + cost: "USD:1", + instructions: 'in state "truth unknown"', + type: "question", + uuid: "uuid-8", + }, + { + cost: "USD:1", + instructions: 'in state "unsupported"', + type: "question", + uuid: "uuid-9", }, ], }, challenge_feedback: { - 1: { state: "solved" }, - 2: { state: "message", message: "Security question was not solved correctly" }, - // FIXME: add missing feedback states here! + "uuid-1": { state: ChallengeFeedbackStatus.Solved.toString() }, + "uuid-2": { + state: ChallengeFeedbackStatus.Message.toString(), + 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" + }, + "uuid-4": { + state: ChallengeFeedbackStatus.Payment.toString(), + taler_pay_uri: "taler://pay/...", + provider: "https://localhost:8080/", + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" + }, + "uuid-5": { + state: ChallengeFeedbackStatus.RateLimitExceeded.toString(), + // "error_code": 8121 + }, + "uuid-6": { + state: ChallengeFeedbackStatus.Redirect.toString(), + redirect_url: "https://videoconf.example.com/", + http_status: 303 + }, + "uuid-7": { + state: ChallengeFeedbackStatus.ServerFailure.toString(), + http_status: 500, + error_response: "some error message or error object", + }, + "uuid-8": { + state: ChallengeFeedbackStatus.TruthUnknown.toString(), + // "error_code": 8108 + }, + "uuid-9": { state: ChallengeFeedbackStatus.Unsupported.toString() }, }, } as ReducerState, ); diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx index ed34bbde2..598999f2e 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -12,27 +12,24 @@ function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) { switch (feedback.state) { case ChallengeFeedbackStatus.Message: return ( -
-

{feedback.message}

-
+
{feedback.message}
); + case ChallengeFeedbackStatus.Solved: + return
case ChallengeFeedbackStatus.Pending: case ChallengeFeedbackStatus.AuthIban: return null; + case ChallengeFeedbackStatus.ServerFailure: + return
Server error.
; case ChallengeFeedbackStatus.RateLimitExceeded: - return
Rate limit exceeded.
; - case ChallengeFeedbackStatus.Redirect: - return
Redirect (FIXME: not supported)
; + return
There were to many failed attempts.
; case ChallengeFeedbackStatus.Unsupported: - return
Challenge not supported by client.
; + return
This client doesn't support solving this type of challenge. Use another version or contact the provider.
; case ChallengeFeedbackStatus.TruthUnknown: - return
Truth unknown
; + return
Provider doesn't recognize the challenge of the policy. Contact the provider for further information.
; + case ChallengeFeedbackStatus.Redirect: default: - return ( -
-
{JSON.stringify(feedback)}
-
- ); + return
; } } @@ -113,6 +110,77 @@ export function ChallengeOverviewScreen(): VNode { const tableBody = policy.challenges.map(({ info, uuid }) => { const isFree = !info.cost || info.cost.endsWith(":0"); const method = authMethods[info.type as KnownAuthMethods]; + + if (!method) { + return
+
+ unknown challenge +
+ +
+ } + + function ChallengeButton({ id, feedback }: { id: string; feedback?: ChallengeFeedback }): VNode { + function selectChallenge(): void { + if (reducer) reducer.transition("select_challenge", { uuid: id }) + } + if (!feedback) { + return
+ +
+ } + switch (feedback.state) { + case ChallengeFeedbackStatus.ServerFailure: + case ChallengeFeedbackStatus.Unsupported: + case ChallengeFeedbackStatus.TruthUnknown: + case ChallengeFeedbackStatus.RateLimitExceeded: return
+ case ChallengeFeedbackStatus.AuthIban: + 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 (
-
- {method && info.feedback?.state !== "solved" ? ( - - reducer.transition("select_challenge", { uuid }) - } - > - {isFree ? "Solve" : `Pay and Solve`} - - ) : null} - {info.feedback?.state === "solved" ? ( - Solved - ) : null} -
+ + +
); }); @@ -156,8 +212,8 @@ export function ChallengeOverviewScreen(): VNode { const opa = !atLeastThereIsOnePolicySolved ? undefined : policy.isPolicySolved - ? undefined - : "0.6"; + ? undefined + : "0.6"; return (
void; provider: string; version: number; providers: string[]; onConfirm: (prov: string, v: number) => Promise; }): VNode { const [otherProvider, setOtherProvider] = useState(provider); - const [otherVersion, setOtherVersion] = useState(`${version}`); + const [otherVersion, setOtherVersion] = useState(version > 0 ? String(version) : ""); return ( diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx index cb6561b3f..05c4bfa42 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ /* This file is part of GNU Taler (C) 2021 Taler Systems S.A. @@ -20,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ReducerState } from 'anastasis-core'; +import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; import { createExample, reducerStatesExample } from '../../utils'; import { SolveScreen as TestedComponent } from './SolveScreen'; @@ -50,7 +49,8 @@ export const NotSupportedChallenge = createExample(TestedComponent, { }], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1' + selected_challenge_uuid: 'ASDASDSAD!1', + } as ReducerState); export const MismatchedChallengeId = createExample(TestedComponent, { @@ -78,7 +78,8 @@ export const SmsChallenge = createExample(TestedComponent, { }], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1' + selected_challenge_uuid: 'ASDASDSAD!1', + } as ReducerState); export const QuestionChallenge = createExample(TestedComponent, { @@ -92,7 +93,8 @@ export const QuestionChallenge = createExample(TestedComponent, { }], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1' + selected_challenge_uuid: 'ASDASDSAD!1', + } as ReducerState); export const EmailChallenge = createExample(TestedComponent, { @@ -106,7 +108,8 @@ export const EmailChallenge = createExample(TestedComponent, { }], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1' + selected_challenge_uuid: 'ASDASDSAD!1', + } as ReducerState); export const PostChallenge = createExample(TestedComponent, { @@ -120,5 +123,181 @@ export const PostChallenge = createExample(TestedComponent, { }], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1' + selected_challenge_uuid: 'ASDASDSAD!1', + +} as ReducerState); + + +export const QuestionChallengeMessageFeedback = createExample(TestedComponent, { + ...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.Message, + message: 'Challenge should be solved' + } + } + +} as ReducerState); + +export const QuestionChallengeServerFailureFeedback = createExample(TestedComponent, { + ...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 QuestionChallengeRedirectFeedback = createExample(TestedComponent, { + ...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.Redirect, + http_status: 302, + redirect_url: 'http://video.taler.net' + } + } + +} as ReducerState); + +export const QuestionChallengeMessageRateLimitExceededFeedback = createExample(TestedComponent, { + ...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 QuestionChallengeUnsupportedFeedback = createExample(TestedComponent, { + ...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.Unsupported, + http_status: 500, + unsupported_method: 'Question' + } + } + } as ReducerState); + +export const QuestionChallengeTruthUnknownFeedback = createExample(TestedComponent, { + ...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.TruthUnknown, + } + } + +} as ReducerState); + +export const QuestionChallengeAuthIbanFeedback = createExample(TestedComponent, { + ...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.AuthIban, + challenge_amount: "EUR:1", + credit_iban: "DE12345789000", + business_name: "Data Loss Incorporated", + wire_transfer_subject: "Anastasis 987654321" + } + } + +} as ReducerState); + +export const QuestionChallengePaymentFeedback = createExample(TestedComponent, { + ...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.Payment, + taler_pay_uri : "taler://pay/...", + provider : "https://localhost:8080/", + payment_secret : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" + } + } +} as ReducerState); + diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index bc1a88db3..35db5ead0 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -8,31 +8,67 @@ import { } from "../../../../anastasis-core/lib"; import { AsyncButton } from "../../components/AsyncButton"; import { TextInput } from "../../components/fields/TextInput"; +import { Notifications } from "../../components/Notifications"; import { useAnastasisContext } from "../../context/anastasis"; -function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) { +function SolveOverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }): VNode { const { feedback } = props; if (!feedback) { - return null; + return
; } switch (feedback.state) { case ChallengeFeedbackStatus.Message: - return ( -
-

{feedback.message}

-
- ); - case ChallengeFeedbackStatus.Pending: + return (); + case ChallengeFeedbackStatus.Payment: + return + To pay you can click here + + }]} /> case ChallengeFeedbackStatus.AuthIban: - return null; + return ; + case ChallengeFeedbackStatus.ServerFailure: + return (); case ChallengeFeedbackStatus.RateLimitExceeded: - return
Rate limit exceeded.
; + return (); case ChallengeFeedbackStatus.Redirect: - return
Redirect (FIXME: not supported)
; + return ( + Please visit this link: {feedback.redirect_url} + + }]} />); case ChallengeFeedbackStatus.Unsupported: - return
Challenge not supported by client.
; + return (); case ChallengeFeedbackStatus.TruthUnknown: - return
Truth unknown
; + return (); default: return (
@@ -79,8 +115,8 @@ export function SolveScreen(): VNode {
invalid state
- -
+ +
); } @@ -114,17 +150,23 @@ export function SolveScreen(): VNode { reducer?.back(); } + const feedback = challengeFeedback[selectedUuid] + const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded + || feedback?.state === ChallengeFeedbackStatus.Redirect + || feedback?.state === ChallengeFeedbackStatus.Unsupported + || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + return (
Cancel - + {!shouldHideConfirm && Confirm - + }
); @@ -160,6 +202,7 @@ function SolveSmsEntry({ }: SolveEntryProps): VNode { return ( +

An sms has been sent to "{challenge.instructions}". Type the code below -- cgit v1.2.3