From 3bd1622e9395c46d93f20fc93ce99ea87e172e40 Mon Sep 17 00:00:00 2001
From: Arnab Chakraborty <11457760+Rocky43007@users.noreply.github.com>
Date: Wed, 6 Mar 2024 01:46:22 -0500
Subject: [PATCH] [MOB-23] Mobile Hardware Information for Overview Page
(#2106)
* wip for iDevices
* Working HardwareModel Info for iOS
* wip
* Merge 'main' into 'mob-hw-info-overview'
* Half-Working `get_volume()`
* Objective c bridge to talk to FS
* Working objc bridge
The bridge works now, and we can now access the iOS file system using the native objective-c APIs instead for proper values, including on the simulator.
* Isolate `icrate` for `ios` deployments only
* Working Stats for Android
* Clean Up + `pnpm format`
* Fix to FSInfoResult Type
Due to the RNFS fork change, I had to change the types to make it so it doesn't fail building and CI.
* iOS Device Name Fix
---
Cargo.lock | 53 +++++++
apps/landing/contentlayer.config.ts | 2 +-
apps/landing/src/app/Downloads/Platform.tsx | 2 +-
apps/mobile/modules/sd-core/ios/build-rust.sh | 8 +-
apps/mobile/package.json | 5 +-
.../src/components/browse/BrowseTags.tsx | 9 +-
apps/mobile/src/components/browse/Jobs.tsx | 4 +-
.../src/components/explorer/Explorer.tsx | 4 +-
apps/mobile/src/components/header/Header.tsx | 7 +-
.../src/components/overview/Devices.tsx | 109 ++++++++++-----
.../src/components/overview/OverviewStats.tsx | 7 +-
.../src/components/settings/SettingsItem.tsx | 4 +-
apps/mobile/src/navigation/TabNavigator.tsx | 130 +++++++++---------
apps/mobile/src/navigation/index.tsx | 2 +-
apps/mobile/src/screens/Location.tsx | 4 +-
apps/mobile/src/screens/settings/Settings.tsx | 2 +-
.../src/screens/settings/info/About.tsx | 9 +-
.../screens/settings/library/TagsSettings.tsx | 5 +-
apps/web/src/ScreenshotWrapper.tsx | 2 +-
core/Cargo.toml | 4 +
core/src/lib.rs | 12 +-
core/src/node/hardware.rs | 81 ++++++++++-
core/src/volume/mod.rs | 61 +++++++-
interface/ErrorFallback.tsx | 2 +-
.../ContextMenu/AssignTagMenuItems.tsx | 26 ++--
.../Explorer/ContextMenu/FilePath/Items.tsx | 4 +-
.../$libraryId/Explorer/FilePath/Thumb.tsx | 2 +-
.../Explorer/QuickPreview/index.tsx | 4 +-
.../Explorer/View/ListView/Item.tsx | 2 +-
.../Explorer/View/ListView/index.tsx | 9 +-
.../Explorer/View/ListView/useRanges.tsx | 4 +-
interface/app/$libraryId/Explorer/store.ts | 6 +-
.../Explorer/useExplorerDroppable.tsx | 2 +-
.../Layout/Sidebar/sections/Local/index.tsx | 8 +-
interface/app/$libraryId/Spacedrop/index.tsx | 50 ++++---
.../app/$libraryId/TopBar/TopBarOptions.tsx | 10 +-
interface/app/$libraryId/node/$id.tsx | 2 +-
.../app/$libraryId/search/AppliedFilters.tsx | 2 +-
interface/app/$libraryId/search/Filters.tsx | 2 +-
.../$libraryId/settings/client/general.tsx | 2 +-
.../locations/IndexerRuleEditor/RuleInput.tsx | 8 +-
.../locations/IndexerRuleEditor/index.tsx | 2 +-
.../settings/library/locations/index.tsx | 4 +-
interface/app/style.scss | 1 -
interface/hooks/useKeybind.ts | 4 +-
interface/hooks/useZodSearchParams.ts | 4 +-
packages/assets/util/index.ts | 14 +-
packages/client/src/cache.tsx | 8 +-
packages/client/src/core.ts | 2 +-
packages/client/src/hooks/usePlausible.tsx | 2 +-
packages/client/src/solid/solid.solid.tsx | 2 +-
packages/client/src/stores/featureFlags.tsx | 2 +-
packages/client/src/utils/index.ts | 13 +-
packages/client/src/utils/jobs/useJobInfo.tsx | 14 +-
packages/ui/src/Dialog.tsx | 4 +-
packages/ui/src/Input.tsx | 2 +-
packages/ui/src/ProgressBar.tsx | 4 +-
pnpm-lock.yaml | 19 ++-
scripts/utils/which.mjs | 4 +-
59 files changed, 518 insertions(+), 258 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index b2a983903..126cdd1ef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1181,6 +1181,25 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
+[[package]]
+name = "block-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "block2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f"
+dependencies = [
+ "block-sys",
+ "objc2",
+]
+
[[package]]
name = "brotli"
version = "3.4.0"
@@ -3693,6 +3712,16 @@ dependencies = [
"png",
]
+[[package]]
+name = "icrate"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e286f4b975ac6c054971a0600a9b76438b332edace54bff79c71c9d3adfc9772"
+dependencies = [
+ "block2",
+ "objc2",
+]
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -5541,6 +5570,28 @@ dependencies = [
"objc_id",
]
+[[package]]
+name = "objc-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459"
+
+[[package]]
+name = "objc2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a9c7f0d511a4ce26b078183179dca908171cfc69f88986fe36c5138e1834476"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ff06a6505cde0766484f38d8479ac8e6d31c66fbc2d5492f65ca8c091456379"
+
[[package]]
name = "objc_exception"
version = "0.1.2"
@@ -7712,9 +7763,11 @@ dependencies = [
"http-body",
"http-range",
"hyper",
+ "icrate",
"image",
"int-enum",
"itertools 0.12.0",
+ "libc",
"mini-moka",
"normpath",
"notify",
diff --git a/apps/landing/contentlayer.config.ts b/apps/landing/contentlayer.config.ts
index b7bb13f3e..3e34ab14f 100644
--- a/apps/landing/contentlayer.config.ts
+++ b/apps/landing/contentlayer.config.ts
@@ -98,7 +98,7 @@ export const Document = defineDocumentType(() => ({
.replace(/^.+?(\/)/, '')
.split('/')
.slice(-1)[0]
- )
+ )
},
section: {
type: 'string',
diff --git a/apps/landing/src/app/Downloads/Platform.tsx b/apps/landing/src/app/Downloads/Platform.tsx
index e7b5decc3..c1ec0c810 100644
--- a/apps/landing/src/app/Downloads/Platform.tsx
+++ b/apps/landing/src/app/Downloads/Platform.tsx
@@ -86,7 +86,7 @@ export function Platform({ platform, ...props }: ComponentProps<'a'> & PlatformP
href={`${BASE_DL_LINK}/${platform.os}/${links[0].arch}`}
{...props}
/>
- )
+ )
: (props: any) =>
: (props: any) =>
;
diff --git a/apps/mobile/modules/sd-core/ios/build-rust.sh b/apps/mobile/modules/sd-core/ios/build-rust.sh
index 32f8c9e32..a6a551065 100755
--- a/apps/mobile/modules/sd-core/ios/build-rust.sh
+++ b/apps/mobile/modules/sd-core/ios/build-rust.sh
@@ -27,10 +27,10 @@ TARGET_DIRECTORY="${__dirname}/../../../../../target"
mkdir -p "$TARGET_DIRECTORY"
TARGET_DIRECTORY="$(CDPATH='' cd -- "$TARGET_DIRECTORY" && pwd -P)"
-if [ "${CONFIGURATION:-}" != "Debug" ]; then
- CARGO_FLAGS=--release
- export CARGO_FLAGS
-fi
+# if [ "${CONFIGURATION:-}" != "Debug" ]; then
+# CARGO_FLAGS=--release
+# export CARGO_FLAGS
+# fi
# Required for CI and for everyone I guess?
export PATH="${CARGO_HOME:-"${HOME}/.cargo"}/bin:$PATH"
diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index fdc6e1a5f..426dd97a9 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -37,9 +37,9 @@
"class-variance-authority": "^0.7.0",
"dayjs": "^1.11.10",
"event-target-polyfill": "^0.0.3",
- "expo": "~50.0.6",
+ "expo": "~50.0.7",
"expo-av": "^13.10.5",
- "expo-blur": "^12.9.1",
+ "expo-blur": "^12.9.2",
"expo-build-properties": "~0.11.1",
"expo-linking": "~6.2.2",
"expo-media-library": "~15.9.1",
@@ -53,6 +53,7 @@
"react-hook-form": "^7.47.0",
"react-native": "0.73.4",
"react-native-circular-progress": "^1.3.9",
+ "react-native-device-info": "^10.13.1",
"react-native-document-picker": "^9.0.1",
"react-native-file-viewer": "^2.1.5",
"react-native-gesture-handler": "~2.14.1",
diff --git a/apps/mobile/src/components/browse/BrowseTags.tsx b/apps/mobile/src/components/browse/BrowseTags.tsx
index 125be6cac..0a33378ec 100644
--- a/apps/mobile/src/components/browse/BrowseTags.tsx
+++ b/apps/mobile/src/components/browse/BrowseTags.tsx
@@ -1,10 +1,10 @@
import { useNavigation } from '@react-navigation/native';
-import { Tag, useCache, useLibraryQuery, useNodes } from '@sd/client';
import { DotsThreeOutlineVertical, Eye, Pen, Plus, Trash } from 'phosphor-react-native';
import React, { useRef } from 'react';
import { Animated, Pressable, Text, View } from 'react-native';
import { FlatList, Swipeable } from 'react-native-gesture-handler';
import { ClassInput } from 'twrnc/dist/esm/types';
+import { Tag, useCache, useLibraryQuery, useNodes } from '@sd/client';
import { ModalRef } from '~/components/layout/Modal';
import { tw, twStyle } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
@@ -24,12 +24,7 @@ type TagItemProps = {
viewStyle?: 'grid' | 'list';
};
-export const TagItem = ({
- tag,
- onPress,
- tagStyle,
- viewStyle = 'grid'
-}: TagItemProps) => {
+export const TagItem = ({ tag, onPress, tagStyle, viewStyle = 'grid' }: TagItemProps) => {
const modalRef = useRef(null);
const renderTagView = () => (
diff --git a/apps/mobile/src/components/browse/Jobs.tsx b/apps/mobile/src/components/browse/Jobs.tsx
index afe3de4f6..5371099bc 100644
--- a/apps/mobile/src/components/browse/Jobs.tsx
+++ b/apps/mobile/src/components/browse/Jobs.tsx
@@ -41,8 +41,8 @@ const Job = ({ progress, message, error }: JobProps) => {
const progressColor = error
? tw.color('red-500')
: progress === 100
- ? tw.color('green-500')
- : tw.color('accent');
+ ? tw.color('green-500')
+ : tw.color('accent');
return (
{
item.type === 'NonIndexedPath'
? item.item.path
: item.type === 'SpacedropPeer'
- ? item.item.name
- : item.item.id.toString()
+ ? item.item.name
+ : item.item.id.toString()
}
renderItem={({ item }) => (
handlePress(item)}>
diff --git a/apps/mobile/src/components/header/Header.tsx b/apps/mobile/src/components/header/Header.tsx
index fc0ec444c..9b979aba4 100644
--- a/apps/mobile/src/components/header/Header.tsx
+++ b/apps/mobile/src/components/header/Header.tsx
@@ -48,10 +48,9 @@ export default function Header({
return (
diff --git a/apps/mobile/src/components/overview/Devices.tsx b/apps/mobile/src/components/overview/Devices.tsx
index 9c7c2dfb8..efdfe3e3d 100644
--- a/apps/mobile/src/components/overview/Devices.tsx
+++ b/apps/mobile/src/components/overview/Devices.tsx
@@ -1,7 +1,9 @@
+import * as RNFS from '@dr.pogodin/react-native-fs';
import { AlphaRSPCError } from '@oscartbeaumont-sd/rspc-client/v2';
import { UseQueryResult } from '@tanstack/react-query';
-import React from 'react';
-import { Text, View } from 'react-native';
+import React, { useEffect, useState } from 'react';
+import { Platform, Text, View } from 'react-native';
+import DeviceInfo from 'react-native-device-info';
import { ScrollView } from 'react-native-gesture-handler';
import { HardwareModel, NodeState, StatisticsResponse } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind';
@@ -23,45 +25,86 @@ export function hardwareModelToIcon(hardwareModel: HardwareModel) {
return 'Laptop';
case 'MacStudio':
return 'SilverBox';
+ case 'IPhone':
+ return 'Mobile';
+ case 'IPad':
+ return 'Tablet';
+ case 'Simulator':
+ return 'Drive';
+ case 'Android':
+ return 'Mobile';
default:
return 'Laptop';
}
}
const Devices = ({ node, stats }: Props) => {
+ // We don't need the totalSpaceEx and freeSpaceEx fields
+ const [sizeInfo, setSizeInfo] = useState>({ freeSpace: 0, totalSpace: 0 });
+ const [deviceName, setDeviceName] = useState("");
+
+ useEffect(() => {
+ const getFSInfo = async () => {
+ return await RNFS.getFSInfo();
+ };
+ getFSInfo().then((size) => {
+ setSizeInfo(size);
+ });
+ }, []);
+
+ const totalSpace =
+ Platform.OS === 'android'
+ ? sizeInfo.totalSpace.toString()
+ : stats.data?.statistics?.total_bytes_capacity || '0';
+ const freeSpace =
+ Platform.OS === 'android'
+ ? sizeInfo.freeSpace.toString()
+ : stats.data?.statistics?.total_bytes_free || '0';
+
+ useEffect(() => {
+ if (Platform.OS === 'android') {
+ DeviceInfo.getDeviceName().then((name) => {
+ setDeviceName(name);
+ });
+ } else if (node) {
+ setDeviceName(node.name);
+ }
+ }, [node]);
+
return (
-
-
-
-
- {node && (
-
- )}
- (
-
- )}
+
+
+
+
+ {node && (
+
-
-
-
-
+ )}
+ (
+
+ )}
+ />
+
+
+
+
);
};
diff --git a/apps/mobile/src/components/overview/OverviewStats.tsx b/apps/mobile/src/components/overview/OverviewStats.tsx
index c117f8fe4..f5a9266bb 100644
--- a/apps/mobile/src/components/overview/OverviewStats.tsx
+++ b/apps/mobile/src/components/overview/OverviewStats.tsx
@@ -2,7 +2,7 @@ import * as RNFS from '@dr.pogodin/react-native-fs';
import { AlphaRSPCError } from '@oscartbeaumont-sd/rspc-client/v2';
import { UseQueryResult } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
-import { Text, View } from 'react-native';
+import { Platform, Text, View } from 'react-native';
import { ClassInput } from 'twrnc/dist/esm/types';
import { byteSize, Statistics, StatisticsResponse, useLibraryContext } from '@sd/client';
import useCounter from '~/hooks/useCounter';
@@ -31,7 +31,7 @@ const StatItem = ({ title, bytes, isLoading, style }: StatItemProps) => {
return (
{
return await RNFS.getFSInfo();
};
getFSInfo().then((size) => {
+ console.log('size', size);
setSizeInfo(size);
});
}, []);
@@ -90,6 +91,8 @@ const OverviewStats = ({ stats }: Props) => {
bytes = BigInt(sizeInfo.freeSpace);
} else if (key === 'total_bytes_capacity') {
bytes = BigInt(sizeInfo.totalSpace);
+ } else if (key === 'total_bytes_used' && Platform.OS === 'android') {
+ bytes = BigInt(sizeInfo.totalSpace - sizeInfo.freeSpace);
}
return (
diff --git a/apps/mobile/src/navigation/TabNavigator.tsx b/apps/mobile/src/navigation/TabNavigator.tsx
index 7efba2af3..d033ce859 100644
--- a/apps/mobile/src/navigation/TabNavigator.tsx
+++ b/apps/mobile/src/navigation/TabNavigator.tsx
@@ -28,71 +28,71 @@ export default function TabNavigator() {
labelStyle: Style;
testID: string;
}[] = [
- {
- name: 'OverviewStack',
- component: OverviewStack,
- icon: (
-
- ),
- label: 'Overview',
- labelStyle: tw`text-[10px] font-semibold`,
- testID: 'overview-tab'
- },
- {
- name: 'NetworkStack',
- component: NetworkStack,
- icon: (
-
- ),
- label: 'Network',
- labelStyle: tw`text-[10px] font-semibold`,
- testID: 'network-tab'
- },
- {
- name: 'BrowseStack',
- component: BrowseStack,
- icon: (
-
- ),
- label: 'Browse',
- labelStyle: tw`text-[10px] font-semibold`,
- testID: 'browse-tab'
- },
- {
- name: 'SettingsStack',
- component: SettingsStack,
- icon: (
-
- ),
- label: 'Settings',
- labelStyle: tw`text-[10px] font-semibold`,
- testID: 'settings-tab'
- }
- ];
+ {
+ name: 'OverviewStack',
+ component: OverviewStack,
+ icon: (
+
+ ),
+ label: 'Overview',
+ labelStyle: tw`text-[10px] font-semibold`,
+ testID: 'overview-tab'
+ },
+ {
+ name: 'NetworkStack',
+ component: NetworkStack,
+ icon: (
+
+ ),
+ label: 'Network',
+ labelStyle: tw`text-[10px] font-semibold`,
+ testID: 'network-tab'
+ },
+ {
+ name: 'BrowseStack',
+ component: BrowseStack,
+ icon: (
+
+ ),
+ label: 'Browse',
+ labelStyle: tw`text-[10px] font-semibold`,
+ testID: 'browse-tab'
+ },
+ {
+ name: 'SettingsStack',
+ component: SettingsStack,
+ icon: (
+
+ ),
+ label: 'Settings',
+ labelStyle: tw`text-[10px] font-semibold`,
+ testID: 'settings-tab'
+ }
+ ];
return (
= Stac
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace ReactNavigation {
- interface RootParamList extends RootStackParamList { }
+ interface RootParamList extends RootStackParamList {}
}
}
diff --git a/apps/mobile/src/screens/Location.tsx b/apps/mobile/src/screens/Location.tsx
index 18e1d108b..e5687ac2d 100644
--- a/apps/mobile/src/screens/Location.tsx
+++ b/apps/mobile/src/screens/Location.tsx
@@ -25,7 +25,7 @@ export default function LocationScreen({ navigation, route }: BrowseStackScreenP
take: 100
}
]);
-
+
const pathsItemsReferences = useMemo(() => paths.data?.items ?? [], [paths.data]);
useNodes(paths.data?.nodes);
const pathsItems = useCache(pathsItemsReferences);
@@ -52,5 +52,5 @@ export default function LocationScreen({ navigation, route }: BrowseStackScreenP
getExplorerStore().path = path ?? '';
}, [id, path]);
- return
+ return ;
}
diff --git a/apps/mobile/src/screens/settings/Settings.tsx b/apps/mobile/src/screens/settings/Settings.tsx
index 1c100eab3..2579eafa8 100644
--- a/apps/mobile/src/screens/settings/Settings.tsx
+++ b/apps/mobile/src/screens/settings/Settings.tsx
@@ -118,7 +118,7 @@ const sections: (debugState: DebugState) => SectionType[] = (debugState) => [
title: 'Debug',
rounded: 'bottom'
}
- ] as const)
+ ] as const)
: [])
]
}
diff --git a/apps/mobile/src/screens/settings/info/About.tsx b/apps/mobile/src/screens/settings/info/About.tsx
index bdb7d14a1..4bf9dd403 100644
--- a/apps/mobile/src/screens/settings/info/About.tsx
+++ b/apps/mobile/src/screens/settings/info/About.tsx
@@ -22,10 +22,11 @@ const AboutScreen = () => {
Spacedrive{' '}
- {`for ${Platform.OS === 'android'
- ? Platform.OS[0]?.toUpperCase() + Platform.OS.slice(1)
- : Platform.OS[0] + Platform.OS.slice(1).toUpperCase()
- }`}
+ {`for ${
+ Platform.OS === 'android'
+ ? Platform.OS[0]?.toUpperCase() + Platform.OS.slice(1)
+ : Platform.OS[0] + Platform.OS.slice(1).toUpperCase()
+ }`}
The file manager from the future.
diff --git a/apps/mobile/src/screens/settings/library/TagsSettings.tsx b/apps/mobile/src/screens/settings/library/TagsSettings.tsx
index 3087022f7..04925fc8c 100644
--- a/apps/mobile/src/screens/settings/library/TagsSettings.tsx
+++ b/apps/mobile/src/screens/settings/library/TagsSettings.tsx
@@ -1,10 +1,7 @@
import Tags from '~/screens/Tags';
const TagsSettingsScreen = () => {
-
- return (
-
- );
+ return ;
};
export default TagsSettingsScreen;
diff --git a/apps/web/src/ScreenshotWrapper.tsx b/apps/web/src/ScreenshotWrapper.tsx
index f08e13bb3..a3e14f430 100644
--- a/apps/web/src/ScreenshotWrapper.tsx
+++ b/apps/web/src/ScreenshotWrapper.tsx
@@ -58,7 +58,7 @@ const ScreenshotWrapper = ({
margin: '0 auto',
position: 'relative',
overflow: 'hidden'
- }
+ }
: {}
}
>
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 581ec163f..a9d80fa7b 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -109,6 +109,7 @@ http-body = "0.4.5"
http-range = "0.1.5"
int-enum = "0.5.0"
itertools = "0.12.0"
+libc = "0.2.153"
mini-moka = "0.10.2"
notify = { git="https://github.com/notify-rs/notify.git", rev="c3929ed114fbb0bc7457a9a498260461596b00ca", default-features = false, features = [
"macos_fsevent",
@@ -141,6 +142,9 @@ features = ["vendored"]
[target.'cfg(target_os = "macos")'.dependencies]
plist = "1"
+[target.'cfg(target_os = "ios")'.dependencies]
+icrate = { version = "0.1.0", features = ["Foundation", "Foundation_NSFileManager", "Foundation_NSString", "Foundation_NSNumber"] }
+
[dev-dependencies]
tracing-test = "^0.2.4"
aovec = "1.1.0"
diff --git a/core/src/lib.rs b/core/src/lib.rs
index d312fa079..0c5f9f331 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -202,11 +202,13 @@ impl Node {
// Set a default if the user hasn't set an override
if std::env::var("RUST_LOG") == Err(std::env::VarError::NotPresent) {
- let level = if cfg!(debug_assertions) {
- "debug"
- } else {
- "info"
- };
+ // let level = if cfg!(debug_assertions) {
+ // "debug"
+ // } else {
+ // "info"
+ // };
+
+ let level = "debug";
// let level = "debug"; // Exists for now to debug the location manager
diff --git a/core/src/node/hardware.rs b/core/src/node/hardware.rs
index 7bc4f8705..179397d90 100644
--- a/core/src/node/hardware.rs
+++ b/core/src/node/hardware.rs
@@ -4,6 +4,7 @@ use std::str;
use serde::{Deserialize, Serialize};
use specta::Type;
use strum_macros::{Display, EnumIter};
+use sysinfo::{System, SystemExt};
#[repr(i32)]
#[derive(Debug, Clone, Display, Copy, EnumIter, Type, Serialize, Deserialize, Eq, PartialEq)]
@@ -19,6 +20,8 @@ pub enum HardwareModel {
IMacPro,
IPad,
IPhone,
+ Simulator,
+ Android,
}
impl HardwareModel {
@@ -62,7 +65,83 @@ pub fn get_hardware_model_name() -> Result {
))
}
}
- #[cfg(not(target_os = "macos"))]
+ #[cfg(target_os = "ios")]
+ {
+ use std::ffi::CString;
+ use std::ptr;
+
+ extern "C" {
+ fn sysctlbyname(
+ name: *const libc::c_char,
+ oldp: *mut libc::c_void,
+ oldlenp: *mut usize,
+ newp: *mut libc::c_void,
+ newlen: usize,
+ ) -> libc::c_int;
+ }
+
+ fn get_device_type() -> Option {
+ let mut size: usize = 0;
+ let name = CString::new("hw.machine").expect("CString::new failed");
+
+ // First, get the size of the buffer needed
+ unsafe {
+ sysctlbyname(
+ name.as_ptr(),
+ ptr::null_mut(),
+ &mut size,
+ ptr::null_mut(),
+ 0,
+ );
+ }
+
+ // Allocate a buffer with the correct size
+ let mut buffer: Vec = vec![0; size];
+
+ // Get the actual machine type
+ unsafe {
+ sysctlbyname(
+ name.as_ptr(),
+ buffer.as_mut_ptr() as *mut libc::c_void,
+ &mut size,
+ ptr::null_mut(),
+ 0,
+ );
+ }
+
+ // Convert the buffer to a String
+ let machine_type = String::from_utf8_lossy(&buffer).trim().to_string();
+
+ // Check if the device is an iPad or iPhone
+ if machine_type.starts_with("iPad") {
+ Some("iPad".to_string())
+ } else if machine_type.starts_with("iPhone") {
+ Some("iPhone".to_string())
+ } else if machine_type.starts_with("arm") {
+ Some("Simulator".to_string())
+ } else {
+ None
+ }
+ }
+
+ if let Some(device_type) = get_device_type() {
+ let hardware_model = HardwareModel::from_display_name(&device_type.as_str());
+
+ Ok(hardware_model)
+ } else {
+ Err(Error::new(
+ std::io::ErrorKind::Other,
+ "Failed to get hardware model name",
+ ))
+ }
+ }
+
+ #[cfg(target_os = "android")]
+ {
+ Ok(HardwareModel::Android)
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
{
Err(Error::new(
std::io::ErrorKind::Unsupported,
diff --git a/core/src/volume/mod.rs b/core/src/volume/mod.rs
index 08522b0e4..610502de9 100644
--- a/core/src/volume/mod.rs
+++ b/core/src/volume/mod.rs
@@ -1,21 +1,29 @@
// Adapted from: https://github.com/kimlimjustin/xplorer/blob/f4f3590d06783d64949766cc2975205a3b689a56/src-tauri/src/drives.rs
-
use sd_cache::Model;
use std::{
+ collections::HashMap,
fmt::Display,
hash::{Hash, Hasher},
+ os::unix::fs::MetadataExt,
path::PathBuf,
sync::OnceLock,
};
+#[cfg(target_os = "ios")]
+use icrate::{
+ objc2::runtime::{Class, Object},
+ objc2::{msg_send, sel},
+ Foundation::{self, ns_string, NSFileManager, NSFileSystemSize, NSNumber, NSString},
+};
+
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use specta::Type;
use sysinfo::{DiskExt, System, SystemExt};
use thiserror::Error;
use tokio::sync::Mutex;
-use tracing::error;
+use tracing::{error, info};
pub mod watcher;
@@ -224,6 +232,51 @@ pub async fn get_volumes() -> Vec {
volumes
}
+#[cfg(target_os = "ios")]
+pub async fn get_volumes() -> Vec {
+ let mut volumes: Vec = Vec::new();
+
+ unsafe {
+ let file_manager = NSFileManager::defaultManager();
+
+ let root_dir = NSString::from_str("/");
+
+ let root_dir_ref = root_dir.as_ref();
+
+ let attributes = file_manager
+ .attributesOfFileSystemForPath_error(root_dir_ref)
+ .unwrap();
+
+ let attributes_ref = attributes.as_ref();
+
+ // Total space
+ let key = NSString::from_str("NSFileSystemSize");
+ let key_ref = key.as_ref();
+
+ let t = attributes_ref.get(key_ref).unwrap();
+ let total_space: u64 = msg_send![t, unsignedLongLongValue];
+
+ // Used space
+ let key = NSString::from_str("NSFileSystemFreeSize");
+ let key_ref = key.as_ref();
+
+ let t = attributes_ref.get(key_ref).unwrap();
+ let free_space: u64 = msg_send![t, unsignedLongLongValue];
+
+ volumes.push(Volume {
+ name: "Root".to_string(),
+ disk_type: DiskType::SSD,
+ file_system: Some("APFS".to_string()),
+ mount_points: vec![PathBuf::from("/")],
+ total_capacity: total_space,
+ available_capacity: free_space,
+ is_root_filesystem: true,
+ });
+ }
+
+ volumes
+}
+
#[cfg(target_os = "macos")]
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
@@ -245,7 +298,9 @@ struct HDIUtilInfo {
images: Vec,
}
-#[cfg(not(target_os = "linux"))]
+// Android does not work via sysinfo and JNI is a pain to maintain. Therefore, we use React-Native-FS to get the volume data of the device.
+// We leave the function though to be built for Android because otherwise, the build will fail.
+#[cfg(not(any(target_os = "linux", target_os = "ios")))]
pub async fn get_volumes() -> Vec {
use futures::future;
use tokio::process::Command;
diff --git a/interface/ErrorFallback.tsx b/interface/ErrorFallback.tsx
index 359e9869e..4fd76d01b 100644
--- a/interface/ErrorFallback.tsx
+++ b/interface/ErrorFallback.tsx
@@ -136,7 +136,7 @@ export function ErrorPage({
? sendReportBtn()
: sentryBrowserLazy.then(({ captureException }) =>
captureException(message)
- )
+ )
}
>
Send report
diff --git a/interface/app/$libraryId/Explorer/ContextMenu/AssignTagMenuItems.tsx b/interface/app/$libraryId/Explorer/ContextMenu/AssignTagMenuItems.tsx
index 9d2005a0f..e3bd572d5 100644
--- a/interface/app/$libraryId/Explorer/ContextMenu/AssignTagMenuItems.tsx
+++ b/interface/app/$libraryId/Explorer/ContextMenu/AssignTagMenuItems.tsx
@@ -200,7 +200,7 @@ const Tags = ({ items, parentRef }: Props & { parentRef: RefObject {
+ items.flatMap((item) => {
if (
item.type === 'Object' ||
item.type === 'Path'
@@ -209,18 +209,24 @@ const Tags = ({ items, parentRef }: Props & { parentRef: RefObject((item) => {
- if (item.type === 'Object') {
- if (!objectsWithTag.has(item.item.id))
+ items.flatMap(
+ (item) => {
+ if (item.type === 'Object') {
+ if (
+ !objectsWithTag.has(
+ item.item.id
+ )
+ )
+ return [item];
+ } else if (item.type === 'Path') {
return [item];
- } else if (item.type === 'Path') {
- return [item];
- }
+ }
- return [];
- }),
+ return [];
+ }
+ ),
unassign
);
diff --git a/interface/app/$libraryId/Explorer/ContextMenu/FilePath/Items.tsx b/interface/app/$libraryId/Explorer/ContextMenu/FilePath/Items.tsx
index 8d89ac076..98a471216 100644
--- a/interface/app/$libraryId/Explorer/ContextMenu/FilePath/Items.tsx
+++ b/interface/app/$libraryId/Explorer/ContextMenu/FilePath/Items.tsx
@@ -41,13 +41,13 @@ export const Delete = new ConditionalItem({
locationId: selectedFilePaths[0].location_id,
rescan,
pathIds: selectedFilePaths.map((p) => p.id)
- }
+ }
: undefined;
const ephemeralArgs = isNonEmpty(selectedEphemeralPaths)
? {
paths: selectedEphemeralPaths.map((p) => p.path)
- }
+ }
: undefined;
const deleteKeybind = useKeysMatcher(['Meta', 'Backspace']);
diff --git a/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx b/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx
index 898b7e7b7..fb82e957b 100644
--- a/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx
+++ b/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx
@@ -152,7 +152,7 @@ export const FileThumb = forwardRef((props, ref) =
? [
'min-h-full min-w-full object-cover object-center',
_childClassName
- ]
+ ]
: className,
props.frame && !(itemData.kind === 'Video' && props.blackBars)
? frameClassName
diff --git a/interface/app/$libraryId/Explorer/QuickPreview/index.tsx b/interface/app/$libraryId/Explorer/QuickPreview/index.tsx
index 654cca4a1..6db4b813c 100644
--- a/interface/app/$libraryId/Explorer/QuickPreview/index.tsx
+++ b/interface/app/$libraryId/Explorer/QuickPreview/index.tsx
@@ -605,8 +605,8 @@ const RenameInput = ({ name, onRename }: RenameInputProps) => {
quickPreview.background
? 'border-white/[.12] bg-white/10 backdrop-blur-sm'
: isDark
- ? 'border-app-line bg-app-input'
- : 'border-black/[.075] bg-black/[.075]'
+ ? 'border-app-line bg-app-input'
+ : 'border-black/[.075] bg-black/[.075]'
)}
onKeyDown={handleKeyDown}
onFocus={() => highlightName()}
diff --git a/interface/app/$libraryId/Explorer/View/ListView/Item.tsx b/interface/app/$libraryId/Explorer/View/ListView/Item.tsx
index e7d0e2057..365763454 100644
--- a/interface/app/$libraryId/Explorer/View/ListView/Item.tsx
+++ b/interface/app/$libraryId/Explorer/View/ListView/Item.tsx
@@ -80,7 +80,7 @@ const InnerCell = memo(
: flexRender(props.cell.column.columnDef.cell, {
...props.cell.getContext(),
selected: props.selected
- })}
+ })}
);
}
diff --git a/interface/app/$libraryId/Explorer/View/ListView/index.tsx b/interface/app/$libraryId/Explorer/View/ListView/index.tsx
index 9e74374c2..b0eabf509 100644
--- a/interface/app/$libraryId/Explorer/View/ListView/index.tsx
+++ b/interface/app/$libraryId/Explorer/View/ListView/index.tsx
@@ -429,7 +429,7 @@ export const ListView = memo(() => {
range.direction
? keyDirection !== range.direction
: backRange?.direction &&
- (backRange.sorted.start.index === frontRange?.sorted.start.index ||
+ (backRange.sorted.start.index === frontRange?.sorted.start.index ||
backRange.sorted.end.index === frontRange?.sorted.end.index)
) {
explorer.removeSelectedItem(range.end.original);
@@ -797,11 +797,11 @@ export const ListView = memo(() => {
explorerSettings.order &&
(orderKey.startsWith('object.')
? orderKey.split('object.')[1] ===
- header.id
+ header.id
: orderKey === header.id)
? getOrderingDirection(
explorerSettings.order
- )
+ )
: null;
const cellContent = flexRender(
@@ -849,7 +849,8 @@ export const ListView = memo(() => {
)
? value.split(
'object.'
- )[1] === header.id
+ )[1] ===
+ header.id
: value ===
header.id;
}
diff --git a/interface/app/$libraryId/Explorer/View/ListView/useRanges.tsx b/interface/app/$libraryId/Explorer/View/ListView/useRanges.tsx
index 39615f17b..980f99b44 100644
--- a/interface/app/$libraryId/Explorer/View/ListView/useRanges.tsx
+++ b/interface/app/$libraryId/Explorer/View/ListView/useRanges.tsx
@@ -130,8 +130,8 @@ export const useRanges = ({ ranges, rows }: UseRangesProps) => {
options.direction === 'down'
? _ranges[targetRangeIndex + 1]
: options.direction === 'up'
- ? _ranges[targetRangeIndex - 1]
- : _ranges[targetRangeIndex + 1] || _ranges[targetRangeIndex - 1];
+ ? _ranges[targetRangeIndex - 1]
+ : _ranges[targetRangeIndex + 1] || _ranges[targetRangeIndex - 1];
if (!closestRange) return;
diff --git a/interface/app/$libraryId/Explorer/store.ts b/interface/app/$libraryId/Explorer/store.ts
index e8ae588c5..9eede0c84 100644
--- a/interface/app/$libraryId/Explorer/store.ts
+++ b/interface/app/$libraryId/Explorer/store.ts
@@ -27,9 +27,9 @@ export type OrderingKeys = T extends Ordering
[K in T['field']]: OrderingValue extends SortOrder
? K
: OrderingValue extends Ordering
- ? `${K}.${OrderingKeys>}`
- : never;
- }[T['field']]
+ ? `${K}.${OrderingKeys>}`
+ : never;
+ }[T['field']]
: never;
export function orderingKey(ordering: Ordering): OrderingKey {
diff --git a/interface/app/$libraryId/Explorer/useExplorerDroppable.tsx b/interface/app/$libraryId/Explorer/useExplorerDroppable.tsx
index 665eb4d82..283db3079 100644
--- a/interface/app/$libraryId/Explorer/useExplorerDroppable.tsx
+++ b/interface/app/$libraryId/Explorer/useExplorerDroppable.tsx
@@ -150,7 +150,7 @@ export const useExplorerDroppable = ({
z.ZodLiteral,
...z.ZodLiteral[]
]
- )
+ )
: z.literal(allowedType)
});
diff --git a/interface/app/$libraryId/Layout/Sidebar/sections/Local/index.tsx b/interface/app/$libraryId/Layout/Sidebar/sections/Local/index.tsx
index a8f0b7071..0651f412e 100644
--- a/interface/app/$libraryId/Layout/Sidebar/sections/Local/index.tsx
+++ b/interface/app/$libraryId/Layout/Sidebar/sections/Local/index.tsx
@@ -105,8 +105,8 @@ export default function LocalSection() {
item.mountPoint === '/'
? 'Root'
: item.index === 0
- ? item.volume.name
- : item.mountPoint;
+ ? item.volume.name
+ : item.mountPoint;
const toPath =
locationId !== undefined
@@ -128,8 +128,8 @@ export default function LocalSection() {
item.volume.file_system === 'exfat'
? 'SD'
: item.volume.name === 'Macintosh HD'
- ? 'HDD'
- : 'Drive'
+ ? 'HDD'
+ : 'Drive'
}
/>
{name}
diff --git a/interface/app/$libraryId/Spacedrop/index.tsx b/interface/app/$libraryId/Spacedrop/index.tsx
index 11bfe5b17..05a78811e 100644
--- a/interface/app/$libraryId/Spacedrop/index.tsx
+++ b/interface/app/$libraryId/Spacedrop/index.tsx
@@ -2,15 +2,21 @@ import { Planet } from '@phosphor-icons/react';
import clsx from 'clsx';
import { useEffect, useRef, useState } from 'react';
import { proxy } from 'valtio';
-import { HardwareModel, useBridgeMutation, useDiscoveredPeers, useP2PEvents, useSelector } from '@sd/client';
+import {
+ HardwareModel,
+ useBridgeMutation,
+ useDiscoveredPeers,
+ useP2PEvents,
+ useSelector
+} from '@sd/client';
import { toast } from '@sd/ui';
import { Icon } from '~/components';
import { useDropzone, useLocale, useOnDndLeave } from '~/hooks';
+import { hardwareModelToIcon } from '~/util/hardware';
import { usePlatform } from '~/util/Platform';
import { TOP_BAR_ICON_STYLE } from '../TopBar/TopBarOptions';
import { useIncomingSpacedropToast, useSpacedropProgressToast } from './toast';
-import { hardwareModelToIcon } from '~/util/hardware';
// TODO: This is super hacky so should probs be rewritten but for now it works.
const hackyState = proxy({
@@ -88,7 +94,7 @@ export function Spacedrop({ triggerClose }: { triggerClose: () => void }) {
const onDropped = (id: string, files: string[]) => {
if (doSpacedrop.isLoading) {
- toast.warning(t("spacedrop_already_progress"));
+ toast.warning(t('spacedrop_already_progress'));
return;
}
@@ -107,21 +113,25 @@ export function Spacedrop({ triggerClose }: { triggerClose: () => void }) {
Spacedrop
-
- {t("spacedrop_description")}
-
- {discoveredPeers.size === 0 &&
-
- {t("no_nodes_found")}
-
-
}
-
- {Array.from(discoveredPeers).map(([id, meta]) => (
-
- ))}
+
{t('spacedrop_description')}
+ {discoveredPeers.size === 0 && (
+
+
{t('no_nodes_found')}
+
+ )}
+
+ {Array.from(discoveredPeers).map(([id, meta]) => (
+
+ ))}
@@ -151,7 +161,9 @@ function Node({
ref={ref}
className={clsx(
'flex items-center justify-start gap-2 rounded-md border bg-app-darkBox px-3 py-2 font-medium text-ink',
- state === 'hovered' ? 'border-solid border-accent-deep' : 'border-dashed border-app-line'
+ state === 'hovered'
+ ? 'border-solid border-accent-deep'
+ : 'border-dashed border-app-line'
)}
onClick={() => {
if (!platform.openFilePickerDialog) {
diff --git a/interface/app/$libraryId/TopBar/TopBarOptions.tsx b/interface/app/$libraryId/TopBar/TopBarOptions.tsx
index d8786e8b4..b5a821d53 100644
--- a/interface/app/$libraryId/TopBar/TopBarOptions.tsx
+++ b/interface/app/$libraryId/TopBar/TopBarOptions.tsx
@@ -96,10 +96,10 @@ function ToolGroup({
const roundingCondition = individual
? 'both'
: index === 0
- ? 'left'
- : index === group.length - 1
- ? 'right'
- : 'none';
+ ? 'left'
+ : index === group.length - 1
+ ? 'right'
+ : 'none';
const popover = usePopover();
const os = useOperatingSystem();
@@ -130,7 +130,7 @@ function ToolGroup({
{typeof icon === 'function'
? icon({
triggerOpen: () => popover.setOpen(true)
- })
+ })
: icon}
diff --git a/interface/app/$libraryId/node/$id.tsx b/interface/app/$libraryId/node/$id.tsx
index 16f0ac771..db6317340 100644
--- a/interface/app/$libraryId/node/$id.tsx
+++ b/interface/app/$libraryId/node/$id.tsx
@@ -36,7 +36,7 @@ export const Component = () => {
? {
type: 'Node',
node: nodeState.data
- }
+ }
: undefined,
settings: explorerSettings,
showPathBar: false,
diff --git a/interface/app/$libraryId/search/AppliedFilters.tsx b/interface/app/$libraryId/search/AppliedFilters.tsx
index 5ac8ee071..502d1ef47 100644
--- a/interface/app/$libraryId/search/AppliedFilters.tsx
+++ b/interface/app/$libraryId/search/AppliedFilters.tsx
@@ -62,7 +62,7 @@ export const AppliedFilters = ({ allowRemove = true }: { allowRemove?: boolean }
return dyanmicFilters;
}
);
- }
+ }
: undefined
}
/>
diff --git a/interface/app/$libraryId/search/Filters.tsx b/interface/app/$libraryId/search/Filters.tsx
index a392355fc..4562903c8 100644
--- a/interface/app/$libraryId/search/Filters.tsx
+++ b/interface/app/$libraryId/search/Filters.tsx
@@ -138,7 +138,7 @@ const FilterOptionList = ({
{option.name}
);
- })}
+ })}
);
};
diff --git a/interface/app/$libraryId/settings/client/general.tsx b/interface/app/$libraryId/settings/client/general.tsx
index 9a57ca242..1f9752ccb 100644
--- a/interface/app/$libraryId/settings/client/general.tsx
+++ b/interface/app/$libraryId/settings/client/general.tsx
@@ -35,7 +35,7 @@ const LANGUAGE_OPTIONS = [
{ value: 'ru', label: 'Русский' },
{ value: 'zh-CN', label: '中文(简体)' },
{ value: 'zh-TW', label: '中文(繁體)' },
- { value: 'it', label: "Italiano"}
+ { value: 'it', label: 'Italiano' }
];
// Sort the languages by their label
diff --git a/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleInput.tsx b/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleInput.tsx
index 6bc15d6ed..6618e46f2 100644
--- a/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleInput.tsx
+++ b/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleInput.tsx
@@ -42,8 +42,8 @@ export const validateInput = (
const regex = isWeb
? null // Non web plataforms use the native file picker, so there is no need to validate
: os === 'windows'
- ? /^[^<>:"/|?*\u0000-\u0031]+$/
- : /^[^\0]+$/;
+ ? /^[^<>:"/|?*\u0000-\u0031]+$/
+ : /^[^\0]+$/;
return {
value: regex?.test(value) || false,
message: value ? 'Invalid path' : 'Value required'
@@ -116,8 +116,8 @@ export const RuleInput = memo(
(os === 'windows'
? 'C:\\Users\\john\\Downloads'
: os === 'macOS'
- ? '/Users/clara/Pictures'
- : '/home/emily/Documents') +
+ ? '/Users/clara/Pictures'
+ : '/home/emily/Documents') +
')'
}
onClick={async () => {
diff --git a/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/index.tsx b/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/index.tsx
index aca8b3980..534ab6cdb 100644
--- a/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/index.tsx
+++ b/interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/index.tsx
@@ -114,7 +114,7 @@ export default function IndexerRuleEditor({
setSelectedRule(
selectedRule === rule ? undefined : rule
);
- }
+ }
: undefined
}
className={clsx(
diff --git a/interface/app/$libraryId/settings/library/locations/index.tsx b/interface/app/$libraryId/settings/library/locations/index.tsx
index 9dfd124d7..1cd1b2a71 100644
--- a/interface/app/$libraryId/settings/library/locations/index.tsx
+++ b/interface/app/$libraryId/settings/library/locations/index.tsx
@@ -18,8 +18,8 @@ export const Component = () => {
const filteredLocations = useMemo(
() =>
- locations?.filter(
- (location) => location.name?.toLowerCase().includes(debouncedSearch.toLowerCase())
+ locations?.filter((location) =>
+ location.name?.toLowerCase().includes(debouncedSearch.toLowerCase())
) ?? [],
[debouncedSearch, locations]
);
diff --git a/interface/app/style.scss b/interface/app/style.scss
index 542fb954b..d6c19bf8f 100644
--- a/interface/app/style.scss
+++ b/interface/app/style.scss
@@ -399,4 +399,3 @@ body {
.wiggle {
animation: wiggle 200ms infinite;
}
-
diff --git a/interface/hooks/useKeybind.ts b/interface/hooks/useKeybind.ts
index 46396384e..8cac897e1 100644
--- a/interface/hooks/useKeybind.ts
+++ b/interface/hooks/useKeybind.ts
@@ -23,8 +23,8 @@ export const useKeybind = (
typeof options === 'object' && 'repeatable' in options
? options.repeatable
: typeof dependencies === 'object' && 'repeatable' in dependencies
- ? dependencies.repeatable
- : false;
+ ? dependencies.repeatable
+ : false;
return useHotkeys(
keyCombination,
diff --git a/interface/hooks/useZodSearchParams.ts b/interface/hooks/useZodSearchParams.ts
index 3e7402a12..f85a27bdc 100644
--- a/interface/hooks/useZodSearchParams.ts
+++ b/interface/hooks/useZodSearchParams.ts
@@ -106,8 +106,8 @@ function parseParams(o: any, schema: any, key: string, value: any) {
shape instanceof z.ZodObject
? shape.shape
: shape instanceof z.ZodEffects
- ? shape._def.schema
- : null;
+ ? shape._def.schema
+ : null;
if (shape === null) {
throw new Error(`Could not find shape for key ${key}`);
}
diff --git a/packages/assets/util/index.ts b/packages/assets/util/index.ts
index 6e214376b..492d61049 100644
--- a/packages/assets/util/index.ts
+++ b/packages/assets/util/index.ts
@@ -52,13 +52,13 @@ export const getIcon = (
(extension && extension in icons
? extension
: // 2. If in light mode, check if the specific kind in light exists.
- !isDark && lightKind in icons
- ? lightKind
- : // 3. Check if a general kind icon exists.
- kind in icons
- ? kind
- : // 4. Default to the document (or document light) icon.
- document) as keyof typeof icons
+ !isDark && lightKind in icons
+ ? lightKind
+ : // 3. Check if a general kind icon exists.
+ kind in icons
+ ? kind
+ : // 4. Default to the document (or document light) icon.
+ document) as keyof typeof icons
];
};
diff --git a/packages/client/src/cache.tsx b/packages/client/src/cache.tsx
index b95b71144..1c8885417 100644
--- a/packages/client/src/cache.tsx
+++ b/packages/client/src/cache.tsx
@@ -236,10 +236,10 @@ function specialMerge(copy: Record, original: unknown) {
export type UseCacheResult = T extends (infer A)[]
? UseCacheResult[]
: T extends object
- ? T extends { '__type': any; '__id': string; '#type': infer U }
- ? UseCacheResult
- : { [K in keyof T]: UseCacheResult }
- : { [K in keyof T]: UseCacheResult };
+ ? T extends { '__type': any; '__id': string; '#type': infer U }
+ ? UseCacheResult
+ : { [K in keyof T]: UseCacheResult }
+ : { [K in keyof T]: UseCacheResult };
export function useCache(data: T | undefined) {
const cache = useCacheContext();
diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts
index b6fb98572..896b0ae79 100644
--- a/packages/client/src/core.ts
+++ b/packages/client/src/core.ts
@@ -312,7 +312,7 @@ export type GenerateThumbsForLocationArgs = { id: number; path: string; regenera
export type GetAll = { backups: Backup[]; directory: string }
-export type HardwareModel = "Other" | "MacStudio" | "MacBookAir" | "MacBookPro" | "MacBook" | "MacMini" | "MacPro" | "IMac" | "IMacPro" | "IPad" | "IPhone"
+export type HardwareModel = "Other" | "MacStudio" | "MacBookAir" | "MacBookPro" | "MacBook" | "MacMini" | "MacPro" | "IMac" | "IMacPro" | "IPad" | "IPhone" | "Simulator" | "Android"
export type IdentifyUniqueFilesArgs = { id: number; path: string }
diff --git a/packages/client/src/hooks/usePlausible.tsx b/packages/client/src/hooks/usePlausible.tsx
index c434264de..ac7b6d0d9 100644
--- a/packages/client/src/hooks/usePlausible.tsx
+++ b/packages/client/src/hooks/usePlausible.tsx
@@ -213,7 +213,7 @@ const submitPlausibleEvent = async ({ event, debugState, ...props }: SubmitEvent
? () => {
const { callback: _, ...event } = fullEvent;
console.log(event);
- }
+ }
: undefined
};
diff --git a/packages/client/src/solid/solid.solid.tsx b/packages/client/src/solid/solid.solid.tsx
index 4e0f385ca..20903b5ec 100644
--- a/packages/client/src/solid/solid.solid.tsx
+++ b/packages/client/src/solid/solid.solid.tsx
@@ -24,7 +24,7 @@ import { useObserverWithOwner } from './useObserver';
type AllowReactiveScope = T extends object
? {
[P in keyof T]: AllowReactiveScope;
- }
+ }
: T | (() => T);
type Props =
diff --git a/packages/client/src/stores/featureFlags.tsx b/packages/client/src/stores/featureFlags.tsx
index 488142bef..e35952725 100644
--- a/packages/client/src/stores/featureFlags.tsx
+++ b/packages/client/src/stores/featureFlags.tsx
@@ -77,7 +77,7 @@ export function toggleFeatureFlag(flags: FeatureFlag | FeatureFlag[]) {
? true
: await confirm(
'This feature will render your database broken and it WILL need to be reset! Use at your own risk!'
- );
+ );
if (result) {
nonLibraryClient.mutation(['toggleFeatureFlag', f as any]);
diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts
index 7420e22e8..9dcffc64a 100644
--- a/packages/client/src/utils/index.ts
+++ b/packages/client/src/utils/index.ts
@@ -83,8 +83,8 @@ export function getIndexedItemFilePath(data: ExplorerItem) {
return data.type === 'Path'
? data.item
: data.type === 'Object'
- ? data.item.file_paths[0] ?? null
- : null;
+ ? data.item.file_paths[0] ?? null
+ : null;
}
export function getItemLocation(data: ExplorerItem) {
@@ -118,11 +118,10 @@ export type UnionToIntersection = (U extends never ? never : (arg: U) => neve
? I
: never;
-export type UnionToTuple = UnionToIntersection T> extends (
- _: never
-) => infer W
- ? [...UnionToTuple>, W]
- : [];
+export type UnionToTuple =
+ UnionToIntersection T> extends (_: never) => infer W
+ ? [...UnionToTuple>, W]
+ : [];
export function formatNumber(n: number) {
if (!n) return '0';
diff --git a/packages/client/src/utils/jobs/useJobInfo.tsx b/packages/client/src/utils/jobs/useJobInfo.tsx
index 1ab0c8181..b60fde4af 100644
--- a/packages/client/src/utils/jobs/useJobInfo.tsx
+++ b/packages/client/src/utils/jobs/useJobInfo.tsx
@@ -51,11 +51,11 @@ export function useJobInfo(job: JobReport, realtimeUpdate: JobProgressEvent | nu
text: isPaused
? job.message
: isRunning && realtimeUpdate?.message
- ? realtimeUpdate.message
- : `${formatNumber(output?.total_paths)} ${plural(
- output?.total_paths,
- 'path'
- )} discovered`
+ ? realtimeUpdate.message
+ : `${formatNumber(output?.total_paths)} ${plural(
+ output?.total_paths,
+ 'path'
+ )} discovered`
}
]
]
@@ -127,7 +127,7 @@ export function useJobInfo(job: JobReport, realtimeUpdate: JobProgressEvent | nu
'thumb'
)}`
}
- ];
+ ];
}
}
};
@@ -169,7 +169,7 @@ export function useJobInfo(job: JobReport, realtimeUpdate: JobProgressEvent | nu
output?.total_objects_linked
)} ${plural(output?.total_objects_linked, 'Object')} linked`
}
- ]
+ ]
: [{ text: addCommasToNumbersInMessage(realtimeUpdate?.message) }]
]
};
diff --git a/packages/ui/src/Dialog.tsx b/packages/ui/src/Dialog.tsx
index 936c29ca9..2f39c0893 100644
--- a/packages/ui/src/Dialog.tsx
+++ b/packages/ui/src/Dialog.tsx
@@ -185,9 +185,9 @@ export function Dialog({
);
const disableCheck = props.errorMessageException
? !form.formState.isValid &&
- !form.formState.errors.root?.serverError?.message?.startsWith(
+ !form.formState.errors.root?.serverError?.message?.startsWith(
props.errorMessageException as string
- )
+ )
: !form.formState.isValid;
const submitButton = (
diff --git a/packages/ui/src/Input.tsx b/packages/ui/src/Input.tsx
index 036cfba09..25cb89234 100644
--- a/packages/ui/src/Input.tsx
+++ b/packages/ui/src/Input.tsx
@@ -81,7 +81,7 @@ export const Input = forwardRef(
: createElement(icon as Icon, {
size: 18,
className: 'text-gray-350'
- })}
+ })}
)}
diff --git a/packages/ui/src/ProgressBar.tsx b/packages/ui/src/ProgressBar.tsx
index 6f0d66835..e7889c1c9 100644
--- a/packages/ui/src/ProgressBar.tsx
+++ b/packages/ui/src/ProgressBar.tsx
@@ -20,8 +20,8 @@ export const ProgressBar = memo((props: ProgressBarProps) => {
const percentage = props.pending
? 0
: 'percent' in props
- ? props.percent
- : Math.round((props.value / props.total) * 100);
+ ? props.percent
+ : Math.round((props.value / props.total) * 100);
if (props.pending) {
return (
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 31b75d67e..23dd9db6a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -382,13 +382,13 @@ importers:
specifier: ^0.0.3
version: 0.0.3
expo:
- specifier: ~50.0.6
+ specifier: ~50.0.7
version: 50.0.7(@babel/core@7.24.0)(@react-native/babel-preset@0.73.21)
expo-av:
specifier: ^13.10.5
version: 13.10.5(expo@50.0.7)
expo-blur:
- specifier: ^12.9.1
+ specifier: ^12.9.2
version: 12.9.2(expo@50.0.7)
expo-build-properties:
specifier: ~0.11.1
@@ -429,6 +429,9 @@ importers:
react-native-circular-progress:
specifier: ^1.3.9
version: 1.3.9(react-native-svg@14.1.0)(react-native@0.73.4)(react@18.2.0)
+ react-native-device-info:
+ specifier: ^10.13.1
+ version: 10.13.1(react-native@0.73.4)
react-native-document-picker:
specifier: ^9.0.1
version: 9.1.1(react-native-windows@0.73.8)(react-native@0.73.4)(react@18.2.0)
@@ -4493,7 +4496,7 @@ packages:
resolution: {integrity: sha512-bOhuFnlRaS7CU33+rFFIWdcET/Vkyn1vsN8BYFwCDEF5P1fVVvYN7bFOsQLTMD3nvi35C1AGmtqUr/Wfv8Xaow==}
engines: {node: '>=12'}
dependencies:
- '@expo/spawn-async': 1.5.0
+ '@expo/spawn-async': 1.7.2
exec-async: 2.2.0
dev: false
@@ -4501,7 +4504,7 @@ packages:
resolution: {integrity: sha512-LKdo/6y4W7llZ6ghsg1kdx2CeH/qR/c6QI/JI8oPUvppsZoeIYjSkdflce978fAMfR8IXoi0wt0jA2w0kWpwbg==}
dependencies:
'@expo/json-file': 8.3.0
- '@expo/spawn-async': 1.5.0
+ '@expo/spawn-async': 1.7.2
ansi-regex: 5.0.1
chalk: 4.1.2
find-up: 5.0.0
@@ -20414,6 +20417,14 @@ packages:
react-native-svg: 14.1.0(react-native@0.73.4)(react@18.2.0)
dev: false
+ /react-native-device-info@10.13.1(react-native@0.73.4):
+ resolution: {integrity: sha512-j/7Z+Yl9Cesjp8vKaVzbuJQKJSVs4ojXATt5WjwipZ0Ss0mBJjqtbc4x5dfZLmQ4y55VVa7c0v8KHca1iqY/TQ==}
+ peerDependencies:
+ react-native: '*'
+ dependencies:
+ react-native: 0.73.4(@babel/core@7.24.0)(@babel/preset-env@7.24.0)(react@18.2.0)
+ dev: false
+
/react-native-document-picker@9.1.1(react-native-windows@0.73.8)(react-native@0.73.4)(react@18.2.0):
resolution: {integrity: sha512-BW+7DbsILuFThlBm7NUFVUmKKf6Awkcf9R0q8wiCU2DlGGtAKQTt2iHpO5+Dn/7WMPB+rqNv3X1HsmJQ0t5R3g==}
peerDependencies:
diff --git a/scripts/utils/which.mjs b/scripts/utils/which.mjs
index 7fa316384..9df6191e5 100644
--- a/scripts/utils/which.mjs
+++ b/scripts/utils/which.mjs
@@ -34,8 +34,8 @@ export async function which(progName) {
Array.from(new Set(env.PATH?.split(':'))).map(dir =>
fs.access(path.join(dir, progName), fs.constants.X_OK)
)
- ).then(
+ ).then(
() => true,
() => false
- )
+ )
}