[ENG-469] Make Prettier and ESLint work together (#706)

* Make Prettier and ESLint work together
- Resolve conflicts between Prettier and ESLint regarding indentation and Tailwind rules order
- Add `.editorconfig` to standardize basic formatting options across tools and editors
- Add `.gitattributes` to hide `pnpm-lock.yaml` in `git diff` output
- Include EditorConfig in the recommended extensions for VSCode
- Replace some instances of `pnpm exec <command>` with `pnpm <command>`
- Remove superfluous Tauri config for Linux

* Revert Prettier changes (it was working correctly before)
 - Update ESLint to read Tailwind config from absolute path
 - Remove redundant Prettier dependency from subprojects
 - Specify the source folder for the lint script in subprojects

* use mobile's tailwind config with eslint

* pnpm format + pnpm lint:fix

---------

Co-authored-by: Utku Bakir <74243531+utkubakir@users.noreply.github.com>
This commit is contained in:
Vítor Vasconcellos
2023-04-14 21:21:21 +00:00
committed by GitHub
parent 18938a89fc
commit 50442ede3e
144 changed files with 981 additions and 463 deletions

56
.editorconfig Normal file
View File

@@ -0,0 +1,56 @@
# EditorConfig is awesome: http://EditorConfig.org
# https://github.com/jokeyrhyme/standard-editorconfig
# top-most EditorConfig file
root = true
# defaults
[*]
charset = utf-8
indent_size = 2
end_of_line = lf
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
# BATS: https://github.com/bats-core/bats-core
# https://github.com/bats-core/bats-core/master/.editorconfig
[*.bats]
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true
# CSS
# https://google.github.io/styleguide/htmlcssguide.xml#General_Formatting_Rules
# http://cssguidelin.es/#syntax-and-formatting
[*.css]
trim_trailing_whitespace = true
# HTML
# https://google.github.io/styleguide/htmlcssguide.xml#General_Formatting_Rules
[*.{htm,html}]
trim_trailing_whitespace = true
# JavaScript, JSON, JSX, JavaScript Modules, TypeScript
# https://github.com/feross/standard
# https://prettier.io
[*.{cjs,js,json,jsx,mjs,ts,tsx}]
indent_size = 4
# Kotlin
# https://android.github.io/kotlin-guides/style.html#indentation
[*.{kt,kts}]
indent_size = 4
# Python
# https://www.python.org/dev/peps/pep-0008/#code-lay-out
[*.py]
indent_size = 4
# Rust
# https://github.com/rust-lang/rust/blob/master/src/doc/style/style/whitespace.md
[*.rs]
indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
pnpm-lock.yaml -diff
package-lock.json -diff

View File

@@ -2,6 +2,7 @@
"recommendations": [
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer",
"oscartbeaumont.rspc-vscode"
"oscartbeaumont.rspc-vscode",
"EditorConfig.EditorConfig"
]
}

6
.vscode/launch.json vendored
View File

@@ -24,7 +24,11 @@
"request": "launch",
"name": "Tauri Production Debug",
"cargo": {
"args": ["build", "--release", "--manifest-path=./apps/desktop/src-tauri/Cargo.toml"],
"args": [
"build",
"--release",
"--manifest-path=./apps/desktop/src-tauri/Cargo.toml"
],
"problemMatcher": "$rustc"
},
"sourceLanguages": ["rust"],

10
.vscode/settings.json vendored
View File

@@ -54,10 +54,14 @@
// "apps/mobile/ios": true
},
"eslint.workingDirectories": [
"apps/desktop",
"apps/landing",
"apps/mobile",
"packages/ui",
"interface",
"packages/client",
"packages/interface"
]
"packages/config",
"packages/ui"
],
"eslint.packageManager": "pnpm",
"eslint.lintTask.enable": true
}

5
.vscode/tasks.json vendored
View File

@@ -47,7 +47,10 @@
{
"type": "cargo",
"command": "run",
"args": ["--manifest-path=./apps/desktop/src-tauri/Cargo.toml", "--no-default-features"],
"args": [
"--manifest-path=./apps/desktop/src-tauri/Cargo.toml",
"--no-default-features"
],
"env": {
"RUST_BACKTRACE": "short"
},

View File

@@ -0,0 +1,7 @@
module.exports = {
extends: [require.resolve('@sd/config/eslint/web.js')],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
};

View File

@@ -11,7 +11,8 @@
"tauri": "tauri",
"build": "node ./src-tauri/build.js",
"dmg": "open ../../target/release/bundle/dmg/",
"typecheck": "tsc -b"
"typecheck": "tsc -b",
"lint": "eslint src --cache"
},
"dependencies": {
"@rspc/client": "^0.0.0-main-7c0a67c1",
@@ -33,7 +34,6 @@
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.1.0",
"prettier": "^2.7.1",
"react-devtools": "^4.27.2",
"sass": "^1.55.0",
"typescript": "^4.8.4",

View File

@@ -5,7 +5,7 @@ process.env.BACKGROUND_FILE = path.join(__dirname, './dmg-background.png');
process.env.BACKGROUND_FILE_NAME = path.basename(process.env.BACKGROUND_FILE);
process.env.BACKGROUND_CLAUSE = `set background picture of opts to file ".background:${process.env.BACKGROUND_FILE_NAME}"`;
const child = spawn('pnpm', ['exec', 'tauri', 'build']);
const child = spawn('pnpm', ['tauri', 'build']);
child.stdout.on('data', (data) => console.log(data.toString()));
child.stderr.on('data', (data) => console.error(data.toString()));
child.on('exit', (code) => {

View File

@@ -7,7 +7,7 @@
"distDir": "../dist",
"devPath": "http://localhost:8001",
"beforeDevCommand": "pnpm dev-vite",
"beforeBuildCommand": "pnpm exec vite build"
"beforeBuildCommand": "pnpm vite build"
},
"tauri": {
"macOSPrivateApi": true,

View File

@@ -1,84 +0,0 @@
{
"package": {
"productName": "Spacedrive",
"version": "0.1.0"
},
"build": {
"distDir": "../dist",
"devPath": "http://localhost:8001",
"beforeDevCommand": "pnpm dev-vite",
"beforeBuildCommand": "pnpm exec vite build"
},
"tauri": {
"macOSPrivateApi": true,
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.spacedrive.desktop",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "Spacedrive Technology Inc.",
"shortDescription": "The universal file manager.",
"longDescription": "A cross-platform universal file explorer, powered by an open-source virtual distributed filesystem.",
"deb": {
"depends": []
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.14",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"allowlist": {
"all": true,
"protocol": {
"assetScope": ["*"]
},
"os": {
"all": true
},
"dialog": {
"all": true,
"open": true,
"save": true
}
},
"windows": [
{
"title": "Spacedrive",
"width": 1200,
"height": 725,
"minWidth": 700,
"minHeight": 500,
"resizable": true,
"fullscreen": false,
"alwaysOnTop": false,
"focus": false,
"fileDropEnabled": false,
"decorations": true,
"transparent": true,
"center": true
}
],
"security": {
"csp": "default-src spacedrive: asset: https://asset.localhost blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}

View File

@@ -3,5 +3,13 @@ module.exports = {
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
},
rules: {
'tailwindcss/classnames-order': [
'warn',
{
config: './tailwind.config.js'
}
]
}
};

View File

@@ -10,7 +10,7 @@
"ios": "expo run:ios",
"xcode": "open ios/spacedrive.xcworkspace",
"android-studio": "open -a '/Applications/Android Studio.app' ./android",
"lint": "eslint src",
"lint": "eslint src --cache",
"typecheck": "tsc -b",
"clean:android": "cd android && ./gradlew clean && cd ../",
"clean:ios": "cd ios && xcodebuild clean && cd ../"

View File

@@ -107,7 +107,7 @@ function AppContainer() {
const { id } = useSnapshot(currentLibraryStore);
return (
<SafeAreaProvider style={tw`bg-app flex-1`}>
<SafeAreaProvider style={tw`flex-1 bg-app`}>
<GestureHandlerRootView style={tw`flex-1`}>
<MenuProvider>
<BottomSheetModalProvider>

View File

@@ -27,7 +27,10 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
setLibName('');
// We do this instead of invalidating the query because it triggers a full app re-render??
queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]);
queryClient.setQueryData(['library.list'], (libraries: any) => [
...(libraries || []),
lib
]);
// Switch to the new library
currentLibraryStore.id = lib.uuid;

View File

@@ -24,7 +24,7 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
<View>
<View style={tw`flex flex-row items-center`}>
<Image source={AppLogo} style={tw`h-[40px] w-[40px]`} />
<Text style={tw`text-ink ml-2 text-lg font-bold`}>Spacedrive</Text>
<Text style={tw`ml-2 text-lg font-bold text-ink`}>Spacedrive</Text>
</View>
<View style={tw`mt-6`} />
{/* Library Manager */}

View File

@@ -29,13 +29,15 @@ const DrawerLibraryManager = () => {
<Pressable onPress={() => setDropdownClosed((v) => !v)}>
<View
style={twStyle(
'bg-sidebar-box flex h-10 w-full flex-row items-center justify-between border px-3 shadow-sm',
'flex h-10 w-full flex-row items-center justify-between border bg-sidebar-box px-3 shadow-sm',
dropdownClosed
? 'border-sidebar-line/50 rounded-md'
: 'border-b-app-box border-sidebar-line bg-sidebar-button rounded-t-md'
? 'rounded-md border-sidebar-line/50'
: 'rounded-t-md border-sidebar-line border-b-app-box bg-sidebar-button'
)}
>
<Text style={tw`text-ink text-sm font-semibold`}>{currentLibrary?.config.name}</Text>
<Text style={tw`text-sm font-semibold text-ink`}>
{currentLibrary?.config.name}
</Text>
<MotiView
animate={{
rotate: dropdownClosed ? '0deg' : '180deg',
@@ -48,21 +50,24 @@ const DrawerLibraryManager = () => {
</View>
</Pressable>
<AnimatedHeight hide={dropdownClosed}>
<View style={tw`bg-sidebar-button border-sidebar-line rounded-b-md p-2`}>
<View style={tw`rounded-b-md border-sidebar-line bg-sidebar-button p-2`}>
{/* Libraries */}
{libraries.data?.map((library) => {
// console.log('library', library);
return (
<Pressable key={library.uuid} onPress={() => (currentLibraryStore.id = library.uuid)}>
<Pressable
key={library.uuid}
onPress={() => (currentLibraryStore.id = library.uuid)}
>
<View
style={twStyle(
'mt-1 p-2',
currentLibrary?.uuid === library.uuid && 'bg-accent rounded'
currentLibrary?.uuid === library.uuid && 'rounded bg-accent'
)}
>
<Text
style={twStyle(
'text-ink text-sm font-semibold',
'text-sm font-semibold text-ink',
currentLibrary?.uuid === library.uuid && 'text-white'
)}
>
@@ -83,7 +88,9 @@ const DrawerLibraryManager = () => {
</CreateLibraryDialog>
{/* Manage Library */}
<Pressable
onPress={() => navigation.navigate('Settings', { screen: 'LibraryGeneralSettings' })}
onPress={() =>
navigation.navigate('Settings', { screen: 'LibraryGeneralSettings' })
}
>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Gear size={18} weight="bold" color="white" style={tw`mr-2`} />

View File

@@ -63,8 +63,10 @@ const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
</View>
{/* Add Location */}
<Pressable onPress={() => importModalRef.current?.present()}>
<View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Location</Text>
<View style={tw`mt-1 rounded border border-dashed border-app-line/80`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>
Add Location
</Text>
</View>
</Pressable>
</CollapsibleView>

View File

@@ -62,7 +62,7 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
</View>
{/* Add Tag */}
<Pressable onPress={() => createTagModalRef.current?.present()}>
<View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}>
<View style={tw`mt-1 rounded border border-dashed border-app-line/80`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Tag</Text>
</View>
</Pressable>

View File

@@ -32,7 +32,10 @@ const Explorer = ({ data }: ExplorerProps) => {
function handlePress(data: ExplorerItem) {
if (isPath(data) && data.item.is_dir) {
navigation.push('Location', { id: data.item.location_id, path: data.item.materialized_path });
navigation.push('Location', {
id: data.item.location_id,
path: data.item.materialized_path
});
} else {
setData(data);
modalRef.current?.present();
@@ -65,7 +68,11 @@ const Explorer = ({ data }: ExplorerProps) => {
keyExtractor={(item) => item.item.id.toString()}
renderItem={({ item }) => (
<Pressable onPress={() => handlePress(item)}>
{layoutMode === 'grid' ? <FileItem data={item} /> : <FileRow data={item} />}
{layoutMode === 'grid' ? (
<FileItem data={item} />
) : (
<FileRow data={item} />
)}
</Pressable>
)}
extraData={layoutMode}

View File

@@ -20,7 +20,7 @@ const FileRow = ({ data }: FileRowProps) => {
>
<FileThumb data={data} size={0.6} />
<View style={tw`ml-3`}>
<Text numberOfLines={1} style={tw`text-ink-dull text-center text-xs font-medium`}>
<Text numberOfLines={1} style={tw`text-center text-xs font-medium text-ink-dull`}>
{filePath?.name}
{filePath?.extension && `.${filePath.extension}`}
</Text>

View File

@@ -27,7 +27,9 @@ const InfoTagPills = ({ data, style }: Props) => {
text={isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]!}
/>
{/* Extension */}
{filePath?.extension && <InfoPill text={filePath.extension} containerStyle={tw`mr-1`} />}
{filePath?.extension && (
<InfoPill text={filePath.extension} containerStyle={tw`mr-1`} />
)}
{/* TODO: What happens if I have too many? */}
{tagsQuery.data?.map((tag) => (
<InfoPill

View File

@@ -4,7 +4,10 @@ import { tw } from '~/lib/tailwind';
export const Switch: FC<SwitchProps> = ({ ...props }) => {
return (
<RNSwitch trackColor={{ false: tw.color('app-line'), true: tw.color('accent') }} {...props} />
<RNSwitch
trackColor={{ false: tw.color('app-line'), true: tw.color('accent') }}
{...props}
/>
);
};
@@ -14,10 +17,13 @@ export const SwitchContainer: FC<SwitchContainerProps> = ({ title, description,
return (
<View style={tw`flex flex-row items-center justify-between pb-6`}>
<View style={tw`w-[80%]`}>
<Text style={tw`text-ink text-sm font-medium`}>{title}</Text>
{description && <Text style={tw`text-ink-dull mt-2 text-sm`}>{description}</Text>}
<Text style={tw`text-sm font-medium text-ink`}>{title}</Text>
{description && <Text style={tw`mt-2 text-sm text-ink-dull`}>{description}</Text>}
</View>
<Switch trackColor={{ false: tw.color('app-line'), true: tw.color('accent') }} {...props} />
<Switch
trackColor={{ false: tw.color('app-line'), true: tw.color('accent') }}
{...props}
/>
</View>
);
};

View File

@@ -17,7 +17,7 @@ export default function Header() {
return (
<View
style={twStyle('border-app-line bg-app-overlay mx-4 rounded border', {
style={twStyle('mx-4 rounded border border-app-line bg-app-overlay', {
marginTop: top + 10
})}
>
@@ -38,7 +38,7 @@ export default function Header() {
style={tw`h-full flex-1 justify-center`}
onPress={() => navigation.navigate('Search')}
>
<Text style={tw`text-ink-dull text-sm font-medium`}>Search</Text>
<Text style={tw`text-sm font-medium text-ink-dull`}>Search</Text>
</Pressable>
</View>
</View>

View File

@@ -29,7 +29,7 @@ const PasswordMeter = (props: PasswordMeterProps) => {
{scoreText}
</Text>
</View>
<View style={tw`bg-app-box/80 mt-2 w-full rounded-full`}>
<View style={tw`mt-2 w-full rounded-full bg-app-box/80`}>
<View
style={twStyle(
{

View File

@@ -11,7 +11,10 @@ const Card = ({ children, ...props }: CardProps) => {
return (
<View
style={twStyle('border-app-line bg-app-overlay rounded-lg border px-4 py-5', style as string)}
style={twStyle(
'rounded-lg border border-app-line bg-app-overlay px-4 py-5',
style as string
)}
{...otherProps}
>
{children}

View File

@@ -48,7 +48,9 @@ const Dialog = (props: DialogProps) => {
<View>
{props.trigger && (
<Pressable
onPress={() => (props.setIsVisible ? props.setIsVisible(true) : setVisible(true))}
onPress={() =>
props.setIsVisible ? props.setIsVisible(true) : setVisible(true)
}
>
{props.trigger}
</Pressable>
@@ -56,7 +58,7 @@ const Dialog = (props: DialogProps) => {
<Modal renderToHardwareTextureAndroid transparent visible={props.isVisible ?? visible}>
{/* Backdrop */}
<Pressable
style={tw`bg-app-box/40 absolute inset-0`}
style={tw`absolute inset-0 bg-app-box/40`}
onPress={handleCloseDialog}
disabled={props.disableBackdropClose || props.loading}
/>
@@ -74,14 +76,14 @@ const Dialog = (props: DialogProps) => {
>
{/* TODO: Blur may look cool here */}
<View
style={tw`border-app-line bg-app shadow-app-shade min-w-[360px] max-w-[380px] overflow-hidden rounded-md border shadow`}
style={tw`min-w-[360px] max-w-[380px] overflow-hidden rounded-md border border-app-line bg-app shadow shadow-app-shade`}
>
<View style={tw`p-5`}>
{/* Title */}
<Text style={tw`text-ink text-base font-bold`}>{props.title}</Text>
<Text style={tw`text-base font-bold text-ink`}>{props.title}</Text>
{/* Description */}
{props.description && (
<Text style={tw`text-ink-dull mt-2 text-sm leading-normal`}>
<Text style={tw`mt-2 text-sm leading-normal text-ink-dull`}>
{props.description}
</Text>
)}
@@ -90,7 +92,7 @@ const Dialog = (props: DialogProps) => {
</View>
{/* Actions */}
<View
style={tw`border-app-line bg-app-highlight flex flex-row items-center border-t p-3`}
style={tw`flex flex-row items-center border-t border-app-line bg-app-highlight p-3`}
>
{props.loading && <PulseAnimation style={tw`h-7`} />}
<View style={tw`grow`} />
@@ -99,7 +101,7 @@ const Dialog = (props: DialogProps) => {
disabled={props.loading} // Disables Close button if loading
onPress={handleCloseDialog}
>
<Text style={tw`text-ink text-sm`}>Close</Text>
<Text style={tw`text-sm text-ink`}>Close</Text>
</Button>
{props.ctaAction && (
<Button
@@ -108,7 +110,7 @@ const Dialog = (props: DialogProps) => {
onPress={props.ctaAction}
disabled={props.ctaDisabled || props.loading}
>
<Text style={tw`text-ink text-sm`}>{props.ctaLabel}</Text>
<Text style={tw`text-sm text-ink`}>{props.ctaLabel}</Text>
</Button>
)}
</View>

View File

@@ -26,13 +26,13 @@ interface ModalHandle extends BottomSheetHandleProps {
const ModalHandle = (props: ModalHandle) => (
<BottomSheetHandle
{...props}
style={tw`bg-app items-end rounded-t-2xl`}
style={tw`items-end rounded-t-2xl bg-app`}
indicatorStyle={tw`bg-app-highlight/60`}
>
{props.showCloseButton && (
<Pressable
onPress={() => props.modalRef.current?.close()}
style={tw`bg-app-button absolute top-5 right-4 h-7 w-7 items-center justify-center rounded-full`}
style={tw`absolute top-5 right-4 h-7 w-7 items-center justify-center rounded-full bg-app-button`}
>
<X size={16} color="white" weight="bold" />
</Pressable>
@@ -61,7 +61,7 @@ export const Modal = forwardRef<ModalRef, ModalProps>((props, ref) => {
handleComponent={(props) => ModalHandle({ modalRef, showCloseButton, ...props })}
{...otherProps}
>
{title && <Text style={tw`text-ink text-center text-base font-medium`}>{title}</Text>}
{title && <Text style={tw`text-center text-base font-medium text-ink`}>{title}</Text>}
{children}
</BottomSheetModal>
);
@@ -107,16 +107,22 @@ export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref)
ref={modalRef}
backgroundStyle={tw`bg-app`}
backdropComponent={ModalBackdrop}
handleComponent={(props) => ModalHandle({ modalRef, showCloseButton: false, ...props })}
handleComponent={(props) =>
ModalHandle({ modalRef, showCloseButton: false, ...props })
}
snapPoints={props.snapPoints ?? ['25%']}
>
{/* Title */}
{props.title && (
<Text style={tw`text-ink text-center text-base font-medium`}>{props.title}</Text>
<Text style={tw`text-center text-base font-medium text-ink`}>
{props.title}
</Text>
)}
<View style={tw`mt-4 px-6`}>
{/* Description */}
{props.description && <Text style={tw`text-ink-dull text-sm`}>{props.description}</Text>}
{props.description && (
<Text style={tw`text-sm text-ink-dull`}>{props.description}</Text>
)}
{/* Children */}
{props.children && props.children}
{/* Buttons */}
@@ -128,7 +134,7 @@ export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref)
disabled={props.loading} // Disables Close button if loading
onPress={() => modalRef.current?.close()}
>
<Text style={tw`text-ink text-sm font-medium`}>Close</Text>
<Text style={tw`text-sm font-medium text-ink`}>Close</Text>
</Button>
{props.ctaAction && (
<Button
@@ -138,7 +144,9 @@ export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref)
onPress={props.ctaAction}
disabled={props.ctaDisabled || props.loading}
>
<Text style={tw`text-ink text-sm font-medium`}>{props.ctaLabel}</Text>
<Text style={tw`text-sm font-medium text-ink`}>
{props.ctaLabel}
</Text>
</Button>
)}
</View>

View File

@@ -26,7 +26,7 @@ const SortByMenu = () => {
<Menu
trigger={
<View style={tw`flex flex-row items-center`}>
<Text style={tw`text-ink mr-0.5 font-medium`}>{sortOptions[sortBy]}</Text>
<Text style={tw`mr-0.5 font-medium text-ink`}>{sortOptions[sortBy]}</Text>
{sortDirection === 'asc' ? <ArrowUpIcon /> : <ArrowDownIcon />}
</View>
}
@@ -35,7 +35,11 @@ const SortByMenu = () => {
<MenuItem
key={value}
icon={
value === sortBy ? (sortDirection === 'asc' ? ArrowUpIcon : ArrowDownIcon) : undefined
value === sortBy
? sortDirection === 'asc'
? ArrowUpIcon
: ArrowDownIcon
: undefined
}
text={text}
value={value}

View File

@@ -26,7 +26,7 @@ type ActionsContainerProps = PropsWithChildren<{
}>;
const ActionsContainer = ({ children, style }: ActionsContainerProps) => (
<View style={twStyle('bg-app-box rounded-lg py-3.5', style)}>{children}</View>
<View style={twStyle('rounded-lg bg-app-box py-3.5', style)}>{children}</View>
);
type ActionsItemProps = {
@@ -53,7 +53,7 @@ const ActionsItem = ({ icon, onPress, title, isDanger = false }: ActionsItemProp
);
};
const ActionDivider = () => <View style={tw`bg-app-line/80 my-3.5 h-[0.5px]`} />;
const ActionDivider = () => <View style={tw`my-3.5 h-[0.5px] bg-app-line/80`} />;
export const ActionsModal = () => {
const fileInfoRef = useRef<ModalRef>(null);
@@ -75,15 +75,18 @@ export const ActionsModal = () => {
</Pressable>
<View style={tw`ml-2 flex-1`}>
{/* Name + Extension */}
<Text style={tw`text-base font-bold text-gray-200`} numberOfLines={1}>
<Text
style={tw`text-base font-bold text-gray-200`}
numberOfLines={1}
>
{filePath?.name}
{filePath?.extension && `.${filePath?.extension}`}
</Text>
<View style={tw`flex flex-row`}>
<Text style={tw`text-ink-faint text-xs`}>
<Text style={tw`text-xs text-ink-faint`}>
{formatBytes(Number(filePath?.size_in_bytes || 0))},
</Text>
<Text style={tw`text-ink-faint text-xs`}>
<Text style={tw`text-xs text-ink-faint`}>
{' '}
{dayjs(filePath?.date_created).format('MMM Do YYYY')}
</Text>

View File

@@ -69,13 +69,18 @@ const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
{data && (
<ModalScrollView style={tw`flex-1 p-4`}>
{/* Back Button */}
<Pressable onPress={() => modalRef.current?.close()} style={tw`absolute z-10 ml-4`}>
<Pressable
onPress={() => modalRef.current?.close()}
style={tw`absolute z-10 ml-4`}
>
<CaretLeft color={tw.color('accent')} size={20} weight="bold" />
</Pressable>
{/* File Icon / Name */}
<View style={tw`items-center`}>
<FileThumb data={data} size={1.6} />
<Text style={tw`mt-2 text-base font-bold text-gray-200`}>{filePathData?.name}</Text>
<Text style={tw`mt-2 text-base font-bold text-gray-200`}>
{filePathData?.name}
</Text>
<InfoTagPills data={data} style={tw`mt-3`} />
</View>
{/* Details */}
@@ -112,7 +117,11 @@ const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
<>
{/* TODO: Note */}
{filePathData.cas_id && (
<MetaItem icon={Snowflake} title="Content ID" value={filePathData.cas_id} />
<MetaItem
icon={Snowflake}
title="Content ID"
value={filePathData.cas_id}
/>
)}
{/* Checksum */}
{filePathData?.integrity_checksum && (

View File

@@ -76,7 +76,10 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
{showPicker && (
<FadeInAnimation>
<View style={tw`mt-4 h-64`}>
<ColorPicker color={tagColor} onColorChangeComplete={(color) => setTagColor(color)} />
<ColorPicker
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
/>
</View>
</FadeInAnimation>
)}

View File

@@ -56,9 +56,9 @@ const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
showCloseButton
>
<View style={tw`p-4`}>
<Text style={tw`text-ink-dull mb-1 ml-1 text-xs font-medium`}>Name</Text>
<Text style={tw`mb-1 ml-1 text-xs font-medium text-ink-dull`}>Name</Text>
<Input value={tagName} onChangeText={(t) => setTagName(t)} />
<Text style={tw`text-ink-dull mb-1 ml-1 mt-3 text-xs font-medium`}>Color</Text>
<Text style={tw`mb-1 ml-1 mt-3 text-xs font-medium text-ink-dull`}>Color</Text>
<View style={tw`ml-2 flex flex-row items-center`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
@@ -70,7 +70,10 @@ const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
{showPicker && (
<FadeInAnimation>
<View style={tw`mt-4 h-64`}>
<ColorPicker color={tagColor} onColorChangeComplete={(color) => setTagColor(color)} />
<ColorPicker
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
/>
</View>
</FadeInAnimation>
)}

View File

@@ -34,7 +34,9 @@ const OverviewStats = () => {
const { data: libraryStatistics } = useLibraryQuery(['library.getStatistics']);
const displayableStatItems = Object.keys(StatItemNames) as unknown as keyof typeof StatItemNames;
const displayableStatItems = Object.keys(
StatItemNames
) as unknown as keyof typeof StatItemNames;
// For Demo purposes as we probably wanna save this to database
// Sets Total Capacity and Free Space of the device
@@ -59,7 +61,13 @@ const OverviewStats = () => {
} else if (key === 'total_bytes_capacity') {
bytes = BigInt(sizeInfo.totalSpace);
}
return <StatItem key={key} title={StatItemNames[key as keyof Statistics]!} bytes={bytes} />;
return (
<StatItem
key={key}
title={StatItemNames[key as keyof Statistics]!}
bytes={bytes}
/>
);
})}
</ScrollView>
) : (

View File

@@ -10,7 +10,7 @@ const button = cva(['items-center justify-center rounded-md border shadow-sm'],
danger: ['border-red-800 bg-red-600'],
gray: ['border-app-line bg-app-button'],
darkGray: ['border-app-box bg-app'],
accent: ['border-accent-deep bg-accent shadow-app-shade/10 shadow-md'],
accent: ['border-accent-deep bg-accent shadow-md shadow-app-shade/10'],
outline: ['border-sidebar-line/60 ']
},
size: {

View File

@@ -12,11 +12,11 @@ export const InfoPill = (props: Props) => {
return (
<View
style={twStyle(
'shadow-app-shade/5 bg-app-highlight rounded-md border border-transparent px-[6px] py-[1px] shadow',
'rounded-md border border-transparent bg-app-highlight px-[6px] py-[1px] shadow shadow-app-shade/5',
props.containerStyle
)}
>
<Text style={twStyle('text-ink-dull text-xs font-medium', props.textStyle)}>
<Text style={twStyle('text-xs font-medium text-ink-dull', props.textStyle)}>
{props.text}
</Text>
</View>
@@ -27,11 +27,11 @@ export function PlaceholderPill(props: Props) {
return (
<View
style={twStyle(
'shadow-app-shade/10 border-app-highlight rounded-md border border-dashed bg-transparent px-[6px] py-[1px] shadow',
'rounded-md border border-dashed border-app-highlight bg-transparent px-[6px] py-[1px] shadow shadow-app-shade/10',
props.containerStyle
)}
>
<Text style={twStyle('text-ink-faint/70 text-xs font-medium', props.textStyle)}>
<Text style={twStyle('text-xs font-medium text-ink-faint/70', props.textStyle)}>
{props.text}
</Text>
</View>

View File

@@ -20,7 +20,7 @@ export const Menu = (props: MenuProps) => (
<View>
<PMenu renderer={renderers.NotAnimatedContextMenu}>
<MenuTrigger>{props.trigger}</MenuTrigger>
<MenuOptions optionsContainerStyle={tw`bg-app-menu rounded p-1`}>
<MenuOptions optionsContainerStyle={tw`rounded bg-app-menu p-1`}>
{props.children}
</MenuOptions>
</PMenu>
@@ -44,7 +44,7 @@ export const MenuItem = ({ icon, ...props }: MenuItemProps) => {
<MenuOption
{...props}
customStyles={{
optionText: tw`text-ink py-0.5 text-sm font-medium`
optionText: tw`py-0.5 text-sm font-medium text-ink`
}}
style={tw`flex flex-row items-center`}
/>

View File

@@ -10,9 +10,11 @@ type SettingsContainerProps = PropsWithChildren<{
export function SettingsContainer({ children, title, description }: SettingsContainerProps) {
return (
<View>
{title && <Text style={tw`text-ink-dull pb-2 pl-3 text-sm font-semibold`}>{title}</Text>}
{title && (
<Text style={tw`pb-2 pl-3 text-sm font-semibold text-ink-dull`}>{title}</Text>
)}
{children}
{description && <Text style={tw`text-ink-dull px-3 pt-2 text-sm`}>{description}</Text>}
{description && <Text style={tw`px-3 pt-2 text-sm text-ink-dull`}>{description}</Text>}
</View>
);
}

View File

@@ -12,12 +12,17 @@ type SettingsItemProps = {
export function SettingsItem(props: SettingsItemProps) {
return (
<Pressable onPress={props.onPress}>
<View style={tw`bg-app-box flex flex-row items-center justify-between px-4`}>
<View style={tw`flex flex-row items-center justify-between bg-app-box px-4`}>
<View style={tw`flex flex-row items-center py-4`}>
{props.leftIcon && props.leftIcon({ size: 20, color: tw.color('ink'), style: tw`mr-3` })}
<Text style={tw`text-ink text-[14px]`}>{props.title}</Text>
{props.leftIcon &&
props.leftIcon({ size: 20, color: tw.color('ink'), style: tw`mr-3` })}
<Text style={tw`text-[14px] text-ink`}>{props.title}</Text>
</View>
{props.rightArea ? props.rightArea : <CaretRight size={20} color={tw.color('ink-dull')} />}
{props.rightArea ? (
props.rightArea
) : (
<CaretRight size={20} color={tw.color('ink-dull')} />
)}
</View>
</Pressable>
);
@@ -26,7 +31,7 @@ export function SettingsItem(props: SettingsItemProps) {
export function SettingsItemDivider() {
return (
<View style={tw`bg-app-overlay`}>
<View style={tw`border-b-app-line mx-3 border-b`} />
<View style={tw`mx-3 border-b border-b-app-line`} />
</View>
);
}

View File

@@ -40,9 +40,9 @@ globalThis.localStorage = {
length: _localStorage.size
};
/*
/*
https://github.com/facebook/hermes/issues/23
We are using "Hermes" on Android & IOS, which for the current version (0.11),
IOS does not support the Intl fully so we need polyfill it.

View File

@@ -13,7 +13,11 @@ const Stack = createStackNavigator<RootStackParamList>();
export default function RootNavigator() {
return (
<Stack.Navigator initialRouteName="Root">
<Stack.Screen name="Root" component={DrawerNavigator} options={{ headerShown: false }} />
<Stack.Screen
name="Root"
component={DrawerNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
<Stack.Screen
name="Search"

View File

@@ -19,7 +19,11 @@ export default function SpacedropStack() {
headerBackTitleStyle: tw`text-base`
}}
>
<Stack.Screen name="Spacedrop" component={SpacedropScreen} options={{ header: Header }} />
<Stack.Screen
name="Spacedrop"
component={SpacedropScreen}
options={{ header: Header }}
/>
{SharedScreens(Stack as any)}
</Stack.Navigator>
);

View File

@@ -15,7 +15,7 @@ export default function NotFoundScreen({ navigation }: RootStackScreenProps<'Not
}
style={tw`mt-4 py-4`}
>
<Text style={tw`text-ink-dull text-sm`}>Go to home screen!</Text>
<Text style={tw`text-sm text-ink-dull`}>Go to home screen!</Text>
</TouchableOpacity>
</View>
);

View File

@@ -18,13 +18,17 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
{/* Header */}
<View style={tw`mx-4 flex flex-row items-center`}>
{/* Search Input */}
<View style={tw`border-app-line bg-app-overlay mr-3 h-10 flex-1 rounded border`}>
<View style={tw`mr-3 h-10 flex-1 rounded border border-app-line bg-app-overlay`}>
<View style={tw`flex h-full flex-row items-center px-3`}>
<View style={tw`mr-3`}>
{loading ? (
<ActivityIndicator size={'small'} color={'white'} />
) : (
<MagnifyingGlass size={20} weight="light" color={tw.color('ink-faint')} />
<MagnifyingGlass
size={20}
weight="light"
color={tw.color('ink-faint')}
/>
)}
</View>
<TextInput
@@ -32,7 +36,7 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
clearButtonMode="never" // can't change the color??
underlineColorAndroid="transparent"
placeholderTextColor={tw.color('ink-dull')}
style={tw`text-ink flex-1 text-sm font-medium`}
style={tw`flex-1 text-sm font-medium text-ink`}
textContentType={'none'}
autoFocus
autoCapitalize="none"

View File

@@ -50,7 +50,10 @@ const Hexagon = () => {
return (
<Svg width={width} height={height} viewBox="0 0 100 100">
<Polygon points="0,25 0,75 50,100 100,75 100,25 50,0" fill={tw.color('bg-app-box/30')} />
<Polygon
points="0,25 0,75 50,100 100,75 100,25 50,0"
fill={tw.color('bg-app-box/30')}
/>
</Svg>
);
};
@@ -100,7 +103,9 @@ function DropItem(props: DropItemProps) {
style={tw`w-full items-center justify-center`}
onPress={() => Alert.alert('TODO')}
>
<View style={tw`bg-app-button h-12 w-12 items-center justify-center rounded-full`}>
<View
style={tw`h-12 w-12 items-center justify-center rounded-full bg-app-button`}
>
{icon}
</View>
{props.name && (

View File

@@ -5,7 +5,7 @@ import { SpacesStackScreenProps } from '~/navigation/tabs/SpacesStack';
export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) {
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`text-ink text-xl font-bold`}>Spaces</Text>
<Text style={tw`text-xl font-bold text-ink`}>Spaces</Text>
</View>
);
}

View File

@@ -29,7 +29,10 @@ const CreatingLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'Creat
const createLibrary = useBridgeMutation('library.create', {
onSuccess: (lib) => {
resetOnboardingStore();
queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]);
queryClient.setQueryData(['library.list'], (libraries: any) => [
...(libraries || []),
lib
]);
// Switch to the new library
currentLibraryStore.id = lib.uuid;
if (obStore.shareTelemetry) {

View File

@@ -32,7 +32,7 @@ export function OnboardingContainer({ children }: React.PropsWithChildren) {
>
{children}
</KeyboardAvoidingView>
<Text style={tw`text-ink-dull/50 absolute bottom-8 text-xs`}>
<Text style={tw`absolute bottom-8 text-xs text-ink-dull/50`}>
&copy; 2022 Spacedrive Technology Inc.
</Text>
</View>
@@ -72,7 +72,7 @@ const GetStartedScreen = ({ navigation }: OnboardingStackScreenProps<'GetStarted
{/* Get Started Button */}
<FadeInUpAnimation delay={1200} style={tw`mt-8`}>
<AnimatedButton variant="accent" onPress={() => navigation.push('NewLibrary')}>
<Text style={tw`text-ink text-center text-base font-medium`}>Get Started</Text>
<Text style={tw`text-center text-base font-medium text-ink`}>Get Started</Text>
</AnimatedButton>
</FadeInUpAnimation>
</OnboardingContainer>

View File

@@ -120,7 +120,7 @@ const MasterPasswordScreen = ({ navigation }: OnboardingStackScreenProps<'Master
disabled={form.formState.isSubmitting}
onPress={handleSetPassword}
>
<Text style={tw`text-ink text-center font-medium`}>
<Text style={tw`text-center font-medium text-ink`}>
{!showPasswordValidate ? 'Set password' : 'Confirm Password'}
</Text>
</Button>
@@ -136,7 +136,9 @@ const MasterPasswordScreen = ({ navigation }: OnboardingStackScreenProps<'Master
form.reset();
}}
>
<Text style={tw`text-ink text-center font-medium`}>Remove password</Text>
<Text style={tw`text-center font-medium text-ink`}>
Remove password
</Text>
</Button>
) : (
<Button
@@ -145,7 +147,9 @@ const MasterPasswordScreen = ({ navigation }: OnboardingStackScreenProps<'Master
disabled={form.formState.isSubmitting}
onPress={handleNoPassword}
>
<Text style={tw`text-ink text-center font-medium`}>Continue without password</Text>
<Text style={tw`text-center font-medium text-ink`}>
Continue without password
</Text>
</Button>
)}
</View>

View File

@@ -38,8 +38,8 @@ const NewLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'NewLibrary
<OnboardingTitle style={tw`mt-4`}>Create a Library</OnboardingTitle>
<View style={tw`w-full px-4`}>
<OnboardingDescription style={tw`mt-4`}>
Libraries are a secure, on-device database. Your files remain where they are, the Library
catalogs them and stores all Spacedrive related data.
Libraries are a secure, on-device database. Your files remain where they are,
the Library catalogs them and stores all Spacedrive related data.
</OnboardingDescription>
<Controller
name="name"
@@ -64,11 +64,11 @@ const NewLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'NewLibrary
)}
<View style={tw`mt-4 flex w-full flex-row items-center justify-center`}>
<Button variant="accent" onPress={handleNewLibrary}>
<Text style={tw`text-ink text-center font-medium`}>New Library</Text>
<Text style={tw`text-center font-medium text-ink`}>New Library</Text>
</Button>
<Text style={tw`text-ink-faint px-4 text-xs font-bold`}>OR</Text>
<Text style={tw`px-4 text-xs font-bold text-ink-faint`}>OR</Text>
<Button onPress={handleImport} variant="outline">
<Text style={tw`text-ink text-center font-medium`}>Import Library</Text>
<Text style={tw`text-center font-medium text-ink`}>Import Library</Text>
</Button>
</View>
</OnboardingContainer>

View File

@@ -18,7 +18,7 @@ const RadioButton = ({ title, description, isSelected, style }: RadioButtonProps
return (
<View
style={twStyle(
'border-app-line bg-app-box/50 flex w-full flex-row items-center rounded-md border p-3',
'flex w-full flex-row items-center rounded-md border border-app-line bg-app-box/50 p-3',
style
)}
>
@@ -31,8 +31,8 @@ const RadioButton = ({ title, description, isSelected, style }: RadioButtonProps
{isSelected && <View style={tw`h-1.5 w-1.5 rounded-full bg-white`} />}
</View>
<View style={tw`flex-1`}>
<Text style={tw`text-ink text-base font-bold`}>{title}</Text>
<Text style={tw`text-ink-faint text-sm`}>{description}</Text>
<Text style={tw`text-base font-bold text-ink`}>{title}</Text>
<Text style={tw`text-sm text-ink-faint`}>{description}</Text>
</View>
</View>
);
@@ -52,8 +52,8 @@ const PrivacyScreen = ({ navigation }: OnboardingStackScreenProps<'Privacy'>) =>
<OnboardingContainer>
<OnboardingTitle>Your Privacy</OnboardingTitle>
<OnboardingDescription style={tw`mt-4`}>
Spacedrive is built for privacy, that's why we're open source and local first. So we'll make
it very clear what data is shared with us.
Spacedrive is built for privacy, that's why we're open source and local first. So
we'll make it very clear what data is shared with us.
</OnboardingDescription>
<View style={tw`w-full`}>
<Pressable onPress={() => setShareTelemetry('share-telemetry')}>
@@ -73,7 +73,7 @@ const PrivacyScreen = ({ navigation }: OnboardingStackScreenProps<'Privacy'>) =>
</Pressable>
</View>
<Button variant="accent" size="sm" onPress={onPress} style={tw`mt-6`}>
<Text style={tw`text-ink text-center text-base font-medium`}>Continue</Text>
<Text style={tw`text-center text-base font-medium text-ink`}>Continue</Text>
</Button>
</OnboardingContainer>
);

View File

@@ -109,7 +109,7 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
return (
<Text
style={twStyle(
'text-ink mb-2 ml-2 text-sm font-bold',
'mb-2 ml-2 text-sm font-bold text-ink',
section.title === 'Client' ? 'mt-2' : 'mt-5'
)}
>
@@ -135,9 +135,9 @@ export default function SettingsScreen({ navigation }: SettingsStackScreenProps<
renderSectionHeader={renderSectionHeader}
ListFooterComponent={
<View style={tw`mt-6 mb-4 items-center`}>
<Text style={tw`text-ink text-base font-bold`}>Spacedrive</Text>
<Text style={tw`text-base font-bold text-ink`}>Spacedrive</Text>
{/* TODO: Get this automatically (expo-device have this?) */}
<Text style={tw`text-ink-faint mt-0.5 text-xs font-medium`}>v0.1.0</Text>
<Text style={tw`mt-0.5 text-xs font-medium text-ink-faint`}>v0.1.0</Text>
</View>
}
showsVerticalScrollIndicator={false}

View File

@@ -17,15 +17,15 @@ const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'General
<Card style={tw`bg-app-box`}>
{/* Card Header */}
<View style={tw`flex flex-row justify-between`}>
<Text style={tw`text-ink font-semibold`}>Connected Node</Text>
<Text style={tw`font-semibold text-ink`}>Connected Node</Text>
<View style={tw`flex flex-row`}>
{/* Peers */}
<View style={tw`bg-app-highlight mr-2 self-start rounded px-1.5 py-[2px]`}>
<Text style={tw`text-ink text-xs font-semibold`}>0 Peers</Text>
<View style={tw`mr-2 self-start rounded bg-app-highlight px-1.5 py-[2px]`}>
<Text style={tw`text-xs font-semibold text-ink`}>0 Peers</Text>
</View>
{/* Status */}
<View style={tw`bg-accent rounded px-1.5 py-[2px]`}>
<Text style={tw`text-ink text-xs font-semibold`}>Running</Text>
<View style={tw`rounded bg-accent px-1.5 py-[2px]`}>
<Text style={tw`text-xs font-semibold text-ink`}>Running</Text>
</View>
</View>
</View>

View File

@@ -50,15 +50,15 @@ function LibraryItem({
<Swipeable
containerStyle={twStyle(
index !== 0 && 'mt-2',
'border-app-line bg-app-overlay rounded-lg border px-4 py-3'
'rounded-lg border border-app-line bg-app-overlay px-4 py-3'
)}
enableTrackpadTwoFingerGesture
renderRightActions={renderRightActions}
>
<View style={tw`flex flex-row items-center justify-between`}>
<View>
<Text style={tw`text-ink font-semibold`}>{library.config.name}</Text>
<Text style={tw`text-ink-dull mt-0.5 text-xs`}>{library.uuid}</Text>
<Text style={tw`font-semibold text-ink`}>{library.config.name}</Text>
<Text style={tw`mt-0.5 text-xs text-ink-dull`}>{library.uuid}</Text>
</View>
<CaretRight color={tw.color('ink-dull')} size={18} />
</View>

View File

@@ -110,8 +110,8 @@ const EditLocationSettingsScreen = ({
)}
/>
<SettingsInputInfo>
The name of this Location, this is what will be displayed in the sidebar. Will not rename
the actual folder on disk.
The name of this Location, this is what will be displayed in the sidebar. Will
not rename the actual folder on disk.
</SettingsInputInfo>
<SettingsInputTitle style={tw`mt-3`}>Local Path</SettingsInputTitle>

View File

@@ -48,7 +48,7 @@ function LocationItem({ location, index, navigation }: LocationItemProps) {
]}
>
<Pressable
style={tw`border-app-line bg-app-button items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
style={tw`items-center justify-center rounded-md border border-app-line bg-app-button py-1.5 px-3 shadow-sm`}
onPress={() => {
navigation.navigate('EditLocationSettings', { id: location.id });
swipeable.close();
@@ -60,7 +60,7 @@ function LocationItem({ location, index, navigation }: LocationItemProps) {
locationId={location.id}
trigger={
<View
style={tw`bg-app-button border-app-line items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
style={tw`items-center justify-center rounded-md border border-app-line bg-app-button py-1.5 px-3 shadow-sm`}
>
<Trash size={18} color="white" />
</View>
@@ -68,7 +68,7 @@ function LocationItem({ location, index, navigation }: LocationItemProps) {
/>
{/* Full Re-scan IS too much here */}
<Pressable
style={tw`border-app-line bg-app-button items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
style={tw`items-center justify-center rounded-md border border-app-line bg-app-button py-1.5 px-3 shadow-sm`}
onPress={() => fullRescan.mutate(location.id)}
>
<Repeat size={18} color="white" />
@@ -80,7 +80,7 @@ function LocationItem({ location, index, navigation }: LocationItemProps) {
return (
<Swipeable
containerStyle={twStyle(
'border-app-line bg-app-overlay rounded-lg border px-4 py-3',
'rounded-lg border border-app-line bg-app-overlay px-4 py-3',
index !== 0 && 'mt-2'
)}
enableTrackpadTwoFingerGesture
@@ -100,15 +100,18 @@ function LocationItem({ location, index, navigation }: LocationItemProps) {
/>
</View>
<View style={tw`mx-4 flex-1`}>
<Text numberOfLines={1} style={tw`text-ink text-sm font-semibold`}>
<Text numberOfLines={1} style={tw`text-sm font-semibold text-ink`}>
{location.name}
</Text>
<View style={tw`bg-app-highlight mt-0.5 self-start rounded py-[1px] px-1`}>
<Text numberOfLines={1} style={tw`text-ink-dull text-xs font-semibold`}>
<View style={tw`mt-0.5 self-start rounded bg-app-highlight py-[1px] px-1`}>
<Text numberOfLines={1} style={tw`text-xs font-semibold text-ink-dull`}>
{location.node.name}
</Text>
</View>
<Text numberOfLines={1} style={tw`text-ink-dull mt-0.5 text-[10px] font-semibold`}>
<Text
numberOfLines={1}
style={tw`mt-0.5 text-[10px] font-semibold text-ink-dull`}
>
{location.path}
</Text>
</View>

View File

@@ -28,7 +28,11 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<UpdateTagModal tag={tag} ref={updateTagModalRef} onSubmit={() => swipeable.close()} />
<UpdateTagModal
tag={tag}
ref={updateTagModalRef}
onSubmit={() => swipeable.close()}
/>
<AnimatedButton onPress={() => updateTagModalRef.current?.present()}>
<Pen size={18} color="white" />
</AnimatedButton>
@@ -47,7 +51,7 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
return (
<Swipeable
containerStyle={twStyle(
'border-app-line bg-app-overlay rounded-lg border px-4 py-3',
'rounded-lg border border-app-line bg-app-overlay px-4 py-3',
index !== 0 && 'mt-2'
)}
enableTrackpadTwoFingerGesture
@@ -55,8 +59,10 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
>
<View style={tw`flex flex-row items-center justify-between`}>
<View style={tw`flex flex-row`}>
<View style={twStyle({ backgroundColor: tag.color! }, 'h-4 w-4 rounded-full')} />
<Text style={tw`text-ink ml-3`}>{tag.name}</Text>
<View
style={twStyle({ backgroundColor: tag.color! }, 'h-4 w-4 rounded-full')}
/>
<Text style={tw`ml-3 text-ink`}>{tag.name}</Text>
</View>
<CaretRight color={tw.color('ink-dull')} size={18} />
</View>

8
apps/web/.eslintrc.js Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
extends: [require.resolve('@sd/config/eslint/web.js')],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
},
ignorePatterns: ['playwright.config.ts', 'tests/**/*']
};

View File

@@ -16,13 +16,22 @@ export function App() {
<div className="flex h-screen w-screen flex-row divide-x divide-gray-300">
<div className="flex flex-col space-y-2 p-2">
<div className="space-x-2">
<button className={ButtonStyles} onClick={() => createDb.mutate('pullOperations')}>
<button
className={ButtonStyles}
onClick={() => createDb.mutate('pullOperations')}
>
Add Database
</button>
<button className={ButtonStyles} onClick={() => removeDbs.mutate('pullOperations')}>
<button
className={ButtonStyles}
onClick={() => removeDbs.mutate('pullOperations')}
>
Remove Databases
</button>
<button className={ButtonStyles} onClick={() => testCreate.mutate('testCreate')}>
<button
className={ButtonStyles}
onClick={() => testCreate.mutate('testCreate')}
>
Test Create
</button>
</div>
@@ -143,9 +152,13 @@ function OperationList(props: { db: string }) {
<tr key={message.id}>
<td className="border border-transparent">{message.id}</td>
<td className="border border-transparent">
{new Date(Number(message.timestamp) / 10000000).toLocaleTimeString()}
{new Date(
Number(message.timestamp) / 10000000
).toLocaleTimeString()}
</td>
<td className="border border-transparent">
{messageType(message.typ)}
</td>
<td className="border border-transparent">{messageType(message.typ)}</td>
</tr>
))}
</table>

View File

@@ -11,7 +11,9 @@ export default (props: { objectId: number }) => {
const submitPlausibleEvent = usePlausibleEvent();
const tags = useLibraryQuery(['tags.list'], { suspense: true });
const tagsForObject = useLibraryQuery(['tags.getForObject', props.objectId], { suspense: true });
const tagsForObject = useLibraryQuery(['tags.getForObject', props.objectId], {
suspense: true
});
const assignTag = useLibraryMutation('tags.assign', {
onSuccess: () => {
@@ -42,7 +44,9 @@ export default (props: { objectId: number }) => {
iconProps={{ size: 15 }}
keybind="⌘N"
onClick={() => {
dialogManager.create((dp) => <CreateDialog {...dp} assignToObject={props.objectId} />);
dialogManager.create((dp) => (
<CreateDialog {...dp} assignToObject={props.objectId} />
));
}}
/>
<Menu.Separator className={clsx('mx-0 mb-0 transition', isScrolled && 'shadow')} />
@@ -91,7 +95,8 @@ export default (props: { objectId: number }) => {
<div
className="mr-0.5 h-[15px] w-[15px] shrink-0 rounded-full border"
style={{
backgroundColor: active && tag.color ? tag.color : 'transparent',
backgroundColor:
active && tag.color ? tag.color : 'transparent',
borderColor: tag.color || '#efefef'
}}
/>

View File

@@ -116,14 +116,16 @@ export default (props: PropsWithChildren) => {
<CM.SubMenu label="More actions..." icon={Plus}>
<CM.Item
onClick={() =>
store.locationId && generateThumbsForLocation.mutate({ id: store.locationId, path: '' })
store.locationId &&
generateThumbsForLocation.mutate({ id: store.locationId, path: '' })
}
label="Regen Thumbnails"
icon={Image}
/>
<CM.Item
onClick={() =>
store.locationId && objectValidator.mutate({ id: store.locationId, path: '' })
store.locationId &&
objectValidator.mutate({ id: store.locationId, path: '' })
}
label="Generate Checksums"
icon={ShieldCheck}

View File

@@ -161,7 +161,11 @@ export default ({ data, className, ...props }: Props) => {
onClick={() => {
if (keyManagerUnlocked && hasMountedKeys) {
dialogManager.create((dp) => (
<EncryptDialog {...dp} location_id={store.locationId!} path_id={data.item.id} />
<EncryptDialog
{...dp}
location_id={store.locationId!}
path_id={data.item.id}
/>
));
} else if (!keyManagerUnlocked) {
showAlertDialog({
@@ -184,7 +188,11 @@ export default ({ data, className, ...props }: Props) => {
onClick={() => {
if (keyManagerUnlocked) {
dialogManager.create((dp) => (
<DecryptDialog {...dp} location_id={store.locationId!} path_id={data.item.id} />
<DecryptDialog
{...dp}
location_id={store.locationId!}
path_id={data.item.id}
/>
));
} else {
showAlertDialog({

View File

@@ -42,8 +42,7 @@ export default (props: Props) => {
onSuccess: () => {
showAlertDialog({
title: 'Success',
value:
'The decryption job has started successfully. You may track the progress in the job overview panel.'
value: 'The decryption job has started successfully. You may track the progress in the job overview panel.'
});
},
onError: () => {
@@ -123,7 +122,9 @@ export default (props: Props) => {
checked={form.watch('mountAssociatedKey')}
onCheckedChange={(e) => form.setValue('mountAssociatedKey', e)}
/>
<span className="ml-3 mt-0.5 text-xs font-medium">Automatically mount key</span>
<span className="ml-3 mt-0.5 text-xs font-medium">
Automatically mount key
</span>
<Tooltip label="The key linked with the file will be automatically mounted">
<Info className="ml-1.5 mt-0.5 h-4 w-4 text-ink-faint" />
</Tooltip>
@@ -143,7 +144,9 @@ export default (props: Props) => {
size="sm"
{...form.register('saveToKeyManager')}
/>
<span className="ml-3 mt-0.5 text-xs font-medium">Save to Key Manager</span>
<span className="ml-3 mt-0.5 text-xs font-medium">
Save to Key Manager
</span>
<Tooltip label="This key will be saved to the key manager">
<Info className="ml-1.5 mt-0.5 h-4 w-4 text-ink-faint" />
</Tooltip>

View File

@@ -48,8 +48,7 @@ export default (props: Props) => {
onSuccess: () => {
showAlertDialog({
title: 'Success',
value:
'The encryption job has started successfully. You may track the progress in the job overview panel.'
value: 'The encryption job has started successfully. You may track the progress in the job overview panel.'
});
},
onError: () => {
@@ -154,9 +153,15 @@ export default (props: Props) => {
<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>
<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>

View File

@@ -46,7 +46,10 @@ const GridViewItem = memo(({ data, selected, index, ...props }: GridViewItemProp
<RenameTextBox
filePathData={filePathData}
selected={selected}
className={clsx('text-center font-medium', selected && 'bg-accent text-white')}
className={clsx(
'text-center font-medium',
selected && 'bg-accent text-white'
)}
style={{
maxHeight: explorerStore.gridItemSize / 3
}}
@@ -109,7 +112,8 @@ export default () => {
const index = explorerStore.selectedRowIndex;
if (
explorerStore.showInspector &&
((lastSelectedIndex === -1 && index !== -1) || (lastSelectedIndex !== -1 && index === -1))
((lastSelectedIndex === -1 && index !== -1) ||
(lastSelectedIndex !== -1 && index === -1))
) {
handleWindowResize();
}
@@ -186,7 +190,12 @@ export default () => {
if (!item) return null;
return (
<GridViewItem key={item.item.id} data={item} selected={isSelected} index={index} />
<GridViewItem
key={item.item.id}
data={item}
selected={isSelected}
index={index}
/>
);
})}
</div>

View File

@@ -26,7 +26,10 @@ export default function Note(props: Props) {
);
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedNote = useCallback((note: string) => debounce(note), [props.data.id, fileSetNote]);
const debouncedNote = useCallback(
(note: string) => debounce(note),
[props.data.id, fileSetNote]
);
// when input is updated, cache note
function handleNoteUpdate(e: React.ChangeEvent<HTMLTextAreaElement>) {

View File

@@ -120,10 +120,18 @@ export const Inspector = ({ data, context, onScroll, ...elementProps }: Props) =
<Divider />
<MetaContainer>
<div className="flex flex-wrap gap-1 overflow-hidden">
<InfoPill>{isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]}</InfoPill>
{filePathData?.extension && <InfoPill>{filePathData.extension}</InfoPill>}
<InfoPill>
{isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]}
</InfoPill>
{filePathData?.extension && (
<InfoPill>{filePathData.extension}</InfoPill>
)}
{tags?.data?.map((tag) => (
<Tooltip key={tag.id} label={tag.name || ''} className="flex overflow-hidden">
<Tooltip
key={tag.id}
label={tag.name || ''}
className="flex overflow-hidden"
>
<InfoPill
className="truncate !text-white"
style={{ backgroundColor: tag.color + 'CC' }}
@@ -149,13 +157,17 @@ export const Inspector = ({ data, context, onScroll, ...elementProps }: Props) =
<MetaTextLine>
<InspectorIcon component={Cube} />
<span className="mr-1.5">Size</span>
<MetaValue>{formatBytes(Number(filePathData?.size_in_bytes || 0))}</MetaValue>
<MetaValue>
{formatBytes(Number(filePathData?.size_in_bytes || 0))}
</MetaValue>
</MetaTextLine>
{fullObjectData.data?.media_data?.duration_seconds && (
<MetaTextLine>
<InspectorIcon component={Clock} />
<span className="mr-1.5">Duration</span>
<MetaValue>{fullObjectData.data.media_data.duration_seconds}</MetaValue>
<MetaValue>
{fullObjectData.data.media_data.duration_seconds}
</MetaValue>
</MetaTextLine>
)}
</MetaContainer>
@@ -165,14 +177,18 @@ export const Inspector = ({ data, context, onScroll, ...elementProps }: Props) =
<MetaTextLine>
<InspectorIcon component={Clock} />
<MetaKeyName className="mr-1.5">Created</MetaKeyName>
<MetaValue>{dayjs(item?.date_created).format('MMM Do YYYY')}</MetaValue>
<MetaValue>
{dayjs(item?.date_created).format('MMM Do YYYY')}
</MetaValue>
</MetaTextLine>
</Tooltip>
<Tooltip label={dayjs(item?.date_created).format('h:mm:ss a')}>
<MetaTextLine>
<InspectorIcon component={Barcode} />
<MetaKeyName className="mr-1.5">Indexed</MetaKeyName>
<MetaValue>{dayjs(filePathData?.date_indexed).format('MMM Do YYYY')}</MetaValue>
<MetaValue>
{dayjs(filePathData?.date_indexed).format('MMM Do YYYY')}
</MetaValue>
</MetaTextLine>
</Tooltip>
</MetaContainer>
@@ -193,8 +209,12 @@ export const Inspector = ({ data, context, onScroll, ...elementProps }: Props) =
<Tooltip label={filePathData?.integrity_checksum || ''}>
<MetaTextLine>
<InspectorIcon component={CircleWavyCheck} />
<MetaKeyName className="mr-1.5">Checksum</MetaKeyName>
<MetaValue>{filePathData?.integrity_checksum}</MetaValue>
<MetaKeyName className="mr-1.5">
Checksum
</MetaKeyName>
<MetaValue>
{filePathData?.integrity_checksum}
</MetaValue>
</MetaTextLine>
</Tooltip>
)}
@@ -202,7 +222,9 @@ export const Inspector = ({ data, context, onScroll, ...elementProps }: Props) =
<Tooltip label={pub_id || ''}>
<MetaTextLine>
<InspectorIcon component={Hash} />
<MetaKeyName className="mr-1.5">Object ID</MetaKeyName>
<MetaKeyName className="mr-1.5">
Object ID
</MetaKeyName>
<MetaValue>{pub_id}</MetaValue>
</MetaTextLine>
</Tooltip>

View File

@@ -182,7 +182,12 @@ export default () => {
const tableHeaderHeight = 34;
const tableEnd = virtualRows[virtualRows.length - 1]?.end || 0;
const padding =
scrollHeight - TOP_BAR_HEIGHT - tableHeaderHeight - paddingY - tableEnd - scrollBarWidth;
scrollHeight -
TOP_BAR_HEIGHT -
tableHeaderHeight -
paddingY -
tableEnd -
scrollBarWidth;
return padding > 0 ? padding : paddingY;
}, [virtualRows]);
@@ -195,7 +200,14 @@ export default () => {
return {
...sizing,
...(scrollWidth && nameWidth
? { Name: nameWidth + scrollWidth - paddingX * 2 - scrollBarWidth - tableLength }
? {
Name:
nameWidth +
scrollWidth -
paddingX * 2 -
scrollBarWidth -
tableLength
}
: {})
};
});
@@ -233,7 +245,8 @@ export default () => {
const index = explorerStore.selectedRowIndex;
if (
explorerStore.showInspector &&
((lastSelectedIndex === -1 && index !== -1) || (lastSelectedIndex !== -1 && index === -1))
((lastSelectedIndex === -1 && index !== -1) ||
(lastSelectedIndex !== -1 && index === -1))
) {
handleResize();
}
@@ -258,7 +271,9 @@ export default () => {
(e) => {
e.preventDefault();
if (explorerStore.selectedRowIndex > 0) {
const currentIndex = rows.findIndex((row) => row.index === explorerStore.selectedRowIndex);
const currentIndex = rows.findIndex(
(row) => row.index === explorerStore.selectedRowIndex
);
const newIndex = rows[currentIndex - 1]?.index;
if (newIndex !== undefined) getExplorerStore().selectedRowIndex = newIndex;
}
@@ -275,7 +290,9 @@ export default () => {
explorerStore.selectedRowIndex !== -1 &&
explorerStore.selectedRowIndex !== (data.length ?? 1) - 1
) {
const currentIndex = rows.findIndex((row) => row.index === explorerStore.selectedRowIndex);
const currentIndex = rows.findIndex(
(row) => row.index === explorerStore.selectedRowIndex
);
const newIndex = rows[currentIndex + 1]?.index;
if (newIndex !== undefined) getExplorerStore().selectedRowIndex = newIndex;
}
@@ -293,7 +310,11 @@ export default () => {
)}
>
{table.getHeaderGroups().map((headerGroup) => (
<div role="rowheader" key={headerGroup.id} className="flex border-b border-app-line/50">
<div
role="rowheader"
key={headerGroup.id}
className="flex border-b border-app-line/50"
>
{headerGroup.headers.map((header, i) => {
const size = header.column.getSize();
return (
@@ -313,7 +334,10 @@ export default () => {
>
{header.isPlaceholder ? null : (
<div className={clsx('flex items-center')}>
{flexRender(header.column.columnDef.header, header.getContext())}
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
<div className="flex-1" />
{{
@@ -322,7 +346,8 @@ export default () => {
}[header.column.getIsSorted() as string] ?? null}
{(i !== headerGroup.headers.length - 1 ||
(i === headerGroup.headers.length - 1 && !locked)) && (
(i === headerGroup.headers.length - 1 &&
!locked)) && (
<div
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => {
@@ -348,7 +373,11 @@ export default () => {
const row = rows[virtualRow.index]!;
const selected = explorerStore.selectedRowIndex === row.index;
return (
<div key={row.id} className="flex pl-4 pr-3" style={{ height: `${virtualRow.size}px` }}>
<div
key={row.id}
className="flex pl-4 pr-3"
style={{ height: `${virtualRow.size}px` }}
>
<ListViewItem
row={row}
index={virtualRow.index}

View File

@@ -161,7 +161,8 @@ export function QuickPreview({ libraryUuid, transformOrigin }: QuickPreviewProps
if (!show || explorerItem.current == null) return null;
const { item } = explorerItem.current;
const locationId = 'location_id' in item ? item.location_id : explorerStore.locationId;
const locationId =
'location_id' in item ? item.location_id : explorerStore.locationId;
if (locationId == null) {
onPreviewError();
return null;
@@ -192,13 +193,18 @@ export function QuickPreview({ libraryUuid, transformOrigin }: QuickPreviewProps
>
<div className="!pointer-events-auto h-5/6 w-11/12 rounded-md border border-app-line bg-app-box text-ink shadow-app-shade">
<nav className="flex w-full flex-row">
<Dialog.Close className="ml-2 text-ink-dull" aria-label="Close">
<Dialog.Close
className="ml-2 text-ink-dull"
aria-label="Close"
>
<XCircle size={16} />
</Dialog.Close>
<Dialog.Title className="mx-auto my-1 font-bold">
Preview -{' '}
<span className="inline-block max-w-xs truncate align-sub text-sm text-ink-dull">
{'name' in item && item.name ? item.name : 'Unkown Object'}
{'name' in item && item.name
? item.name
: 'Unkown Object'}
</span>
</Dialog.Title>
</nav>

View File

@@ -19,7 +19,10 @@ export default forwardRef<HTMLInputElement, Props>((props, forwardedRef) => {
if ((event.key === 'f' && event.metaKey) || event.ctrlKey) {
event.preventDefault();
forwardedRef?.current?.focus();
} else if (forwardedRef?.current === document.activeElement && event.key === 'Escape') {
} else if (
forwardedRef?.current === document.activeElement &&
event.key === 'Escape'
) {
forwardedRef.current?.blur();
setSearchValue('');
}
@@ -39,7 +42,10 @@ export default forwardRef<HTMLInputElement, Props>((props, forwardedRef) => {
else if (forwardedRef) forwardedRef.current = el;
}}
placeholder="Search"
className={clsx('w-52 transition-all duration-200 focus-within:w-60', props.className)}
className={clsx(
'w-52 transition-all duration-200 focus-within:w-60',
props.className
)}
size="sm"
onChange={(e) => setSearchValue(e.target.value)}
onBlur={() => setSearchValue('')}
@@ -51,11 +57,20 @@ export default forwardRef<HTMLInputElement, Props>((props, forwardedRef) => {
)}
>
{platform === 'browser' ? (
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
<Shortcut
chars="⌘F"
aria-label={'Press Command-F to focus search bar'}
/>
) : os === 'macOS' ? (
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
<Shortcut
chars="⌘F"
aria-label={'Press Command-F to focus search bar'}
/>
) : (
<Shortcut chars="CTRL+F" aria-label={'Press CTRL-F to focus search bar'} />
<Shortcut
chars="CTRL+F"
aria-label={'Press CTRL-F to focus search bar'}
/>
)}
</div>
}

View File

@@ -45,7 +45,8 @@ export default () => {
}, [topBarRef]);
const topBarCondition =
(topBarWidth < 1000 && countToolOptions >= 8) || (topBarWidth < 600 && countToolOptions >= 6);
(topBarWidth < 1000 && countToolOptions >= 8) ||
(topBarWidth < 600 && countToolOptions >= 6);
return (
<div
@@ -70,13 +71,23 @@ export default () => {
<SearchBar formClassName="justify-center" ref={searchRef} />
<div data-tauri-drag-region className="flex flex-row justify-end w-full">
<div data-tauri-drag-region className={`gap-0 ${topBarCondition ? 'hidden' : 'flex'}`}>
<div data-tauri-drag-region className="flex w-full flex-row justify-end">
<div
data-tauri-drag-region
className={`gap-0 ${topBarCondition ? 'hidden' : 'flex'}`}
>
{toolBarRouteOptions[getPageName].options.map((group) => {
return (Object.keys(group) as groupKeys[]).map((groupKey) => {
return group[groupKey]?.map(
(
{ icon, onClick, popOverComponent, toolTipLabel, topBarActive, individual },
{
icon,
onClick,
popOverComponent,
toolTipLabel,
topBarActive,
individual
},
index
) => {
const groupCount = Object.keys(group).length;
@@ -108,7 +119,9 @@ export default () => {
</TopBarButton>
}
>
<div className="block w-[250px] ">{popOverComponent}</div>
<div className="block w-[250px] ">
{popOverComponent}
</div>
</Popover>
) : (
<TopBarButton

View File

@@ -13,7 +13,7 @@ export interface TopBarButtonProps {
}
const topBarButtonStyle = cva(
'text-ink hover:text-ink text-md relative hover:bg-app-selected radix-state-open:bg-app-selected mr-[1px] flex border-none !p-0.5 font-medium outline-none transition-colors duration-100',
'text-md relative mr-[1px] flex border-none !p-0.5 font-medium text-ink outline-none transition-colors duration-100 hover:bg-app-selected hover:text-ink radix-state-open:bg-app-selected',
{
variants: {
active: {
@@ -37,7 +37,11 @@ const topBarButtonStyle = cva(
export default forwardRef<HTMLButtonElement, TopBarButtonProps>(
({ active, rounding, className, ...props }, ref) => {
return (
<Button {...props} ref={ref} className={topBarButtonStyle({ active, rounding, className })}>
<Button
{...props}
ref={ref}
className={topBarButtonStyle({ active, rounding, className })}
>
{props.children}
{props.checkIcon && active && (
<Check className="absolute right-2 m-0.5 h-5 w-5 text-ink-dull" />

View File

@@ -23,11 +23,14 @@ export default ({ className = '' }: Props) => {
</TopBarButton>
}
>
<div className="flex flex-col p-2 overflow-hidden">
<div className="flex flex-col overflow-hidden p-2">
{toolBarRouteOptions[getPageName].options.map((group) => {
return (Object.keys(group) as groupKeys[]).map((groupKey) => {
return group[groupKey]?.map(
({ icon, onClick, popOverComponent, toolTipLabel, topBarActive }, index) => {
(
{ icon, onClick, popOverComponent, toolTipLabel, topBarActive },
index
) => {
const groupCount = Object.keys(group).length;
const groupIndex = Object.keys(group).indexOf(groupKey);
return (
@@ -42,7 +45,7 @@ export default ({ className = '' }: Props) => {
onClick={onClick}
checkIcon={true}
>
<div className="flex items-center justify-between w-full">
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-1">
{icon}
{toolTipLabel}
@@ -51,7 +54,9 @@ export default ({ className = '' }: Props) => {
</TopBarButton>
}
>
<div className="block w-[250px] ">{popOverComponent}</div>
<div className="block w-[250px] ">
{popOverComponent}
</div>
</Popover>
) : (
<TopBarButton

View File

@@ -33,7 +33,7 @@ export default function Explorer(props: Props) {
}, [locationId]);
return (
<div className="flex flex-col w-full h-screen bg-app">
<div className="flex h-screen w-full flex-col bg-app">
<TopBar />
<div className="flex flex-1">
@@ -45,7 +45,10 @@ export default function Explorer(props: Props) {
{expStore.showInspector && props.data?.items[expStore.selectedRowIndex] && (
<div className="w-[260px] shrink-0">
<Inspector data={props.data?.items[expStore.selectedRowIndex]} onScroll={onScroll} />
<Inspector
data={props.data?.items[expStore.selectedRowIndex]}
onScroll={onScroll}
/>
</div>
)}
</div>

View File

@@ -103,7 +103,11 @@ export const Key = ({ data }: { data: Key }) => {
<KeyIcon
className={clsx(
'ml-1 mr-3 h-5 w-5',
data.mounted ? (data.locked ? 'text-accent' : 'text-accent') : 'text-gray-400/80'
data.mounted
? data.locked
? 'text-accent'
: 'text-accent'
: 'text-gray-400/80'
)}
/>
<div className="flex flex-col ">
@@ -111,7 +115,9 @@ export const Key = ({ data }: { data: Key }) => {
<div className="font-semibold">{data.name}</div>
{data.mounted && (
<div className="ml-2 inline rounded bg-gray-500 px-1 text-[8pt] font-medium text-gray-300">
{data.nodes?.length || 0 > 0 ? `${data.nodes?.length || 0} nodes` : 'This node'}
{data.nodes?.length || 0 > 0
? `${data.nodes?.length || 0} nodes`
: 'This node'}
</div>
)}
{data.default && (

View File

@@ -41,7 +41,12 @@ export default () => {
<div className="mb-1 p-3">
<KeyHeading>Mount key</KeyHeading>
<PasswordInput ref={ref} value={key} onChange={(e) => setKey(e.target.value)} autoFocus />
<PasswordInput
ref={ref}
value={key}
onChange={(e) => setKey(e.target.value)}
autoFocus
/>
<div className="flex flex-row space-x-2">
<div className="relative mt-2 flex grow">
@@ -115,9 +120,15 @@ export default () => {
<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>
<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>

View File

@@ -49,7 +49,10 @@ export default () => {
if (masterPassword !== '') {
setMasterPassword('');
setSecretKey('');
unlockKeyManager.mutate({ password: masterPassword, secret_key: secretKey });
unlockKeyManager.mutate({
password: masterPassword,
secret_key: secretKey
});
}
}}
>

View File

@@ -20,7 +20,7 @@ export default () => {
const { library } = useClientContext();
return (
<div className="flex flex-col pb-10 overflow-x-hidden overflow-y-scroll no-scrollbar mask-fade-out grow">
<div className="no-scrollbar mask-fade-out flex grow flex-col overflow-x-hidden overflow-y-scroll pb-10">
<div className="space-y-0.5">
<SidebarLink to="overview">
<Icon component={Planet} />

View File

@@ -73,7 +73,8 @@ export default () => {
size="sm"
variant="gray"
onClick={() => {
if (nodeState?.data?.data_path) platform.openPath!(nodeState?.data?.data_path);
if (nodeState?.data?.data_path)
platform.openPath!(nodeState?.data?.data_path);
}}
>
Open

View File

@@ -19,7 +19,7 @@ export default () => {
className="text-ink-faint ring-offset-sidebar"
>
<Tooltip label="Settings">
<Gear className="w-5 h-5" />
<Gear className="h-5 w-5" />
</Tooltip>
</ButtonLink>
<Popover

View File

@@ -8,6 +8,6 @@ export default () => {
return isRunningJob ? (
<Loader className="h-[20px] w-[20px]" />
) : (
<CheckCircle className="w-5 h-5" />
<CheckCircle className="h-5 w-5" />
);
};

View File

@@ -36,7 +36,9 @@ const getNiceData = (job: JobReport): Record<string, JobNiceData> => ({
icon: Camera
},
file_identifier: {
name: `Extracted metadata for ${numberWithCommas(job.metadata?.total_orphan_paths || 0)} files`,
name: `Extracted metadata for ${numberWithCommas(
job.metadata?.total_orphan_paths || 0
)} files`,
icon: Eye
},
object_validator: {
@@ -124,7 +126,9 @@ export function JobsManager() {
<Job key={job.id} job={job} />
))}
{jobs.data?.length === 0 && runningJobs.data?.length === 0 && (
<div className="flex h-32 items-center justify-center text-ink-dull">No jobs.</div>
<div className="flex h-32 items-center justify-center text-ink-dull">
No jobs.
</div>
)}
</div>
</div>
@@ -141,6 +145,7 @@ function Job({ job }: { job: JobReport }) {
const isRunning = job.status === 'Running';
return (
// Do we actually need bg-opacity-60 here? Where is the bg?
// eslint-disable-next-line tailwindcss/migration-from-tailwind-2
<div className="flex items-center border-b border-app-line/50 bg-opacity-60 p-2 pl-4">
<Tooltip label={job.status}>
<niceData.icon className={clsx('mr-3 h-5 w-5')} />
@@ -164,7 +169,9 @@ function Job({ job }: { job: JobReport }) {
<span className="mx-1 opacity-50">&#8226;</span>
{
<span className="text-xs">
{isRunning ? 'Unknown time remaining' : dayjs(job.date_created).toNow(true) + ' ago'}
{isRunning
? 'Unknown time remaining'
: dayjs(job.date_created).toNow(true) + ' ago'}
</span>
}
</div>

View File

@@ -29,7 +29,7 @@ export const LibrarySection = () => {
return (
<SidebarLink
className="relative w-full group"
className="group relative w-full"
to={`location/${location.id}`}
key={location.id}
>

View File

@@ -5,7 +5,7 @@ import { NavLink, NavLinkProps } from 'react-router-dom';
import { useOperatingSystem } from '~/hooks/useOperatingSystem';
const styles = cva(
'max-w ring-offset-sidebar focus:ring-accent flex grow flex-row items-center gap-0.5 truncate rounded px-2 py-1 text-sm font-medium outline-none focus:ring-2 focus:ring-offset-2',
'max-w flex grow flex-row items-center gap-0.5 truncate rounded px-2 py-1 text-sm font-medium outline-none ring-offset-sidebar focus:ring-2 focus:ring-accent focus:ring-offset-2',
{
variants: {
active: {

View File

@@ -7,8 +7,8 @@ export default (
actionArea?: React.ReactNode;
}>
) => (
<div className="mt-5 group">
<div className="flex items-center justify-between mb-1">
<div className="group mt-5">
<div className="mb-1 flex items-center justify-between">
<CategoryHeading className="ml-1">{props.name}</CategoryHeading>
<div className="text-ink-faint opacity-0 transition-all duration-300 hover:!opacity-100 group-hover:opacity-30">
{props.actionArea}

View File

@@ -14,7 +14,9 @@ export default () => {
return (
<div data-tauri-drag-region className={clsx('shrink-0', macOnly(os, 'h-7'))}>
{/* We do not provide the onClick handlers for 'MacTrafficLights' because this is only used in demo mode */}
{showControls && <MacTrafficLights className="absolute top-[13px] left-[13px] z-50" />}
{showControls && (
<MacTrafficLights className="absolute top-[13px] left-[13px] z-50" />
)}
</div>
);
}

View File

@@ -1,4 +1,4 @@
import { OperatingSystem } from "~/util/Platform";
import { OperatingSystem } from '~/util/Platform';
export const macOnly = (platform: OperatingSystem | undefined, classnames: string) =>
platform === 'macOS' ? classnames : '';

View File

@@ -56,7 +56,9 @@ const Layout = () => {
<QuickPreview libraryUuid={library.uuid} />
</LibraryContextProvider>
) : (
<h1 className="p-4 text-white">Please select or create a library in the sidebar.</h1>
<h1 className="p-4 text-white">
Please select or create a library in the sidebar.
</h1>
)}
</div>
<Toasts />

View File

@@ -19,7 +19,7 @@ export const Component = () => {
)}
>
<DragRegion ref={ref} />
<div className="flex flex-col w-full h-screen p-5 pt-0">
<div className="flex h-screen w-full flex-col p-5 pt-0">
<Outlet />
</div>
</div>

View File

@@ -19,7 +19,8 @@ export const Component = () => {
const { location_id, path, limit } = useExplorerParams();
// we destructure this since `mutate` is a stable reference but the object it's in is not
const { mutate: mutateQuickRescan, ...quickRescan } = useLibraryMutation('locations.quickRescan');
const { mutate: mutateQuickRescan, ...quickRescan } =
useLibraryMutation('locations.quickRescan');
const explorerState = getExplorerStore();
useEffect(() => {

View File

@@ -69,7 +69,11 @@ const StatItem = (props: StatItemProps) => {
<span className="text-2xl">
{isLoading && (
<div>
<Skeleton enableAnimation={true} baseColor={'#21212e'} highlightColor={'#13131a'} />
<Skeleton
enableAnimation={true}
baseColor={'#21212e'}
highlightColor={'#13131a'}
/>
</div>
)}
<div
@@ -96,12 +100,12 @@ export const Component = () => {
overviewMounted = true;
return (
<div className="flex flex-col w-full h-screen">
<div className="flex h-screen w-full flex-col">
<ScreenHeading>Overview</ScreenHeading>
{/* STAT HEADER */}
<div className="flex w-full">
{/* STAT CONTAINER */}
<div className="flex h-20 -mb-1 overflow-hidden">
<div className="-mb-1 flex h-20 overflow-hidden">
{Object.entries(stats?.data || []).map(([key, value]) => {
if (!displayableStatItems.includes(key)) return null;
return (
@@ -116,7 +120,7 @@ export const Component = () => {
</div>
<div className="grow" />
</div>
<div className="grid grid-cols-5 gap-3 pb-4 mt-4">
<div className="mt-4 grid grid-cols-5 gap-3 pb-4">
<CategoryButton icon={Heart} category="Favorites" />
<CategoryButton icon={FileText} category="Documents" />
<CategoryButton icon={Camera} category="Movies" />
@@ -129,10 +133,10 @@ export const Component = () => {
<CategoryButton icon={Heart} category="Favorites" />
</div>
<Card className="text-ink-dull">
<b>Note: </b>&nbsp; This is a pre-alpha build of Spacedrive, many features are yet to be
functional.
<b>Note: </b>&nbsp; This is a pre-alpha build of Spacedrive, many features are yet
to be functional.
</Card>
<div className="flex w-full h-4 shrink-0" />
<div className="flex h-4 w-full shrink-0" />
</div>
);
};
@@ -145,7 +149,7 @@ interface CategoryButtonProps {
function CategoryButton({ category, icon: Icon }: CategoryButtonProps) {
return (
<Card className="items-center !px-3">
<Icon weight="fill" className="w-6 h-6 mr-3 text-ink-dull opacity-20" />
<Icon weight="fill" className="mr-3 h-6 w-6 text-ink-dull opacity-20" />
<div>
<h2 className="text-sm font-medium">{category}</h2>
<p className="text-xs text-ink-faint">23,324 items</p>

View File

@@ -12,8 +12,12 @@ export default ({ mini, ...props }: PropsWithChildren<Props>) => {
return (
<div className="flex flex-row">
<div className={clsx('flex w-full flex-col', !mini && 'pb-6', props.className)}>
<h3 className="mb-1 text-sm font-medium text-gray-700 dark:text-gray-100">{props.title}</h3>
{!!props.description && <p className="mb-2 text-sm text-gray-400 ">{props.description}</p>}
<h3 className="mb-1 text-sm font-medium text-gray-700 dark:text-gray-100">
{props.title}
</h3>
{!!props.description && (
<p className="mb-2 text-sm text-gray-400 ">{props.description}</p>
)}
{!mini && props.children}
</div>
{mini && props.children}

View File

@@ -15,7 +15,10 @@ export const Component = () => {
return (
<>
<Heading title="General Settings" description="General settings related to this client." />
<Heading
title="General Settings"
description="General settings related to this client."
/>
<Card className="px-5">
<div className="my-2 flex w-full flex-col">
<div className="flex flex-row items-center justify-between">
@@ -50,7 +53,9 @@ export const Component = () => {
</div>
<div className="mt-5 flex items-center space-x-3">
<Switch size="sm" checked />
<span className="text-sm font-medium text-ink-dull">Run daemon when app closed</span>
<span className="text-sm font-medium text-ink-dull">
Run daemon when app closed
</span>
</div>
<div className="mt-3">
<div

View File

@@ -73,7 +73,9 @@ export default (props: UseDialogProps) => {
className="mt-3 mb-2"
right={
<Button
onClick={() => setShow((old) => ({ ...old, masterPassword: !old.masterPassword }))}
onClick={() =>
setShow((old) => ({ ...old, masterPassword: !old.masterPassword }))
}
size="icon"
type="button"
>

View File

@@ -77,7 +77,9 @@ export default (props: UseDialogProps) => {
setKey(e);
}}
>
{keys.data && <KeyListSelectOptions keys={keys.data.map((key) => key.uuid)} />}
{keys.data && (
<KeyListSelectOptions keys={keys.data.map((key) => key.uuid)} />
)}
</Select>
</div>
</div>
@@ -96,13 +98,24 @@ export default (props: UseDialogProps) => {
</div>
<div className="flex flex-col">
<span className="text-xs font-bold">Hashing</span>
<Select className="mt-2 text-gray-300" value={hashingAlgo} disabled onChange={() => {}}>
<Select
className="mt-2 text-gray-300"
value={hashingAlgo}
disabled
onChange={() => {}}
>
<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>
<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>

View File

@@ -110,14 +110,18 @@ export default (props: UseDialogProps) => {
<Button
type="button"
onClick={() => {
navigator.clipboard.writeText(form.watch('masterPassword') as string);
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 }))}
onClick={() =>
setShow((old) => ({ ...old, masterPassword: !old.masterPassword }))
}
size="icon"
type="button"
>
@@ -134,7 +138,9 @@ export default (props: UseDialogProps) => {
{...form.register('masterPassword2', { required: true })}
right={
<Button
onClick={() => setShow((old) => ({ ...old, masterPassword2: !old.masterPassword2 }))}
onClick={() =>
setShow((old) => ({ ...old, masterPassword2: !old.masterPassword2 }))
}
size="icon"
type="button"
>
@@ -167,9 +173,15 @@ export default (props: UseDialogProps) => {
<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>
<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>

View File

@@ -130,7 +130,10 @@ export const Component = () => {
if (masterPassword !== '') {
setMasterPassword('');
setSecretKey('');
unlockKeyManager.mutate({ password: masterPassword, secret_key: secretKey });
unlockKeyManager.mutate({
password: masterPassword,
secret_key: secretKey
});
}
}}
>
@@ -188,7 +191,11 @@ export const Component = () => {
<Subheading title="Secret key" />
{!viewSecretKey && (
<div className="flex flex-row">
<Button size="sm" variant="gray" onClick={() => setViewSecretKey(true)}>
<Button
size="sm"
variant="gray"
onClick={() => setViewSecretKey(true)}
>
View Secret Key
</Button>
</div>
@@ -215,7 +222,9 @@ export const Component = () => {
size="sm"
variant="gray"
className="mr-2"
onClick={() => dialogManager.create((dp) => <MasterPasswordDialog {...dp} />)}
onClick={() =>
dialogManager.create((dp) => <MasterPasswordDialog {...dp} />)
}
>
Change Master Password
</Button>
@@ -257,7 +266,9 @@ export const Component = () => {
size="sm"
variant="gray"
className="mr-2"
onClick={() => dialogManager.create((dp) => <BackupRestoreDialog {...dp} />)}
onClick={() =>
dialogManager.create((dp) => <BackupRestoreDialog {...dp} />)
}
>
Restore
</Button>

View File

@@ -122,8 +122,8 @@ export const Component = () => {
<FlexCol>
<Input label="Display Name" {...form.register('name')} />
<InfoText>
The name of this Location, this is what will be displayed in the sidebar. Will not
rename the actual folder on disk.
The name of this Location, this is what will be displayed in the
sidebar. Will not rename the actual folder on disk.
</InfoText>
</FlexCol>
<FlexCol>
@@ -134,7 +134,8 @@ export const Component = () => {
{...form.register('path')}
/>
<InfoText>
The path to this Location, this is where the files will be stored on disk.
The path to this Location, this is where the files will be stored on
disk.
</InfoText>
</FlexCol>
</div>
@@ -145,7 +146,9 @@ export const Component = () => {
<Switch {...form.register('generatePreviewMedia')} size="sm" />
</ToggleSection>
<ToggleSection>
<Label className="grow">Sync preview media for this Location with your devices</Label>
<Label className="grow">
Sync preview media for this Location with your devices
</Label>
<Switch {...form.register('syncPreviewMedia')} size="sm" />
</ToggleSection>
<ToggleSection>
@@ -174,7 +177,11 @@ export const Component = () => {
<div className="flex space-x-5">
<FlexCol>
<div>
<Button onClick={() => fullRescan.mutate(locationId)} size="sm" variant="outline">
<Button
onClick={() => fullRescan.mutate(locationId)}
size="sm"
variant="outline"
>
<ArrowsClockwise className="mr-1.5 -mt-0.5 inline h-4 w-4" />
Full Reindex
</Button>
@@ -194,12 +201,17 @@ export const Component = () => {
</Button>
</div>
<InfoText>
Extract data from Library as an archive, useful to preserve Location folder structure.
Extract data from Library as an archive, useful to preserve Location
folder structure.
</InfoText>
</FlexCol>
<FlexCol>
<div>
<Button size="sm" variant="colored" className="border-red-500 bg-red-500">
<Button
size="sm"
variant="colored"
className="border-red-500 bg-red-500"
>
<Trash className="mr-1.5 -mt-0.5 inline h-4 w-4" />
Delete
</Button>

View File

@@ -16,7 +16,9 @@ export const AddLocationButton = ({ className, ...props }: ButtonProps) => {
openDirectoryPickerDialog(platform)
.then((path) => {
if (path !== '')
dialogManager.create((dp) => <AddLocationDialog path={path ?? ''} {...dp} />);
dialogManager.create((dp) => (
<AddLocationDialog path={path ?? ''} {...dp} />
));
})
.catch((error) => showAlertDialog({ title: 'Error', value: String(error) }))
}

View File

@@ -85,7 +85,10 @@ export const AddLocationDialog = (props: Props) => {
// });
break;
case 'ADD_LIBRARY':
await addLocationToLibrary.mutateAsync({ path, indexer_rules_ids: indexerRulesIds });
await addLocationToLibrary.mutateAsync({
path,
indexer_rules_ids: indexerRulesIds
});
break;
default:
throw new Error('Unimplemented custom remote error handling');

Some files were not shown because too many files have changed in this diff Show More