Files
spacedrive/interface/app/$libraryId/settings/library/keys/MasterPasswordDialog.tsx
Vítor Vasconcellos 0d3805339e [ENG-591] - Fix some funky behaviors (#827)
* WIP

* Some minor fixes for light theme
 - Fix `useIsDark` not reading the initial theme value (only reacting to theme changes)
 - Fix `Inspector` always showing a dark image when no item was selected
 - Fix `Thumb` video extension using black text on light theme

* Improve form error messages
 - Fix `addLocationDialog` not registering the path input
 - Remove `@hookform/error-message`

* Fix Dialog not respecting max-width
 - Fix ErrorMessage animation jumping

* A lot of misc fixes
 - Implement an `useExplorerItemData` (cleaner fix for thumbnail flicker)
 - Fix broken image showing for `Thumb` due a rece condition when props are updated
 - Implement an `ExternalObject` component that hacks an alternative for `onLoad` and `onError` events for <object>
 - Fix `Overview` broken layout when `Inspector` is open and window is small
 - Improve `IndexerRuleEditor` UX in `AddLocationDialog`
 - Improve the way `IndexerRuleEditor` handles rules deletion
 - Fix `IndexerRuleEditor` closing the the new rule form even when the rule creation fails
 - Add an editable prop to `IndexerRuleEditor` to disable all editable functions
 - Fix `getIcon` fallbacking to Document instead of the dark version of an icon if it exists
 - Add some missing colors to white theme

* Format

* Fix Backup restore key dialog not resetting after error

* Feedback

* Format

* Normalize imports

* Fix ColorPicker export

* Fix Thumb video ext not showing in MediaView with show square thumbnails
 - Fix AddLocationDialog Error resetting when changing IndexRules
2023-05-20 03:11:10 +00:00

188 lines
5.1 KiB
TypeScript

import { ArrowsClockwise, Clipboard, Eye, EyeSlash } from 'phosphor-react';
import { useState } from 'react';
import {
Algorithm,
HASHING_ALGOS,
HashingAlgoSlug,
hashingAlgoSlugSchema,
useLibraryMutation
} from '@sd/client';
import { Button, Dialog, Input, Select, SelectOption, UseDialogProps, useDialog } from '@sd/ui';
import { useZodForm, z } from '@sd/ui/src/forms';
import { PasswordMeter, showAlertDialog } from '~/components';
import { generatePassword } from '~/util';
const schema = z.object({
masterPassword: z.string(),
masterPassword2: z.string(),
encryptionAlgo: z.string(),
hashingAlgo: hashingAlgoSlugSchema
});
export default (props: UseDialogProps) => {
const changeMasterPassword = useLibraryMutation('keys.changeMasterPassword', {
onSuccess: () => {
showAlertDialog({
title: 'Success',
value: 'Your master password was changed successfully'
});
},
onError: () => {
// this should never really happen
showAlertDialog({
title: 'Master Password Change Error',
value: 'There was an error while changing your master password.'
});
}
});
const [show, setShow] = useState({
masterPassword: false,
masterPassword2: false
});
const MP1CurrentEyeIcon = show.masterPassword ? EyeSlash : Eye;
const MP2CurrentEyeIcon = show.masterPassword2 ? EyeSlash : Eye;
const form = useZodForm({
schema,
defaultValues: {
encryptionAlgo: 'XChaCha20Poly1305',
hashingAlgo: 'Argon2id-s',
masterPassword: '',
masterPassword2: ''
}
});
const onSubmit: Parameters<typeof form.handleSubmit>[0] = (data) => {
if (data.masterPassword !== data.masterPassword2) {
showAlertDialog({
title: 'Error',
value: 'Passwords are not the same, please try again.'
});
} else {
const hashing_algorithm = HASHING_ALGOS[data.hashingAlgo];
return changeMasterPassword.mutateAsync({
algorithm: data.encryptionAlgo as Algorithm,
hashing_algorithm,
password: data.masterPassword
});
}
};
return (
<Dialog
form={form}
onSubmit={onSubmit}
dialog={useDialog(props)}
title="Change Master Password"
description="Select a new master password for your key manager."
ctaDanger={true}
ctaLabel="Change"
>
<Input
placeholder="New password"
type={show.masterPassword ? 'text' : 'password'}
className="mb-2 mt-3"
{...form.register('masterPassword', { required: true })}
right={
<div className="flex">
<Button
onClick={() => {
const password = generatePassword(32);
form.setValue('masterPassword', password);
form.setValue('masterPassword2', password);
setShow((old) => ({
...old,
masterPassword: true,
masterPassword2: true
}));
}}
size="icon"
type="button"
>
<ArrowsClockwise className="h-4 w-4" />
</Button>
<Button
type="button"
onClick={() => {
navigator.clipboard.writeText(
form.watch('masterPassword') as string
);
}}
size="icon"
>
<Clipboard className="h-4 w-4" />
</Button>
<Button
onClick={() =>
setShow((old) => ({ ...old, masterPassword: !old.masterPassword }))
}
size="icon"
type="button"
>
<MP1CurrentEyeIcon className="h-4 w-4" />
</Button>
</div>
}
/>
<Input
placeholder="New password (again)"
type={show.masterPassword2 ? 'text' : 'password'}
className="mb-2"
{...form.register('masterPassword2', { required: true })}
right={
<Button
onClick={() =>
setShow((old) => ({ ...old, masterPassword2: !old.masterPassword2 }))
}
size="icon"
type="button"
>
<MP2CurrentEyeIcon className="h-4 w-4" />
</Button>
}
/>
<PasswordMeter password={form.watch('masterPassword')} />
<div className="mb-3 mt-4 grid w-full grid-cols-2 gap-4">
<div className="flex flex-col">
<span className="text-xs font-bold">Encryption</span>
<Select
className="mt-2"
value={form.watch('encryptionAlgo')}
onChange={(e) => form.setValue('encryptionAlgo', e)}
>
<SelectOption value="XChaCha20Poly1305">XChaCha20-Poly1305</SelectOption>
<SelectOption value="Aes256Gcm">AES-256-GCM</SelectOption>
</Select>
</div>
<div className="flex flex-col">
<span className="text-xs font-bold">Hashing</span>
<Select
className="mt-2"
value={form.watch('hashingAlgo')}
onChange={(e) => form.setValue('hashingAlgo', e as HashingAlgoSlug)}
>
<SelectOption value="Argon2id-s">Argon2id (standard)</SelectOption>
<SelectOption value="Argon2id-h">Argon2id (hardened)</SelectOption>
<SelectOption value="Argon2id-p">Argon2id (paranoid)</SelectOption>
<SelectOption value="BalloonBlake3-s">
BLAKE3-Balloon (standard)
</SelectOption>
<SelectOption value="BalloonBlake3-h">
BLAKE3-Balloon (hardened)
</SelectOption>
<SelectOption value="BalloonBlake3-p">
BLAKE3-Balloon (paranoid)
</SelectOption>
</Select>
</div>
</div>
</Dialog>
);
};