Use tsx syntax in TOTPView.
This commit is contained in:
parent
e4d749cce0
commit
23cc9b60c9
|
@ -1,119 +0,0 @@
|
||||||
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
|
|
||||||
import { DoesNotExistError } from "../../../types/internalErrors";
|
|
||||||
import { Page } from "../../../types/Page";
|
|
||||||
import { SecretTitleElement } from "../SecretTitleElement";
|
|
||||||
import { getTOTPCode } from "../../../api/totp/getTOTPCode";
|
|
||||||
import { getTOTPKeys } from "../../../api/totp/getTOTPKeys";
|
|
||||||
import { makeElement } from "z-makeelement";
|
|
||||||
import { objectToMap } from "../../../utils";
|
|
||||||
import { setErrorText } from "../../../pageUtils";
|
|
||||||
import i18next from "i18next";
|
|
||||||
|
|
||||||
export interface TOTPListElement extends HTMLElement {
|
|
||||||
setCode(code: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TOTPViewPage extends Page {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.refresher = undefined;
|
|
||||||
this.totpListElements = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
refresher: number;
|
|
||||||
totpListElements: Record<string, TOTPListElement>;
|
|
||||||
|
|
||||||
async goBack(): Promise<void> {
|
|
||||||
await this.router.changePage("SECRETS_HOME");
|
|
||||||
}
|
|
||||||
|
|
||||||
async render(): Promise<void> {
|
|
||||||
const totpList = makeElement({ tag: "div" });
|
|
||||||
await this.router.setPageContent(
|
|
||||||
makeElement({
|
|
||||||
tag: "div",
|
|
||||||
children: [
|
|
||||||
makeElement({
|
|
||||||
tag: "button",
|
|
||||||
text: i18next.t("totp_view_new_btn"),
|
|
||||||
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
|
|
||||||
onclick: async () => {
|
|
||||||
await this.router.changePage("NEW_TOTP");
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
makeElement({
|
|
||||||
tag: "p",
|
|
||||||
id: "loadingText",
|
|
||||||
text: i18next.t("totp_view_loading"),
|
|
||||||
}),
|
|
||||||
makeElement({ tag: "br" }),
|
|
||||||
makeElement({ tag: "br" }),
|
|
||||||
totpList,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await getTOTPKeys(this.state.baseMount);
|
|
||||||
for (const totpKeyName of res) {
|
|
||||||
const totpListElement = this.makeTOTPListElement(totpKeyName);
|
|
||||||
totpList.appendChild(totpListElement);
|
|
||||||
this.totpListElements[totpKeyName] = totpListElement;
|
|
||||||
await this.updateTOTPElement(totpKeyName, totpListElement);
|
|
||||||
}
|
|
||||||
document.getElementById("loadingText").remove();
|
|
||||||
} catch (e: unknown) {
|
|
||||||
const error = e as Error;
|
|
||||||
if (error == DoesNotExistError) {
|
|
||||||
const loadingText = document.getElementById("loadingText");
|
|
||||||
loadingText.innerText = i18next.t("totp_view_empty");
|
|
||||||
} else {
|
|
||||||
setErrorText(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const totpRefresher = async () => {
|
|
||||||
await Promise.all(
|
|
||||||
Array.from(objectToMap(this.totpListElements)).map((kv: [string, TOTPListElement]) => {
|
|
||||||
return this.updateTOTPElement(...kv);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await totpRefresher();
|
|
||||||
this.refresher = setInterval(async () => {
|
|
||||||
await totpRefresher();
|
|
||||||
}, 3000) as unknown as number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cleanup(): Promise<void> {
|
|
||||||
clearInterval(this.refresher);
|
|
||||||
this.totpListElements = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateTOTPElement(totpKeyName: string, totpListElement: TOTPListElement): Promise<void> {
|
|
||||||
totpListElement.setCode(await getTOTPCode(this.state.baseMount, totpKeyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
makeTOTPListElement(totpKeyName: string): TOTPListElement {
|
|
||||||
const totpKeyBox = CopyableInputBox(totpKeyName, false);
|
|
||||||
const totpValueBox = CopyableInputBox(i18next.t("totp_view_loading_box"));
|
|
||||||
|
|
||||||
const gridElement = makeElement({
|
|
||||||
tag: "div",
|
|
||||||
class: ["uk-grid", "uk-grid-small", "uk-text-expand"],
|
|
||||||
children: [totpKeyBox, totpValueBox],
|
|
||||||
}) as TOTPListElement;
|
|
||||||
|
|
||||||
gridElement.setCode = (code: string) => totpValueBox.setText(code);
|
|
||||||
|
|
||||||
return gridElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPageTitle(): Promise<Element | string> {
|
|
||||||
return await SecretTitleElement(this.router);
|
|
||||||
}
|
|
||||||
|
|
||||||
get name(): string {
|
|
||||||
return i18next.t("totp_view_title");
|
|
||||||
}
|
|
||||||
}
|
|
128
src/pages/Secrets/TOTP/TOTPView.tsx
Normal file
128
src/pages/Secrets/TOTP/TOTPView.tsx
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import { CopyableInputBox } from "../../../elements/ReactCopyableInputBox";
|
||||||
|
import { DoesNotExistError } from "../../../types/internalErrors";
|
||||||
|
import { Page } from "../../../types/Page";
|
||||||
|
import { SecretTitleElement } from "../SecretTitleElement";
|
||||||
|
import { getTOTPCode } from "../../../api/totp/getTOTPCode";
|
||||||
|
import { getTOTPKeys } from "../../../api/totp/getTOTPKeys";
|
||||||
|
import { makeElement } from "z-makeelement";
|
||||||
|
import { objectToMap } from "../../../utils";
|
||||||
|
import { setErrorText } from "../../../pageUtils";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { render, JSX } from "preact";
|
||||||
|
|
||||||
|
export interface TOTPListElement extends HTMLElement {
|
||||||
|
setCode(code: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function quickHash(str: string): string {
|
||||||
|
var hash = 0;
|
||||||
|
if (str.length == 0) {
|
||||||
|
return String(hash).toString();
|
||||||
|
}
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
var char = str.charCodeAt(i);
|
||||||
|
hash = ((hash << 5) - hash) + char;
|
||||||
|
hash = hash & hash;
|
||||||
|
}
|
||||||
|
return String(hash).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function TOTPGridItem(props: { item_key: string, item_value: string }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div class="uk-grid uk-grid-small uk-text-left" uk-grid>
|
||||||
|
<CopyableInputBox text={props.item_key} copyable />
|
||||||
|
<CopyableInputBox text={props.item_value} copyable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TOTPViewPage extends Page {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.refresher = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresher: number;
|
||||||
|
|
||||||
|
async goBack(): Promise<void> {
|
||||||
|
await this.router.changePage("SECRETS_HOME");
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(): Promise<void> {
|
||||||
|
console.log(quickHash("abc"));
|
||||||
|
|
||||||
|
render((
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-primary uk-margin-bottom"
|
||||||
|
onClick={async () => {
|
||||||
|
await this.router.changePage("NEW_TOTP");
|
||||||
|
}}>
|
||||||
|
{i18next.t("totp_view_new_btn")}
|
||||||
|
</button>
|
||||||
|
<p id="loadingText">{i18next.t("totp_view_loading")}</p>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<div id="totpList"></div>
|
||||||
|
</div>
|
||||||
|
), this.router.pageContentElement)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.updateTOTPElements();
|
||||||
|
document.getElementById("loadingText").remove();
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as Error;
|
||||||
|
if (error == DoesNotExistError) {
|
||||||
|
const loadingText = document.getElementById("loadingText");
|
||||||
|
loadingText.innerText = i18next.t("totp_view_empty");
|
||||||
|
} else {
|
||||||
|
setErrorText(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totpRefresher = async () => {
|
||||||
|
await this.updateTOTPElements();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.refresher = setInterval(async () => {
|
||||||
|
await totpRefresher();
|
||||||
|
}, 3000) as unknown as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async cleanup(): Promise<void> {
|
||||||
|
clearInterval(this.refresher);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateTOTPElements(): Promise<void> {
|
||||||
|
render((
|
||||||
|
<>
|
||||||
|
{await Promise.all(Array.from(await getTOTPKeys(this.state.baseMount)).map(async (key) =>
|
||||||
|
<TOTPGridItem
|
||||||
|
item_key={String(key).toString()}
|
||||||
|
item_value={await getTOTPCode(this.state.baseMount, key)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
), document.querySelector("#totpList"))
|
||||||
|
}
|
||||||
|
|
||||||
|
makeTOTPListElement(totpKeyName: string): TOTPListElement {
|
||||||
|
|
||||||
|
const gridElement = makeElement({
|
||||||
|
tag: "div",
|
||||||
|
class: ["uk-grid", "uk-grid-small", "uk-text-expand"],
|
||||||
|
}) as TOTPListElement;
|
||||||
|
|
||||||
|
|
||||||
|
return gridElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPageTitle(): Promise<Element | string> {
|
||||||
|
return await SecretTitleElement(this.router);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return i18next.t("totp_view_title");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue