mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-21 15:07:54 -04:00
* 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
188 lines
5.1 KiB
TypeScript
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>
|
|
);
|
|
};
|