fix: clipboard & 2fa improvements

A workaround that shows the content that would have been copied if `navigator.clipboard` is unavailable for whatever reason.

2FA input autofocuses & submits on enter.
This commit is contained in:
Jayvin Hernandez 2023-02-26 11:33:57 -08:00 committed by GitHub
parent 0848702f65
commit 8c5ff4f230
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 171 additions and 93 deletions

View file

@ -106,6 +106,13 @@ export default function File({
const handleCopy = () => {
clipboard.copy(`${window.location.protocol}//${window.location.host}${image.url}`);
setOpen(false);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: '',

View file

@ -4,8 +4,10 @@ import {
Box,
Burger,
Button,
Group,
Header,
Image,
Input,
MediaQuery,
Menu,
Navbar,
@ -214,7 +216,23 @@ export default function Layout({ children, props }) {
labels: { confirm: 'Copy', cancel: 'Cancel' },
onConfirm: async () => {
clipboard.copy(token);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: (
<Text size='sm'>
Zipline is unable to copy to clipboard due to security reasons. However, you can still copy
the token manually.
<br />
<Group position='left' spacing='sm'>
<Text>Your token is:</Text>
<Input size='sm' onFocus={(e) => e.target.select()} type='text' value={token} />
</Group>
</Text>
),
color: 'red',
});
else
showNotification({
title: 'Token Copied',
message: 'Your token has been copied to your clipboard.',

View file

@ -52,6 +52,13 @@ export default function Dashboard({ disableMediaPreview, exifEnabled }) {
const copyImage = async ({ original }) => {
clipboard.copy(`${window.location.protocol}//${window.location.host}${original.url}`);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: (

View file

@ -165,6 +165,13 @@ export default function Folders({ disableMediaPreview, exifEnabled }) {
aria-label='copy link'
onClick={() => {
clipboard.copy(`${window.location.origin}/folder/${folder.id}`);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied folder link',
message: (

View file

@ -158,6 +158,13 @@ export default function Invites() {
const handleCopy = async (invite) => {
clipboard.copy(`${window.location.protocol}//${window.location.host}/auth/register?code=${invite.code}`);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: '',

View file

@ -1,5 +1,6 @@
import { Button, Center, Image, Modal, NumberInput, Text, Title } from '@mantine/core';
import { showNotification } from '@mantine/notifications';
import { useForm } from '@mantine/form';
import { CheckIcon, CrossIcon } from 'components/icons';
import useFetch from 'hooks/useFetch';
import { useEffect, useState } from 'react';
@ -10,6 +11,7 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) {
const [disabled, setDisabled] = useState(false);
const [code, setCode] = useState(undefined);
const [error, setError] = useState('');
const form = useForm();
useEffect(() => {
(async () => {
@ -114,6 +116,11 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) {
</>
)}
<form
onSubmit={form.onSubmit(() => {
deleteTotp ? disableTotp() : verifyCode();
})}
>
<NumberInput
placeholder='2FA Code'
label='Verify'
@ -123,6 +130,7 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) {
minLength={6}
value={code}
onChange={(e) => setCode(e)}
data-autofocus
error={error}
/>
@ -136,6 +144,7 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) {
>
Verify{deleteTotp ? ' and Disable' : ''}
</Button>
</form>
</Modal>
);
}

View file

@ -27,6 +27,13 @@ export default function MetadataView({ fileId }) {
const copy = (value) => {
clipboard.copy(value);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: value,

View file

@ -128,6 +128,12 @@ export default function File({ chunks: chunks_config }) {
setTimeout(() => setProgress(0), 1000);
clipboard.copy(json.files[0]);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
}
ready = true;

View file

@ -7,6 +7,13 @@ export default function showFilesModal(clipboard, modals, files: string[]) {
const open = (idx: number) => window.open(files[idx], '_blank');
const copy = (idx: number) => {
clipboard.copy(files[idx]);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: <Link href={files[idx]}>{files[idx]}</Link>,

View file

@ -14,6 +14,13 @@ export default function URLCard({ url }: { url: URLResponse }) {
const copyURL = (u) => {
clipboard.copy(`${window.location.protocol}//${window.location.host}${u.url}`);
if (!navigator.clipboard)
showNotification({
title: 'Unable to copy to clipboard',
message: 'Zipline is unable to copy to clipboard due to security reasons.',
color: 'red',
});
else
showNotification({
title: 'Copied to clipboard',
message: '',

View file

@ -98,6 +98,7 @@ export default function Login({ title, user_registration, oauth_registration, oa
title={<Title order={3}>Two-Factor Authentication Required</Title>}
size='lg'
>
<form onSubmit={form.onSubmit(() => onSubmit(form.values))}>
<NumberInput
placeholder='2FA Code'
label='Verify'
@ -107,19 +108,14 @@ export default function Login({ title, user_registration, oauth_registration, oa
minLength={6}
value={code}
onChange={(e) => setCode(e)}
data-autofocus
error={error}
/>
<Button
disabled={disabled}
size='lg'
fullWidth
mt='md'
rightIcon={<CheckIcon />}
onClick={() => onSubmit(form.values)}
>
<Button disabled={disabled} size='lg' fullWidth mt='md' rightIcon={<CheckIcon />} type='submit'>
Verify &amp; Login
</Button>
</form>
</Modal>
<Center sx={{ height: '100vh' }}>
<div>