1
0
Fork 0

Add ability to add totp codes by scanning a QR code. Closes #30.

This commit is contained in:
Kitteh 2021-05-25 12:36:10 +01:00
parent 4927707450
commit 64e3e9911b
2 changed files with 82 additions and 32 deletions

View file

@ -1,31 +1,54 @@
import { Component, JSX, createRef, render } from "preact";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../types/Page"; import { Page } from "../../../types/Page";
import { QRScanner } from "../../../elements/QRScanner";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { addNewTOTP } from "../../../api/totp/addNewTOTP"; import { addNewTOTP } from "../../../api/totp/addNewTOTP";
import { render } from "preact";
import { setErrorText } from "../../../pageUtils"; import { setErrorText } from "../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
function replaceAll(str: string, replace: string, replaceWith: string): string { function replaceAll(str: string, replace: string, replaceWith: string): string {
return str.replace(new RegExp(replace, "g"), replaceWith); return str.replace(new RegExp(replace, "g"), replaceWith);
} }
function removeDashSpaces(str: string): string { function removeDashSpaces(str: string): string {
str = replaceAll(str, "-", ""); str = replaceAll(str, "-", "");
str = replaceAll(str, " ", ""); str = replaceAll(str, " ", "");
return str; return str;
} }
export class TOTPNewPage extends Page { export class TOTPNewForm extends Component<{ page: Page }, { qrMode: boolean }> {
constructor() { constructor() {
super(); super();
this.state = {
qrMode: false,
};
} }
async goBack(): Promise<void> {
await this.router.changePage("TOTP_VIEW"); uriInputRef = createRef<HTMLInputElement>();
async onSubmit(data: FormData): Promise<void> {
const page = this.props.page;
const parms = {
url: data.get("uri") as string,
key: removeDashSpaces(data.get("key") as string).toUpperCase(),
name: data.get("name") as string,
generate: false,
};
try {
await addNewTOTP(page.state.baseMount, parms);
await page.router.changePage("TOTP_VIEW");
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
}
} }
async render(): Promise<void> {
render( render(): JSX.Element {
return (
<Form onSubmit={(data) => this.onSubmit(data)}> <Form onSubmit={(data) => this.onSubmit(data)}>
<Margin> <Margin>
<input <input
@ -36,49 +59,74 @@ export class TOTPNewPage extends Page {
required required
/> />
</Margin> </Margin>
<p>{i18next.t("totp_new_info")}</p>
<Margin> <p hidden={this.state.qrMode}>{i18next.t("totp_new_info")}</p>
<input
class="uk-input uk-form-width-medium"
name="uri"
type="text"
placeholder={i18next.t("totp_new_uri_input")}
/>
</Margin>
<Margin> <Margin>
<input <input
class="uk-input uk-form-width-medium" class="uk-input uk-form-width-medium"
name="key" name="key"
type="text" type="text"
hidden={this.state.qrMode}
placeholder={i18next.t("totp_new_key_input")} placeholder={i18next.t("totp_new_key_input")}
/> />
</Margin> </Margin>
<Margin>
<input
class="uk-input uk-form-width-medium"
ref={this.uriInputRef}
name="uri"
type="text"
hidden={this.state.qrMode}
placeholder={i18next.t("totp_new_uri_input")}
/>
</Margin>
{this.state.qrMode && (
<QRScanner
onScan={(uri) => {
this.uriInputRef.current.value = uri;
this.setState({ qrMode: !this.state.qrMode });
}}
/>
)}
<MarginInline>
<button
class="uk-button uk-button-primary"
type="button"
onClick={() => {
this.setState({ qrMode: !this.state.qrMode });
}}
>
{!this.state.qrMode
? i18next.t("totp_new_switch_to_qr_btn")
: i18next.t("totp_new_switch_back_to_manual_input_btn")}
</button>
</MarginInline>
<p id="errorText" class="uk-text-danger" /> <p id="errorText" class="uk-text-danger" />
<MarginInline> <MarginInline>
<button class="uk-button uk-button-primary" type="submit"> <button class="uk-button uk-button-primary" type="submit">
{i18next.t("totp_new_add_btn")} {i18next.t("totp_new_add_btn")}
</button> </button>
</MarginInline> </MarginInline>
</Form>, </Form>
this.router.pageContentElement,
); );
} }
}
async onSubmit(data: FormData): Promise<void> { export class TOTPNewPage extends Page {
const parms = { constructor() {
url: data.get("uri") as string, super();
key: removeDashSpaces(data.get("key") as string).toUpperCase(), }
name: data.get("name") as string, async goBack(): Promise<void> {
generate: false, await this.router.changePage("TOTP_VIEW");
}; }
async render(): Promise<void> {
try { render(<TOTPNewForm page={this} />, this.router.pageContentElement);
await addNewTOTP(this.state.baseMount, parms);
await this.router.changePage("TOTP_VIEW");
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
}
} }
async renderPageTitle(): Promise<void> { async renderPageTitle(): Promise<void> {

View file

@ -163,6 +163,8 @@ module.exports = {
totp_new_uri_input: "URI", totp_new_uri_input: "URI",
totp_new_key_input: "Key", totp_new_key_input: "Key",
totp_new_add_btn: "Add TOTP Key", totp_new_add_btn: "Add TOTP Key",
totp_new_switch_to_qr_btn: "Switch to QR Input",
totp_new_switch_back_to_manual_input_btn: "Switch back to manual input",
// TOTP Delete Page // TOTP Delete Page
totp_delete_title: "Delete TOTP Key", totp_delete_title: "Delete TOTP Key",