aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-webui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-11-10 15:43:15 -0300
committerSebastian <sebasjm@gmail.com>2021-11-10 15:43:15 -0300
commitea13e19ece2deeb4ab9731373f68b1dcf5b6fa88 (patch)
treec568777c803cf710693544640b18ce9714d4cb63 /packages/anastasis-webui
parentf9dedc06d6825640c9f1aca462779757cb4194e4 (diff)
file upload
Diffstat (limited to 'packages/anastasis-webui')
-rw-r--r--packages/anastasis-webui/src/components/fields/FileInput.tsx47
-rw-r--r--packages/anastasis-webui/src/components/fields/TextInput.tsx2
-rw-r--r--packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx5
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx42
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx2
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&nbsp;
- <FileInput label="click here" bind={[secretValue, setSecretValue]} />
- &nbsp;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.