1
0
Fork 0
VaultUI/src/ui/pages/Unseal.tsx

146 lines
3.8 KiB
TypeScript

import { Component, JSX, render } from "preact";
import { Form } from "../elements/Form";
import { MarginInline } from "../elements/MarginInline";
import { QRScanner } from "../elements/QRScanner";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { PageTitle } from "../elements/PageTitle";
import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import { toStr } from "../../utils";
import i18next from "i18next";
const UnsealInputModes = {
FORM_INPUT: "FORM_INPUT",
QR_INPUT: "QR_INPUT",
};
export type UnsealFormInputProps = {
onSubmit: (code: string) => void;
};
export function UnsealFormInput(props: UnsealFormInputProps): JSX.Element {
return (
<Form
onSubmit={(data: FormData) => {
props.onSubmit(data.get("unsealKey") as string);
}}
>
<MarginInline>
<input
class="uk-input uk-form-width-medium"
name="unsealKey"
type="password"
placeholder={i18next.t("unseal_key_input_placeholder")}
required
/>
</MarginInline>
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("unseal_submit_key_btn")}
</button>
</MarginInline>
</Form>
);
}
type UnsealPageState = {
mode: string;
keys_submitted: number;
keys_needed: number;
};
export class Unseal extends Component<DefaultPageProps, UnsealPageState> {
constructor() {
super();
this.state = {
mode: UnsealInputModes.FORM_INPUT,
keys_submitted: 0,
keys_needed: 0,
};
}
timer: number;
async submitKey(key: string): Promise<void> {
try {
await this.props.api.submitUnsealKey(key);
this.updateStateWithSealStatus();
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}
updateStateWithSealStatus(): void {
void this.props.api.getSealStatus().then((data) => {
this.setState({
keys_submitted: data.progress,
keys_needed: data.t,
});
if (!data.sealed) {
route("/");
}
});
}
componentWillUnmount(): void {
clearInterval(this.timer);
}
componentDidMount(): void {
this.updateStateWithSealStatus();
this.timer = setInterval(() => {
this.updateStateWithSealStatus();
}, 500) as unknown as number;
}
render(): JSX.Element {
return (
<>
<PageTitle title={i18next.t("unseal_vault_text")} />
<div>
<progress
class="uk-progress"
value={this.state.keys_submitted}
max={this.state.keys_needed}
/>
<p id="errorText" class="uk-text-danger uk-margin-top" />
<p>
{i18next.t("unseal_keys_progress", {
progress: toStr(this.state.keys_submitted),
keys_needed: toStr(this.state.keys_needed),
})}
</p>
{this.state.mode == UnsealInputModes.FORM_INPUT && (
<UnsealFormInput onSubmit={(code) => this.submitKey(code)} />
)}
{this.state.mode == UnsealInputModes.QR_INPUT && (
<QRScanner onScan={(code) => this.submitKey(code)} />
)}
<button
class="uk-button uk-button-primary"
onClick={async () => {
let newMethod: string;
if (this.state.mode == UnsealInputModes.FORM_INPUT) {
newMethod = UnsealInputModes.QR_INPUT;
} else {
newMethod = UnsealInputModes.FORM_INPUT;
}
this.setState({ mode: newMethod });
}}
>
{this.state.mode == UnsealInputModes.QR_INPUT
? i18next.t("unseal_input_btn")
: i18next.t("unseal_qr_btn")}
</button>
</div>
</>
);
}
}