Add ability to add totp codes by scanning a QR code. Closes #30.
This commit is contained in:
parent
4927707450
commit
64e3e9911b
|
@ -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> {
|
||||||
|
|
2
src/translations/en.js
vendored
2
src/translations/en.js
vendored
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue