Clean up filesystem sync setting

This commit is contained in:
Gregory Schier
2025-01-13 16:46:56 -08:00
parent 658e2179ca
commit 49f5e980de
6 changed files with 40 additions and 48 deletions

View File

@@ -24,6 +24,9 @@ crate-type = ["staticlib", "cdylib", "lib"]
[profile.release] [profile.release]
strip = true # Automatically strip symbols from the binary. strip = true # Automatically strip symbols from the binary.
[features]
cargo-clippy = []
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.0.4", features = [] } tauri-build = { version = "2.0.4", features = [] }

View File

@@ -1764,7 +1764,7 @@ pub fn run() {
.level(if is_dev() { log::LevelFilter::Debug } else { log::LevelFilter::Info }) .level(if is_dev() { log::LevelFilter::Debug } else { log::LevelFilter::Info })
.build(), .build(),
) )
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| { .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
// When trying to open a new app instance (common operation on Linux), // When trying to open a new app instance (common operation on Linux),
// focus the first existing window we find instead of opening a new one // focus the first existing window we find instead of opening a new one
// TODO: Keep track of the last focused window and always focus that one // TODO: Keep track of the last focused window and always focus that one

View File

@@ -6,7 +6,6 @@ import { getWorkspaceMeta } from '../lib/store';
import { Button } from './core/Button'; import { Button } from './core/Button';
import { PlainInput } from './core/PlainInput'; import { PlainInput } from './core/PlainInput';
import { VStack } from './core/Stacks'; import { VStack } from './core/Stacks';
import type { SyncToFilesystemSettingProps } from './SyncToFilesystemSetting';
import { SyncToFilesystemSetting } from './SyncToFilesystemSetting'; import { SyncToFilesystemSetting } from './SyncToFilesystemSetting';
interface Props { interface Props {
@@ -15,9 +14,7 @@ interface Props {
export function CreateWorkspaceDialog({ hide }: Props) { export function CreateWorkspaceDialog({ hide }: Props) {
const [name, setName] = useState<string>(''); const [name, setName] = useState<string>('');
const [settingSyncDir, setSettingSyncDir] = useState< const [settingSyncDir, setSettingSyncDir] = useState<string | null>(null);
Parameters<SyncToFilesystemSettingProps['onChange']>[0]
>({ value: null, enabled: false });
return ( return (
<VStack <VStack
@@ -27,15 +24,14 @@ export function CreateWorkspaceDialog({ hide }: Props) {
className="pb-3 max-h-[50vh]" className="pb-3 max-h-[50vh]"
onSubmit={async (e) => { onSubmit={async (e) => {
e.preventDefault(); e.preventDefault();
const { enabled, value } = settingSyncDir ?? {}; if (!settingSyncDir) return;
if (enabled && !value) return;
const workspace = await upsertWorkspace.mutateAsync({ name }); const workspace = await upsertWorkspace.mutateAsync({ name });
if (workspace == null) return; if (workspace == null) return;
// Do getWorkspaceMeta instead of naively creating one because it might have // Do getWorkspaceMeta instead of naively creating one because it might have
// been created already when the store refreshes the workspace meta after // been created already when the store refreshes the workspace meta after
const workspaceMeta = await getWorkspaceMeta(workspace.id); const workspaceMeta = await getWorkspaceMeta(workspace.id);
upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir: value }); upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir });
// Navigate to workspace // Navigate to workspace
await router.navigate({ await router.navigate({
@@ -46,19 +42,14 @@ export function CreateWorkspaceDialog({ hide }: Props) {
hide(); hide();
}} }}
> >
<PlainInput require label="Workspace Name" defaultValue={name} onChange={setName} /> <PlainInput require label="Name" defaultValue={name} onChange={setName} />
<SyncToFilesystemSetting <SyncToFilesystemSetting
onChange={setSettingSyncDir} onChange={setSettingSyncDir}
value={settingSyncDir.value} value={settingSyncDir}
allowNonEmptyDirectory // Will do initial import when the workspace is created allowNonEmptyDirectory // Will do initial import when the workspace is created
/> />
<Button <Button type="submit" color="primary" className="ml-auto mt-3">
type="submit"
color="primary"
className="ml-auto mt-3"
disabled={settingSyncDir.enabled && !settingSyncDir.value}
>
Create Workspace Create Workspace
</Button> </Button>
</VStack> </VStack>

View File

@@ -1,11 +1,11 @@
import { readDir } from '@tauri-apps/plugin-fs'; import { readDir } from '@tauri-apps/plugin-fs';
import { useState } from 'react'; import { useState } from 'react';
import { Checkbox } from './core/Checkbox'; import { Banner } from './core/Banner';
import { VStack } from './core/Stacks'; import { VStack } from './core/Stacks';
import { SelectFile } from './SelectFile'; import { SelectFile } from './SelectFile';
export interface SyncToFilesystemSettingProps { export interface SyncToFilesystemSettingProps {
onChange: (args: { value: string | null; enabled: boolean }) => void; onChange: (filePath: string | null) => void;
value: string | null; value: string | null;
allowNonEmptyDirectory?: boolean; allowNonEmptyDirectory?: boolean;
} }
@@ -15,47 +15,37 @@ export function SyncToFilesystemSetting({
value, value,
allowNonEmptyDirectory, allowNonEmptyDirectory,
}: SyncToFilesystemSettingProps) { }: SyncToFilesystemSettingProps) {
const [useSyncDir, setUseSyncDir] = useState<boolean>(!!value);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
return ( return (
<VStack space={1.5} className="w-full"> <details open={value != null}>
<Checkbox <summary>Sync to filesystem</summary>
checked={useSyncDir} <VStack className="my-2" space={3}>
onChange={(enabled) => { <Banner color="info">
setUseSyncDir(enabled); When enabled, workspace data syncs to the chosen folder as text files, ideal for backup
if (!enabled) { and Git collaboration.
// Set value to null when disabling </Banner>
onChange({ value: null, enabled }); {error && <div className="text-danger">{error}</div>}
} else {
onChange({ value, enabled });
}
}}
title="Sync to a filesystem directory"
/>
{error && <div className="text-danger">{error}</div>}
{useSyncDir && (
<SelectFile <SelectFile
directory directory
color="primary"
size="xs" size="xs"
noun="Directory" noun="Directory"
filePath={value} filePath={value}
onChange={async ({ filePath }) => { onChange={async ({ filePath }) => {
setError(null); if (filePath != null) {
if (filePath == null) {
setUseSyncDir(false);
} else {
const files = await readDir(filePath); const files = await readDir(filePath);
if (files.length > 0 && !allowNonEmptyDirectory) { if (files.length > 0 && !allowNonEmptyDirectory) {
setError('Directory must be empty'); setError('The directory must be empty');
return; return;
} }
} }
onChange({ value: filePath, enabled: useSyncDir }); onChange(filePath);
}} }}
/> />
)} </VStack>
</VStack> </details>
); );
} }

View File

@@ -41,7 +41,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
return ( return (
<VStack space={3} alignItems="start" className="pb-3 h-full"> <VStack space={3} alignItems="start" className="pb-3 h-full">
<Input <Input
label="Workspace Name" label="Name"
defaultValue={workspace.name} defaultValue={workspace.name}
onChange={(name) => upsertWorkspace.mutate({ ...workspace, name })} onChange={(name) => upsertWorkspace.mutate({ ...workspace, name })}
stateKey={`name.${workspace.id}`} stateKey={`name.${workspace.id}`}
@@ -60,9 +60,9 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
<VStack space={6} className="mt-3 w-full" alignItems="start"> <VStack space={6} className="mt-3 w-full" alignItems="start">
<SyncToFilesystemSetting <SyncToFilesystemSetting
value={workspaceMeta.settingSyncDir} value={workspaceMeta.settingSyncDir}
onChange={({ value: settingSyncDir }) => { onChange={(settingSyncDir) =>
upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir }); upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir })
}} }
/> />
<Separator /> <Separator />
<Button <Button

View File

@@ -10,7 +10,15 @@ const sizes = {
/** @type {import("tailwindcss").Config} */ /** @type {import("tailwindcss").Config} */
module.exports = { module.exports = {
darkMode: ['class', '[data-resolved-appearance="dark"]'], darkMode: ['class', '[data-resolved-appearance="dark"]'],
content: ['./index.html', './**/*.{html,js,jsx,ts,tsx}'], content: [
'./*.{html,ts,tsx}',
'./commands/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./hooks/**/*.{ts,tsx}',
'./init/**/*.{ts,tsx}',
'./lib/**/*.{ts,tsx}',
'./routes/**/*.{ts,tsx}',
],
theme: { theme: {
extend: { extend: {
opacity: { opacity: {