diff options
author | Sebastian <sebasjm@gmail.com> | 2021-11-10 15:43:15 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-11-10 15:43:15 -0300 |
commit | ea13e19ece2deeb4ab9731373f68b1dcf5b6fa88 (patch) | |
tree | c568777c803cf710693544640b18ce9714d4cb63 /packages | |
parent | f9dedc06d6825640c9f1aca462779757cb4194e4 (diff) |
file upload
Diffstat (limited to 'packages')
5 files changed, 69 insertions, 29 deletions
diff --git a/packages/anastasis-webui/src/components/fields/FileInput.tsx b/packages/anastasis-webui/src/components/fields/FileInput.tsx index 52d6eab4a..adf51afb0 100644 --- a/packages/anastasis-webui/src/components/fields/FileInput.tsx +++ b/packages/anastasis-webui/src/components/fields/FileInput.tsx @@ -20,11 +20,26 @@ */ import { h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; -import { TextInputProps } from "./TextInput"; const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; -export function FileInput(props: TextInputProps): VNode { +export interface FileTypeContent { + content: string; + type: string; + name: string; +} + +export interface FileInputProps { + label: string; + grabFocus?: boolean; + disabled?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + onChange: (v: FileTypeContent | undefined) => void; +} + +export function FileInput(props: FileInputProps): VNode { const inputRef = useRef<HTMLInputElement>(null); useLayoutEffect(() => { if (props.grabFocus) { @@ -32,18 +47,19 @@ export function FileInput(props: TextInputProps): VNode { } }, [props.grabFocus]); - const value = props.bind[0]; - // const [dirty, setDirty] = useState(false) - const image = useRef<HTMLInputElement>(null); + const fileInputRef = useRef<HTMLInputElement>(null); const [sizeError, setSizeError] = useState(false); - function onChange(v: string): void { - // setDirty(true); - props.bind[1](v); - } return ( <div class="field"> <label class="label"> - <a onClick={() => image.current?.click()}>{props.label}</a> + <a class="button" onClick={(e) => fileInputRef.current?.click()}> + <div class="icon is-small "> + <i class="mdi mdi-folder" /> + </div> + <span> + {props.label} + </span> + </a> {props.tooltip && ( <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> <i class="mdi mdi-information" /> @@ -52,18 +68,19 @@ export function FileInput(props: TextInputProps): VNode { </label> <div class="control"> <input - ref={image} + ref={fileInputRef} style={{ display: "none" }} type="file" - name={String(name)} + // name={String(name)} onChange={(e) => { const f: FileList | null = e.currentTarget.files; if (!f || f.length != 1) { - return onChange(""); + return props.onChange(undefined); } + console.log(f) if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { setSizeError(true); - return onChange(""); + return props.onChange(undefined); } setSizeError(false); return f[0].arrayBuffer().then((b) => { @@ -73,7 +90,7 @@ export function FileInput(props: TextInputProps): VNode { "", ), ); - return onChange(`data:${f[0].type};base64,${b64}` as any); + return props.onChange({content: `data:${f[0].type};base64,${b64}`, name: f[0].name, type: f[0].type}); }); }} /> diff --git a/packages/anastasis-webui/src/components/fields/TextInput.tsx b/packages/anastasis-webui/src/components/fields/TextInput.tsx index fd0c658ed..4f417730c 100644 --- a/packages/anastasis-webui/src/components/fields/TextInput.tsx +++ b/packages/anastasis-webui/src/components/fields/TextInput.tsx @@ -4,6 +4,7 @@ import { useLayoutEffect, useRef, useState } from "preact/hooks"; export interface TextInputProps { label: string; grabFocus?: boolean; + disabled?: boolean; error?: string; placeholder?: string; tooltip?: string; @@ -33,6 +34,7 @@ export function TextInput(props: TextInputProps): VNode { <div class="control has-icons-right"> <input value={value} + disabled={props.disabled} placeholder={props.placeholder} class={showError ? "input is-danger" : "input"} onInput={(e) => { diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx index c301b287a..3b3b441ed 100644 --- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx @@ -1,3 +1,4 @@ +import { AuthenticationProviderStatusOk } from "anastasis-core"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; @@ -22,6 +23,7 @@ export function ReviewPoliciesScreen(): VNode { reducer.currentReducerState.authentication_methods ?? []; const policies = reducer.currentReducerState.policies ?? []; + const providers = reducer.currentReducerState.authentication_providers ?? {} if (editingPolicy !== undefined) { return ( @@ -96,6 +98,7 @@ export function ReviewPoliciesScreen(): VNode { </h3> {!methods.length && <p>No auth method found</p>} {methods.map((m, i) => { + const p = providers[m.provider] as AuthenticationProviderStatusOk return ( <p key={i} @@ -107,7 +110,7 @@ export function ReviewPoliciesScreen(): VNode { </span> <span> {m.instructions} recovery provided by{" "} - <a href={m.provider}>{m.provider}</a> + <a href={m.provider} target="_blank" rel="noreferrer" >{p.business_name}</a> </span> </p> ); diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx index 59af8a9ee..226e43ddf 100644 --- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx @@ -5,11 +5,16 @@ import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; import { TextInput } from "../../components/fields/TextInput"; -import { FileInput } from "../../components/fields/FileInput"; +import { FileInput, FileTypeContent } from "../../components/fields/FileInput"; export function SecretEditorScreen(): VNode { const reducer = useAnastasisContext(); const [secretValue, setSecretValue] = useState(""); + const [secretFile, _setSecretFile] = useState<FileTypeContent | undefined>(undefined); + function setSecretFile(v) { + setSecretValue("") // reset secret value when uploading a file + _setSecretFile(v) + } const currentSecretName = reducer?.currentReducerState && @@ -29,15 +34,20 @@ export function SecretEditorScreen(): VNode { } const secretNext = async (): Promise<void> => { + const secret = secretFile ? { + value: encodeCrock(stringToBytes(secretValue)), + filename: secretFile.name, + mime: secretFile.type, + } : { + value: encodeCrock(stringToBytes(secretValue)), + mime: "text/plain", + } return reducer.runTransaction(async (tx) => { await tx.transition("enter_secret_name", { name: secretName, }); await tx.transition("enter_secret", { - secret: { - value: encodeCrock(stringToBytes(secretValue)), - mime: "text/plain", - }, + secret, expiration: { t_ms: new Date().getTime() + 1000 * 60 * 60 * 24 * 365 * 5, }, @@ -45,12 +55,16 @@ export function SecretEditorScreen(): VNode { await tx.transition("next", {}); }); }; + const errors = !secretName ? 'Add a secret name' : ( + (!secretValue && !secretFile) ? 'Add a secret value or a choose a file to upload' : undefined + ) return ( <AnastasisClientFrame + hideNext={errors} title="Backup: Provide secret to backup" onNext={() => secretNext()} > - <div> + <div class="block"> <TextInput label="Secret name:" tooltip="The secret name allows you to identify your secret when restoring it. It is a label that you can choose freely." @@ -58,16 +72,20 @@ export function SecretEditorScreen(): VNode { bind={[secretName, setSecretName]} /> </div> - <div> + <div class="block"> <TextInput + disabled={!!secretFile} label="Enter the secret as text:" bind={[secretValue, setSecretValue]} /> - {/* <div style={{ display: "flex" }}> - or - <FileInput label="click here" bind={[secretValue, setSecretValue]} /> - to import a file - </div> */} + </div> + <div class="block"> + Or upload a secret file + <FileInput label="Choose file" onChange={setSecretFile} /> + {secretFile && <div> + Uploading secret file <b>{secretFile.name}</b> <a onClick={() => setSecretFile(undefined)}>cancel</a> + </div> + } </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx index 0a14021dd..03725621c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx @@ -30,7 +30,7 @@ export function AuthMethodQuestionSetup({ <AnastasisClientFrame hideNav title="Add Security Question"> <div> <p> - For2 security question authentication, you need to provide a question + For security question authentication, you need to provide a question and its answer. When recovering your secret, you will be shown the question and you will need to type the answer exactly as you typed it here. |