mirror of
https://github.com/quantum5/totp.git
synced 2025-04-24 05:31:59 -04:00
Avoid pointless React namespace
This commit is contained in:
parent
8e02568a43
commit
bd2166e825
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, {HTMLProps, SyntheticEvent, useCallback} from 'react';
|
||||
|
||||
type HTMLAnchorProps = Omit<React.HTMLProps<HTMLAnchorElement>, 'href' | 'onClick'>;
|
||||
type HTMLAnchorProps = Omit<HTMLProps<HTMLAnchorElement>, 'href' | 'onClick'>;
|
||||
|
||||
export default function ActionLink({onClick, className, ...props}: HTMLAnchorProps & { onClick: () => void }) {
|
||||
const handleClick = React.useCallback((e: React.SyntheticEvent) => {
|
||||
const handleClick = useCallback((e: SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}, [onClick]);
|
||||
|
|
34
src/App.tsx
34
src/App.tsx
|
@ -1,4 +1,4 @@
|
|||
import React, {useState} from 'react';
|
||||
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import base32Decode from 'base32-decode';
|
||||
import {NumberInput, TextInput} from './Input';
|
||||
import OTPOutput from './OTPOutput';
|
||||
|
@ -18,12 +18,12 @@ function parseState() {
|
|||
function App() {
|
||||
const [advanced, setAdvanced] = useState(false);
|
||||
|
||||
const [state, setState] = React.useState(() => parseState() || defaults);
|
||||
const [state, setState] = useState(() => parseState() || defaults);
|
||||
const {secret, step, digits, algorithm} = state;
|
||||
const [offset, setOffset] = React.useState(0);
|
||||
const [remaining, setRemaining] = React.useState(0);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [remaining, setRemaining] = useState(0);
|
||||
|
||||
const [validSecret, decoded] = React.useMemo(() => {
|
||||
const [validSecret, decoded] = useMemo(() => {
|
||||
try {
|
||||
return [true, base32Decode(secret.toUpperCase(), 'RFC4648')];
|
||||
} catch (e) {
|
||||
|
@ -35,13 +35,13 @@ function App() {
|
|||
const validDigits = digits > 0 && digits <= 10;
|
||||
const valid = validSecret && validStep && validDigits && !!secret;
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!validStep) return;
|
||||
const now = Date.now();
|
||||
setOffset(Math.floor(now / (1000 * step)));
|
||||
}, [validStep, step]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!validStep) return;
|
||||
const now = Date.now();
|
||||
const nextUpdate = Math.floor(now / 1000) * 1000 + 1000;
|
||||
|
@ -55,50 +55,50 @@ function App() {
|
|||
return () => clearTimeout(timer);
|
||||
}, [validStep, step, offset, remaining]);
|
||||
|
||||
const showAdvanced = React.useCallback(() => {
|
||||
const showAdvanced = useCallback(() => {
|
||||
setAdvanced(true);
|
||||
}, []);
|
||||
|
||||
const hideAdvanced = React.useCallback(() => {
|
||||
const hideAdvanced = useCallback(() => {
|
||||
setAdvanced(false);
|
||||
}, []);
|
||||
|
||||
const setSecret = React.useCallback(
|
||||
const setSecret = useCallback(
|
||||
(secret: string) => setState((state) => ({...state, secret})),
|
||||
[],
|
||||
);
|
||||
|
||||
const setStep = React.useCallback(
|
||||
const setStep = useCallback(
|
||||
(step: number) => setState((state) => ({...state, step})),
|
||||
[],
|
||||
);
|
||||
|
||||
const setDigits = React.useCallback(
|
||||
const setDigits = useCallback(
|
||||
(digits: number) => setState((state) => ({...state, digits})),
|
||||
[],
|
||||
);
|
||||
|
||||
const setAlgorithm = React.useCallback(
|
||||
const setAlgorithm = useCallback(
|
||||
(algorithm: string) => setState((state) => ({...state, algorithm})),
|
||||
[],
|
||||
);
|
||||
|
||||
const onReset = React.useCallback(
|
||||
const onReset = useCallback(
|
||||
() => setState((state) => ({...state, step: 30, digits: 6, algorithm: 'sha1'})),
|
||||
[],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const value = serializeState(state);
|
||||
history.replaceState(null, '', window.location.pathname + window.location.search + (value && `#!${value}`));
|
||||
}, [state]);
|
||||
|
||||
const onHashChange = React.useCallback(() => {
|
||||
const onHashChange = useCallback(() => {
|
||||
const state = parseState();
|
||||
state && setState(state);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
return () => window.removeEventListener('hashchange', onHashChange);
|
||||
}, [onHashChange]);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import React, {ReactNode, useEffect, useId} from 'react';
|
||||
import {Collapse} from 'bootstrap';
|
||||
|
||||
export default function Collapsible({children, show}: { children: React.ReactNode; show: boolean }) {
|
||||
const id = React.useId();
|
||||
export default function Collapsible({children, show}: { children: ReactNode; show: boolean }) {
|
||||
const id = useId();
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const collapse = Collapse.getOrCreateInstance(`#${id}`, {toggle: show});
|
||||
if (show)
|
||||
collapse.show();
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import React, {useCallback, useEffect, useId, useState} from 'react';
|
||||
import Copy from './copy.svg?react';
|
||||
import {Popover} from 'bootstrap';
|
||||
|
||||
export default function CopyButton({text}: { text: string }) {
|
||||
const id = React.useId();
|
||||
const [popover, setPopover] = React.useState(false);
|
||||
const id = useId();
|
||||
const [popover, setPopover] = useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!popover) return;
|
||||
const instance = Popover.getOrCreateInstance(`#${id}`, {
|
||||
trigger: 'manual',
|
||||
|
@ -20,7 +20,7 @@ export default function CopyButton({text}: { text: string }) {
|
|||
}, 1000);
|
||||
}, [id, popover]);
|
||||
|
||||
const onCopy = React.useCallback(async () => {
|
||||
const onCopy = useCallback(async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setPopover(true);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, {ReactNode, HTMLProps, useCallback, useId} from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
type CommonProps = {
|
||||
label: React.ReactNode;
|
||||
error?: React.ReactNode;
|
||||
label: ReactNode;
|
||||
error?: ReactNode;
|
||||
};
|
||||
|
||||
type TextInputProps = {
|
||||
|
@ -11,12 +11,12 @@ type TextInputProps = {
|
|||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
type HTMLInputProps = Omit<React.HTMLProps<HTMLInputElement>, 'onChange' | 'label'>;
|
||||
type HTMLInputProps = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'label'>;
|
||||
|
||||
function BaseInput({label, value, onChange, error, ...props}: CommonProps & HTMLInputProps & TextInputProps) {
|
||||
const id = React.useId();
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
|
||||
const id = useId();
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
|
||||
[onChange],
|
||||
);
|
||||
|
||||
|
@ -38,6 +38,6 @@ export function NumberInput({value, onChange, ...props}: CommonProps & {
|
|||
min?: number;
|
||||
max?: number;
|
||||
}) {
|
||||
const handleChange = React.useCallback((value: string) => onChange(+value), [onChange]);
|
||||
const handleChange = useCallback((value: string) => onChange(+value), [onChange]);
|
||||
return <BaseInput type="number" value={`${value}`} onChange={handleChange} {...props}/>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, {useMemo} from 'react';
|
||||
import {HOTP, HOTPOptions} from '@otplib/core';
|
||||
import {createDigest} from '@otplib/plugin-crypto-js';
|
||||
import classNames from 'classnames';
|
||||
|
@ -25,7 +25,7 @@ export default function OTPOutput({secret, offset, algorithm, digits}: {
|
|||
algorithm: HashAlgorithm;
|
||||
digits: number;
|
||||
}) {
|
||||
const otp = React.useMemo(() => new HOTP<HOTPOptions>({
|
||||
const otp = useMemo(() => new HOTP<HOTPOptions>({
|
||||
createDigest,
|
||||
digits,
|
||||
algorithm: ALGORITHMS[algorithm],
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import React, {ChangeEvent, ReactNode, useCallback, useId} from 'react';
|
||||
|
||||
export default function Select<T extends string>({label, options, value, onChange}: {
|
||||
label: React.ReactNode;
|
||||
label: ReactNode;
|
||||
options: Record<T, string>;
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
}) {
|
||||
const id = React.useId();
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLSelectElement>) => onChange(e.target.value as T),
|
||||
const id = useId();
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLSelectElement>) => onChange(e.target.value as T),
|
||||
[onChange],
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue