Mobile Actions & File Info Modal (#573)

* better drawer

* fix build?

* style changes

* fix subfolder navigation

* bigger icons and drawer style tweaks

* Disable Menu animation

* more subfolder navigation & title stuff

* move tag dialogs to modal

* cleanup

* Fix text input sizing

* Improve modal component

* Hide modal close button & fix position

* update packages

* style create tag / update tag modals.

* move dialogs to modal & new modal component

* move IsPath and isObject to client + small fixes

* fix FileModal

* move kind to client

* move everything to components + cleanup

* move format bytes, info pills, work on fileinfo and actions modal, update packages, rename xcode build step

* Update Device.tsx

remove placeholder data

* small fixes

* actions modal header and some styling

* fav button

* actions item, divider and container components

* file actions

* file info modal

* remove bg-green from Inspector

* Add more info to fileinfo modal

* Delete unused Device component

* fix merge stuff

* linting + changed how we export tw + rename tw.style to twStyle
This commit is contained in:
Utku 2023-02-20 08:24:59 +03:00 committed by GitHub
parent fd39dc3a3d
commit e9e8d2286c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
98 changed files with 1438 additions and 1049 deletions

View file

@ -19,6 +19,7 @@ fontsource
fontttf
fontwoff
gopackage
gorhom
haml
headlessui
heroicons
@ -74,11 +75,13 @@ titlebar
tmpl
tryghost
tsparticles
twrnc
typescriptdef
unlisten
Waitlist
webp
windi
wunsub
xcworkspace
yarnerror
zustand

View file

@ -52,6 +52,7 @@ specta
storedkey
stringly
tobiaslutke
typecheck
vdfs
vijay
zacharysmith

View file

@ -1,6 +0,0 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View file

@ -14,12 +14,9 @@
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": false,
"bundleIdentifier": "com.spacedrive.app"
},
"android": {
"package": "com.spacedrive.app"
"supportsTablet": false
},
"android": {},
"privacy": "hidden",
"extra": {
"eas": {

View file

@ -1,9 +1,19 @@
{
"cli": {
"version": ">= 0.56.0"
"version": ">= 0.56.0",
"promptToConfigurePushNotifications": false
},
"build": {
"production": {
"node": "18.12.1"
},
"preview": {
"extends": "production",
"distribution": "internal"
},
"development": {
"extends": "production",
"developmentClient": true,
"distribution": "internal",
"android": {
"gradleCommand": ":app:assembleDebug"
@ -11,11 +21,7 @@
"ios": {
"buildConfiguration": "Debug"
}
},
"preview": {
"distribution": "internal"
},
"production": {}
}
},
"submit": {
"production": {}

View file

@ -1,7 +1,4 @@
import { registerRootComponent } from 'expo';
import { AppWrapper } from './src/main';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(AppWrapper);

View file

@ -20,21 +20,23 @@ target 'Spacedrive' do
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => flags[:hermes_enabled] || podfile_properties['expo.jsEngine'] == 'hermes',
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes',
:fabric_enabled => flags[:fabric_enabled],
# An absolute path to your application root.
:app_path => "#{Dir.pwd}/.."
:app_path => "#{Pod::Config.instance.installation_root}/..",
#
# Uncomment to opt-in to using Flipper
# Note that if you have use_frameworks! enabled, Flipper will not work
# :flipper_configuration => !ENV['CI'] ? FlipperConfiguration.enabled : FlipperConfiguration.disabled,
)
# Uncomment to opt-in to using Flipper
# Note that if you have use_frameworks! enabled, Flipper will not work
#
# if !ENV['CI']
# use_flipper!()
# end
post_install do |installer|
react_native_post_install(installer)
react_native_post_install(
installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
:mac_catalyst_enabled => false
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
# This is necessary for Xcode 14, because it signs resource bundles by default
@ -56,5 +58,4 @@ target 'Spacedrive' do
Pod::UI.warn e
end
end
end
end

View file

@ -266,7 +266,7 @@ PODS:
- glog
- react-native-document-picker (8.1.3):
- React-Core
- react-native-safe-area-context (4.4.1):
- react-native-safe-area-context (4.5.0):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
@ -342,13 +342,13 @@ PODS:
- React-Core
- RNCMaskedView (0.2.8):
- React-Core
- RNFlashList (1.4.0):
- RNFlashList (1.4.1):
- React-Core
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.8.0):
- RNGestureHandler (2.9.0):
- React-Core
- RNReanimated (2.13.0):
- RNReanimated (2.14.4):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
@ -375,10 +375,10 @@ PODS:
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (3.18.2):
- RNScreens (3.19.0):
- React-Core
- React-RCTImage
- RNSVG (13.6.0):
- RNSVG (13.8.0):
- React-Core
- Yoga (1.14.0)
@ -592,7 +592,7 @@ SPEC CHECKSUMS:
React-jsinspector: badd81696361249893a80477983e697aab3c1a34
React-logger: fdda34dd285bdb0232e059b19d9606fa0ec3bb9c
react-native-document-picker: 958e2bc82e128be69055be261aeac8d872c8d34c
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
React-perflogger: e68d3795cf5d247a0379735cbac7309adf2fb931
React-RCTActionSheet: 05452c3b281edb27850253db13ecd4c5a65bc247
React-RCTAnimation: 578eebac706428e68466118e84aeacf3a282b4da
@ -607,14 +607,14 @@ SPEC CHECKSUMS:
ReactCommon: c9246996e73bf75a2c6c3ff15f1e16707cdc2da9
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a
RNFlashList: 8ec7f7454721145fe84566bb9e88bcf58981c9fe
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
RNReanimated: ce445c233a6ff5600223484a88ad5704945d972a
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
RNSVG: 3a79c0c4992213e4f06c08e62730c5e7b9e4dc17
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
RNReanimated: 6668b0587bebd4b15dd849b99e5a9c70fc12ed95
RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb
RNSVG: c1e76b81c76cdcd34b4e1188852892dc280eb902
Yoga: eca980a5771bf114c41a754098cd85e6e0d90ed7
PODFILE CHECKSUM: 144e94249445b1054f55a9e9533c8e6e80450047
PODFILE CHECKSUM: 4065c8b949a453403939de6e852185dbd374d972
COCOAPODS: 1.11.3

View file

@ -10,9 +10,9 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
28422E8DCA8CF99CD27EE152 /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B51EE3BD2C384416ED4B40AC /* libPods-Spacedrive.a */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
5574975428A2496C00851D5A /* SDCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 5574975328A2496C00851D5A /* SDCore.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
B77FB5363D45EA26C44D71AF /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6BAE3F0EF420529C3ABCFEB /* libPods-Spacedrive.a */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
C95AE27BB525EFF3F02CEC11 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E71A6EFA9EA8F4C11F42FA /* ExpoModulesProvider.swift */; };
/* End PBXBuildFile section */
@ -25,16 +25,16 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
38392C44BCEE00E37027EE7F /* Pods-Spacedrive.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.debug.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.debug.xcconfig"; sourceTree = "<group>"; };
5574975328A2496C00851D5A /* SDCore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDCore.m; sourceTree = "<group>"; };
5574975528A2498000851D5A /* SDCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDCore.h; sourceTree = "<group>"; };
5574975628A24E0D00851D5A /* sdcore-universal-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "sdcore-universal-ios.a"; path = "../../../target/sdcore-universal-ios.a"; sourceTree = "<group>"; };
561FA870378F3C6C8C923B82 /* Pods-Spacedrive.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.debug.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.debug.xcconfig"; sourceTree = "<group>"; };
56E71A6EFA9EA8F4C11F42FA /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SplashScreen.storyboard; sourceTree = "<group>"; };
B51EE3BD2C384416ED4B40AC /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Expo.plist; path = Supporting/Expo.plist; sourceTree = "<group>"; };
D31F33B310E620CF1C91A764 /* Pods-Spacedrive.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.release.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F6BAE3F0EF420529C3ABCFEB /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; };
FEA54987FCCB6972B471EA9D /* Pods-Spacedrive.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.release.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -42,7 +42,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B77FB5363D45EA26C44D71AF /* libPods-Spacedrive.a in Frameworks */,
28422E8DCA8CF99CD27EE152 /* libPods-Spacedrive.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -79,7 +79,7 @@
children = (
5574975628A24E0D00851D5A /* sdcore-universal-ios.a */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
F6BAE3F0EF420529C3ABCFEB /* libPods-Spacedrive.a */,
B51EE3BD2C384416ED4B40AC /* libPods-Spacedrive.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -125,8 +125,8 @@
D65327D7A22EEC0BE12398D9 /* Pods */ = {
isa = PBXGroup;
children = (
561FA870378F3C6C8C923B82 /* Pods-Spacedrive.debug.xcconfig */,
FEA54987FCCB6972B471EA9D /* Pods-Spacedrive.release.xcconfig */,
38392C44BCEE00E37027EE7F /* Pods-Spacedrive.debug.xcconfig */,
D31F33B310E620CF1C91A764 /* Pods-Spacedrive.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -146,15 +146,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Spacedrive" */;
buildPhases = (
EE23FABC21723647AF9773BD /* [CP] Check Pods Manifest.lock */,
BC1F37E21575379C81F561DC /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
350DC403297BF2B8009CD6A1 /* Build sd-core-ios */,
350DC403297BF2B8009CD6A1 /* Build Spacedrive Core */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
F34AD9F4411557890BECFB5B /* [CP] Embed Pods Frameworks */,
37F7445AEFB5A9FBEDBB3916 /* [CP] Copy Pods Resources */,
65CB42BC732A49C59CCA544A /* [CP] Embed Pods Frameworks */,
FFF29AD1ADC998E2209017DB /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -224,7 +224,7 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\n`node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n";
};
350DC403297BF2B8009CD6A1 /* Build sd-core-ios */ = {
350DC403297BF2B8009CD6A1 /* Build Spacedrive Core */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
@ -234,7 +234,7 @@
);
inputPaths = (
);
name = "Build sd-core-ios";
name = "Build Spacedrive Core";
outputFileListPaths = (
);
outputPaths = (
@ -243,27 +243,25 @@
shellPath = /bin/zsh;
shellScript = "env -i CONFIGURATION=$CONFIGURATION PLATFORM_NAME=$PLATFORM_NAME ./build-rust.sh\n";
};
37F7445AEFB5A9FBEDBB3916 /* [CP] Copy Pods Resources */ = {
65CB42BC732A49C59CCA544A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
);
name = "[CP] Copy Pods Resources";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
EE23FABC21723647AF9773BD /* [CP] Check Pods Manifest.lock */ = {
BC1F37E21575379C81F561DC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -285,24 +283,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F34AD9F4411557890BECFB5B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -322,6 +302,26 @@
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > `node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/.packager.env'\"`\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open `node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/launchPackager.command'\"` || echo \"Can't start packager automatically\"\n fi\nfi\n";
showEnvVarsInLog = 0;
};
FFF29AD1ADC998E2209017DB /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -341,7 +341,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 561FA870378F3C6C8C923B82 /* Pods-Spacedrive.debug.xcconfig */;
baseConfigurationReference = 38392C44BCEE00E37027EE7F /* Pods-Spacedrive.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
@ -441,7 +441,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FEA54987FCCB6972B471EA9D /* Pods-Spacedrive.release.xcconfig */;
baseConfigurationReference = D31F33B310E620CF1C91A764 /* Pods-Spacedrive.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;

View file

@ -12,45 +12,46 @@
"android-studio": "open -a '/Applications/Android Studio.app' ./android",
"lint": "eslint src",
"postinstall": "node scripts/postinstall.js",
"typecheck": "tsc -b"
"typecheck": "tsc -b",
"eas-build-pre-install": "npm i -g pnpm@7.18.2"
},
"dependencies": {
"@gorhom/bottom-sheet": "^4.4.5",
"@react-native-async-storage/async-storage": "~1.17.11",
"@react-native-masked-view/masked-view": "0.2.8",
"@react-navigation/bottom-tabs": "^6.5.2",
"@react-navigation/drawer": "^6.5.6",
"@react-navigation/native": "^6.0.13",
"@react-navigation/stack": "^6.3.10",
"@react-navigation/bottom-tabs": "^6.5.4",
"@react-navigation/drawer": "^6.5.8",
"@react-navigation/native": "^6.1.3",
"@react-navigation/stack": "^6.3.12",
"@rspc/client": "^0.0.0-main-7c0a67c1",
"@rspc/react": "^0.0.0-main-7c0a67c1",
"@sd/assets": "workspace:*",
"@sd/client": "workspace:*",
"@shopify/flash-list": "1.4.0",
"@tanstack/react-query": "^4.20.9",
"@shopify/flash-list": "1.4.1",
"@tanstack/react-query": "^4.24.4",
"byte-size": "^8.1.0",
"class-variance-authority": "^0.4.0",
"dayjs": "^1.11.6",
"expo": "^47.0.10",
"dayjs": "^1.11.5",
"expo": "^47.0.13",
"expo-linking": "~3.3.0",
"expo-media-library": "~15.0.0",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2",
"intl": "^1.2.5",
"lottie-react-native": "5.1.4",
"moti": "^0.21.0",
"moti": "^0.22.0",
"phosphor-react-native": "^1.1.2",
"react": "18.1.0",
"react-hook-form": "^7.41.5",
"react-hook-form": "^7.43.0",
"react-native": "0.70.5",
"react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.8.0",
"react-native-gesture-handler": "~2.9.0",
"react-native-popup-menu": "^0.16.1",
"react-native-reanimated": "~2.13.0",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.2",
"react-native-svg": "13.6.0",
"react-native-reanimated": "~2.14.4",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.19.0",
"react-native-svg": "13.8.0",
"react-native-wheel-color-picker": "^1.2.0",
"twrnc": "^3.5.0",
"use-count-up": "^3.0.1",
@ -58,13 +59,13 @@
"valtio": "^1.8.0"
},
"devDependencies": {
"@rnx-kit/metro-config": "^1.3.2",
"@rnx-kit/metro-config": "^1.3.5",
"@sd/config": "workspace:*",
"@types/react": "~18.0.26",
"@types/react-native": "~0.70.8",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-module-resolver": "^5.0.0",
"eslint-plugin-react-native": "^4.0.0",
"metro-minify-terser": "^0.73.6",
"metro-minify-terser": "^0.74.1",
"react-native-svg-transformer": "^1.0.0",
"typescript": "^4.9.4"
}

View file

@ -1,6 +1,10 @@
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { DefaultTheme, NavigationContainer, Theme } from '@react-navigation/native';
import { loggerLink } from '@rspc/client';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
import { useEffect } from 'react';
@ -16,12 +20,16 @@ import {
useCurrentLibrary,
useInvalidateQuery
} from '@sd/client';
import { GlobalModals } from './containers/modal/GlobalModals';
import { GlobalModals } from './components/modal/GlobalModals';
import { reactNativeLink } from './lib/rspcReactNativeTransport';
import tw from './lib/tailwind';
import { tw } from './lib/tailwind';
import RootNavigator from './navigation';
import OnboardingNavigator from './navigation/OnboardingNavigator';
dayjs.extend(advancedFormat);
dayjs.extend(relativeTime);
dayjs.extend(duration);
const NavigatorTheme: Theme = {
...DefaultTheme,
colors: {
@ -45,9 +53,15 @@ function AppContainer() {
<BottomSheetModalProvider>
<StatusBar style="light" />
<NavigationContainer theme={NavigatorTheme}>
{!library ? <OnboardingNavigator /> : <RootNavigator />}
{!library ? (
<OnboardingNavigator />
) : (
<>
<RootNavigator />
<GlobalModals />
</>
)}
</NavigationContainer>
<GlobalModals />
</BottomSheetModalProvider>
</MenuProvider>
</GestureHandlerRootView>

View file

@ -3,7 +3,7 @@ import { PropsWithChildren, ReactNode } from 'react';
import { StyleSheet, View } from 'react-native';
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
import Layout from '~/constants/Layout';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
// Anything wrapped with FadeIn will fade in on mount.
export const FadeInAnimation = ({ children, delay }: PropsWithChildren<{ delay?: number }>) => (

View file

@ -1,143 +0,0 @@
import { Cloud, Desktop, DeviceMobileCamera, Laptop, Lock } from 'phosphor-react-native';
import { FlatList, Text, View } from 'react-native';
import { ExplorerItem } from '@sd/client';
import tw from '~/lib/tailwind';
import FileItem from '../explorer/FileItem';
const placeholderFileItems: ExplorerItem[] = [
{
type: 'Path',
item: {
date_created: '2020-01-01T00:00:00.000Z',
date_indexed: '2020-01-01T00:00:00.000Z',
date_modified: '2020-01-01T00:00:00.000Z',
extension: '',
cas_id: '3',
integrity_checksum: '',
id: 2,
name: 'Documents',
key_id: null,
is_dir: true,
location_id: 1,
materialized_path: '/Documents',
object_id: 5,
parent_id: 1,
object: {
extension: 'tsx',
id: 3,
pub_id: [3],
name: 'App.tsx',
key_id: null,
date_created: '2020-01-01T00:00:00.000Z',
date_indexed: '2020-01-01T00:00:00.000Z',
date_modified: '2020-01-01T00:00:00.000Z',
favorite: false,
has_thumbnail: false,
has_thumbstrip: false,
has_video_preview: false,
hidden: false,
important: false,
ipfs_id: '',
kind: 2,
note: '',
size_in_bytes: '0'
}
},
has_thumbnail: false
},
{
type: 'Object',
has_thumbnail: false,
item: {
date_created: '2020-01-01T00:00:00.000Z',
date_indexed: '2020-01-01T00:00:00.000Z',
date_modified: '2020-01-01T00:00:00.000Z',
extension: '',
id: 1,
pub_id: [1],
name: 'Minecraft',
key_id: null,
favorite: false,
file_paths: [],
has_thumbnail: false,
has_thumbstrip: false,
has_video_preview: false,
hidden: false,
important: false,
ipfs_id: '',
kind: 4,
note: '',
size_in_bytes: '0'
}
},
{
type: 'Object',
has_thumbnail: false,
item: {
date_created: '2020-01-01T00:00:00.000Z',
date_indexed: '2020-01-01T00:00:00.000Z',
date_modified: '2020-01-01T00:00:00.000Z',
extension: '',
id: 5,
pub_id: [5],
name: 'Minecraft',
key_id: null,
favorite: false,
file_paths: [],
has_thumbnail: false,
has_thumbstrip: false,
has_video_preview: false,
hidden: false,
important: false,
ipfs_id: '',
kind: 5,
note: '',
size_in_bytes: '0'
}
}
];
type DeviceProps = {
name: string;
size: string;
type: keyof typeof DeviceIcon;
locations: { name: string; folder?: boolean; format?: string; icon?: string }[];
runningJob?: { amount: number; task: string };
};
const DeviceIcon = {
phone: <DeviceMobileCamera color="white" weight="fill" size={18} style={tw`mr-2`} />,
laptop: <Laptop color="white" weight="fill" size={18} style={tw`mr-2`} />,
desktop: <Desktop color="white" weight="fill" size={18} style={tw`mr-2`} />,
server: <Cloud color="white" weight="fill" size={18} style={tw`mr-2`} />
};
const Device = ({ name, size, type }: DeviceProps) => {
return (
<View style={tw`bg-app-overlay border-app-line my-2 rounded-md border`}>
<View style={tw`flex flex-row items-center px-3.5 pt-3 pb-2`}>
<View style={tw`flex flex-row items-center`}>
{DeviceIcon[type]}
<Text style={tw`text-ink text-base font-semibold`}>{name || 'Unnamed Device'}</Text>
{/* P2P Lock */}
<View style={tw`bg-app-box ml-2 flex flex-row items-center rounded py-[1px] px-[4px]`}>
<Lock weight="bold" size={12} color={tw.color('ink-dull')} />
<Text style={tw`text-ink-dull ml-0.5 text-xs font-semibold`}>P2P</Text>
</View>
</View>
{/* Size */}
<Text style={tw`text-ink-dull ml-2 text-sm font-semibold`}>{size}</Text>
</View>
<FlatList
data={placeholderFileItems}
renderItem={({ item }) => <FileItem data={item} />}
keyExtractor={(item) => item.item.id.toString()}
horizontal
contentContainerStyle={tw`mt-3 mb-5`}
showsHorizontalScrollIndicator={false}
/>
</View>
);
};
export default Device;

View file

@ -9,6 +9,7 @@ type Props = {
children: React.ReactNode;
};
// TODO: Move to a Modal component
const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props) => {
const [libName, setLibName] = useState('');
const [isOpen, setIsOpen] = useState(false);

View file

@ -3,9 +3,8 @@ import { DrawerContentComponentProps } from '@react-navigation/drawer/lib/typesc
import { Gear } from 'phosphor-react-native';
import { Image, Platform, Pressable, Text, View } from 'react-native';
import Layout from '~/constants/Layout';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { getStackNameFromState } from '~/utils/nav';
import Divider from '../../components/primitive/Divider';
import DrawerLibraryManager from './DrawerLibraryManager';
import DrawerLocations from './DrawerLocations';
import DrawerTags from './DrawerTags';
@ -19,14 +18,14 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
const stackName = getStackNameFromState(state);
return (
<DrawerContentScrollView style={tw`flex-1 px-4 py-2`} scrollEnabled={false}>
<DrawerContentScrollView style={tw`flex-1 px-3 py-2`} scrollEnabled={false}>
<View style={twStyle('justify-between', { height: drawerHeight })}>
<View>
<View style={tw`flex flex-row items-center`}>
<Image source={require('@sd/assets/images/logo.png')} style={tw`h-[35px] w-[35px]`} />
<Text style={tw`text-ink ml-2 text-base font-bold`}>Spacedrive</Text>
<Image source={require('@sd/assets/images/logo.png')} style={tw`h-[40px] w-[40px]`} />
<Text style={tw`text-ink ml-2 text-lg font-bold`}>Spacedrive</Text>
</View>
<Divider style={tw`my-4`} />
<View style={tw`mt-6`} />
{/* Library Manager */}
<DrawerLibraryManager />
{/* Locations */}

View file

@ -1,14 +1,14 @@
import { useDrawerStatus } from '@react-navigation/drawer';
import { useNavigation } from '@react-navigation/native';
import { MotiView } from 'moti';
import { CaretRight, Gear, Lock, Plus } from 'phosphor-react-native';
import { CaretDown, Gear, Lock, Plus } from 'phosphor-react-native';
import { useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import { Alert, Pressable, Text, View } from 'react-native';
import { useCurrentLibrary } from '~/../../../packages/client/src';
import tw, { twStyle } from '~/lib/tailwind';
import { AnimatedHeight } from '../../components/animation/layout';
import Divider from '../../components/primitive/Divider';
import { tw, twStyle } from '~/lib/tailwind';
import { AnimatedHeight } from '../animation/layout';
import CreateLibraryDialog from '../dialog/CreateLibraryDialog';
import Divider from '../primitive/Divider';
const DrawerLibraryManager = () => {
const [dropdownClosed, setDropdownClosed] = useState(true);
@ -28,23 +28,26 @@ const DrawerLibraryManager = () => {
<Pressable onPress={() => setDropdownClosed((v) => !v)}>
<View
style={twStyle(
'border-app-darkLine bg-app-box flex h-10 w-full flex-row items-center justify-between border px-3 shadow-sm',
dropdownClosed ? 'rounded' : 'border-b-app-box rounded-t'
'bg-sidebar-box flex h-10 w-full flex-row items-center justify-between border px-3 shadow-sm',
dropdownClosed
? 'border-sidebar-line/50 rounded-md'
: 'border-b-app-box border-sidebar-line bg-sidebar-button rounded-t-md'
)}
>
<Text style={tw`text-ink text-sm font-semibold`}>{currentLibrary?.config.name}</Text>
<MotiView
animate={{
rotateZ: dropdownClosed ? '0deg' : '90deg'
rotate: dropdownClosed ? '0deg' : '180deg',
translateX: dropdownClosed ? 0 : -9
}}
transition={{ type: 'timing' }}
transition={{ type: 'timing', duration: 100 }}
>
<CaretRight color={tw.color('text-ink')} size={16} style={tw`ml-2`} />
<CaretDown color="white" size={18} weight="bold" style={tw`ml-2`} />
</MotiView>
</View>
</Pressable>
<AnimatedHeight hide={dropdownClosed}>
<View style={tw`border-app-darkLine bg-app-box rounded-b border-x border-b p-2`}>
<View style={tw`bg-sidebar-button border-sidebar-line rounded-b-md border-x border-b p-2`}>
{/* Libraries */}
{libraries?.map((library) => (
<Pressable key={library.uuid} onPress={() => switchLibrary(library.uuid)}>
@ -67,25 +70,27 @@ const DrawerLibraryManager = () => {
))}
<Divider style={tw`my-2`} />
{/* Menu */}
{/* Create Library */}
<CreateLibraryDialog>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Plus size={18} weight="bold" color="white" style={tw`mr-2`} />
<Text style={tw`text-sm font-semibold text-white`}>New Library</Text>
</View>
</CreateLibraryDialog>
{/* Manage Library */}
<Pressable
onPress={() => navigation.navigate('Settings', { screen: 'LibraryGeneralSettings' })}
>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Gear size={16} color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-ink text-sm font-semibold`}>Library Settings</Text>
<Gear size={18} weight="bold" color="white" style={tw`mr-2`} />
<Text style={tw`text-sm font-semibold text-white`}>Manage Library</Text>
</View>
</Pressable>
{/* Create Library */}
<CreateLibraryDialog>
{/* Lock */}
<Pressable onPress={() => Alert.alert('TODO')}>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Plus size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-ink text-sm font-semibold`}>Add Library</Text>
</View>
</CreateLibraryDialog>
<Pressable onPress={() => console.log('TODO: lock')}>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Lock size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-ink text-sm font-semibold`}>Lock</Text>
<Lock size={18} weight="bold" color="white" style={tw`mr-2`} />
<Text style={tw`text-sm font-semibold text-white`}>Lock</Text>
</View>
</Pressable>
</View>

View file

@ -1,12 +1,12 @@
import { BottomSheetModal } from '@gorhom/bottom-sheet';
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
import { useNavigation } from '@react-navigation/native';
import { useRef } from 'react';
import { Pressable, Text, View } from 'react-native';
import { useLibraryQuery } from '@sd/client';
import tw, { twStyle } from '~/lib/tailwind';
import FolderIcon from '../../components/icons/FolderIcon';
import CollapsibleView from '../../components/layout/CollapsibleView';
import { ModalRef } from '~/components/layout/Modal';
import { tw, twStyle } from '~/lib/tailwind';
import FolderIcon from '../icons/FolderIcon';
import CollapsibleView from '../layout/CollapsibleView';
import ImportModal from '../modal/ImportModal';
type DrawerLocationItemProps = {
@ -20,8 +20,8 @@ const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
return (
<Pressable onPress={onPress}>
<View style={twStyle('mb-[4px] flex flex-row items-center rounded py-2 px-1')}>
<FolderIcon size={18} />
<Text style={twStyle('ml-2 text-sm font-medium text-gray-300')} numberOfLines={1}>
<FolderIcon size={20} />
<Text style={twStyle('ml-1.5 font-medium text-gray-300')} numberOfLines={1}>
{folderName}
</Text>
</View>
@ -36,7 +36,7 @@ type DrawerLocationsProp = {
const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
const navigation = useNavigation<DrawerNavigationHelpers>();
const importModalRef = useRef<BottomSheetModal>();
const importModalRef = useRef<ModalRef>();
const { data: locations } = useLibraryQuery(['locations.list'], { keepPreviousData: true });
@ -45,7 +45,7 @@ const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
<CollapsibleView
title="Locations"
titleStyle={tw`text-sm font-semibold text-gray-300`}
containerStyle={tw`mt-6 mb-3`}
containerStyle={tw`mt-6 mb-3 ml-1`}
>
<View style={tw`mt-2`}>
{locations?.map((location) => (

View file

@ -1,10 +1,12 @@
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
import { useNavigation } from '@react-navigation/native';
import { useRef } from 'react';
import { ColorValue, Pressable, Text, View } from 'react-native';
import { useLibraryQuery } from '@sd/client';
import tw, { twStyle } from '~/lib/tailwind';
import CollapsibleView from '../../components/layout/CollapsibleView';
import CreateTagDialog from '../dialog/tag/CreateTagDialog';
import { ModalRef } from '~/components/layout/Modal';
import { tw, twStyle } from '~/lib/tailwind';
import CollapsibleView from '../layout/CollapsibleView';
import CreateTagModal from '../modal/tag/CreateTagModal';
type DrawerTagItemProps = {
tagName: string;
@ -17,7 +19,7 @@ const DrawerTagItem: React.FC<DrawerTagItemProps> = (props) => {
return (
<Pressable onPress={onPress}>
<View style={twStyle('mb-[4px] flex flex-row items-center rounded py-2 px-1')}>
<View style={twStyle('h-3 w-3 rounded-full', { backgroundColor: tagColor })} />
<View style={twStyle('h-3.5 w-3.5 rounded-full', { backgroundColor: tagColor })} />
<Text style={twStyle('ml-2 text-sm font-medium text-gray-300')} numberOfLines={1}>
{tagName}
</Text>
@ -35,11 +37,13 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
const { data: tags } = useLibraryQuery(['tags.list'], { keepPreviousData: true });
const createTagModalRef = useRef<ModalRef>();
return (
<CollapsibleView
title="Tags"
titleStyle={tw`text-sm font-semibold text-gray-300`}
containerStyle={tw`mt-6 mb-3`}
containerStyle={tw`mt-6 mb-3 ml-1`}
>
<View style={tw`mt-2`}>
{tags?.map((tag) => (
@ -57,11 +61,12 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
))}
</View>
{/* Add Tag */}
<CreateTagDialog>
<View style={tw`border-opacity/80 border-app-line mt-1 rounded border border-dashed`}>
<Pressable onPress={() => createTagModalRef.current.present()}>
<View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Tag</Text>
</View>
</CreateTagDialog>
</Pressable>
<CreateTagModal ref={createTagModalRef} />
</CollapsibleView>
);
};

View file

@ -1,16 +1,15 @@
import { FlashList } from '@shopify/flash-list';
import { useNavigation } from '@react-navigation/native';
import { Rows, SquaresFour } from 'phosphor-react-native';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { Pressable, View } from 'react-native';
import { ExplorerData, ExplorerItem } from '@sd/client';
import { ExplorerData, ExplorerItem, isPath } from '@sd/client';
import SortByMenu from '~/components/menu/SortByMenu';
import Layout from '~/constants/Layout';
import SortByMenu from '~/containers/menu/SortByMenu';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SharedScreenProps } from '~/navigation/SharedScreens';
import { getExplorerStore } from '~/stores/explorerStore';
import { useFileModalStore } from '~/stores/modalStore';
import { isPath } from '~/types/helper';
import { useActionsModalStore } from '~/stores/modalStore';
import FileItem from './FileItem';
import FileRow from './FileRow';
@ -29,21 +28,14 @@ const Explorer = ({ data }: ExplorerProps) => {
getExplorerStore().layoutMode = kind;
}
useEffect(() => {
// Set screen title to location name.
navigation.setOptions({
title: data?.context.name
});
}, [data, navigation]);
const { modalRef, setData } = useActionsModalStore();
const { fileRef, setData } = useFileModalStore();
function handlePress(item: ExplorerItem) {
if (isPath(item) && item.item.is_dir) {
navigation.navigate('Location', { id: item.item.location_id });
function handlePress(data: ExplorerItem) {
if (isPath(data) && data.item.is_dir) {
navigation.push('Location', { id: data.item.location_id, path: data.item.materialized_path });
} else {
setData(item);
fileRef.current.present();
setData(data);
modalRef.current.present();
}
}

View file

@ -1,9 +1,8 @@
import { Text, View } from 'react-native';
import { ExplorerItem, ObjectKind } from '@sd/client';
import { ExplorerItem, ObjectKind, isObject } from '@sd/client';
import Layout from '~/constants/Layout';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { getExplorerStore } from '~/stores/explorerStore';
import { isObject } from '~/types/helper';
import FileThumb from './FileThumb';
type FileItemProps = {

View file

@ -1,9 +1,8 @@
import React from 'react';
import { Text, View } from 'react-native';
import { ExplorerItem, ObjectKind } from '@sd/client';
import tw, { twStyle } from '~/lib/tailwind';
import { ExplorerItem, ObjectKind, isObject } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind';
import { getExplorerStore } from '~/stores/explorerStore';
import { isObject } from '~/types/helper';
import FileThumb from './FileThumb';
type FileRowProps = {

View file

@ -1,9 +1,8 @@
import { Image, View } from 'react-native';
import { DocumentDirectoryPath } from 'react-native-fs';
import { ExplorerItem } from '@sd/client';
import { isObject, isPath } from '~/types/helper';
import { ExplorerItem, isObject, isPath } from '@sd/client';
// import icons from '../../assets/icons/file';
import tw from '../../lib/tailwind';
import { tw } from '../../lib/tailwind';
import FolderIcon from '../icons/FolderIcon';
type FileThumbProps = {
@ -38,8 +37,8 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
</FileThumbWrapper>
);
const cas_id = isObject(data) ? data.item.file_paths[0].cas_id : data.item.cas_id;
if (!cas_id) return undefined;
const casId = isObject(data) ? data.item.file_paths[0]?.cas_id : data.item.cas_id;
if (!casId) return undefined;
// Icon
let icon = undefined;
@ -57,9 +56,8 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
);
}
const url = getThumbnailUrlById(cas_id);
const url = getThumbnailUrlById(casId);
// TODO: Not styled yet
if (data.has_thumbnail && url) {
return (
<FileThumbWrapper size={size}>

View file

@ -0,0 +1,34 @@
import { Heart } from 'phosphor-react-native';
import { useState } from 'react';
import { Pressable, PressableProps } from 'react-native';
import { Object as SDObject, queryClient, useLibraryMutation } from '@sd/client';
type Props = {
data: SDObject;
style: PressableProps['style'];
};
const FavoriteButton = (props: Props) => {
const [favorite, setFavorite] = useState(props.data.favorite);
const { mutate: toggleFavorite, isLoading } = useLibraryMutation('files.setFavorite', {
onSuccess: () => {
// TODO: Not sure why rust isn't invalidating these...
queryClient.invalidateQueries(['locations.getExplorerData']);
queryClient.invalidateQueries(['tags.getExplorerData']);
setFavorite(!favorite);
}
});
return (
<Pressable
disabled={isLoading}
onPress={() => toggleFavorite({ id: props.data.id, favorite: !favorite })}
style={props.style}
>
<Heart color="white" size={22} weight={favorite ? 'fill' : 'regular'} />
</Pressable>
);
};
export default FavoriteButton;

View file

@ -0,0 +1,48 @@
import React from 'react';
import { Alert, Pressable, View, ViewStyle } from 'react-native';
import { ExplorerItem, ObjectKind, isObject, isPath, useLibraryQuery } from '@sd/client';
import { InfoPill, PlaceholderPill } from '~/components/primitive/InfoPill';
import { tw, twStyle } from '~/lib/tailwind';
type Props = {
data: ExplorerItem;
style?: ViewStyle;
};
const InfoTagPills = ({ data, style }: Props) => {
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
const tagsQuery = useLibraryQuery(['tags.getForObject', objectData?.id], {
enabled: Boolean(objectData)
});
const isDir = data && isPath(data) ? data.item.is_dir : false;
const item = data?.item;
return (
<View style={twStyle('mt-1 flex flex-row flex-wrap', style)}>
{/* Kind */}
<InfoPill
containerStyle={tw`mr-1`}
text={isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]}
/>
{/* Extension */}
{item.extension && <InfoPill text={item.extension} containerStyle={tw`mr-1`} />}
{/* TODO: What happens if I have too many? */}
{tagsQuery.data?.map((tag) => (
<InfoPill
key={tag.id}
text={tag.name}
containerStyle={twStyle('mr-1', { backgroundColor: tag.color + 'CC' })}
textStyle={tw`text-white`}
/>
))}
<Pressable onPress={() => Alert.alert('TODO')}>
<PlaceholderPill text={'Add Tag'} />
</Pressable>
</View>
);
};
export default InfoTagPills;

View file

@ -0,0 +1,34 @@
import { useCallback, useState } from 'react';
import { Text, View } from 'react-native';
import { useDebouncedCallback } from 'use-debounce';
import { useLibraryMutation } from '@sd/client';
import { Object as SDObject } from '@sd/client';
type Props = {
data: SDObject;
};
const Note = (props: Props) => {
const [note, setNote] = useState(props.data.note || '');
const { mutate: fileSetNote } = useLibraryMutation('files.setNote');
const debounce = useDebouncedCallback(
(note: string) =>
fileSetNote({
id: props.data.id,
note
}),
2000
);
const debouncedNote = useCallback((note: string) => debounce(note), [props.data.id, fileSetNote]);
return (
<View>
<Text>Note</Text>
</View>
);
};
export default Note;

View file

@ -5,7 +5,7 @@ import { MotiView } from 'moti';
import { List } from 'phosphor-react-native';
import { Pressable, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
// Default header with search bar and button to open drawer
export default function Header() {

View file

@ -2,7 +2,7 @@ import { MotiView } from 'moti';
import { CaretRight } from 'phosphor-react-native';
import { PropsWithChildren, useReducer } from 'react';
import { Pressable, StyleProp, Text, TextStyle, View, ViewStyle } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { AnimatedHeight } from '../animation/layout';
type CollapsibleViewProps = PropsWithChildren<{
@ -26,9 +26,9 @@ const CollapsibleView = ({ title, titleStyle, containerStyle, children }: Collap
translateX: hide ? 0 : 5,
translateY: hide ? 0 : 5
}}
transition={{ type: 'timing' }}
transition={{ type: 'timing', duration: 150 }}
>
<CaretRight color={tw.color('ink-dull')} size={16} style={tw`mr-3`} />
<CaretRight color="white" weight="bold" size={16} style={tw`mr-3`} />
</MotiView>
</Pressable>
<AnimatedHeight hide={hide}>{children}</AnimatedHeight>

View file

@ -1,7 +1,7 @@
import { MotiView } from 'moti';
import { ReactNode, useState } from 'react';
import { KeyboardAvoidingView, Modal, Platform, Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { PulseAnimation } from '../animation/lottie';
import { Button } from '../primitive/Button';

View file

@ -4,30 +4,146 @@ import {
BottomSheetHandle,
BottomSheetHandleProps,
BottomSheetModal,
BottomSheetModalProps
BottomSheetModalProps,
BottomSheetScrollView
} from '@gorhom/bottom-sheet';
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
import { forwardRef } from 'react';
import tw from '~/lib/tailwind';
import { X } from 'phosphor-react-native';
import { ReactNode, forwardRef } from 'react';
import { Pressable, Text, View } from 'react-native';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw } from '~/lib/tailwind';
import { Button } from '../primitive/Button';
const ModalBackdrop = (props: BottomSheetBackdropProps) => (
<BottomSheetBackdrop {...props} appearsOnIndex={0} disappearsOnIndex={-1} opacity={0.75} />
);
const ModalHandle = (props: BottomSheetHandleProps) => (
interface ModalHandle extends BottomSheetHandleProps {
showCloseButton: boolean;
modalRef: React.RefObject<BottomSheetModal>;
}
const ModalHandle = (props: ModalHandle) => (
<BottomSheetHandle
{...props}
style={tw`bg-app rounded-t-xl`}
indicatorStyle={tw`bg-app-highlight`}
/>
style={tw`bg-app items-end rounded-t-2xl`}
indicatorStyle={tw`bg-app-highlight/60`}
>
{props.showCloseButton && (
<Pressable
onPress={() => props.modalRef.current.close()}
style={tw`bg-app-button absolute top-5 right-4 h-7 w-7 items-center justify-center rounded-full`}
>
<X size={16} color="white" weight="bold" />
</Pressable>
)}
</BottomSheetHandle>
);
export const Modal = forwardRef<BottomSheetModalMethods, BottomSheetModalProps>((props, ref) => (
<BottomSheetModal
ref={ref}
backdropComponent={ModalBackdrop}
handleComponent={ModalHandle}
backgroundStyle={tw`bg-app`}
{...props}
/>
));
export type ModalRef = BottomSheetModal;
interface ModalProps extends BottomSheetModalProps {
children: React.ReactNode;
title?: string;
showCloseButton?: boolean;
}
export const Modal = forwardRef<ModalRef, ModalProps>((props, ref) => {
const { children, title, showCloseButton = false, ...otherProps } = props;
const modalRef = useForwardedRef(ref);
return (
<BottomSheetModal
ref={modalRef}
backgroundStyle={tw`bg-app`}
backdropComponent={ModalBackdrop}
handleComponent={(props) => ModalHandle({ modalRef, showCloseButton, ...props })}
{...otherProps}
>
{title && <Text style={tw`text-ink text-center text-base font-medium`}>{title}</Text>}
{children}
</BottomSheetModal>
);
});
export const ModalScrollView = BottomSheetScrollView;
type ConfirmModalProps = {
title: string;
description?: string;
ctaAction?: () => void;
ctaLabel: string;
ctaDanger?: boolean;
ctaDisabled?: boolean;
loading?: boolean;
/**
* Disables backdrop press to close the modal.
*/
disableBackdropClose?: boolean;
/**
* Children will be rendered below the description and above the CTA button.
*/
children?: React.ReactNode;
snapPoints?: (string | number)[];
/**
* Trigger to open the modal.
* You can also use ref to open the modal
*/
trigger?: ReactNode;
};
// TODO: Add loading state
// Drop-in replacement for Dialog, can be used to get confirmation from the user, e.g. deleting a library
export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref) => {
const modalRef = useForwardedRef(ref);
return (
<>
{props.trigger && (
<Pressable onPress={() => modalRef.current.present()}>{props.trigger}</Pressable>
)}
<BottomSheetModal
ref={modalRef}
backgroundStyle={tw`bg-app`}
backdropComponent={ModalBackdrop}
handleComponent={(props) => ModalHandle({ modalRef, showCloseButton: false, ...props })}
snapPoints={props.snapPoints ?? ['25%']}
>
{/* Title */}
{props.title && (
<Text style={tw`text-ink text-center text-base font-medium`}>{props.title}</Text>
)}
<View style={tw`mt-4 px-6`}>
{/* Description */}
{props.description && <Text style={tw`text-ink-dull text-sm`}>{props.description}</Text>}
{/* Children */}
{props.children && props.children}
{/* Buttons */}
<View style={tw`flex flex-row pt-5`}>
<Button
variant="gray"
style={tw`flex-1`}
size="lg"
disabled={props.loading} // Disables Close button if loading
onPress={() => modalRef.current.close()}
>
<Text style={tw`text-ink text-sm font-medium`}>Close</Text>
</Button>
{props.ctaAction && (
<Button
style={tw`ml-4 flex-1`}
variant={props.ctaDanger ? 'danger' : 'accent'}
size="lg"
onPress={props.ctaAction}
disabled={props.ctaDisabled || props.loading}
>
<Text style={tw`text-ink text-sm font-medium`}>{props.ctaLabel}</Text>
</Button>
)}
</View>
</View>
</BottomSheetModal>
</>
);
});

View file

@ -2,7 +2,7 @@ import { ArrowDown, ArrowUp } from 'phosphor-react-native';
import { useState } from 'react';
import { Text, View } from 'react-native';
import { Menu, MenuItem } from '~/components/primitive/Menu';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
const sortOptions = {
name: 'Name',

View file

@ -1,4 +1,4 @@
import { FileModal } from './FileModal';
import { ActionsModal } from './inspector/ActionsModal';
/*
* Global Modals
@ -7,7 +7,7 @@ import { FileModal } from './FileModal';
export function GlobalModals() {
return (
<>
<FileModal />
<ActionsModal />
</>
);
}

View file

@ -1,17 +1,16 @@
import { BottomSheetModal } from '@gorhom/bottom-sheet';
import * as ML from 'expo-media-library';
import { forwardRef, useCallback } from 'react';
import { Alert, Platform, Text, View } from 'react-native';
import DocumentPicker from 'react-native-document-picker';
import { useLibraryMutation } from '@sd/client';
// import RFS from 'react-native-fs';
import { Modal } from '~/components/layout/Modal';
import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
// WIP component
const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
const modalRef = useForwardedRef(ref);
const { mutate: createLocation } = useLibraryMutation('locations.create', {
@ -41,6 +40,9 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
// Temporary until we decide on the user flow
const handlePhotosButton = useCallback(async () => {
Alert.alert('TODO');
return;
// Check if we have full access to the photos library
let permission = await ML.getPermissionsAsync();
// {"accessPrivileges": "none", "canAskAgain": true, "expires": "never", "granted": false, "status": "undetermined"}
@ -125,8 +127,8 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
// }, []);
return (
<Modal ref={modalRef} snapPoints={['20%']}>
<View style={tw`bg-app-box flex-1 px-6 pt-1 pb-2`}>
<Modal ref={modalRef} snapPoints={['25%']}>
<View style={tw`flex-1 px-8 pt-8 pb-2`}>
{/* <Button size="md" variant="accent" style={tw`my-2`} onPress={testFN}>
<Text>TEST</Text>
</Button> */}
@ -136,6 +138,7 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
<Button size="md" variant="accent" onPress={handlePhotosButton}>
<Text>Import from Photos</Text>
</Button>
<Text style={tw`mt-4 text-center text-white`}>TODO</Text>
</View>
</Modal>
);

View file

@ -1,15 +1,15 @@
import { useState } from 'react';
import { useRef } from 'react';
import { queryClient, useBridgeMutation } from '@sd/client';
import Dialog from '~/components/layout/Dialog';
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
type Props = {
libraryUuid: string;
onSubmit?: () => void;
children: React.ReactNode;
trigger: React.ReactNode;
};
const DeleteLibraryDialog = ({ children, onSubmit, libraryUuid }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const DeleteLibraryModal = ({ trigger, onSubmit, libraryUuid }: Props) => {
const modalRef = useRef<ModalRef>();
const { mutate: deleteLibrary, isLoading: deleteLibLoading } = useBridgeMutation(
'library.delete',
@ -19,24 +19,21 @@ const DeleteLibraryDialog = ({ children, onSubmit, libraryUuid }: Props) => {
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
modalRef.current.close();
}
}
);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
<ConfirmModal
title="Delete Library"
description="Deleting a library will permanently the database, the files themselves will not be deleted."
ctaLabel="Delete"
ctaAction={() => deleteLibrary(libraryUuid)}
loading={deleteLibLoading}
trigger={children}
trigger={trigger}
ctaDanger
/>
);
};
export default DeleteLibraryDialog;
export default DeleteLibraryModal;

View file

@ -1,15 +1,15 @@
import { useState } from 'react';
import { useRef } from 'react';
import { useLibraryMutation } from '@sd/client';
import Dialog from '~/components/layout/Dialog';
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
type Props = {
locationId: number;
onSubmit?: () => void;
children: React.ReactNode;
trigger: React.ReactNode;
};
const DeleteLocationDialog = ({ children, onSubmit, locationId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const DeleteLocationModal = ({ trigger, onSubmit, locationId }: Props) => {
const modalRef = useRef<ModalRef>();
const { mutate: deleteLoc, isLoading: deleteLocLoading } = useLibraryMutation(
'locations.delete',
@ -18,24 +18,22 @@ const DeleteLocationDialog = ({ children, onSubmit, locationId }: Props) => {
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
modalRef.current.close();
}
}
);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
<ConfirmModal
ref={modalRef}
title="Delete Location"
description="Deleting a location will also remove all files associated with it from the Spacedrive database, the files themselves will not be deleted."
ctaLabel="Delete"
ctaAction={() => deleteLoc(locationId)}
loading={deleteLocLoading}
trigger={children}
trigger={trigger}
ctaDanger
/>
);
};
export default DeleteLocationDialog;
export default DeleteLocationModal;

View file

@ -1,38 +1,36 @@
import { useState } from 'react';
import { useRef } from 'react';
import { useLibraryMutation } from '@sd/client';
import Dialog from '~/components/layout/Dialog';
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
type Props = {
tagId: number;
onSubmit?: () => void;
children: React.ReactNode;
trigger: React.ReactNode;
};
const DeleteTagDialog = ({ children, onSubmit, tagId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const DeleteTagModal = ({ trigger, onSubmit, tagId }: Props) => {
const modalRef = useRef<ModalRef>();
const { mutate: deleteTag, isLoading: deleteTagLoading } = useLibraryMutation('tags.delete', {
onSuccess: () => {
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
modalRef.current.close();
}
});
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
<ConfirmModal
title="Delete Tag"
description="Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked."
ctaLabel="Delete"
ctaAction={() => deleteTag(tagId)}
loading={deleteTagLoading}
trigger={children}
trigger={trigger}
ctaDanger
/>
);
};
export default DeleteTagDialog;
export default DeleteTagModal;

View file

@ -0,0 +1,127 @@
import dayjs from 'dayjs';
import {
Copy,
Icon,
Info,
LockSimple,
LockSimpleOpen,
Package,
Pencil,
Share,
TrashSimple
} from 'phosphor-react-native';
import { PropsWithChildren, useRef } from 'react';
import { Pressable, Text, View, ViewStyle } from 'react-native';
import { formatBytes, isObject } from '@sd/client';
import FileThumb from '~/components/explorer/FileThumb';
import FavoriteButton from '~/components/explorer/sections/FavoriteButton';
import InfoTagPills from '~/components/explorer/sections/InfoTagPills';
import { Modal, ModalRef } from '~/components/layout/Modal';
import { tw, twStyle } from '~/lib/tailwind';
import { useActionsModalStore } from '~/stores/modalStore';
import FileInfoModal from './FileInfoModal';
type ActionsContainerProps = PropsWithChildren<{
style?: ViewStyle;
}>;
const ActionsContainer = ({ children, style }: ActionsContainerProps) => (
<View style={twStyle('bg-app-box rounded-lg py-3.5', style)}>{children}</View>
);
type ActionsItemProps = {
title: string;
icon: Icon;
onPress?: () => void;
isDanger?: boolean;
};
const ActionsItem = ({ icon, onPress, title, isDanger = false }: ActionsItemProps) => {
const Icon = icon;
return (
<Pressable onPress={onPress} style={tw`flex flex-row items-center justify-between px-4`}>
<Text
style={twStyle(
'text-base font-medium leading-none',
isDanger ? 'text-red-600' : 'text-ink'
)}
>
{title}
</Text>
<Icon color={isDanger ? 'red' : 'white'} size={22} />
</Pressable>
);
};
const ActionDivider = () => <View style={tw`bg-app-line/80 my-3.5 h-[0.5px]`} />;
export const ActionsModal = () => {
const fileInfoRef = useRef<ModalRef>(null);
const { modalRef, data } = useActionsModalStore();
const item = data?.item;
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
return (
<>
<Modal ref={modalRef} snapPoints={['60%', '90%']}>
{data && (
<View style={tw`flex-1 px-4`}>
<View style={tw`flex flex-row items-center`}>
{/* Thumbnail/Icon */}
<Pressable onPress={() => fileInfoRef.current.present()}>
<FileThumb data={data} size={1} />
</Pressable>
<View style={tw`ml-2 flex-1`}>
{/* Name + Extension */}
<Text style={tw`text-base font-bold text-gray-200`} numberOfLines={1}>
{item.name}
{item.extension && `.${item.extension}`}
</Text>
<View style={tw`flex flex-row`}>
<Text style={tw`text-ink-faint text-xs`}>
{formatBytes(Number(objectData?.size_in_bytes || 0))},
</Text>
<Text style={tw`text-ink-faint text-xs`}>
{' '}
{dayjs(item.date_created).format('MMM Do YYYY')}
</Text>
</View>
<InfoTagPills data={data} />
</View>
<FavoriteButton style={tw`mr-4`} data={objectData} />
</View>
<View style={tw`my-3`} />
{/* Actions */}
<ActionsContainer>
<ActionsItem
icon={Info}
title="Show Info"
onPress={() => fileInfoRef.current.present()}
/>
</ActionsContainer>
<ActionsContainer style={tw`mt-2`}>
<ActionsItem icon={Pencil} title="Rename" />
<ActionDivider />
<ActionsItem icon={Copy} title="Duplicate" />
<ActionDivider />
<ActionsItem icon={Share} title="Share" />
</ActionsContainer>
<ActionsContainer style={tw`mt-2`}>
<ActionsItem icon={LockSimple} title="Encrypt" />
<ActionDivider />
<ActionsItem icon={LockSimpleOpen} title="Decrypt" />
<ActionDivider />
<ActionsItem icon={Package} title="Compress" />
<ActionDivider />
<ActionsItem icon={TrashSimple} title="Delete" isDanger />
</ActionsContainer>
</View>
)}
</Modal>
<FileInfoModal ref={fileInfoRef} data={data} />
</>
);
};

View file

@ -0,0 +1,132 @@
import dayjs from 'dayjs';
import {
Barcode,
CaretLeft,
CircleWavyCheck,
Clock,
Cube,
Icon,
Snowflake
} from 'phosphor-react-native';
import { forwardRef } from 'react';
import { Pressable, Text, View } from 'react-native';
import { ExplorerItem, formatBytes, isObject, useLibraryQuery } from '@sd/client';
import FileThumb from '~/components/explorer/FileThumb';
import InfoTagPills from '~/components/explorer/sections/InfoTagPills';
import { Modal, ModalRef, ModalScrollView } from '~/components/layout/Modal';
import Divider from '~/components/primitive/Divider';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw } from '~/lib/tailwind';
type MetaItemProps = {
title: string;
value: string | number;
icon?: Icon;
};
function MetaItem({ title, value, icon }: MetaItemProps) {
const Icon = icon;
return (
<>
<View style={tw`flex flex-row items-center`}>
<View style={tw`w-30 flex flex-row items-center`}>
{icon && <Icon color="white" size={18} style={tw`mr-1`} />}
<Text style={tw`text-sm font-medium text-white`}>{title}</Text>
</View>
<Text style={tw`text-sm text-gray-400`}>{value}</Text>
</View>
<Divider style={tw`my-3.5`} />
</>
);
}
type FileInfoModalProps = {
data: ExplorerItem;
};
const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
const { data } = props;
const modalRef = useForwardedRef(ref);
const item = data?.item;
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
const filePathData = data ? (isObject(data) ? data.item.file_paths[0] : data.item) : null;
const fullObjectData = useLibraryQuery(['files.get', { id: objectData?.id || -1 }], {
enabled: objectData?.id !== undefined
});
return (
<Modal
ref={modalRef}
enableContentPanningGesture={false}
enablePanDownToClose={false}
snapPoints={['70%']}
>
{data && (
<ModalScrollView style={tw`flex-1 p-4`}>
{/* Back Button */}
<Pressable onPress={() => modalRef.current.close()} style={tw`absolute z-10 ml-4`}>
<CaretLeft color={tw.color('accent')} size={20} weight="bold" />
</Pressable>
{/* File Icon / Name */}
<View style={tw`items-center`}>
<FileThumb data={data} size={1.6} />
<Text style={tw`mt-2 text-base font-bold text-gray-200`}>{item.name}</Text>
<InfoTagPills data={data} style={tw`mt-3`} />
</View>
{/* Details */}
<Divider style={tw`mt-6 mb-4`} />
<>
{/* Size */}
<MetaItem
title="Size"
icon={Cube}
value={formatBytes(Number(objectData?.size_in_bytes || 0))}
/>
{/* Duration */}
{fullObjectData.data?.media_data?.duration_seconds && (
<MetaItem
title="Duration"
value={fullObjectData.data.media_data.duration_seconds}
icon={Clock}
/>
)}
{/* Created */}
<MetaItem
icon={Clock}
title="Created"
value={dayjs(item.date_created).format('MMM Do YYYY')}
/>
{/* Indexed */}
<MetaItem
icon={Barcode}
title="Indexed"
value={dayjs(item.date_indexed).format('MMM Do YYYY')}
/>
{filePathData && (
<>
{/* TODO: Note */}
<MetaItem icon={Snowflake} title="Content ID" value={filePathData.cas_id} />
{/* Checksum */}
{filePathData?.integrity_checksum && (
<MetaItem
icon={CircleWavyCheck}
title="Checksum"
value={filePathData?.integrity_checksum}
/>
)}
</>
)}
</>
</ModalScrollView>
)}
</Modal>
);
});
export default FileInfoModal;

View file

@ -0,0 +1,111 @@
import { forwardRef, useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { queryClient, useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout';
import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind';
const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
const modalRef = useForwardedRef(ref);
const [tagName, setTagName] = useState('');
const [tagColor, setTagColor] = useState('#A717D9');
const [showPicker, setShowPicker] = useState(false);
// TODO: Use react-hook-form?
const { mutate: createTag } = useLibraryMutation('tags.create', {
onSuccess: () => {
// Reset form
setTagName('');
setTagColor('#A717D9');
setShowPicker(false);
queryClient.invalidateQueries(['tags.list']);
},
onSettled: () => {
// Close modal
modalRef.current.dismiss();
}
});
useEffect(() => {
modalRef.current.snapToIndex(showPicker ? 1 : 0);
}, [modalRef, showPicker]);
return (
<Modal
ref={modalRef}
snapPoints={['30%', '60%']}
title="Create Tag"
onDismiss={() => {
// Resets form onDismiss
setTagName('');
setTagColor('#A717D9');
setShowPicker(false);
}}
// Disable panning gestures
enableHandlePanningGesture={false}
enableContentPanningGesture={false}
showCloseButton
>
<View style={tw`p-4`}>
<View style={tw`mt-4 flex flex-row items-center`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
style={twStyle({ backgroundColor: tagColor }, 'h-6 w-6 rounded-full')}
/>
<Input
style={tw`ml-2 flex-1`}
value={tagName}
onChangeText={(text) => setTagName(text)}
placeholder="Name"
/>
</View>
{/* Color Picker */}
{showPicker && (
<FadeInAnimation>
<View style={tw`mt-4 h-64`}>
<ColorPicker
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View>
</FadeInAnimation>
)}
<Button
variant="accent"
size="md"
onPress={() => createTag({ color: tagColor, name: tagName })}
style={tw`mt-6`}
disabled={tagName.length === 0}
>
<Text style={tw`text-sm font-medium text-white`}>Create</Text>
</Button>
</View>
</Modal>
);
});
export default CreateTagModal;

View file

@ -0,0 +1,110 @@
import { forwardRef, useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { Tag, queryClient, useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout';
import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind';
type Props = {
tag: Tag;
onSubmit?: () => void;
};
const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
const modalRef = useForwardedRef(ref);
const [tagName, setTagName] = useState(props.tag.name);
const [tagColor, setTagColor] = useState(props.tag.color);
const [showPicker, setShowPicker] = useState(false);
const { mutate: updateTag, isLoading } = useLibraryMutation('tags.update', {
onSuccess: () => {
// Reset form
setShowPicker(false);
queryClient.invalidateQueries(['tags.list']);
props.onSubmit?.();
},
onSettled: () => {
modalRef.current.dismiss();
}
});
useEffect(() => {
modalRef.current.snapToIndex(showPicker ? 1 : 0);
}, [modalRef, showPicker]);
return (
<Modal
ref={modalRef}
snapPoints={['35%', '65%']}
onDismiss={() => {
// Resets form onDismiss
setShowPicker(false);
}}
title="Update Tag"
// Disable panning gestures
enableHandlePanningGesture={false}
enableContentPanningGesture={false}
showCloseButton
>
<View style={tw`p-4`}>
<Text style={tw`text-ink-dull mb-1 ml-1 text-xs font-medium`}>Name</Text>
<Input value={tagName} onChangeText={(t) => setTagName(t)} />
<Text style={tw`text-ink-dull mb-1 ml-1 mt-3 text-xs font-medium`}>Color</Text>
<View style={tw`ml-2 flex flex-row items-center`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
style={twStyle({ backgroundColor: tagColor }, 'h-5 w-5 rounded-full')}
/>
{/* TODO: Make this editable. Need to make sure color is a valid hexcode and update the color on picker etc. etc. */}
<Input editable={false} value={tagColor} style={tw`ml-2 flex-1`} />
</View>
{showPicker && (
<FadeInAnimation>
<View style={tw`mt-4 h-64`}>
<ColorPicker
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View>
</FadeInAnimation>
)}
{/* TODO: Add loading to button */}
<Button
variant="accent"
size="md"
onPress={() => updateTag({ id: props.tag.id, color: tagColor, name: tagName })}
style={tw`mt-6`}
disabled={tagName.length === 0}
>
<Text style={tw`text-sm font-medium text-white`}>Save</Text>
</Button>
</View>
</Modal>
);
});
export default UpdateTagModal;

View file

@ -3,8 +3,8 @@ import { FC, useEffect, useState } from 'react';
import { ScrollView, Text, View } from 'react-native';
import RNFS from 'react-native-fs';
import { Statistics, useLibraryQuery } from '@sd/client';
import useCounter from '../hooks/useCounter';
import tw, { twStyle } from '../lib/tailwind';
import useCounter from '~/hooks/useCounter';
import { tw, twStyle } from '~/lib/tailwind';
const StatItemNames: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: 'Total capacity',

View file

@ -2,7 +2,7 @@ import { VariantProps, cva } from 'class-variance-authority';
import { MotiPressable, MotiPressableProps } from 'moti/interactions';
import { FC, useMemo } from 'react';
import { Pressable, PressableProps } from 'react-native';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
const button = cva(['items-center justify-center rounded-md border shadow-sm'], {
variants: {

View file

@ -1,5 +1,5 @@
import { StyleProp, Text, View, ViewStyle } from 'react-native';
import tw from '~/lib/tailwind';
import { StyleProp, View, ViewStyle } from 'react-native';
import { tw } from '~/lib/tailwind';
type DividerProps = {
style?: StyleProp<ViewStyle>;

View file

@ -0,0 +1,39 @@
import React from 'react';
import { Text, TextStyle, View, ViewStyle } from 'react-native';
import { tw, twStyle } from '~/lib/tailwind';
type Props = {
text: string;
containerStyle?: ViewStyle;
textStyle?: TextStyle;
};
export const InfoPill = (props: Props) => {
return (
<View
style={twStyle(
'shadow-app-shade/5 bg-app-highlight rounded-md border border-transparent px-[6px] py-[1px] shadow',
props.containerStyle
)}
>
<Text style={twStyle('text-ink-dull text-xs font-medium', props.textStyle)}>
{props.text}
</Text>
</View>
);
};
export function PlaceholderPill(props: Props) {
return (
<View
style={twStyle(
'shadow-app-shade/10 border-app-highlight rounded-md border border-dashed bg-transparent px-[6px] py-[1px] shadow',
props.containerStyle
)}
>
<Text style={twStyle('text-ink-faint/70 text-xs font-medium', props.textStyle)}>
{props.text}
</Text>
</View>
);
}

View file

@ -1,9 +1,9 @@
import { VariantProps, cva } from 'class-variance-authority';
import { FC } from 'react';
import { TextInputProps as RNTextInputProps, TextInput } from 'react-native';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
const input = cva(['rounded-md border text-sm shadow-sm'], {
const input = cva(['rounded-md border text-sm leading-tight shadow-sm'], {
variants: {
variant: {
default: 'border-app-line bg-app text-ink'

View file

@ -5,9 +5,10 @@ import {
MenuOptionProps,
MenuOptions,
MenuTrigger,
Menu as PMenu
Menu as PMenu,
renderers
} from 'react-native-popup-menu';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
type MenuProps = {
trigger: React.ReactNode;
@ -17,7 +18,7 @@ type MenuProps = {
// TODO: Still looks a bit off...
export const Menu = (props: MenuProps) => (
<View>
<PMenu>
<PMenu renderer={renderers.NotAnimatedContextMenu}>
<MenuTrigger>{props.trigger}</MenuTrigger>
<MenuOptions optionsContainerStyle={tw`bg-app-menu rounded p-1`}>
{props.children}

View file

@ -1,6 +1,6 @@
import { FC } from 'react';
import { Switch as RNSwitch, SwitchProps, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
export const Switch: FC<SwitchProps> = ({ ...props }) => {
return (

View file

@ -1,6 +1,6 @@
import { PropsWithChildren } from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
type SettingsContainerProps = PropsWithChildren<{
title?: string;
@ -9,7 +9,7 @@ type SettingsContainerProps = PropsWithChildren<{
export function SettingsContainer({ children, title, description }: SettingsContainerProps) {
return (
<View style={tw``}>
<View>
{title && <Text style={tw`text-ink-dull pb-2 pl-3 text-sm font-semibold`}>{title}</Text>}
{children}
{description && <Text style={tw`text-ink-dull px-3 pt-2 text-sm`}>{description}</Text>}

View file

@ -1,6 +1,6 @@
import { CaretRight, Icon } from 'phosphor-react-native';
import { Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
type SettingsItemProps = {
title: string;
@ -12,12 +12,12 @@ type SettingsItemProps = {
export function SettingsItem(props: SettingsItemProps) {
return (
<Pressable onPress={props.onPress}>
<View style={tw`bg-app-overlay flex flex-row items-center justify-between px-3`}>
<View style={tw`bg-app-overlay flex flex-row items-center justify-between px-4`}>
<View style={tw`flex flex-row items-center py-4`}>
{props.leftIcon && props.leftIcon({ size: 18, color: tw.color('ink'), style: tw`mr-2` })}
<Text style={tw`text-ink text-sm`}>{props.title}</Text>
{props.leftIcon && props.leftIcon({ size: 20, color: tw.color('ink'), style: tw`mr-3` })}
<Text style={tw`text-ink text-[14px]`}>{props.title}</Text>
</View>
{props.rightArea ? props.rightArea : <CaretRight size={20} color={tw.color('ink-faint')} />}
{props.rightArea ? props.rightArea : <CaretRight size={20} color={tw.color('ink-dull')} />}
</View>
</Pressable>
);

View file

@ -1,98 +0,0 @@
import React, { useState } from 'react';
import { Pressable, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { queryClient, useLibraryMutation } from '@sd/client';
import Dialog from '~/components/layout/Dialog';
import { Input } from '~/components/primitive/Input';
import tw, { twStyle } from '~/lib/tailwind';
type Props = {
onSubmit?: () => void;
disableBackdropClose?: boolean;
children: React.ReactNode;
};
const CreateTagDialog = ({ children, onSubmit, disableBackdropClose }: Props) => {
const [tagName, setTagName] = useState('');
const [tagColor, setTagColor] = useState('#A717D9');
const [isOpen, setIsOpen] = useState(false);
const { mutate: createTag, isLoading } = useLibraryMutation('tags.create', {
onSuccess: () => {
// Reset form
setTagName('');
setTagColor('#A717D9');
setShowPicker(false);
queryClient.invalidateQueries(['tags.list']);
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
}
});
const [showPicker, setShowPicker] = useState(false);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
title="Create New Tag"
description="Choose a name and color."
ctaLabel="Create"
ctaAction={() => createTag({ color: tagColor, name: tagName })}
loading={isLoading}
ctaDisabled={tagName.length === 0}
trigger={children}
disableBackdropClose={disableBackdropClose}
onClose={() => {
setTagName('');
setTagColor('#A717D9');
setShowPicker(false);
}} // Resets form onClose
>
<View style={tw`flex flex-row items-center`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
style={twStyle({ backgroundColor: tagColor }, 'h-5 w-5 rounded-full')}
/>
<Input
style={tw`ml-2 flex-1`}
value={tagName}
onChangeText={(text) => setTagName(text)}
placeholder="Name"
/>
</View>
{showPicker && (
<View style={tw`my-4 h-64`}>
<ColorPicker
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View>
)}
</Dialog>
);
};
export default CreateTagDialog;

View file

@ -1,92 +0,0 @@
import React, { useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { Tag, queryClient, useLibraryMutation } from '@sd/client';
import Dialog from '~/components/layout/Dialog';
import { Input } from '~/components/primitive/Input';
import tw, { twStyle } from '~/lib/tailwind';
type Props = {
tag: Tag;
onSubmit?: () => void;
children: React.ReactNode;
};
const UpdateTagDialog = ({ children, onSubmit, tag }: Props) => {
const [tagName, setTagName] = useState(tag.name);
const [tagColor, setTagColor] = useState(tag.color);
const [isOpen, setIsOpen] = useState(false);
const { mutate: updateTag, isLoading } = useLibraryMutation('tags.update', {
onSuccess: () => {
// Reset form
setShowPicker(false);
queryClient.invalidateQueries(['tags.list']);
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
}
});
const [showPicker, setShowPicker] = useState(false);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
title="Update Tag"
ctaLabel="Save"
ctaAction={() => updateTag({ id: tag.id, color: tagColor, name: tagName })}
loading={isLoading}
ctaDisabled={tagName.length === 0}
trigger={children}
onClose={() => {
setShowPicker(false); // Reset form
}}
>
<Text style={tw`text-ink-dull mb-1 ml-1 mt-3 text-xs font-medium`}>Name</Text>
<Input value={tagName} onChangeText={(t) => setTagName(t)} />
<Text style={tw`text-ink-dull mb-1 ml-1 mt-3 text-xs font-medium`}>Color</Text>
<View style={tw`ml-2 flex flex-row items-center`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
style={twStyle({ backgroundColor: tagColor }, 'h-5 w-5 rounded-full')}
/>
{/* TODO: Make this editable. Need to make sure color is a valid hexcode and update the color on picker etc. etc. */}
<Input editable={false} value={tagColor} style={tw`ml-2 flex-1`} />
</View>
{showPicker && (
<View style={tw`mt-4 h-64`}>
<ColorPicker
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View>
)}
</Dialog>
);
};
export default UpdateTagDialog;

View file

@ -1,107 +0,0 @@
import { BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet';
import dayjs from 'dayjs';
import { CaretLeft } from 'phosphor-react-native';
import { useRef } from 'react';
import { Button, Pressable, Text, View } from 'react-native';
import { default as FileIcon, default as FileThumb } from '../../components/explorer/FileThumb';
import { Modal } from '../../components/layout/Modal';
import Divider from '../../components/primitive/Divider';
import tw from '../../lib/tailwind';
import { useFileModalStore } from '../../stores/modalStore';
type MetaItemProps = {
title: string;
value: string;
};
function MetaItem({ title, value }: MetaItemProps) {
return (
<View>
<Text style={tw`text-sm font-bold text-white`}>{title}</Text>
<Text style={tw`mt-1 text-sm text-gray-400`}>{value}</Text>
</View>
);
}
export const FileModal = () => {
const { fileRef, data } = useFileModalStore();
const fileDetailsRef = useRef<BottomSheetModal>(null);
const item = data.item;
return (
<>
<Modal ref={fileRef} snapPoints={['60%', '90%']}>
{data && (
<View style={tw`bg-app flex-1 p-4`}>
{/* File Icon / Name */}
<View style={tw`flex flex-row items-center`}>
<FileIcon data={data} size={1.6} />
{/* File Name, Details etc. */}
<View style={tw`ml-2`}>
<Text style={tw`text-base font-bold text-gray-200`}>{item.name}</Text>
<View style={tw`mt-2 flex flex-row`}>
<Text style={tw`text-xs text-gray-400`}>5 MB,</Text>
<Text style={tw`ml-1 text-xs text-gray-400`}>
{item.extension.toUpperCase()},
</Text>
<Text style={tw`ml-1 text-xs text-gray-400`}>15 Aug</Text>
</View>
<Pressable style={tw`mt-2`} onPress={() => fileDetailsRef.current.present()}>
<Text style={tw`text-accent text-sm`}>More</Text>
</Pressable>
</View>
</View>
{/* Divider */}
<Divider style={tw`my-6`} />
{/* Buttons */}
<Button onPress={() => fileRef.current.close()} title="Copy" color="white" />
<Button onPress={() => fileRef.current.close()} title="Move" color="white" />
<Button onPress={() => fileRef.current.close()} title="Share" color="white" />
<Button onPress={() => fileRef.current.close()} title="Delete" color="white" />
</View>
)}
</Modal>
{/* Details Modal */}
<Modal
ref={fileDetailsRef}
enableContentPanningGesture={false}
enablePanDownToClose={false}
snapPoints={['70%']}
>
{data && (
<BottomSheetScrollView style={tw`bg-app flex-1 p-4`}>
{/* Back Button */}
<Pressable style={tw`ml-4 w-full`} onPress={() => fileDetailsRef.current.close()}>
<CaretLeft color={tw.color('accent')} size={20} />
</Pressable>
{/* File Icon / Name */}
<View style={tw`items-center`}>
<FileThumb data={data} size={1.8} />
<Text style={tw`mt-3 text-base font-bold text-gray-200`}>{item.name}</Text>
</View>
{/* Details */}
<Divider style={tw`mt-6 mb-4`} />
<>
{/* Temp, we need cas id */}
{item.id && <MetaItem title="Unique Content ID" value={'555555555'} />}
<Divider style={tw`my-4`} />
<MetaItem title="URI" value={`/Users/utku/Somewhere/vite.config.js`} />
<Divider style={tw`my-4`} />
<MetaItem
title="Date Created"
value={dayjs(item.date_created).format('MMMM Do yyyy, h:mm:ss aaa')}
/>
<Divider style={tw`my-4`} />
<MetaItem
title="Date Indexed"
value={dayjs(item.date_indexed).format('MMMM Do yyyy, h:mm:ss aaa')}
/>
</>
</BottomSheetScrollView>
)}
</Modal>
</>
);
};

View file

@ -7,7 +7,7 @@ export function useAutoForm<TFieldValues extends FieldValues = FieldValues, TCon
form: UseFormReturn<TFieldValues, TContext>,
callback: (data: any) => void,
/**
*Wait time in miliseconds
*Wait time in milliseconds
*/
waitTime = 500
) {

View file

@ -2,5 +2,6 @@ import { create } from 'twrnc';
const tw = create(require(`../../tailwind.config.js`));
export default tw;
export const twStyle = tw.style;
const twStyle = tw.style;
export { tw, twStyle };

View file

@ -43,9 +43,9 @@ globalThis.localStorage = {
https://github.com/facebook/hermes/issues/23
We are using "Hermes" on Android & IOS, which for the current version (0.11),
IOS does not support the Intl fully so we need pollyfill it.
IOS does not support the Intl fully so we need polyfill it.
NOTE: We can be picky about what we "pollyfill" to optimize but for now this works.
NOTE: We can be picky about what we "polyfill" to optimize but for now this works.
*/
if (Platform.OS === 'ios') {
@ -54,7 +54,7 @@ if (Platform.OS === 'ios') {
}
// This is insane. We load all data from `AsyncStorage` into the `_localStorage` global and then once complete we import the app.
// This way the polyfilled `localStorage` implementation has its data populated before the global stores within `@sd/client` are initialised (as they are initialised on import).
// This way the polyfilled `localStorage` implementation has its data populated before the global stores within `@sd/client` are initialized (as they are initialized on import).
const App = lazy(async () => {
const keys = await AsyncStorage.getAllKeys();
const values = await AsyncStorage.multiGet(keys);

View file

@ -1,8 +1,8 @@
import { DrawerScreenProps, createDrawerNavigator } from '@react-navigation/drawer';
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
import { StackScreenProps } from '@react-navigation/stack';
import DrawerContent from '~/containers/drawer/DrawerContent';
import tw from '~/lib/tailwind';
import DrawerContent from '~/components/drawer/DrawerContent';
import { tw } from '~/lib/tailwind';
import type { RootStackParamList } from '.';
import type { TabParamList } from './TabNavigator';
import TabNavigator from './TabNavigator';
@ -17,7 +17,7 @@ export default function DrawerNavigator() {
headerShown: false,
drawerStyle: {
backgroundColor: tw.color('app-darkBox'),
width: '75%'
width: '70%'
},
overlayColor: 'transparent',
drawerType: 'slide',

View file

@ -1,5 +1,5 @@
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import SettingsScreen from '~/screens/settings/Settings';
import AppearanceSettingsScreen from '~/screens/settings/client/AppearanceSettings';
import ExtensionsSettingsScreen from '~/screens/settings/client/ExtensionsSettings';

View file

@ -2,7 +2,7 @@ import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigatio
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
import { CirclesFour, Planet, ShareNetwork } from 'phosphor-react-native';
import React from 'react';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import type { HomeDrawerScreenProps } from './DrawerNavigator';
import NodesStack, { NodesStackParamList } from './tabs/NodesStack';
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
@ -36,7 +36,7 @@ export default function TabNavigator() {
/>
),
tabBarLabel: 'Overview',
tabBarLabelStyle: tw`text-tiny font-semibold`
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
<Tab.Screen
@ -51,7 +51,7 @@ export default function TabNavigator() {
/>
),
tabBarLabel: 'Nodes',
tabBarLabelStyle: tw`text-tiny font-semibold`
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
<Tab.Screen
@ -66,7 +66,7 @@ export default function TabNavigator() {
/>
),
tabBarLabel: 'Spaces',
tabBarLabelStyle: tw`text-tiny font-semibold`
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
</Tab.Navigator>

View file

@ -1,6 +1,6 @@
import { NavigatorScreenParams } from '@react-navigation/native';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import NotFoundScreen from '~/screens/NotFound';
import SearchScreen from '~/screens/Search';
import type { DrawerNavParamList } from './DrawerNavigator';

View file

@ -1,7 +1,7 @@
import { CompositeScreenProps } from '@react-navigation/native';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import Header from '~/components/header/Header';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import NodesScreen from '~/screens/Nodes';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';

View file

@ -1,7 +1,7 @@
import { CompositeScreenProps } from '@react-navigation/native';
import { StackScreenProps, TransitionPresets, createStackNavigator } from '@react-navigation/stack';
import tw from '~/lib/tailwind';
import Header from '../../components/header/Header';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import Header from '~/components/header/Header';
import { tw } from '~/lib/tailwind';
import OverviewScreen from '../../screens/Overview';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';

View file

@ -1,7 +1,7 @@
import { CompositeScreenProps } from '@react-navigation/native';
import { StackScreenProps, TransitionPresets, createStackNavigator } from '@react-navigation/stack';
import tw from '~/lib/tailwind';
import Header from '../../components/header/Header';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import Header from '~/components/header/Header';
import { tw } from '~/lib/tailwind';
import SpacesScreen from '../../screens/Spaces';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';

View file

@ -18,9 +18,23 @@ export default function LocationScreen({ navigation, route }: SharedScreenProps<
]);
useEffect(() => {
// Not sure why we do this.
// Set screen title to location.
if (path && path !== '') {
// Nested location.
navigation.setOptions({
title: path.split('/')[0]
});
} else {
navigation.setOptions({
title: data?.context.name
});
}
}, [data, navigation, path]);
useEffect(() => {
getExplorerStore().locationId = id;
}, [id]);
getExplorerStore().path = path;
}, [id, path]);
return <Explorer data={data} />;
}

View file

@ -1,5 +1,5 @@
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { NodesStackScreenProps } from '~/navigation/tabs/NodesStack';
export default function NodesScreen({ navigation }: NodesStackScreenProps<'Nodes'>) {

View file

@ -1,5 +1,5 @@
import { Text, TouchableOpacity, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { RootStackScreenProps } from '~/navigation';
export default function NotFoundScreen({ navigation }: RootStackScreenProps<'NotFound'>) {

View file

@ -1,50 +1,15 @@
import { View } from 'react-native';
import VirtualizedListWrapper from '~/components/layout/VirtualizedListWrapper';
import OverviewStats from '~/containers/OverviewStats';
import tw from '~/lib/tailwind';
import OverviewStats from '~/components/overview/OverviewStats';
import { tw } from '~/lib/tailwind';
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
// const placeholderDevices: any = [
// {
// name: "James' iPhone 12",
// size: '47.9GB',
// locations: [],
// type: 'phone'
// },
// {
// name: "James' MacBook Pro",
// size: '1TB',
// locations: [],
// type: 'laptop'
// },
// {
// name: "James' Toaster",
// size: '1PB',
// locations: [],
// type: 'desktop'
// },
// {
// name: 'Spacedrive Server',
// size: '5GB',
// locations: [],
// type: 'server'
// }
// ];
export default function OverviewScreen({ navigation }: OverviewStackScreenProps<'Overview'>) {
return (
<VirtualizedListWrapper>
<View style={tw`mt-4 px-4`}>
{/* Stats */}
<OverviewStats />
{/* Devices */}
{/* <FlatList
data={placeholderDevices}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<Device locations={[]} name={item.name} size={item.size} type={item.type} />
)}
/> */}
</View>
</VirtualizedListWrapper>
);

View file

@ -3,7 +3,7 @@ import { useState } from 'react';
import { ActivityIndicator, Pressable, Text, TextInput, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Button } from '~/components/primitive/Button';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { RootStackScreenProps } from '~/navigation';
const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {

View file

@ -1,5 +1,5 @@
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SpacesStackScreenProps } from '~/navigation/tabs/SpacesStack';
export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) {

View file

@ -1,18 +1,25 @@
import { Text, View } from 'react-native';
import { useEffect } from 'react';
import { useLibraryQuery } from '@sd/client';
import Explorer from '~/components/explorer/Explorer';
import tw from '~/lib/tailwind';
import { SharedScreenProps } from '~/navigation/SharedScreens';
import { getExplorerStore } from '~/stores/explorerStore';
export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'>) {
const { id } = route.params;
const { data } = useLibraryQuery(['tags.getExplorerData', id]);
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`text-ink text-xl font-bold`}>Tag {id}</Text>
<Explorer data={data} />
</View>
);
useEffect(() => {
// Set screen title to tag name.
navigation.setOptions({
title: data?.context.name
});
}, [data?.context.name, navigation]);
useEffect(() => {
getExplorerStore().locationId = id;
// getExplorerStore().path = path;
}, [id]);
return <Explorer data={data} />;
}

View file

@ -1,7 +1,7 @@
import { Text, View } from 'react-native';
import CreateLibraryDialog from '~/components/dialog/CreateLibraryDialog';
import { AnimatedButton } from '~/components/primitive/Button';
import CreateLibraryDialog from '~/containers/dialog/CreateLibraryDialog';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => {

View file

@ -1,7 +1,7 @@
import { Image, Text, View } from 'react-native';
import { FadeInUpAnimation, LogoAnimation } from '~/components/animation/layout';
import { AnimatedButton } from '~/components/primitive/Button';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => {

View file

@ -15,7 +15,7 @@ import {
import React from 'react';
import { SectionList, Text, View } from 'react-native';
import { SettingsItem, SettingsItemDivider } from '~/components/settings/SettingsItem';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackParamList, SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
type SectionType = {
@ -109,7 +109,7 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
return (
<Text
style={twStyle(
'text-ink-dull mb-2 ml-2 text-sm font-semibold',
'text-ink-dull mb-2 ml-3 text-sm font-semibold',
section.title === 'Client' ? 'mt-2' : 'mt-5'
)}
>
@ -136,7 +136,7 @@ export default function SettingsScreen({ navigation }: SettingsStackScreenProps<
ListFooterComponent={
<View style={tw`mt-6 mb-4 items-center`}>
<Text style={tw`text-ink text-sm font-bold`}>Spacedrive</Text>
<Text style={tw`text-ink-dull mt-0.5 text-xs`}>v0.1.0</Text>
<Text style={tw`text-ink-faint mt-0.5 text-xs`}>v0.1.0</Text>
</View>
}
showsVerticalScrollIndicator={false}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const AppearanceSettingsScreen = ({

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const ExtensionsSettingsScreen = ({

View file

@ -4,7 +4,7 @@ import { useBridgeQuery } from '@sd/client';
import Card from '~/components/layout/Card';
import Divider from '~/components/primitive/Divider';
import { Input } from '~/components/primitive/Input';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'GeneralSettings'>) => {

View file

@ -3,9 +3,9 @@ import React from 'react';
import { Animated, FlatList, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import { LibraryConfigWrapped, useBridgeQuery } from '@sd/client';
import DeleteLibraryModal from '~/components/modal/confirm-modals/DeleteLibraryModal';
import { AnimatedButton } from '~/components/primitive/Button';
import DeleteLibraryDialog from '~/containers/dialog/DeleteLibraryDialog';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function LibraryItem({
@ -34,11 +34,14 @@ function LibraryItem({
<AnimatedButton size="md" onPress={() => navigation.replace('LibraryGeneralSettings')}>
<Pen size={18} color="white" />
</AnimatedButton>
<DeleteLibraryDialog libraryUuid={library.uuid}>
<AnimatedButton size="md" style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
</DeleteLibraryDialog>
<DeleteLibraryModal
libraryUuid={library.uuid}
trigger={
<AnimatedButton size="md" style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
}
/>
</Animated.View>
);
};

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const PrivacySettingsScreen = ({ navigation }: SettingsStackScreenProps<'PrivacySettings'>) => {

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const AboutScreen = ({ navigation }: SettingsStackScreenProps<'About'>) => {

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const SupportScreen = ({ navigation }: SettingsStackScreenProps<'Support'>) => {

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const KeysSettingsScreen = ({ navigation }: SettingsStackScreenProps<'KeysSettings'>) => {

View file

@ -9,7 +9,7 @@ import { Switch } from '~/components/primitive/Switch';
import { SettingsContainer } from '~/components/settings/SettingsContainer';
import { SettingsItem } from '~/components/settings/SettingsItem';
import { useAutoForm } from '~/hooks/useAutoForm';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const LibraryGeneralSettingsScreen = ({
@ -60,9 +60,8 @@ const LibraryGeneralSettingsScreen = ({
{/* Export */}
<SettingsItem title="Export Library" onPress={() => Alert.alert('TODO')} />
<View style={tw`mt-4`} />
{/* Delete Library
TODO: Open delete library dialog here, but do handle library switching
And what happens if there is no library set ? */}
{/* Delete Library */}
{/* TODO: Open delete library dialog here, but do handle library switching and what happens if there is no library set ? */}
<SettingsContainer description="This is permanent, your files will not be deleted, only the Spacedrive library.">
<SettingsItem
title="Delete Library"

View file

@ -10,8 +10,8 @@ import {
useOnlineLocations
} from '@sd/client';
import FolderIcon from '~/components/icons/FolderIcon';
import DeleteLocationDialog from '~/containers/dialog/DeleteLocationDialog';
import tw, { twStyle } from '~/lib/tailwind';
import DeleteLocationModal from '~/components/modal/confirm-modals/DeleteLocationModal';
import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function LocationItem({ location, index }: { location: Location & { node: Node }; index: number }) {
@ -34,13 +34,16 @@ function LocationItem({ location, index }: { location: Location & { node: Node }
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<DeleteLocationDialog locationId={location.id}>
<View
style={tw`border-app-line bg-app-button items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
>
<Trash size={18} color="white" />
</View>
</DeleteLocationDialog>
<DeleteLocationModal
locationId={location.id}
trigger={
<View
style={tw`bg-app-button border-app-line items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
>
<Trash size={18} color="white" />
</View>
}
/>
{/* Full Re-scan IS too much here */}
<Pressable
style={tw`border-app-line bg-app-button mx-2 items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
@ -68,7 +71,7 @@ function LocationItem({ location, index }: { location: Location & { node: Node }
<View
style={twStyle(
'absolute right-0 bottom-0.5 h-2 w-2 rounded-full',
onlineLocations.some((l) => arraysEqual(location.pub_id, l))
onlineLocations?.some((l) => arraysEqual(location.pub_id, l))
? 'bg-green-500'
: 'bg-red-500'
)}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const NodesSettingsScreen = ({ navigation }: SettingsStackScreenProps<'NodesSettings'>) => {

View file

@ -1,14 +1,18 @@
import { CaretRight, Pen, Trash } from 'phosphor-react-native';
import { useRef } from 'react';
import { Animated, FlatList, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import { Tag, useLibraryQuery } from '@sd/client';
import { ModalRef } from '~/components/layout/Modal';
import DeleteTagModal from '~/components/modal/confirm-modals/DeleteTagModal';
import UpdateTagModal from '~/components/modal/tag/UpdateTagModal';
import { AnimatedButton } from '~/components/primitive/Button';
import DeleteTagDialog from '~/containers/dialog/tag/DeleteTagDialog';
import UpdateTagDialog from '~/containers/dialog/tag/UpdateTagDialog';
import tw, { twStyle } from '~/lib/tailwind';
import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function TagItem({ tag, index }: { tag: Tag; index: number }) {
const updateTagModalRef = useRef<ModalRef>();
const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>,
_dragX: Animated.AnimatedInterpolation<number>,
@ -24,16 +28,18 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<UpdateTagDialog tag={tag} onSubmit={() => swipeable.close()}>
<AnimatedButton size="md">
<Pen size={18} color="white" />
</AnimatedButton>
</UpdateTagDialog>
<DeleteTagDialog tagId={tag.id}>
<AnimatedButton size="md" style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
</DeleteTagDialog>
<UpdateTagModal tag={tag} ref={updateTagModalRef} onSubmit={() => swipeable.close()} />
<AnimatedButton size="md" onPress={() => updateTagModalRef.current.present()}>
<Pen size={18} color="white" />
</AnimatedButton>
<DeleteTagModal
tagId={tag.id}
trigger={
<AnimatedButton size="md" style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
}
/>
</Animated.View>
);
};

View file

@ -8,6 +8,7 @@ export type ExplorerKind = 'Location' | 'Tag' | 'Space';
const state = {
locationId: null as number | null,
path: '',
layoutMode: 'grid' as ExplorerLayoutMode,
// Using gridNumColumns instead of fixed size. We dynamically calculate the item size.
gridNumColumns: 3,

View file

@ -1,16 +1,16 @@
import { BottomSheetModal } from '@gorhom/bottom-sheet';
import { createRef } from 'react';
import { proxy, ref, useSnapshot } from 'valtio';
import { ExplorerItem } from '@sd/client';
import { ModalRef } from '~/components/layout/Modal';
export const fileModalStore = proxy({
fileRef: ref(createRef<BottomSheetModal>()),
export const actionsModalStore = proxy({
modalRef: ref(createRef<ModalRef>()),
data: null as ExplorerItem | null,
setData: (data: ExplorerItem) => {
fileModalStore.data = data;
actionsModalStore.data = data;
}
});
export function useFileModalStore() {
return useSnapshot(fileModalStore);
export function useActionsModalStore() {
return useSnapshot(actionsModalStore);
}

View file

@ -1,11 +1 @@
import { ExplorerItem } from '@sd/client';
export type valueof<T> = T[keyof T];
export function isPath(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Path' }> {
return item.type === 'Path';
}
export function isObject(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Object' }> {
return item.type === 'Object';
}

View file

@ -1,14 +1,9 @@
// Extented colors are copied from packages/ui/style/colors.scss
// Extended colors are copied from packages/ui/style/colors.scss
module.exports = {
content: ['./screens/**/*.{js,ts,jsx}', './components/**/*.{js,ts,jsx}', 'App.tsx'],
theme: {
extend: {
fontSize: {
tiny: '.65rem',
// Default: '0.875rem'
sm: '.85rem'
},
colors: {
// Brand blue
accent: {
@ -61,6 +56,11 @@ module.exports = {
950: 'hsla(230, 15%, 95%, 1)',
1000: 'hsla(230, 15%, 100%, 1)'
},
sidebar: {
box: 'hsla(230, 15%, 16%, 1)',
line: 'hsla(230, 15%, 23%, 1)',
button: 'hsla(230, 15%, 18%, 1)'
},
gray: {
DEFAULT: '#505468',
50: '#F1F1F4',

View file

@ -60,10 +60,8 @@ To run mobile app
2. `./.github/scripts/setup-system.sh mobile`
_The should setup most of the dependencies for the mobile app to build._
3. You must also ensure you have [NDK 24.0.8215888 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio
4. `cd apps/mobile && pnpm i` - This is a separate workspace, you need to do this!
5. `pnpm android` - runs on Android Emulator
6. `pnpm ios` - runs on iOS Emulator
7. `pnpm dev` - For already bundled app - This is only temporarily supported. The final app will require the Spacedrive Rust code which isn't included in Expo Go.
4. `pnpm mobile android` - runs on Android Emulator
5. `pnpm mobile ios` - runs on iOS Emulator
### Troubleshooting

View file

@ -13,9 +13,6 @@ const currentLibraryUuidStore = valtioPersist('sdActiveLibrary', {
id: null as string | null
});
// Cringe method to get rspc working on mobile.
export const mobileSync = currentLibraryUuidStore;
const CringeContext = createContext<{
onNoLibrary: OnNoLibraryFunc;
}>(undefined!);

View file

@ -0,0 +1,11 @@
export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

View file

@ -1,3 +1,16 @@
import { ExplorerItem } from '../core';
export * from './objectKind';
export * from './formatBytes';
export function isPath(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Path' }> {
return item.type === 'Path';
}
export function isObject(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Object' }> {
return item.type === 'Object';
}
export function arraysEqual<T>(a: T[], b: T[]) {
if (a === b) return true;
if (a == null || b == null) return false;
@ -5,5 +18,3 @@ export function arraysEqual<T>(a: T[], b: T[]) {
return a.every((n, i) => b[i] === n);
}
export * from './objectKind';

View file

@ -17,7 +17,13 @@ import {
TrashSimple
} from 'phosphor-react';
import { PropsWithChildren, useMemo } from 'react';
import { ExplorerItem, getLibraryIdRaw, useLibraryMutation, useLibraryQuery } from '@sd/client';
import {
ExplorerItem,
getLibraryIdRaw,
isObject,
useLibraryMutation,
useLibraryQuery
} from '@sd/client';
import { ContextMenu as CM, dialogManager } from '@sd/ui';
import { getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore';
import { useOperatingSystem } from '~/hooks/useOperatingSystem';
@ -28,7 +34,6 @@ import { DecryptFileDialog } from '../dialog/DecryptFileDialog';
import { DeleteFileDialog } from '../dialog/DeleteFileDialog';
import { EncryptFileDialog } from '../dialog/EncryptFileDialog';
import { EraseFileDialog } from '../dialog/EraseFileDialog';
import { isObject } from './utils';
const AssignTagMenuItems = (props: { objectId: number }) => {
const tags = useLibraryQuery(['tags.list'], { suspense: true });

View file

@ -1,11 +1,10 @@
import clsx from 'clsx';
import { HTMLAttributes } from 'react';
import { ExplorerItem, ObjectKind } from '@sd/client';
import { ExplorerItem, ObjectKind, isObject } from '@sd/client';
import { cva, tw } from '@sd/ui';
import { getExplorerStore } from '~/hooks/useExplorerStore';
import { FileItemContextMenu } from './ExplorerContextMenu';
import FileThumb from './FileThumb';
import { isObject } from './utils';
const NameArea = tw.div`flex justify-center`;

View file

@ -4,12 +4,10 @@ import executable from '@sd/assets/images/Executable.png';
import file from '@sd/assets/images/File.png';
import video from '@sd/assets/images/Video.png';
import clsx from 'clsx';
import { Suspense, lazy, useMemo } from 'react';
import { ExplorerItem } from '@sd/client';
import { ExplorerItem, isObject, isPath } from '@sd/client';
import { useExplorerStore } from '~/hooks/useExplorerStore';
import { usePlatform } from '~/util/Platform';
import { Folder } from '../icons/Folder';
import { isObject, isPath } from './utils';
interface Props {
data: ExplorerItem;
@ -20,22 +18,20 @@ interface Props {
kind?: string;
}
const icons = import.meta.glob('../../../../assets/icons/*.svg');
// const icons = import.meta.glob('../../../../assets/icons/*.svg');
export default function FileThumb({ data, ...props }: Props) {
const platform = usePlatform();
const store = useExplorerStore();
const item = data.item;
// const Icon = useMemo(() => {
// const icon = icons[`../../../../assets/icons/${item.extension}.svg`];
const Icon = useMemo(() => {
const icon = icons[`../../../../assets/icons/${item.extension}.svg`];
const Icon = icon
? lazy(() => icon().then((v) => ({ default: (v as any).ReactComponent })))
: undefined;
return Icon;
}, [item.extension]);
// const Icon = icon
// ? lazy(() => icon().then((v) => ({ default: (v as any).ReactComponent })))
// : undefined;
// return Icon;
// }, [item.extension]);
if (isPath(data) && data.item.is_dir) return <Folder size={props.size * 0.7} />;

View file

@ -3,7 +3,14 @@ import clsx from 'clsx';
import dayjs from 'dayjs';
import { Barcode, CircleWavyCheck, Clock, Cube, Link, Lock, Snowflake } from 'phosphor-react';
import { useEffect, useState } from 'react';
import { ExplorerContext, ExplorerItem, ObjectKind, useLibraryQuery } from '@sd/client';
import {
ExplorerContext,
ExplorerItem,
ObjectKind,
formatBytes,
isObject,
useLibraryQuery
} from '@sd/client';
import { Button, tw } from '@sd/ui';
import { DefaultProps } from '../primitive/types';
import { Tooltip } from '../tooltip/Tooltip';
@ -11,7 +18,6 @@ import FileThumb from './FileThumb';
import { Divider } from './inspector/Divider';
import FavoriteButton from './inspector/FavoriteButton';
import Note from './inspector/Note';
import { isObject } from './utils';
export const InfoPill = tw.span`inline border border-transparent px-1 text-[11px] font-medium shadow shadow-app-shade/5 bg-app-selected rounded-md text-ink-dull`;
@ -77,7 +83,7 @@ export const Inspector = ({ data, context, ...elementProps }: Props) => {
iconClassNames="my-3 max-h-[150px]"
size={230}
kind={ObjectKind[objectData?.kind || 0]}
className="flex shrink grow-0 bg-green-500"
className="flex shrink grow-0"
data={data}
/>
</div>
@ -112,24 +118,22 @@ export const Inspector = ({ data, context, ...elementProps }: Props) => {
</MetaContainer>
)}
<Divider />
{
<MetaContainer>
<div className="flex flex-wrap gap-1">
<InfoPill>{isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]}</InfoPill>
{item && <InfoPill>{item.extension}</InfoPill>}
{tags?.data?.map((tag) => (
<InfoPill
className="!text-white"
key={tag.id}
style={{ backgroundColor: tag.color + 'CC' }}
>
{tag.name}
</InfoPill>
))}
<PlaceholderPill>Add Tag</PlaceholderPill>
</div>
</MetaContainer>
}
<MetaContainer>
<div className="flex flex-wrap gap-1">
<InfoPill>{isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]}</InfoPill>
{item && <InfoPill>{item.extension}</InfoPill>}
{tags?.data?.map((tag) => (
<InfoPill
className="!text-white"
key={tag.id}
style={{ backgroundColor: tag.color + 'CC' }}
>
{tag.name}
</InfoPill>
))}
<PlaceholderPill>Add Tag</PlaceholderPill>
</div>
</MetaContainer>
<Divider />
<MetaContainer className="!flex-row space-x-2">
<MetaTextLine>
@ -193,15 +197,3 @@ export const Inspector = ({ data, context, ...elementProps }: Props) => {
</div>
);
};
function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

View file

@ -2,11 +2,10 @@ import { useVirtualizer } from '@tanstack/react-virtual';
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useKey, useOnWindowResize } from 'rooks';
import { ExplorerContext, ExplorerItem } from '@sd/client';
import { ExplorerContext, ExplorerItem, isPath } from '@sd/client';
import { ExplorerLayoutMode, getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore';
import FileItem from './FileItem';
import FileRow from './FileRow';
import { isPath } from './utils';
const TOP_BAR_HEIGHT = 46;
const GRID_TEXT_AREA_HEIGHT = 25;

View file

@ -1,9 +0,0 @@
import { ExplorerItem, FilePath } from '@sd/client';
export function isPath(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Path' }> {
return item.type === 'Path';
}
export function isObject(item: ExplorerItem): item is Extract<ExplorerItem, { type: 'Object' }> {
return item.type === 'Object';
}

View file

@ -186,46 +186,46 @@ importers:
'@gorhom/bottom-sheet': ^4.4.5
'@react-native-async-storage/async-storage': ~1.17.11
'@react-native-masked-view/masked-view': 0.2.8
'@react-navigation/bottom-tabs': ^6.5.2
'@react-navigation/drawer': ^6.5.6
'@react-navigation/native': ^6.0.13
'@react-navigation/stack': ^6.3.10
'@rnx-kit/metro-config': ^1.3.2
'@react-navigation/bottom-tabs': ^6.5.4
'@react-navigation/drawer': ^6.5.8
'@react-navigation/native': ^6.1.3
'@react-navigation/stack': ^6.3.12
'@rnx-kit/metro-config': ^1.3.5
'@rspc/client': ^0.0.0-main-7c0a67c1
'@rspc/react': ^0.0.0-main-7c0a67c1
'@sd/assets': workspace:*
'@sd/client': workspace:*
'@sd/config': workspace:*
'@shopify/flash-list': 1.4.0
'@tanstack/react-query': ^4.20.9
'@shopify/flash-list': 1.4.1
'@tanstack/react-query': ^4.24.4
'@types/react': ~18.0.26
'@types/react-native': ~0.70.8
babel-plugin-module-resolver: ^4.1.0
babel-plugin-module-resolver: ^5.0.0
byte-size: ^8.1.0
class-variance-authority: ^0.4.0
dayjs: ^1.11.6
dayjs: ^1.11.5
eslint-plugin-react-native: ^4.0.0
expo: ^47.0.10
expo: ^47.0.13
expo-linking: ~3.3.0
expo-media-library: ~15.0.0
expo-splash-screen: ~0.17.5
expo-status-bar: ~1.4.2
intl: ^1.2.5
lottie-react-native: 5.1.4
metro-minify-terser: ^0.73.6
moti: ^0.21.0
metro-minify-terser: ^0.74.1
moti: ^0.22.0
phosphor-react-native: ^1.1.2
react: 18.1.0
react-hook-form: ^7.41.5
react-hook-form: ^7.43.0
react-native: 0.70.5
react-native-document-picker: ^8.1.1
react-native-fs: ^2.20.0
react-native-gesture-handler: ~2.8.0
react-native-gesture-handler: ~2.9.0
react-native-popup-menu: ^0.16.1
react-native-reanimated: ~2.13.0
react-native-safe-area-context: 4.4.1
react-native-screens: ~3.18.2
react-native-svg: 13.6.0
react-native-reanimated: ~2.14.4
react-native-safe-area-context: 4.5.0
react-native-screens: ~3.19.0
react-native-svg: 13.8.0
react-native-svg-transformer: ^1.0.0
react-native-wheel-color-picker: ^1.2.0
twrnc: ^3.5.0
@ -234,19 +234,19 @@ importers:
use-debounce: ^9.0.2
valtio: ^1.8.0
dependencies:
'@gorhom/bottom-sheet': 4.4.5_cuwuttpow3vlkihawr5ojezmtm
'@gorhom/bottom-sheet': 4.4.5_w366bumyfs4uyicayprrehskae
'@react-native-async-storage/async-storage': 1.17.11_react-native@0.70.5
'@react-native-masked-view/masked-view': 0.2.8_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/bottom-tabs': 6.5.3_4yxn5o6hbqm4eqjpnbll7jojyu
'@react-navigation/drawer': 6.5.7_yrgdxcwem6xwiw4ihiusk6rm7m
'@react-navigation/native': 6.1.2_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/stack': 6.3.11_omkqc3xm6hu7gsihfbjfmuohle
'@react-navigation/bottom-tabs': 6.5.4_tzhjy2gc5kvqqitykreeetpfsq
'@react-navigation/drawer': 6.5.8_5pbm2y6oxn4wiaxvhnj4nykkku
'@react-navigation/native': 6.1.3_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/stack': 6.3.12_tvuqzzu4d6qsmbauwbo7nuei7e
'@rspc/client': 0.0.0-main-7c0a67c1
'@rspc/react': 0.0.0-main-7c0a67c1_7aie4jek34mpa466duyggl5x24
'@rspc/react': 0.0.0-main-7c0a67c1_6niuqkvdadibsmemv5phcmgdcq
'@sd/assets': link:../../packages/assets
'@sd/client': link:../../packages/client
'@shopify/flash-list': 1.4.0_tj3nonr5gneraukzjkxpsiy7yu
'@tanstack/react-query': 4.22.0_tj3nonr5gneraukzjkxpsiy7yu
'@shopify/flash-list': 1.4.1_tj3nonr5gneraukzjkxpsiy7yu
'@tanstack/react-query': 4.24.4_tj3nonr5gneraukzjkxpsiy7yu
byte-size: 8.1.0
class-variance-authority: 0.4.0_typescript@4.9.4
dayjs: 1.11.7
@ -257,33 +257,33 @@ importers:
expo-status-bar: 1.4.2
intl: 1.2.5
lottie-react-native: 5.1.4_tj3nonr5gneraukzjkxpsiy7yu
moti: 0.21.0_xxoigs3lrtexqhfttsykzq4gba
phosphor-react-native: 1.1.2_6rmyt7g6mjmjm44v4kwwqzmzjy
moti: 0.22.0_wwrhtnmkhk6ixblrbdwhxojxqi
phosphor-react-native: 1.1.2_tdhmirddlb52bzkqlwgrsfpe6m
react: 18.1.0
react-hook-form: 7.42.1_react@18.1.0
react-hook-form: 7.43.0_react@18.1.0
react-native: 0.70.5_react@18.1.0
react-native-document-picker: 8.1.3_tj3nonr5gneraukzjkxpsiy7yu
react-native-fs: 2.20.0_react-native@0.70.5
react-native-gesture-handler: 2.8.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-gesture-handler: 2.9.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-popup-menu: 0.16.1
react-native-reanimated: 2.13.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.4.1_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.18.2_tj3nonr5gneraukzjkxpsiy7yu
react-native-svg: 13.6.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.14.4_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.5.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.19.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-svg: 13.8.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-wheel-color-picker: 1.2.0
twrnc: 3.6.0_react-native@0.70.5
use-count-up: 3.0.1_react@18.1.0
use-debounce: 9.0.3_react@18.1.0
valtio: 1.9.0_react@18.1.0
devDependencies:
'@rnx-kit/metro-config': 1.3.3_tj3nonr5gneraukzjkxpsiy7yu
'@rnx-kit/metro-config': 1.3.5_tj3nonr5gneraukzjkxpsiy7yu
'@sd/config': link:../../packages/config
'@types/react': 18.0.27
'@types/react-native': 0.70.9
babel-plugin-module-resolver: 4.1.0
babel-plugin-module-resolver: 5.0.0
eslint-plugin-react-native: 4.0.0
metro-minify-terser: 0.73.7
react-native-svg-transformer: 1.0.0_t622mrclk7o6j6mwz34ema6fve
metro-minify-terser: 0.74.1
react-native-svg-transformer: 1.0.0_kqpsw65h73u7hvgblav6crprhi
typescript: 4.9.4
apps/server:
@ -735,6 +735,15 @@ packages:
source-map: 0.5.7
dev: true
/@babel/generator/7.20.14:
resolution: {integrity: sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@jridgewell/gen-mapping': 0.3.2
jsesc: 2.5.2
dev: true
/@babel/generator/7.20.7:
resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==}
engines: {node: '>=6.9.0'}
@ -1072,6 +1081,14 @@ packages:
'@babel/types': 7.17.0
dev: true
/@babel/parser/7.20.13:
resolution: {integrity: sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.20.7
dev: true
/@babel/parser/7.20.7:
resolution: {integrity: sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==}
engines: {node: '>=6.0.0'}
@ -1372,7 +1389,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.12.9
'@babel/helper-plugin-utils': 7.10.4
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9
'@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.12.9
dev: true
@ -2612,8 +2629,8 @@ packages:
'@babel/helper-plugin-utils': 7.20.2
dev: true
/@babel/plugin-transform-typescript/7.20.7:
resolution: {integrity: sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw==}
/@babel/plugin-transform-typescript/7.20.13:
resolution: {integrity: sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@ -2625,6 +2642,19 @@ packages:
- supports-color
dev: false
/@babel/plugin-transform-typescript/7.20.13_@babel+core@7.20.12:
resolution: {integrity: sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.20.12
'@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
/@babel/plugin-transform-typescript/7.20.7_@babel+core@7.20.12:
resolution: {integrity: sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw==}
engines: {node: '>=6.9.0'}
@ -2907,7 +2937,7 @@ packages:
dependencies:
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-validator-option': 7.18.6
'@babel/plugin-transform-typescript': 7.20.7
'@babel/plugin-transform-typescript': 7.20.13
transitivePeerDependencies:
- supports-color
dev: false
@ -2921,7 +2951,7 @@ packages:
'@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-validator-option': 7.18.6
'@babel/plugin-transform-typescript': 7.20.7_@babel+core@7.20.12
'@babel/plugin-transform-typescript': 7.20.13_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
@ -2999,6 +3029,24 @@ packages:
transitivePeerDependencies:
- supports-color
/@babel/traverse/7.20.13:
resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.14
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.13
'@babel/types': 7.20.7
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types/7.17.0:
resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
engines: {node: '>=6.9.0'}
@ -3997,7 +4045,7 @@ packages:
/@gar/promisify/1.1.3:
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
/@gorhom/bottom-sheet/4.4.5_cuwuttpow3vlkihawr5ojezmtm:
/@gorhom/bottom-sheet/4.4.5_w366bumyfs4uyicayprrehskae:
resolution: {integrity: sha512-Z5Z20wshLUB8lIdtMKoJaRnjd64wBR/q8EeVPThrg+skrcBwBPHfUwZJ2srB0rEszA/01ejSJy/ixyd7Ra7vUA==}
peerDependencies:
react: '*'
@ -4009,8 +4057,8 @@ packages:
invariant: 2.2.4
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-gesture-handler: 2.8.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.13.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-gesture-handler: 2.9.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.14.4_tj3nonr5gneraukzjkxpsiy7yu
dev: false
/@gorhom/portal/1.0.14_tj3nonr5gneraukzjkxpsiy7yu:
@ -5363,8 +5411,8 @@ packages:
/@react-native/polyfills/2.0.0:
resolution: {integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==}
/@react-navigation/bottom-tabs/6.5.3_4yxn5o6hbqm4eqjpnbll7jojyu:
resolution: {integrity: sha512-ZA2Ko9fNwNaaSNn7738KpEk8Doi+yjRfTg8Wb/WvduIaK/28qNLAYWBCUEVjBC55y/9zJOzwc4R8Av2J2MG/4g==}
/@react-navigation/bottom-tabs/6.5.4_tzhjy2gc5kvqqitykreeetpfsq:
resolution: {integrity: sha512-C2Tf+SsO9zc+p/MKUkILso8Dw4KTIscXPi7YhdepyZbM8ZYMGfnZKzffGjLFlLhQXtsFQQuHMelD8sIStTkgyQ==}
peerDependencies:
'@react-navigation/native': ^6.0.0
react: '*'
@ -5372,13 +5420,13 @@ packages:
react-native-safe-area-context: '>= 3.0.0'
react-native-screens: '>= 3.0.0'
dependencies:
'@react-navigation/elements': 1.3.13_7faqyzkwzhg3btbxjjkwqxstpy
'@react-navigation/native': 6.1.2_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/elements': 1.3.14_tyo4vetelyebz5cucognw6udfm
'@react-navigation/native': 6.1.3_tj3nonr5gneraukzjkxpsiy7yu
color: 4.2.3
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-safe-area-context: 4.4.1_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.18.2_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.5.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.19.0_tj3nonr5gneraukzjkxpsiy7yu
warn-once: 0.1.1
dev: false
@ -5396,8 +5444,8 @@ packages:
use-latest-callback: 0.1.5
dev: false
/@react-navigation/drawer/6.5.7_yrgdxcwem6xwiw4ihiusk6rm7m:
resolution: {integrity: sha512-k8l+G05WrP+Il8USxEcaBtZ2Jv/JFn9dS8O1IlqIoddZITrFjsyDhk3zxeE3RWDcc9u0o7HgHfgKHPXxcT8QRQ==}
/@react-navigation/drawer/6.5.8_5pbm2y6oxn4wiaxvhnj4nykkku:
resolution: {integrity: sha512-24okwc1gVJex+NMwau1QfxIRUDfFvdbnwFS/N0bCUXDVu8bqsMeYA00C3xwCpvdRmANbBH4rFRniNbJLAvD4Jg==}
peerDependencies:
'@react-navigation/native': ^6.0.0
react: '*'
@ -5407,34 +5455,34 @@ packages:
react-native-safe-area-context: '>= 3.0.0'
react-native-screens: '>= 3.0.0'
dependencies:
'@react-navigation/elements': 1.3.13_7faqyzkwzhg3btbxjjkwqxstpy
'@react-navigation/native': 6.1.2_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/elements': 1.3.14_tyo4vetelyebz5cucognw6udfm
'@react-navigation/native': 6.1.3_tj3nonr5gneraukzjkxpsiy7yu
color: 4.2.3
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-gesture-handler: 2.8.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.13.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.4.1_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.18.2_tj3nonr5gneraukzjkxpsiy7yu
react-native-gesture-handler: 2.9.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.14.4_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.5.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.19.0_tj3nonr5gneraukzjkxpsiy7yu
warn-once: 0.1.1
dev: false
/@react-navigation/elements/1.3.13_7faqyzkwzhg3btbxjjkwqxstpy:
resolution: {integrity: sha512-LqqK5s2ZfYHn2cQ376jC5V9dQztLH5ixkkJj9WR7JY2g4SghDd39WJhL3Jillw1Mu3F3b9sZwvAK+QkXhnDeAA==}
/@react-navigation/elements/1.3.14_tyo4vetelyebz5cucognw6udfm:
resolution: {integrity: sha512-RBbPhYq+KNFPAkWPaHB9gypq0jTGp/0fkMwRLToJ8jkLtWG4LV+JoQ/erFQnVARkR3Q807n0VnES15EYP4ITMQ==}
peerDependencies:
'@react-navigation/native': ^6.0.0
react: '*'
react-native: '*'
react-native-safe-area-context: '>= 3.0.0'
dependencies:
'@react-navigation/native': 6.1.2_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/native': 6.1.3_tj3nonr5gneraukzjkxpsiy7yu
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-safe-area-context: 4.4.1_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.5.0_tj3nonr5gneraukzjkxpsiy7yu
dev: false
/@react-navigation/native/6.1.2_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-qLUe0asHofr5EhxKjvUBJ9DrPPmR4535IEwmW3oU4DRb3cLbNysjajJKHL8kcYtqPvn9Bx9QZG2x0PMb2vN23A==}
/@react-navigation/native/6.1.3_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-DB5FyG6aqGfcjjVozljF5NEkjWaSymIbQHfWwsjL0YrvC1gfc7E53QXDOjxZ/wfbCo8qZs8RIC/LAgclP2YK/w==}
peerDependencies:
react: '*'
react-native: '*'
@ -5453,8 +5501,8 @@ packages:
nanoid: 3.3.4
dev: false
/@react-navigation/stack/6.3.11_omkqc3xm6hu7gsihfbjfmuohle:
resolution: {integrity: sha512-GWOAyJfPEsjVwDWec1ERwWL5LvManJucCRUZetJqCBs4/mV7HXEt2x6l3SMitHxH1+K+9XuYXI+wBTbK1WDYOA==}
/@react-navigation/stack/6.3.12_tvuqzzu4d6qsmbauwbo7nuei7e:
resolution: {integrity: sha512-sDclO/EflZUbfxZZlz5fk3A0wfegeCOEKGznP9n53s+JrDJL2Dc24Gufg56ENLNRX3CiE1KzsaM4SQLJYf/zsA==}
peerDependencies:
'@react-navigation/native': ^6.0.0
react: '*'
@ -5463,14 +5511,14 @@ packages:
react-native-safe-area-context: '>= 3.0.0'
react-native-screens: '>= 3.0.0'
dependencies:
'@react-navigation/elements': 1.3.13_7faqyzkwzhg3btbxjjkwqxstpy
'@react-navigation/native': 6.1.2_tj3nonr5gneraukzjkxpsiy7yu
'@react-navigation/elements': 1.3.14_tyo4vetelyebz5cucognw6udfm
'@react-navigation/native': 6.1.3_tj3nonr5gneraukzjkxpsiy7yu
color: 4.2.3
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-gesture-handler: 2.8.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.4.1_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.18.2_tj3nonr5gneraukzjkxpsiy7yu
react-native-gesture-handler: 2.9.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-safe-area-context: 4.5.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-screens: 3.19.0_tj3nonr5gneraukzjkxpsiy7yu
warn-once: 0.1.1
dev: false
@ -5590,9 +5638,10 @@ packages:
engines: {node: '>=14'}
dev: false
/@rnx-kit/babel-preset-metro-react-native/1.1.3:
resolution: {integrity: sha512-D5ifPbKKDkpQfumu7iELo1HNaRxy38yGx7rys50ulENAIytCrtOOLwkxdpSKDa+rAXsKoqlcEBCpszdAI3fZxQ==}
/@rnx-kit/babel-preset-metro-react-native/1.1.4:
resolution: {integrity: sha512-ev82sa8Q5Z4a7kQ9pfCKYvpPpPesn0bgOFX8mNx5Gb3uZENb1i1oqySsmw1Qrrf/1KCMi4DKXOI7KezUl8Kf4g==}
peerDependencies:
'@babel/core': ^7.0.0
'@babel/plugin-transform-typescript': ^7.0.0
metro-react-native-babel-preset: '*'
peerDependenciesMeta:
@ -5601,7 +5650,6 @@ packages:
dependencies:
babel-plugin-const-enum: 1.2.0
transitivePeerDependencies:
- '@babel/core'
- supports-color
dev: true
@ -5611,14 +5659,15 @@ packages:
chalk: 4.1.2
dev: true
/@rnx-kit/metro-config/1.3.3_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-s6UXYbGNRGzoOfkwiPYlBFnMJyAGmac1gNnIgdO+7XVyX/gein/IzD/lhGvg8H3H7vKSl0R1N2mX6bNzAlpGcw==}
/@rnx-kit/metro-config/1.3.5_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-8KI7sIaj1ExpH2Et37CXwy+ajdqRqXloGbHYSE4RYvda+PSJ/guU9+moY5NXaRujJtwOFA8qk3/nDvx2suMDeA==}
peerDependencies:
metro-config: '>=0.58.0'
metro-react-native-babel-preset: '*'
react: '*'
react-native: '*'
dependencies:
'@rnx-kit/babel-preset-metro-react-native': 1.1.3
'@rnx-kit/babel-preset-metro-react-native': 1.1.4
'@rnx-kit/console': 1.0.11
'@rnx-kit/tools-node': 1.3.1
'@rnx-kit/tools-workspaces': 0.1.3
@ -5627,7 +5676,6 @@ packages:
transitivePeerDependencies:
- '@babel/core'
- '@babel/plugin-transform-typescript'
- metro-react-native-babel-preset
- supports-color
dev: true
@ -5679,14 +5727,14 @@ packages:
/@rspc/client/0.0.0-main-7c0a67c1:
resolution: {integrity: sha512-PpFkqhp8KlfdFSa8f/Ff5nOg8jhHNq12q8p0ZLYU/1K68JVPQRIk432zpXg48QT/MQbMrogx/PF5E7refhEtvw==}
/@rspc/react/0.0.0-main-7c0a67c1_7aie4jek34mpa466duyggl5x24:
/@rspc/react/0.0.0-main-7c0a67c1_6niuqkvdadibsmemv5phcmgdcq:
resolution: {integrity: sha512-SpsYJ+k/wXUCBDwVuu0WYOVRnrYzQaXyjHGIy/bSVphtYkMhJuZ3UlP2/YxMX4hj4wlDpsTZECzvez5D1vX+pw==}
peerDependencies:
'@tanstack/react-query': ^4.8.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@rspc/client': 0.0.0-main-7c0a67c1
'@tanstack/react-query': 4.22.0_tj3nonr5gneraukzjkxpsiy7yu
'@tanstack/react-query': 4.24.4_tj3nonr5gneraukzjkxpsiy7yu
react: 18.1.0
dev: false
@ -5769,8 +5817,8 @@ packages:
tslib: 1.14.1
dev: false
/@shopify/flash-list/1.4.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-PvPOyk353LuETFnNA038+QaJsAFlCQ2TYC7DHP3YnYqTX72g2BM6qLoLsPaptXKuoXX+dinOo0MbEm7HDjTy1g==}
/@shopify/flash-list/1.4.1_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-yM5dlhqolO/R8FKomqCrSYz0Cc82vJDikxhbu1CXXGp3rPvo/ceP9jJyKueW96SXHsn/87fcSq2BjztWjlp74Q==}
peerDependencies:
'@babel/runtime': '*'
react: '*'
@ -7539,6 +7587,10 @@ packages:
/@tanstack/query-core/4.22.0:
resolution: {integrity: sha512-OeLyBKBQoT265f5G9biReijeP8mBxNFwY7ZUu1dKL+YzqpG5q5z7J/N1eT8aWyKuhyDTiUHuKm5l+oIVzbtrjw==}
/@tanstack/query-core/4.24.4:
resolution: {integrity: sha512-9dqjv9eeB6VHN7lD3cLo16ZAjfjCsdXetSAD5+VyKqLUvcKTL0CklGQRJu+bWzdrS69R6Ea4UZo8obHYZnG6aA==}
dev: false
/@tanstack/react-query-devtools/4.22.0_gp275tdwez2a4eujt75dnnp754:
resolution: {integrity: sha512-YeYFBnfqvb+ZlA0IiJqiHNNSzepNhI1p2o9i8NlhQli9+Zrn230M47OBaBUs8qr3DD1dC2zGB1Dis50Ktz8gAA==}
peerDependencies:
@ -7587,8 +7639,8 @@ packages:
react-dom: 18.2.0_react@18.2.0
use-sync-external-store: 1.2.0_react@18.2.0
/@tanstack/react-query/4.22.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-P9o+HjG42uB/xHR6dMsJaPhtZydSe4v0xdG5G/cEj1oHZAXelMlm67/rYJNQGKgBamKElKogj+HYGF+NY2yHYg==}
/@tanstack/react-query/4.24.4_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@ -7599,7 +7651,7 @@ packages:
react-native:
optional: true
dependencies:
'@tanstack/query-core': 4.22.0
'@tanstack/query-core': 4.24.4
react: 18.1.0
react-native: 0.70.5_react@18.1.0
use-sync-external-store: 1.2.0_react@18.1.0
@ -7940,10 +7992,6 @@ packages:
resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==}
dev: true
/@types/invariant/2.2.35:
resolution: {integrity: sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==}
dev: false
/@types/is-function/1.0.1:
resolution: {integrity: sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q==}
dev: true
@ -9311,7 +9359,7 @@ packages:
dependencies:
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-syntax-typescript': 7.20.0
'@babel/traverse': 7.20.12
'@babel/traverse': 7.20.13
transitivePeerDependencies:
- supports-color
dev: true
@ -9353,6 +9401,18 @@ packages:
pkg-up: 3.1.0
reselect: 4.1.7
resolve: 1.22.1
dev: false
/babel-plugin-module-resolver/5.0.0:
resolution: {integrity: sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q==}
engines: {node: '>= 16'}
dependencies:
find-babel-config: 2.0.0
glob: 8.1.0
pkg-up: 3.1.0
reselect: 4.1.7
resolve: 1.22.1
dev: true
/babel-plugin-named-exports-order/0.0.2:
resolution: {integrity: sha512-OgOYHOLoRK+/mvXU9imKHlG6GkPLYrUCvFXG/CM93R/aNNO8pOOF4aS+S8CCHMDQoNSeiOYEZb/G6RwL95Jktw==}
@ -12457,6 +12517,15 @@ packages:
dependencies:
json5: 0.5.1
path-exists: 3.0.0
dev: false
/find-babel-config/2.0.0:
resolution: {integrity: sha512-dOKT7jvF3hGzlW60Gc3ONox/0rRZ/tz7WCil0bqA1In/3I8f1BctpXahRnEKDySZqci7u+dqq93sZST9fOJpFw==}
engines: {node: '>=16.0.0'}
dependencies:
json5: 2.2.3
path-exists: 4.0.0
dev: true
/find-cache-dir/2.1.0:
resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
@ -13043,6 +13112,17 @@ packages:
once: 1.4.0
path-is-absolute: 1.0.1
/glob/8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.6
once: 1.4.0
dev: true
/global-dirs/0.1.1:
resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==}
engines: {node: '>=4'}
@ -14522,6 +14602,7 @@ packages:
/json5/0.5.1:
resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==}
hasBin: true
dev: false
/json5/1.0.2:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
@ -15250,8 +15331,8 @@ packages:
- supports-color
- utf-8-validate
/metro-minify-terser/0.73.7:
resolution: {integrity: sha512-gbv1fmMOZm6gJ6dQoD+QktlCi2wk6nlTR8j8lQCjeeXGbs6O9e5XLWNPOexHqo7S69bdbohEnfZnLJFcxgHeNw==}
/metro-minify-terser/0.74.1:
resolution: {integrity: sha512-R8l2KbYm1ct9benFSXBjeBDJ48BIyyQo3ECG0TSDX5efLSAS9z0scOCTUC9mcAJ9CO1tjc9ZrjHMNWAOoHO6XA==}
dependencies:
terser: 5.16.1
dev: true
@ -15644,13 +15725,13 @@ packages:
engines: {node: '>=10'}
hasBin: true
/moti/0.21.0_xxoigs3lrtexqhfttsykzq4gba:
resolution: {integrity: sha512-b4UJux4sbHeTDWLU7IfJ1ct3p2N/v6fabQTjeXTXrBcna/Ha3QFJ5TKq3F7hOGeHzt5b8vLanfLdPbJRC/v3jg==}
/moti/0.22.0_wwrhtnmkhk6ixblrbdwhxojxqi:
resolution: {integrity: sha512-KK3fhK8igrqE+mqac4JGajvpwM9WX2QCVn1HmwgXrzUC2dA6jyA2sybmV9Fke0Ahe7/dITKXRZNcd73OTqxdOA==}
peerDependencies:
react-native-reanimated: '*'
dependencies:
framer-motion: 6.5.1_react@18.1.0
react-native-reanimated: 2.13.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-reanimated: 2.14.4_tj3nonr5gneraukzjkxpsiy7yu
transitivePeerDependencies:
- react
- react-dom
@ -16509,7 +16590,7 @@ packages:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
dev: false
/phosphor-react-native/1.1.2_6rmyt7g6mjmjm44v4kwwqzmzjy:
/phosphor-react-native/1.1.2_tdhmirddlb52bzkqlwgrsfpe6m:
resolution: {integrity: sha512-uWYDG4S70JP89jmXljCA0K6/0mjQDHH8GVHFwF+dmbdYkALIE84jMMSemkFtjdsazB1j+xDBJcGaKN3PdEC8uQ==}
peerDependencies:
react: '*'
@ -16519,7 +16600,7 @@ packages:
caniuse-lite: 1.0.30001446
react: 18.1.0
react-native: 0.70.5_react@18.1.0
react-native-svg: 13.6.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-svg: 13.8.0_tj3nonr5gneraukzjkxpsiy7yu
dev: false
/phosphor-react/1.4.1_react@18.2.0:
@ -17395,15 +17476,6 @@ packages:
react-side-effect: 2.1.2_react@18.2.0
dev: false
/react-hook-form/7.42.1_react@18.1.0:
resolution: {integrity: sha512-2UIGqwMZksd5HS55crTT1ATLTr0rAI4jS7yVuqTaoRVDhY2Qc4IyjskCmpnmdYqUNOYFy04vW253tb2JRVh+IQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.1.0
dev: false
/react-hook-form/7.42.1_react@18.2.0:
resolution: {integrity: sha512-2UIGqwMZksd5HS55crTT1ATLTr0rAI4jS7yVuqTaoRVDhY2Qc4IyjskCmpnmdYqUNOYFy04vW253tb2JRVh+IQ==}
engines: {node: '>=12.22.0'}
@ -17413,6 +17485,15 @@ packages:
react: 18.2.0
dev: false
/react-hook-form/7.43.0_react@18.1.0:
resolution: {integrity: sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.1.0
dev: false
/react-inspector/5.1.1_react@18.2.0:
resolution: {integrity: sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg==}
peerDependencies:
@ -17514,8 +17595,8 @@ packages:
utf8: 3.0.0
dev: false
/react-native-gesture-handler/2.8.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-poOSfz/w0IyD6Qwq7aaIRRfEaVTl1ecQFoyiIbpOpfNTjm2B1niY2FLrdVQIOtIOe+K9nH55Qal04nr4jGkHdQ==}
/react-native-gesture-handler/2.9.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-a0BcH3Qb1tgVqUutc6d3VuWQkI1AM3+fJx8dkxzZs9t06qA27QgURYFoklpabuWpsUTzuKRpxleykp25E8m7tg==}
peerDependencies:
react: '*'
react-native: '*'
@ -17536,8 +17617,8 @@ packages:
resolution: {integrity: sha512-xRS7mRh0exwu7Iw8PPVHdM11d13A/KzYjy0/fZx3zVtxISxPkNaDGayau6oa7HqO3Nj0oS9ulFCYjcQfG6vahA==}
dev: false
/react-native-reanimated/2.13.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-yUHyYVIegWWIza4+nVyS3CSmI/Mc8kLFVHw2c6gnSHaYhYA4LeEjH/jBkoMzHk9Xd0Ra3cwtjYKAMG8OTp6JVg==}
/react-native-reanimated/2.14.4_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-DquSbl7P8j4SAmc+kRdd75Ianm8G+IYQ9T4AQ6lrpLVeDkhZmjWI0wkutKWnp6L7c5XNVUrFDUf69dwETLCItQ==}
peerDependencies:
'@babel/core': ^7.0.0-0
react: '*'
@ -17545,7 +17626,7 @@ packages:
dependencies:
'@babel/plugin-transform-object-assign': 7.18.6
'@babel/preset-typescript': 7.18.6
'@types/invariant': 2.2.35
convert-source-map: 1.9.0
invariant: 2.2.4
lodash.isequal: 4.5.0
react: 18.1.0
@ -17556,8 +17637,8 @@ packages:
- supports-color
dev: false
/react-native-safe-area-context/4.4.1_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-N9XTjiuD73ZpVlejHrUWIFZc+6Z14co1K/p1IFMkImU7+avD69F3y+lhkqA2hN/+vljdZrBSiOwXPkuo43nFQA==}
/react-native-safe-area-context/4.5.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-0WORnk9SkREGUg2V7jHZbuN5x4vcxj/1B0QOcXJjdYWrzZHgLcUzYWWIUecUPJh747Mwjt/42RZDOaFn3L8kPQ==}
peerDependencies:
react: '*'
react-native: '*'
@ -17575,8 +17656,8 @@ packages:
react-native: 0.70.5_react@18.1.0
dev: false
/react-native-screens/3.18.2_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-ANUEuvMUlsYJ1QKukEhzhfrvOUO9BVH9Nzg+6eWxpn3cfD/O83yPBOF8Mx6x5H/2+sMy+VS5x/chWOOo/U7QJw==}
/react-native-screens/3.19.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-Ehsmy7jr3H3j5pmN+/FqsAaIAD+k+xkcdePfLcg4rYRbN5X7fJPgaqhcmiCcZ0YxsU8ttsstP9IvRLNQuIkRRA==}
peerDependencies:
react: '*'
react-native: '*'
@ -17587,7 +17668,7 @@ packages:
warn-once: 0.1.1
dev: false
/react-native-svg-transformer/1.0.0_t622mrclk7o6j6mwz34ema6fve:
/react-native-svg-transformer/1.0.0_kqpsw65h73u7hvgblav6crprhi:
resolution: {integrity: sha512-ALHU5VvLLyKM/BvyEG7VYJmqglvaXtU7mGRCxrEwwpJO/GBf1ZMUzc4AeJAjSodj7yYtlDYRxNSt9ySWpaa6JQ==}
peerDependencies:
react-native: '>=0.59.0'
@ -17597,13 +17678,13 @@ packages:
'@svgr/plugin-svgo': 6.5.1_@svgr+core@6.5.1
path-dirname: 1.0.2
react-native: 0.70.5_react@18.1.0
react-native-svg: 13.6.0_tj3nonr5gneraukzjkxpsiy7yu
react-native-svg: 13.8.0_tj3nonr5gneraukzjkxpsiy7yu
transitivePeerDependencies:
- supports-color
dev: true
/react-native-svg/13.6.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-1wjHCMJ8siyZbDZ0MX5wM+Jr7YOkb6GADn4/Z+/u1UwJX8WfjarypxDF3UO1ugMHa+7qor39oY+URMcrgPpiww==}
/react-native-svg/13.8.0_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-G8Mx6W86da+vFimZBJvA93POw8yz0fgDS5biy6oIjMWVJVQSDzCyzwO/zY0yuZmCDhKSZzogl5m0wXXvW2OcTA==}
peerDependencies:
react: '*'
react-native: '*'
@ -19598,7 +19679,7 @@ packages:
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.2
acorn: 8.8.1
acorn: 8.8.2
commander: 2.20.3
source-map-support: 0.5.21
dev: true