mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-22 23:48:26 -04:00
* add Plausible analytics to landing page
* proxy plausible through vercel
* fix typo & add other options
* add plausible to `sd/client`
* add telemetry sharing option into library config
* add telemetry config option to lib creation dialog
* revert error message change but keep the typo fix
* add telemetry sharing & error handling to client context
* add important note about requiring the tracker component in root/base layouts
* add the `PlausibleTracker`
* grammatical tweaks
* some TS cleanup
* disable analytics in debug mode
* further component improvements and use custom event props
* more cleanup
* remove tracking from onboarding (no telemetry sharing config option)
* update comment
* add fancy new plausible hooks/tracking
* add `pageview` monitoring hook to `$libraryId` layout
* add library creation events to onboarding and creation dialog
* revert `useCurrentLibraryId()` error handling & add important comment
* minor comment tweaks
* replace `usage` with `telemetry`
* add missing newline
* add location create & delete events
* add tag create & delete events
* add/update library create & delete events
* add fn for getting telemetry settings for library by uuid
* add more events + fix a few bugs
* update generics
* add `telemetryState` `valtio` store
* use new telemetry state
* remove old artifacts from `ClientContext`
* Revert "add telemetry sharing option into library config"
This reverts commit afb9f892ab.
* update events, docs & generics
* add `tagAssign` event
* light comment updates
* const names, comments, etc
* add additional info to props and update comment
* add telemetry sharing to debug state (for sharing telemetry in debug mode)
* update `debugState` item name
* change how `Switch` updates the store in privacy settings
* remove `getTelemetryState` from `telemetryState`
* cleanup library creation event handling/telemetry config updating
* add `DebugPopover` to onboarding in debug mode
* improve code quality/comments
* remove useless comment
* rename `ob_store` and `shareTelemetryDataWithDevelopers`
* fix typo
* add `telemetryLogger` and prevent multiple of the same events from firing consecutively
* add more unique path matching and fix an issue with events
* rename `telemetryLogger` -> `telemetryLogging`
---------
Co-authored-by: brxken128 <77554505+brxken128@users.noreply.github.com>
225 lines
6.6 KiB
TypeScript
225 lines
6.6 KiB
TypeScript
import { useQueryClient } from '@tanstack/react-query';
|
|
import { ArrowsClockwise, Clipboard, Eye, EyeSlash, Info } from 'phosphor-react';
|
|
import { useState } from 'react';
|
|
import {
|
|
Algorithm,
|
|
HASHING_ALGOS,
|
|
HashingAlgoSlug,
|
|
generatePassword,
|
|
hashingAlgoSlugSchema,
|
|
useBridgeMutation,
|
|
usePlausibleEvent
|
|
} from '@sd/client';
|
|
import {
|
|
Button,
|
|
CheckBox,
|
|
Dialog,
|
|
PasswordMeter,
|
|
Select,
|
|
SelectOption,
|
|
Tooltip,
|
|
UseDialogProps,
|
|
useDialog
|
|
} from '@sd/ui';
|
|
import { forms } from '@sd/ui';
|
|
import { usePlatform } from '~/util/Platform';
|
|
|
|
const { Input, z, useZodForm } = forms;
|
|
|
|
const schema = z.object({
|
|
name: z.string(),
|
|
password: z.string(),
|
|
password_validate: z.string(),
|
|
algorithm: z.string(),
|
|
hashing_algorithm: hashingAlgoSlugSchema,
|
|
share_telemetry: z.boolean()
|
|
});
|
|
|
|
export default (props: UseDialogProps) => {
|
|
const dialog = useDialog(props);
|
|
const platform = usePlatform();
|
|
const createLibraryEvent = usePlausibleEvent({ platformType: platform.platform });
|
|
|
|
const form = useZodForm({
|
|
schema,
|
|
defaultValues: {
|
|
password: '',
|
|
algorithm: 'XChaCha20Poly1305',
|
|
hashing_algorithm: 'Argon2id-s'
|
|
}
|
|
});
|
|
|
|
const [showMasterPassword1, setShowMasterPassword1] = useState(false);
|
|
const [showMasterPassword2, setShowMasterPassword2] = useState(false);
|
|
const MP1CurrentEyeIcon = showMasterPassword1 ? EyeSlash : Eye;
|
|
const MP2CurrentEyeIcon = showMasterPassword2 ? EyeSlash : Eye;
|
|
|
|
const queryClient = useQueryClient();
|
|
const createLibrary = useBridgeMutation('library.create', {
|
|
onSuccess: (library) => {
|
|
queryClient.setQueryData(['library.list'], (libraries: any) => [
|
|
...(libraries || []),
|
|
library
|
|
]);
|
|
|
|
createLibraryEvent({
|
|
event: {
|
|
type: 'libraryCreate'
|
|
}
|
|
});
|
|
},
|
|
onError: (err: any) => {
|
|
console.error(err);
|
|
}
|
|
});
|
|
|
|
const onSubmit = form.handleSubmit(async (data) => {
|
|
if (data.password !== data.password_validate) {
|
|
alert('Passwords are not the same');
|
|
} else {
|
|
await createLibrary.mutateAsync({
|
|
...data,
|
|
algorithm: data.algorithm as Algorithm,
|
|
hashing_algorithm: HASHING_ALGOS[data.hashing_algorithm],
|
|
auth: {
|
|
type: 'Password',
|
|
value: data.password
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
return (
|
|
<Dialog
|
|
form={form}
|
|
onSubmit={onSubmit}
|
|
dialog={dialog}
|
|
title="Create New Library"
|
|
description="Choose a name for your new library, you can configure this and more settings from the library settings later on."
|
|
submitDisabled={!form.formState.isValid}
|
|
ctaLabel="Create"
|
|
>
|
|
<div className="relative flex flex-col">
|
|
<p className="my-2 text-sm font-bold">Library name</p>
|
|
<Input
|
|
className="w-full grow"
|
|
placeholder="My Cool Library"
|
|
{...form.register('name', { required: true })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="mt-3 mb-1 flex flex-row items-center">
|
|
<div className="space-x-2">
|
|
<CheckBox
|
|
className="bg-app-selected"
|
|
defaultChecked={true}
|
|
{...form.register('share_telemetry', { required: true })}
|
|
/>
|
|
</div>
|
|
<span className="mt-1 text-xs font-medium">Share anonymous usage</span>
|
|
<Tooltip label="Share completely anonymous telemetry data to help the developers improve the app">
|
|
<Info className="text-ink-faint ml-1.5 h-4 w-4" />
|
|
</Tooltip>
|
|
</div>
|
|
|
|
{/* TODO: Proper UI for this. Maybe checkbox for encrypted or not and then reveal these fields. Select encrypted by default. */}
|
|
{/* <span className="text-sm">Make the secret key field empty to skip key setup.</span> */}
|
|
|
|
<div className="relative flex flex-col">
|
|
<p className="mt-2 mb-1 text-center text-[0.95rem] font-bold">Key Manager</p>
|
|
<div className="my-1 h-[2px] w-full bg-gray-500" />
|
|
|
|
<p className="my-2 text-sm font-bold">Master password</p>
|
|
<div className="relative mb-2 flex grow">
|
|
<Input
|
|
className="grow !py-0.5"
|
|
placeholder="Password"
|
|
type={showMasterPassword1 ? 'text' : 'password'}
|
|
{...form.register('password')}
|
|
/>
|
|
<Button
|
|
onClick={() => {
|
|
const password = generatePassword(32);
|
|
|
|
form.setValue('password', password);
|
|
form.setValue('password_validate', password);
|
|
|
|
setShowMasterPassword1(true);
|
|
setShowMasterPassword2(true);
|
|
}}
|
|
size="icon"
|
|
className="absolute right-[65px] top-[5px] border-none"
|
|
>
|
|
<ArrowsClockwise className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(form.watch('password') as string);
|
|
}}
|
|
size="icon"
|
|
className="absolute right-[35px] top-[5px] border-none"
|
|
>
|
|
<Clipboard className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
onClick={() => setShowMasterPassword1(!showMasterPassword1)}
|
|
size="icon"
|
|
className="absolute right-[5px] top-[5px] border-none"
|
|
>
|
|
<MP1CurrentEyeIcon className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="relative flex flex-col">
|
|
<p className="my-2 text-sm font-bold">Master password (again)</p>
|
|
<div className="relative mb-2 flex grow">
|
|
<Input
|
|
className="grow !py-0.5"
|
|
placeholder="Password"
|
|
type={showMasterPassword2 ? 'text' : 'password'}
|
|
{...form.register('password_validate')}
|
|
/>
|
|
<Button
|
|
onClick={() => setShowMasterPassword2(!showMasterPassword2)}
|
|
size="icon"
|
|
className="absolute right-[5px] top-[5px] border-none"
|
|
>
|
|
<MP2CurrentEyeIcon className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 mb-3 grid w-full grid-cols-2 gap-4">
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-bold">Encryption</span>
|
|
<Select
|
|
className="mt-2"
|
|
value={form.watch('algorithm')}
|
|
onChange={(e) => form.setValue('algorithm', e)}
|
|
>
|
|
<SelectOption value="XChaCha20Poly1305">XChaCha20-Poly1305</SelectOption>
|
|
<SelectOption value="Aes256Gcm">AES-256-GCM</SelectOption>
|
|
</Select>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-bold">Hashing</span>
|
|
<Select
|
|
className="mt-2"
|
|
value={form.watch('hashing_algorithm')}
|
|
onChange={(e) => form.setValue('hashing_algorithm', 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>
|
|
|
|
<PasswordMeter password={form.watch('password')} />
|
|
</Dialog>
|
|
);
|
|
};
|