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) => - )} + + + + + {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 - ) + ) }