diff --git a/app/src/App.css b/app/src/App.css index a6a8855f..58bd3514 100644 --- a/app/src/App.css +++ b/app/src/App.css @@ -247,8 +247,10 @@ textarea.otp { color: #DDDDDD !important; } -.ui.inverted.accordion .title, -.ui.inverted.accordion .content { +/* Direct children only, so nested components that use the `content`/`title` class names (e.g. a Message's + Message.Content) are not given the accordion's dark background. */ +.ui.inverted.accordion > .title, +.ui.inverted.accordion > .content { background-color: #171616 !important; } diff --git a/app/src/api.js b/app/src/api.js index 261e7d59..01a3445e 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -7,7 +7,6 @@ import { COLLECTIONS_API, DEFAULT_LIMIT, Downloaders, - OTP_API, VIDEOS_API, ZIM_API } from "./components/Vars"; @@ -584,39 +583,6 @@ export async function refreshChannel(channelId) { } return response; } - -export async function encryptOTP(otp, plaintext) { - let body = {otp, plaintext}; - const response = await apiPost(`${OTP_API}/encrypt_otp`, body); - if (response.ok) { - return await response.json(); - } else { - const message = await getErrorMessage(response, 'Failed to encrypt OTP. See server logs.'); - toast({ - type: 'error', - title: 'Failed to encrypt OTP', - description: message, - time: 5000, - }) - } -} - -export async function decryptOTP(otp, ciphertext) { - let body = {otp, ciphertext}; - const response = await apiPost(`${OTP_API}/decrypt_otp`, body); - if (response.ok) { - return await response.json(); - } else { - const message = await getErrorMessage(response, 'Failed to decrypt OTP. See server logs.'); - toast({ - type: 'error', - title: 'Failed to decrypt OTP', - description: message, - time: 5000, - }) - } -} - export async function getCategories() { const response = await apiGet(`${API_URI}/inventory/categories`); if (response.ok) { diff --git a/app/src/components/Apps.js b/app/src/components/Apps.js index 55458509..877cc183 100644 --- a/app/src/components/Apps.js +++ b/app/src/components/Apps.js @@ -1,8 +1,11 @@ -import React, {useContext} from 'react'; +import React, {useContext, useState} from 'react'; import { + Checkbox, Divider, + Icon, Input, Label, + Radio, SegmentGroup, StatisticLabel, StatisticValue, @@ -10,136 +13,157 @@ import { TableRow } from "semantic-ui-react"; import {Link, Route, Routes} from "react-router"; -import {decryptOTP, encryptOTP} from "../api"; import { - APIButton, + appendChecksum, + decryptOTP, + encryptOTP, + formatMessage, + generateHtml, + OTP_CHARS, + OTP_CHARS_ALPHA, + stripChecksum, + validateCharset +} from "./otp"; +import { ErrorMessage, humanFileSize, humanNumber, mimetypeColor, PageContainer, + SimpleAccordion, toLocaleString, useTitle } from "./Common"; import {ThemeContext} from "../contexts/contexts"; -import {Button, Header, Loader, Segment, Statistic, Table, TextArea} from "./Theme"; +import {Button, Form, Header, Loader, Segment, Statistic, Table, TextArea} from "./Theme"; import {useStatistics, useVINDecoder} from "../hooks/customHooks"; import {CalculatorsPage} from "./Calculators"; -class Encrypt extends React.Component { - constructor(props) { - super(props); - this.state = { - otp: '', plaintext: '', ciphertext: '', +function Encrypt({chars, checksum}) { + // Encrypt happens live as the user types or pastes. Nothing leaves the browser. + const [otp, setOtp] = useState(''); + const [plaintext, setPlaintext] = useState(''); + + const handleInputChange = (event, {name, value}) => { + if (name === 'otp') { + setOtp(value); + } else { + setPlaintext(value); + } + }; + + let ciphertext = ''; + let error = ''; + if (otp && plaintext && chars) { + try { + const raw = encryptOTP(otp, plaintext, chars).ciphertext; + // When enabled, append a checksum character then re-group the whole message+checksum. + ciphertext = checksum ? formatMessage(appendChecksum(raw, chars)) : raw; + } catch (e) { + error = e.message; } } - handleSubmit = async () => { - let {otp, plaintext, ciphertext} = await encryptOTP(this.state.otp, this.state.plaintext); - this.setState({otp, plaintext, ciphertext}); - } - - handleInputChange = async (event, {name, value}) => { - this.setState({[name]: value}); - } - - render() { - const disabled = !(this.state.otp && this.state.plaintext); - - return <> -
Encrypt
- - -

Key

-