1
0
Fork 0

replace modals with changing page content for better accessability

This commit is contained in:
ChaotiCryptidz 2022-01-19 12:08:06 +00:00
parent 107c0b6e1b
commit a03a2655d0
13 changed files with 293 additions and 256 deletions

View file

@ -13,12 +13,12 @@
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/prismjs": "^1.16.6", "@types/prismjs": "^1.16.6",
"@types/uikit": "^3.3.2", "@types/uikit": "^3.3.2",
"@typescript-eslint/eslint-plugin": "^5.9.1", "@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.9.1", "@typescript-eslint/parser": "^5.10.0",
"babel-loader": "^8.2.3", "babel-loader": "^8.2.3",
"css-loader": "^6.5.1", "css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.3.1", "css-minimizer-webpack-plugin": "^3.4.1",
"eslint": "^8.6.0", "eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.4", "eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
@ -38,7 +38,7 @@
"dependencies": { "dependencies": {
"clipboard": "^2.0.8", "clipboard": "^2.0.8",
"codejar": "^3.5.0", "codejar": "^3.5.0",
"core-js": "^3.20.2", "core-js": "^3.20.3",
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"hjson": "^3.2.2", "hjson": "^3.2.2",

View file

@ -26,7 +26,6 @@
@import "uikit/src/scss/components/label.scss"; @import "uikit/src/scss/components/label.scss";
// JavaScript // JavaScript
@import "uikit/src/scss/components/modal.scss";
@import "uikit/src/scss/components/slider.scss"; @import "uikit/src/scss/components/slider.scss";
@import "uikit/src/scss/components/switcher.scss"; @import "uikit/src/scss/components/switcher.scss";
@import "uikit/src/scss/components/notification.scss"; @import "uikit/src/scss/components/notification.scss";

View file

@ -16,9 +16,9 @@ module.exports = {
not_implemented: "Noch nicht implementiert", not_implemented: "Noch nicht implementiert",
// Copyable Modal // Copyable Modal
copy_modal_download_btn: "Download", copy_box_download_btn: "Download",
copy_modal_copy_btn: "Kopieren", copy_box_copy_btn: "Kopieren",
copy_modal_close_btn: "Schließen", copy_box_close_btn: "Schließen",
// Generic Loading Text // Generic Loading Text
content_loading: "Laden..", content_loading: "Laden..",
@ -206,7 +206,7 @@ module.exports = {
transit_encrypt_input_placeholder: "Normaler-Text oder base64", transit_encrypt_input_placeholder: "Normaler-Text oder base64",
transit_encrypt_already_encoded_checkbox: "Ist der text schon in base64 verschlüsselt?", transit_encrypt_already_encoded_checkbox: "Ist der text schon in base64 verschlüsselt?",
transit_encrypt_encrypt_btn: "Verschlüsseln", transit_encrypt_encrypt_btn: "Verschlüsseln",
transit_encrypt_encryption_result_modal_title: "Verachlüsselungs ergebnis", transit_encrypt_encryption_result_title: "Verachlüsselungs ergebnis",
// Transit Decrypt Page // Transit Decrypt Page
transit_decrypt_title: "Transit Decrypt", transit_decrypt_title: "Transit Decrypt",
@ -214,7 +214,7 @@ module.exports = {
transit_decrypt_input_placeholder: "Cyphertext", transit_decrypt_input_placeholder: "Cyphertext",
transit_decrypt_decode_checkbox: "Soll der Text base64 codiert werden?", transit_decrypt_decode_checkbox: "Soll der Text base64 codiert werden?",
transit_decrypt_decrypt_btn: "Entschlüsseln", transit_decrypt_decrypt_btn: "Entschlüsseln",
transit_decrypt_decryption_result_modal_title: "Entschlüsselungs ergebnis", transit_decrypt_decryption_result_title: "Entschlüsselungs ergebnis",
// Transit Rewrap Page // Transit Rewrap Page
transit_rewrap_title: "Transit Rewrap", transit_rewrap_title: "Transit Rewrap",
@ -223,7 +223,7 @@ module.exports = {
transit_rewrap_latest_version_option_text: "{{version_num}} (neuste)", transit_rewrap_latest_version_option_text: "{{version_num}} (neuste)",
transit_rewrap_input_placeholder: "Cyphertext", transit_rewrap_input_placeholder: "Cyphertext",
transit_rewrap_rewrap_btn: "Rewrap", transit_rewrap_rewrap_btn: "Rewrap",
transit_rewrap_result_modal_title: "Rewrap Result", transit_rewrap_result_title: "Rewrap Result",
// Delete Secret Engine Page // Delete Secret Engine Page
delete_secrets_engine_title: "Delete Secret Engine ({{mount}})", delete_secrets_engine_title: "Delete Secret Engine ({{mount}})",

View file

@ -35,9 +35,9 @@ module.exports = {
not_implemented: "Not Yet Implemented", not_implemented: "Not Yet Implemented",
// Copyable Modal // Copyable Modal
copy_modal_download_btn: "Download", copy_box_download_btn: "Download",
copy_modal_copy_btn: "Copy", copy_box_copy_btn: "Copy",
copy_modal_close_btn: "Close", copy_box_close_btn: "Close",
// Generic Loading Text // Generic Loading Text
content_loading: "Loading..", content_loading: "Loading..",
@ -227,14 +227,14 @@ module.exports = {
transit_encrypt_suffix: " (encrypt)", transit_encrypt_suffix: " (encrypt)",
transit_encrypt_input_placeholder: "Plaintext or base64", transit_encrypt_input_placeholder: "Plaintext or base64",
transit_encrypt_already_encoded_checkbox: "Is the data already encoded in base64?", transit_encrypt_already_encoded_checkbox: "Is the data already encoded in base64?",
transit_encrypt_encryption_result_modal_title: "Encryption Result", transit_encrypt_encryption_result_title: "Encryption Result",
// Transit Decrypt Page // Transit Decrypt Page
transit_decrypt_title: "Transit Decrypt", transit_decrypt_title: "Transit Decrypt",
transit_decrypt_suffix: " (decrypt)", transit_decrypt_suffix: " (decrypt)",
transit_decrypt_input_placeholder: "Cyphertext", transit_decrypt_input_placeholder: "Cyphertext",
transit_decrypt_decode_checkbox: "Should the plaintext be base64 decoded?", transit_decrypt_decode_checkbox: "Should the plaintext be base64 decoded?",
transit_decrypt_decryption_result_modal_title: "Decryption Result", transit_decrypt_decryption_result_title: "Decryption Result",
// Transit Rewrap Page // Transit Rewrap Page
transit_rewrap_title: "Transit Rewrap", transit_rewrap_title: "Transit Rewrap",
@ -242,7 +242,7 @@ module.exports = {
transit_rewrap_version_option_text: "{{version_num}}", transit_rewrap_version_option_text: "{{version_num}}",
transit_rewrap_latest_version_option_text: "{{version_num}} (latest)", transit_rewrap_latest_version_option_text: "{{version_num}} (latest)",
transit_rewrap_input_placeholder: "Cyphertext", transit_rewrap_input_placeholder: "Cyphertext",
transit_rewrap_result_modal_title: "Rewrap Result", transit_rewrap_result_title: "Rewrap Result",
// Delete Secret Engine Page // Delete Secret Engine Page
delete_secrets_engine_title: "Delete Secret Engine ({{mount}})", delete_secrets_engine_title: "Delete Secret Engine ({{mount}})",

View file

@ -14,9 +14,9 @@ module.exports = {
notification_copy_success: "Copié dans le presse-papiers.", notification_copy_success: "Copié dans le presse-papiers.",
// Copyable Modal // Copyable Modal
copy_modal_download_btn: "Télécharger", copy_box_download_btn: "Télécharger",
copy_modal_copy_btn: "Copier", copy_box_copy_btn: "Copier",
copy_modal_close_btn: "Fermer", copy_box_close_btn: "Fermer",
// Copyable Input Box // Copyable Input Box
copy_input_box_copy_icon_text: "Bouton Copier", copy_input_box_copy_icon_text: "Bouton Copier",
@ -140,7 +140,7 @@ module.exports = {
transit_encrypt_input_placeholder: "Texte brut ou base64", transit_encrypt_input_placeholder: "Texte brut ou base64",
transit_encrypt_already_encoded_checkbox: "Les données sont-elles déjà encodées en base64 ?", transit_encrypt_already_encoded_checkbox: "Les données sont-elles déjà encodées en base64 ?",
transit_encrypt_encrypt_btn: "Chiffrer", transit_encrypt_encrypt_btn: "Chiffrer",
transit_encrypt_encryption_result_modal_title: "Résultat chiffré", transit_encrypt_encryption_result_title: "Résultat chiffré",
// Transit decrypt Page // Transit decrypt Page
transit_decrypt_title: "Déchiffrement Transit", transit_decrypt_title: "Déchiffrement Transit",
@ -148,5 +148,5 @@ module.exports = {
transit_decrypt_input_placeholder: "Message chiffré", transit_decrypt_input_placeholder: "Message chiffré",
transit_decrypt_decode_checkbox: "Est-ce que le texte brut doit être encodé en base64 ?", transit_decrypt_decode_checkbox: "Est-ce que le texte brut doit être encodé en base64 ?",
transit_decrypt_decrypt_btn: "Déchiffrer", transit_decrypt_decrypt_btn: "Déchiffrer",
transit_decrypt_decryption_result_modal_title: "Résultat déchiffré", transit_decrypt_decryption_result_title: "Résultat déchiffré",
}; };

View file

@ -35,9 +35,9 @@ module.exports = {
not_implemented: "Функция ещё не реализована", not_implemented: "Функция ещё не реализована",
// Copyable Modal // Copyable Modal
copy_modal_download_btn: "Загрузить", copy_box_download_btn: "Загрузить",
copy_modal_copy_btn: "Копировать", copy_box_copy_btn: "Копировать",
copy_modal_close_btn: "Закрыть", copy_box_close_btn: "Закрыть",
// Generic Loading Text // Generic Loading Text
content_loading: "Загрузка..", content_loading: "Загрузка..",
@ -222,14 +222,14 @@ module.exports = {
transit_encrypt_suffix: " (шифр.)", transit_encrypt_suffix: " (шифр.)",
transit_encrypt_input_placeholder: "Текст или base64", transit_encrypt_input_placeholder: "Текст или base64",
transit_encrypt_already_encoded_checkbox: "Данные уже закодированы в base64?", transit_encrypt_already_encoded_checkbox: "Данные уже закодированы в base64?",
transit_encrypt_encryption_result_modal_title: "Результат шифрования", transit_encrypt_encryption_result_title: "Результат шифрования",
// Transit Decrypt Page // Transit Decrypt Page
transit_decrypt_title: "Расшифрование Transit", transit_decrypt_title: "Расшифрование Transit",
transit_decrypt_suffix: " (расшифр.)", transit_decrypt_suffix: " (расшифр.)",
transit_decrypt_input_placeholder: "Шифр-текст", transit_decrypt_input_placeholder: "Шифр-текст",
transit_decrypt_decode_checkbox: "Нужно ли раскодировать текст из base64?", transit_decrypt_decode_checkbox: "Нужно ли раскодировать текст из base64?",
transit_decrypt_decryption_result_modal_title: "Результат расшифрования", transit_decrypt_decryption_result_title: "Результат расшифрования",
// Transit Rewrap Page // Transit Rewrap Page
transit_rewrap_title: "Перешифрование Transit", transit_rewrap_title: "Перешифрование Transit",
@ -237,7 +237,7 @@ module.exports = {
transit_rewrap_version_option_text: "{{version_num}}", transit_rewrap_version_option_text: "{{version_num}}",
transit_rewrap_latest_version_option_text: "{{version_num}} (последняя версия)", transit_rewrap_latest_version_option_text: "{{version_num}} (последняя версия)",
transit_rewrap_input_placeholder: "Шифр-текст", transit_rewrap_input_placeholder: "Шифр-текст",
transit_rewrap_result_modal_title: "Результат перешифрования", transit_rewrap_result_title: "Результат перешифрования",
// Delete Secret Engine Page // Delete Secret Engine Page
delete_secrets_engine_title: "Удалить обработчик тайн ({{mount}})", delete_secrets_engine_title: "Удалить обработчик тайн ({{mount}})",

View file

@ -1,4 +1,4 @@
import { JSX } from "preact"; import { JSX, RefObject } from "preact";
import { route } from "preact-router"; import { route } from "preact-router";
export type ButtonProps = { export type ButtonProps = {
@ -8,6 +8,7 @@ export type ButtonProps = {
route?: string; route?: string;
onClick?: () => unknown; onClick?: () => unknown;
type?: "submit" | null; type?: "submit" | null;
buttonRef?: RefObject<HTMLButtonElement>;
}; };
export function Button(props: ButtonProps): JSX.Element { export function Button(props: ButtonProps): JSX.Element {
@ -15,6 +16,8 @@ export function Button(props: ButtonProps): JSX.Element {
if (props.route) { if (props.route) {
return ( return (
<button <button
{...props}
ref={props.buttonRef}
class={classes} class={classes}
onClick={() => { onClick={() => {
route(props.route); route(props.route);
@ -26,7 +29,13 @@ export function Button(props: ButtonProps): JSX.Element {
); );
} else { } else {
return ( return (
<button class={classes} type={props.type} onClick={props.onClick}> <button
{...props}
ref={props.buttonRef}
class={classes}
type={props.type}
onClick={props.onClick}
>
{props.text} {props.text}
</button> </button>
); );

View file

@ -2,22 +2,26 @@ import { JSX } from "preact";
import Prism from "prismjs"; import Prism from "prismjs";
export type CodeBlockProps = { export type CodeBlockProps = {
language: string;
code: string; code: string;
language?: string;
}; };
export function CodeBlock(props: CodeBlockProps): JSX.Element { export function CodeBlock(props: CodeBlockProps): JSX.Element {
const highlightedCode = Prism.highlight( if (props.language) {
props.code, const highlightedCode = Prism.highlight(
Prism.languages[props.language], props.code,
props.language, Prism.languages[props.language],
); props.language,
);
return ( return (
<pre <pre
class="code-block language-json line-numbers" class={"code-block " + "language-" + props.language}
dir="ltr" dir="ltr"
dangerouslySetInnerHTML={{ __html: highlightedCode }} dangerouslySetInnerHTML={{ __html: highlightedCode }}
/> />
); );
} else {
return <pre class="code-block" dir="ltr" dangerouslySetInnerHTML={{ __html: props.code }} />;
}
} }

View file

@ -0,0 +1,57 @@
import { Button } from "./Button";
import { CodeBlock } from "./CodeBlock";
import { Component, JSX, createRef } from "preact";
import { addClipboardNotifications } from "../../pageUtils";
import ClipboardJS from "clipboard";
import FileSaver from "file-saver";
import i18next from "i18next";
export type CopyableBoxProps = {
title: string;
contentString: string;
goBack?: () => void;
};
export class CopyableBox extends Component<CopyableBoxProps> {
copyButtonRef = createRef<HTMLButtonElement>();
saveAsFile(): void {
const blob = new Blob([this.props.contentString], {
type: "text/plain;charset=utf-8",
});
FileSaver.saveAs(blob, "result.txt");
}
componentDidMount(): void {
console.log(this.copyButtonRef.current);
const clipboard = new ClipboardJS(this.copyButtonRef.current);
addClipboardNotifications(clipboard, 600);
}
render(): JSX.Element {
return (
<div>
<div>
<h2>{this.props.title}</h2>
</div>
<CodeBlock code={this.props.contentString} />
<p>
<Button
text={i18next.t("copy_box_download_btn")}
color="primary"
onClick={() => this.saveAsFile()}
/>
<Button
text={i18next.t("copy_box_copy_btn")}
color="primary"
buttonRef={this.copyButtonRef}
data-clipboard-text={this.props.contentString}
/>
{this.props.goBack && (
<Button text={i18next.t("common_back")} color="secondary" onClick={this.props.goBack} />
)}
</p>
</div>
);
}
}

View file

@ -1,68 +0,0 @@
import { Component, JSX, createRef } from "preact";
import { addClipboardNotifications } from "../../pageUtils";
import ClipboardJS from "clipboard";
import FileSaver from "file-saver";
import i18next from "i18next";
export type CopyableModalProps = {
id: string;
name: string;
contentString: string;
};
export class CopyableModal extends Component<CopyableModalProps, unknown> {
ref = createRef();
copyButtonRef = createRef<HTMLButtonElement>();
saveAsFile(): void {
const blob = new Blob([this.props.contentString], {
type: "text/plain;charset=utf-8",
});
FileSaver.saveAs(blob, "result.txt");
}
componentDidMount(): void {
const clipboard = new ClipboardJS(this.copyButtonRef.current);
addClipboardNotifications(clipboard, 600);
}
render(): JSX.Element {
return (
<div id={this.props.id} ref={this.ref} class="modal-section" uk-modal>
<div class="uk-modal-dialog">
<button class="uk-modal-close-default" type="button" uk-close />
<div class="uk-modal-header">
<h2>{this.props.name}</h2>
</div>
<div class="uk-modal-body">
<pre class="wrap-pre">{this.props.contentString}</pre>
</div>
<div class="uk-modal-footer uk-text-right">
<button
class="uk-button uk-button-primary"
type="button"
onClick={() => this.saveAsFile()}
>
{i18next.t("copy_modal_download_btn")}
</button>
<button
class="uk-button uk-button-primary"
ref={this.copyButtonRef}
type="button"
data-clipboard-text={this.props.contentString}
>
{i18next.t("copy_modal_copy_btn")}
</button>
<button
class="uk-button uk-button-secondary uk-modal-close"
type="button"
data-clipboard-text={this.props.contentString}
>
{i18next.t("copy_modal_close_btn")}
</button>
</div>
</div>
</div>
);
}
}

View file

@ -1,6 +1,6 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component, render } from "preact"; import { Component } from "preact";
import { CopyableModal } from "../../../elements/CopyableModal"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { FileUploadInput } from "../../../elements/FileUploadInput"; import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
@ -9,41 +9,59 @@ import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils"; import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils"; import { setErrorText } from "../../../../pageUtils";
import UIkit from "uikit";
import i18next from "i18next"; import i18next from "i18next";
export class TransitDecrypt extends Component<DefaultPageProps> { export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: string }> {
render() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; const secretItem = this.props.matches["secretItem"];
return (
<> const title = (
<SecretTitleElement <SecretTitleElement
type="transit" type="transit"
baseMount={baseMount} baseMount={baseMount}
item={secretItem} item={secretItem}
suffix={i18next.t("transit_decrypt_suffix")} suffix={i18next.t("transit_decrypt_suffix")}
/> />
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="ciphertext"
placeholder={i18next.t("transit_decrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="ciphertext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_decrypt")} color="primary" type="submit" />
<div id="modalAttachmentPoint" />
</Form>
</>
); );
if (!this.state.plaintext) {
return (
<>
{title}
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="ciphertext"
placeholder={i18next.t("transit_decrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="ciphertext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_decrypt")} color="primary" type="submit" />
</Form>
</>
);
} else {
return (
<>
{title}
<CopyableBox
title={i18next.t("transit_decrypt_decryption_result_title")}
contentString={this.state.plaintext}
goBack={() => {
this.setState({ plaintext: null });
}}
/>
</>
);
}
} }
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
@ -67,17 +85,11 @@ export class TransitDecrypt extends Component<DefaultPageProps> {
}); });
let plaintext = res.plaintext; let plaintext = res.plaintext;
if (decodeBase64 == "on") { if (decodeBase64 == "on") {
// Really don't supposed to be doing this...
plaintext = atob(plaintext); plaintext = atob(plaintext);
} }
render(
<CopyableModal this.setState({ plaintext: plaintext });
id="transitResultModal"
name={i18next.t("transit_decrypt_decryption_result_modal_title")}
contentString={plaintext}
/>,
document.querySelector("#modalAttachmentPoint"),
);
UIkit.modal(document.querySelector("#transitResultModal")).show();
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
setErrorText(`API Error: ${error.message}`); setErrorText(`API Error: ${error.message}`);

View file

@ -1,6 +1,6 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component, render } from "preact"; import { Component } from "preact";
import { CopyableModal } from "../../../elements/CopyableModal"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { FileUploadInput } from "../../../elements/FileUploadInput"; import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
@ -9,41 +9,59 @@ import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils"; import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils"; import { setErrorText } from "../../../../pageUtils";
import UIkit from "uikit";
import i18next from "i18next"; import i18next from "i18next";
export class TransitEncrypt extends Component<DefaultPageProps> { export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: string }> {
render() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; const secretItem = this.props.matches["secretItem"];
return (
<> const title = (
<SecretTitleElement <SecretTitleElement
type="transit" type="transit"
baseMount={baseMount} baseMount={baseMount}
item={secretItem} item={secretItem}
suffix={i18next.t("transit_encrypt_suffix")} suffix={i18next.t("transit_encrypt_suffix")}
/> />
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="plaintext"
placeholder={i18next.t("transit_encrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="plaintext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_encrypt")} color="primary" type="submit" />
<div id="modalAttachmentPoint" />
</Form>
</>
); );
if (!this.state.ciphertext) {
return (
<>
{title}
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="plaintext"
placeholder={i18next.t("transit_encrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="plaintext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_encrypt")} color="primary" type="submit" />
</Form>
</>
);
} else {
return (
<>
{title}
<CopyableBox
title={i18next.t("transit_encrypt_encryption_result_title")}
contentString={this.state.ciphertext}
goBack={() => {
this.setState({ ciphertext: null });
}}
/>
</>
);
}
} }
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
@ -66,15 +84,7 @@ export class TransitEncrypt extends Component<DefaultPageProps> {
const res = await this.props.api.transitEncrypt(baseMount, secretItem, { const res = await this.props.api.transitEncrypt(baseMount, secretItem, {
plaintext: plaintext, plaintext: plaintext,
}); });
render( this.setState({ ciphertext: res.ciphertext });
<CopyableModal
id="transitResultModal"
name={i18next.t("transit_encrypt_encryption_result_modal_title")}
contentString={res.ciphertext}
/>,
document.querySelector("#modalAttachmentPoint"),
);
UIkit.modal(document.querySelector("#transitResultModal")).show();
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
setErrorText(`API Error: ${error.message}`); setErrorText(`API Error: ${error.message}`);

View file

@ -1,6 +1,6 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component, render } from "preact"; import { Component } from "preact";
import { CopyableModal } from "../../../elements/CopyableModal"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
@ -8,12 +8,16 @@ import { SecretTitleElement } from "../SecretTitleElement";
import { TransitKeyType } from "../../../../api/types/transit"; import { TransitKeyType } from "../../../../api/types/transit";
import { objectToMap } from "../../../../utils"; import { objectToMap } from "../../../../utils";
import { setErrorText } from "../../../../pageUtils"; import { setErrorText } from "../../../../pageUtils";
import UIkit from "uikit";
import i18next from "i18next"; import i18next from "i18next";
type versionOption = { version: string; label: string }; type versionOption = { version: string; label: string };
export class TransitRewrap extends Component<DefaultPageProps, { transitKey: TransitKeyType }> { type TransitRewrapState = {
transitKey: TransitKeyType;
ciphertext: string;
};
export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapState> {
async componentDidMount() { async componentDidMount() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; const secretItem = this.props.matches["secretItem"];
@ -28,58 +32,76 @@ export class TransitRewrap extends Component<DefaultPageProps, { transitKey: Tra
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; const secretItem = this.props.matches["secretItem"];
const stringVersions = Array.from( const title = (
objectToMap(this.state.transitKey.keys).keys(), <SecretTitleElement
).reverse() as unknown as string[]; type="transit"
baseMount={baseMount}
const versions = stringVersions.map((val): number => parseInt(val, 10)); item={secretItem}
suffix={i18next.t("transit_rewrap_suffix")}
// get the selectable version options in the same />
// format the official UI uses.
// e.g: ["2 (latest)", "1"]
const options: versionOption[] = versions.map((val): versionOption => {
const i18nkey =
val == Math.max(...versions)
? "transit_rewrap_latest_version_option_text"
: "transit_rewrap_version_option_text";
return {
version: String(val),
label: i18next.t(i18nkey, { version_num: String(val) }),
};
});
return (
<>
<SecretTitleElement
type="transit"
baseMount={baseMount}
item={secretItem}
suffix={i18next.t("transit_rewrap_suffix")}
/>
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-width-1-2" name="version">
{options.map((option) => (
<option label={option.label} value={option.version}>
{option.label}
</option>
))}
</select>
</Margin>
<Margin>
<textarea
class="uk-textarea uk-width-1-2"
name="ciphertext"
placeholder={i18next.t("transit_rewrap_input_placeholder")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_rewrap")} color="primary" type="submit" />
<div id="modalAttachmentPoint" />
</Form>
</>
); );
if (!this.state.ciphertext) {
const stringVersions = Array.from(
objectToMap(this.state.transitKey.keys).keys(),
).reverse() as unknown as string[];
const versions = stringVersions.map((val): number => parseInt(val, 10));
// get the selectable version options in the same
// format the official UI uses.
// e.g: ["2 (latest)", "1"]
const options: versionOption[] = versions.map((val): versionOption => {
const i18nkey =
val == Math.max(...versions)
? "transit_rewrap_latest_version_option_text"
: "transit_rewrap_version_option_text";
return {
version: String(val),
label: i18next.t(i18nkey, { version_num: String(val) }),
};
});
return (
<>
{title}
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-width-1-2" name="version">
{options.map((option) => (
<option label={option.label} value={option.version}>
{option.label}
</option>
))}
</select>
</Margin>
<Margin>
<textarea
class="uk-textarea uk-width-1-2"
name="ciphertext"
placeholder={i18next.t("transit_rewrap_input_placeholder")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Button text={i18next.t("transit_rewrap")} color="primary" type="submit" />
</Form>
</>
);
} else {
return (
<>
{title}
<CopyableBox
title={i18next.t("transit_rewrap_result_title")}
contentString={this.state.ciphertext}
goBack={() => {
this.setState({ ciphertext: null });
}}
/>
</>
);
}
} }
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
@ -90,15 +112,7 @@ export class TransitRewrap extends Component<DefaultPageProps, { transitKey: Tra
ciphertext: data.get("ciphertext") as string, ciphertext: data.get("ciphertext") as string,
key_version: parseInt(data.get("version") as string, 10), key_version: parseInt(data.get("version") as string, 10),
}); });
render( this.setState({ ciphertext: res.ciphertext });
<CopyableModal
id="transitResultModal"
name={i18next.t("transit_rewrap_result_modal_title")}
contentString={res.ciphertext}
/>,
document.querySelector("#modalAttachmentPoint"),
);
UIkit.modal(document.querySelector("#transitResultModal")).show();
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
setErrorText(`API Error: ${error.message}`); setErrorText(`API Error: ${error.message}`);