Mobile - Settings & Tags (#479)

* Sync tailwind configs

* Switch to new colors part 1

* new colors part 2

* switched to new colors

* settings screens template

* settings progress

* Setting header titles

* Refactor settings icon

* Fix tsconfig relative path issue

* Move mobile eslint config to @sd/config

* Add no-restricted-imports rule to eslint

* rename TextInput to Input & tailwind font sizing

* General Settings screen + Card component

* Library settings screen & delete lib dialog

* autoform hook

* Mini tweaks and new packages

* Exclude android & ios from search

* Add Switch (Input)

* Library General settings screen

* Refactor settings + Switch and Switch Container

* Locations screen & delete and rescan locations

* Rename folder + small tweaks

* Make things Swipeable

* Create Tag Dialog with color picker

* Upgrade to Expo 47

* More tag stuff

* fix pnpm lock

* regen pnpm-lock

* Change CI node version to 16

* Move `isVideo` into shared package

Co-authored-by: Oscar Beaumont <oscar@otbeaumont.me>
This commit is contained in:
Utku 2022-12-13 10:18:03 +03:00 committed by GitHub
parent 78a5754f4e
commit 42f09b0918
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 3714 additions and 2684 deletions

View file

@ -28,7 +28,7 @@ jobs:
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 17 node-version: 16
cache: 'pnpm' cache: 'pnpm'
- name: Install pnpm dependencies - name: Install pnpm dependencies
@ -80,7 +80,7 @@ jobs:
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 17 node-version: 16
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v2.0.1 uses: pnpm/action-setup@v2.0.1
@ -138,7 +138,7 @@ jobs:
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 17 node-version: 16
cache: 'pnpm' cache: 'pnpm'
- name: Install pnpm dependencies - name: Install pnpm dependencies

17
.vscode/settings.json vendored
View file

@ -37,9 +37,18 @@
"rust-analyzer.procMacro.enable": true, "rust-analyzer.procMacro.enable": true,
"rust-analyzer.diagnostics.experimental.enable": false, "rust-analyzer.diagnostics.experimental.enable": false,
"tailwindCSS.experimental.classRegex": [ "tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
"tw`([^`]*)", "tw`([^`]*)",
"tw\\.[^`]+`([^`]*)`", "tw\\.[^`]+`([^`]*)`",
"tw\\(.*?\\).*?`([^`]*)" "tw\\(.*?\\).*?`([^`]*)",
], "tw\\.style\\(([^)]*)\\)"
],
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.code-search": true,
// Hiding these folders bcs they create a lot of noise in the search results
"apps/mobile/android": true,
"apps/mobile/ios": true
}
} }

View file

@ -1,42 +1,7 @@
module.exports = { module.exports = {
env: { ...require('@sd/config/eslint-react-native.js'),
'react-native/react-native': true
},
parser: '@typescript-eslint/parser',
parserOptions: { parserOptions: {
ecmaFeatures: { tsconfigRootDir: __dirname,
jsx: true project: './tsconfig.json'
},
ecmaVersion: 12,
sourceType: 'module'
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended'
],
plugins: ['react', 'react-native'],
rules: {
'react/display-name': 'off',
'react/prop-types': 'off',
'react/no-unescaped-entities': 'off',
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-control-regex': 'off',
'no-mixed-spaces-and-tabs': ['warn', 'smart-tabs']
},
ignorePatterns: ['**/*.js', '**/*.json', 'node_modules', 'android', 'ios', '.expo'],
settings: {
react: {
version: 'detect'
}
} }
}; };

View file

@ -1,3 +1,2 @@
Make sure to run `pnpm i` in this folder after making changes to the `packages`. - Make sure to run `pnpm i` if you make any change to the `package` mobile uses like `assets`.
- If iOS build fails with `node not found` error, run `echo "export NODE_BINARY=$(command -v node)" >> .xcode.env.local` on `mobile/ios/` directory.
- Note: If you add/remove something from `packages/assets` folder, you need to delete node_modules and run `pnpm i` again to link it.

View file

@ -36,6 +36,17 @@ target 'Spacedrive' do
post_install do |installer| post_install do |installer|
react_native_post_install(installer) react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer) __apply_Xcode_12_5_M1_post_install_workaround(installer)
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end end
post_integrate do |installer| post_integrate do |installer|

View file

@ -1,343 +1,343 @@
PODS: PODS:
- boost (1.76.0) - boost (1.76.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXApplication (4.2.2): - EXApplication (5.0.1):
- ExpoModulesCore - ExpoModulesCore
- EXConstants (13.2.4): - EXConstants (14.0.2):
- ExpoModulesCore - ExpoModulesCore
- EXFileSystem (14.1.0): - EXFileSystem (15.1.1):
- ExpoModulesCore - ExpoModulesCore
- EXFont (10.2.1): - EXFont (11.0.1):
- ExpoModulesCore - ExpoModulesCore
- EXMediaLibrary (14.2.0): - EXMediaLibrary (15.0.0):
- ExpoModulesCore - ExpoModulesCore
- React-Core - React-Core
- Expo (46.0.16): - Expo (47.0.8):
- ExpoModulesCore - ExpoModulesCore
- ExpoKeepAwake (10.2.0): - ExpoKeepAwake (11.0.1):
- ExpoModulesCore - ExpoModulesCore
- ExpoModulesCore (0.11.8): - ExpoModulesCore (1.0.3):
- React-Core - React-Core
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- EXSplashScreen (0.16.2): - EXSplashScreen (0.17.5):
- ExpoModulesCore - ExpoModulesCore
- React-Core - React-Core
- FBLazyVector (0.69.6) - FBLazyVector (0.70.5)
- FBReactNativeSpec (0.69.6): - FBReactNativeSpec (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.69.6) - RCTRequired (= 0.70.5)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Core (= 0.69.6) - React-Core (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- fmt (6.2.1) - fmt (6.2.1)
- glog (0.3.5) - glog (0.3.5)
- hermes-engine (0.69.6) - hermes-engine (0.70.5)
- libevent (2.1.12) - libevent (2.1.12)
- lottie-ios (3.4.4) - lottie-ios (3.4.4)
- lottie-react-native (5.1.4): - lottie-react-native (5.1.4):
- lottie-ios (~> 3.4.0) - lottie-ios (~> 3.4.0)
- React-Core - React-Core
- RCT-Folly (2021.06.28.00-v2): - RCT-Folly (2021.07.22.00):
- boost - boost
- DoubleConversion - DoubleConversion
- fmt (~> 6.2.1) - fmt (~> 6.2.1)
- glog - glog
- RCT-Folly/Default (= 2021.06.28.00-v2) - RCT-Folly/Default (= 2021.07.22.00)
- RCT-Folly/Default (2021.06.28.00-v2): - RCT-Folly/Default (2021.07.22.00):
- boost - boost
- DoubleConversion - DoubleConversion
- fmt (~> 6.2.1) - fmt (~> 6.2.1)
- glog - glog
- RCT-Folly/Futures (2021.06.28.00-v2): - RCT-Folly/Futures (2021.07.22.00):
- boost - boost
- DoubleConversion - DoubleConversion
- fmt (~> 6.2.1) - fmt (~> 6.2.1)
- glog - glog
- libevent - libevent
- RCTRequired (0.69.6) - RCTRequired (0.70.5)
- RCTTypeSafety (0.69.6): - RCTTypeSafety (0.70.5):
- FBLazyVector (= 0.69.6) - FBLazyVector (= 0.70.5)
- RCTRequired (= 0.69.6) - RCTRequired (= 0.70.5)
- React-Core (= 0.69.6) - React-Core (= 0.70.5)
- React (0.69.6): - React (0.70.5):
- React-Core (= 0.69.6) - React-Core (= 0.70.5)
- React-Core/DevSupport (= 0.69.6) - React-Core/DevSupport (= 0.70.5)
- React-Core/RCTWebSocket (= 0.69.6) - React-Core/RCTWebSocket (= 0.70.5)
- React-RCTActionSheet (= 0.69.6) - React-RCTActionSheet (= 0.70.5)
- React-RCTAnimation (= 0.69.6) - React-RCTAnimation (= 0.70.5)
- React-RCTBlob (= 0.69.6) - React-RCTBlob (= 0.70.5)
- React-RCTImage (= 0.69.6) - React-RCTImage (= 0.70.5)
- React-RCTLinking (= 0.69.6) - React-RCTLinking (= 0.70.5)
- React-RCTNetwork (= 0.69.6) - React-RCTNetwork (= 0.70.5)
- React-RCTSettings (= 0.69.6) - React-RCTSettings (= 0.70.5)
- React-RCTText (= 0.69.6) - React-RCTText (= 0.70.5)
- React-RCTVibration (= 0.69.6) - React-RCTVibration (= 0.70.5)
- React-bridging (0.69.6): - React-bridging (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-callinvoker (0.69.6) - React-callinvoker (0.70.5)
- React-Codegen (0.69.6): - React-Codegen (0.70.5):
- FBReactNativeSpec (= 0.69.6) - FBReactNativeSpec (= 0.70.5)
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.69.6) - RCTRequired (= 0.70.5)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Core (= 0.69.6) - React-Core (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-Core (0.69.6): - React-Core (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.69.6) - React-Core/Default (= 0.70.5)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/CoreModulesHeaders (0.69.6): - React-Core/CoreModulesHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/Default (0.69.6): - React-Core/Default (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/DevSupport (0.69.6): - React-Core/DevSupport (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.69.6) - React-Core/Default (= 0.70.5)
- React-Core/RCTWebSocket (= 0.69.6) - React-Core/RCTWebSocket (= 0.70.5)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-jsinspector (= 0.69.6) - React-jsinspector (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTActionSheetHeaders (0.69.6): - React-Core/RCTActionSheetHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTAnimationHeaders (0.69.6): - React-Core/RCTAnimationHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTBlobHeaders (0.69.6): - React-Core/RCTBlobHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTImageHeaders (0.69.6): - React-Core/RCTImageHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTLinkingHeaders (0.69.6): - React-Core/RCTLinkingHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTNetworkHeaders (0.69.6): - React-Core/RCTNetworkHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTSettingsHeaders (0.69.6): - React-Core/RCTSettingsHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTTextHeaders (0.69.6): - React-Core/RCTTextHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTVibrationHeaders (0.69.6): - React-Core/RCTVibrationHeaders (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-Core/RCTWebSocket (0.69.6): - React-Core/RCTWebSocket (0.70.5):
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.69.6) - React-Core/Default (= 0.70.5)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- Yoga - Yoga
- React-CoreModules (0.69.6): - React-CoreModules (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/CoreModulesHeaders (= 0.69.6) - React-Core/CoreModulesHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-RCTImage (= 0.69.6) - React-RCTImage (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-cxxreact (0.69.6): - React-cxxreact (0.70.5):
- boost (= 1.76.0) - boost (= 1.76.0)
- DoubleConversion - DoubleConversion
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.69.6) - React-callinvoker (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsinspector (= 0.69.6) - React-jsinspector (= 0.70.5)
- React-logger (= 0.69.6) - React-logger (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- React-runtimeexecutor (= 0.69.6) - React-runtimeexecutor (= 0.70.5)
- React-hermes (0.69.6): - React-hermes (0.70.5):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.06.28.00-v2) - RCT-Folly/Futures (= 2021.07.22.00)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.69.6) - React-jsiexecutor (= 0.70.5)
- React-jsinspector (= 0.69.6) - React-jsinspector (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- React-jsi (0.69.6): - React-jsi (0.70.5):
- boost (= 1.76.0) - boost (= 1.76.0)
- DoubleConversion - DoubleConversion
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-jsi/Default (= 0.69.6) - React-jsi/Default (= 0.70.5)
- React-jsi/Default (0.69.6): - React-jsi/Default (0.70.5):
- boost (= 1.76.0) - boost (= 1.76.0)
- DoubleConversion - DoubleConversion
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-jsiexecutor (0.69.6): - React-jsiexecutor (0.70.5):
- DoubleConversion - DoubleConversion
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- React-jsinspector (0.69.6) - React-jsinspector (0.70.5)
- React-logger (0.69.6): - React-logger (0.70.5):
- glog - glog
- react-native-document-picker (8.1.2): - react-native-document-picker (8.1.2):
- React-Core - React-Core
- react-native-safe-area-context (4.3.1): - react-native-safe-area-context (4.4.1):
- RCT-Folly - RCT-Folly
- RCTRequired - RCTRequired
- RCTTypeSafety - RCTTypeSafety
- React - React-Core
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- React-perflogger (0.69.6) - React-perflogger (0.70.5)
- React-RCTActionSheet (0.69.6): - React-RCTActionSheet (0.70.5):
- React-Core/RCTActionSheetHeaders (= 0.69.6) - React-Core/RCTActionSheetHeaders (= 0.70.5)
- React-RCTAnimation (0.69.6): - React-RCTAnimation (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTAnimationHeaders (= 0.69.6) - React-Core/RCTAnimationHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTBlob (0.69.6): - React-RCTBlob (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTBlobHeaders (= 0.69.6) - React-Core/RCTBlobHeaders (= 0.70.5)
- React-Core/RCTWebSocket (= 0.69.6) - React-Core/RCTWebSocket (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-RCTNetwork (= 0.69.6) - React-RCTNetwork (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTImage (0.69.6): - React-RCTImage (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTImageHeaders (= 0.69.6) - React-Core/RCTImageHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-RCTNetwork (= 0.69.6) - React-RCTNetwork (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTLinking (0.69.6): - React-RCTLinking (0.70.5):
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTLinkingHeaders (= 0.69.6) - React-Core/RCTLinkingHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTNetwork (0.69.6): - React-RCTNetwork (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTNetworkHeaders (= 0.69.6) - React-Core/RCTNetworkHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTSettings (0.69.6): - React-RCTSettings (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.69.6) - RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTSettingsHeaders (= 0.69.6) - React-Core/RCTSettingsHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTText (0.69.6): - React-RCTText (0.70.5):
- React-Core/RCTTextHeaders (= 0.69.6) - React-Core/RCTTextHeaders (= 0.70.5)
- React-RCTVibration (0.69.6): - React-RCTVibration (0.70.5):
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.69.6) - React-Codegen (= 0.70.5)
- React-Core/RCTVibrationHeaders (= 0.69.6) - React-Core/RCTVibrationHeaders (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.69.6) - ReactCommon/turbomodule/core (= 0.70.5)
- React-runtimeexecutor (0.69.6): - React-runtimeexecutor (0.70.5):
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (0.69.6): - ReactCommon/turbomodule/core (0.70.5):
- DoubleConversion - DoubleConversion
- glog - glog
- RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly (= 2021.07.22.00)
- React-bridging (= 0.69.6) - React-bridging (= 0.70.5)
- React-callinvoker (= 0.69.6) - React-callinvoker (= 0.70.5)
- React-Core (= 0.69.6) - React-Core (= 0.70.5)
- React-cxxreact (= 0.69.6) - React-cxxreact (= 0.70.5)
- React-jsi (= 0.69.6) - React-jsi (= 0.70.5)
- React-logger (= 0.69.6) - React-logger (= 0.70.5)
- React-perflogger (= 0.69.6) - React-perflogger (= 0.70.5)
- RNCAsyncStorage (1.17.10): - RNCAsyncStorage (1.17.10):
- React-Core - React-Core
- RNCMaskedView (0.2.8): - RNCMaskedView (0.2.8):
@ -346,9 +346,9 @@ PODS:
- React-Core - React-Core
- RNFS (2.20.0): - RNFS (2.20.0):
- React-Core - React-Core
- RNGestureHandler (2.5.0): - RNGestureHandler (2.8.0):
- React-Core - React-Core
- RNReanimated (2.10.0): - RNReanimated (2.12.0):
- DoubleConversion - DoubleConversion
- FBLazyVector - FBLazyVector
- FBReactNativeSpec - FBReactNativeSpec
@ -375,10 +375,10 @@ PODS:
- React-RCTText - React-RCTText
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- RNScreens (3.15.0): - RNScreens (3.18.2):
- React-Core - React-Core
- React-RCTImage - React-RCTImage
- RNSVG (13.0.0): - RNSVG (13.4.0):
- React-Core - React-Core
- Yoga (1.14.0) - Yoga (1.14.0)
@ -392,7 +392,7 @@ DEPENDENCIES:
- EXMediaLibrary (from `../../../node_modules/expo-media-library/ios`) - EXMediaLibrary (from `../../../node_modules/expo-media-library/ios`)
- Expo (from `../../../node_modules/expo`) - Expo (from `../../../node_modules/expo`)
- ExpoKeepAwake (from `../../../node_modules/expo-keep-awake/ios`) - ExpoKeepAwake (from `../../../node_modules/expo-keep-awake/ios`)
- ExpoModulesCore (from `../../../node_modules/expo-modules-core/ios`) - ExpoModulesCore (from `../../../node_modules/expo-modules-core`)
- EXSplashScreen (from `../../../node_modules/expo-splash-screen/ios`) - EXSplashScreen (from `../../../node_modules/expo-splash-screen/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
@ -466,7 +466,7 @@ EXTERNAL SOURCES:
ExpoKeepAwake: ExpoKeepAwake:
:path: "../../../node_modules/expo-keep-awake/ios" :path: "../../../node_modules/expo-keep-awake/ios"
ExpoModulesCore: ExpoModulesCore:
:path: "../../../node_modules/expo-modules-core/ios" :path: "../../../node_modules/expo-modules-core"
EXSplashScreen: EXSplashScreen:
:path: "../../../node_modules/expo-splash-screen/ios" :path: "../../../node_modules/expo-splash-screen/ios"
FBLazyVector: FBLazyVector:
@ -559,62 +559,62 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234 boost: a7c83b31436843459a1961bfd74b96033dc77234
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
EXApplication: e418d737a036e788510f2c4ad6c10a7d54d18586 EXApplication: 034b1c40a8e9fe1bff76a1e511ee90dff64ad834
EXConstants: 7c44785d41d8e959d527d23d29444277a4d1ee73 EXConstants: 3c86653c422dd77e40d10cbbabb3025003977415
EXFileSystem: 927e0a8885aa9c49e50fc38eaba2c2389f2f1019 EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6
EXFont: 06df627203afcb8a3b3152ec06eb2f11f46f0cff EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80
EXMediaLibrary: e8c8a365701629b7b21dd29769b86e8320610f18 EXMediaLibrary: b1c4f78878e45f6a359aff3a059e1660c41b73ab
Expo: 7ac824960a6059d6c68e73f432c8e6bf6d92a0ef Expo: 36b5f625d36728adbdd1934d4d57182f319ab832
ExpoKeepAwake: 0e8f18142e71bbf2c7f6aa66ebed249ba1420320 ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318
ExpoModulesCore: 39ec590ce622289c060183aba57f77b1e73b4e11 ExpoModulesCore: b5d21c8880afda6fb6ee95469f9ac2ec9b98e995
EXSplashScreen: 799bece80089219b2c989c1082d70f3b00995cda EXSplashScreen: 3e989924f61a8dd07ee4ea584c6ba14be9b51949
FBLazyVector: 739d2f9719faecb463c7aa191591af31c8c94182 FBLazyVector: affa4ba1bfdaac110a789192f4d452b053a86624
FBReactNativeSpec: 957de82f66e31f2f14bbec34e37242282fdd26de FBReactNativeSpec: fe8b5f1429cfe83a8d72dc8ed61dc7704cac8745
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: c2c873a670bc435451449f918c2b3ab3c39255fc hermes-engine: 7fe5fc6ef707b7fdcb161b63898ec500e285653d
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8 lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8
lottie-react-native: b702fab740cdb952a8e2354713d3beda63ff97b0 lottie-react-native: b702fab740cdb952a8e2354713d3beda63ff97b0
RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: c8c080849a3670601d5c7056023a2176067a69d8 RCTRequired: 21229f84411088e5d8538f21212de49e46cc83e2
RCTTypeSafety: 710aef40f5ae246bc5fff7e873855b17ed11c180 RCTTypeSafety: 62eed57a32924b09edaaf170a548d1fc96223086
React: b6bb382534be4de9d367ef3d04f92108c1768160 React: f0254ccddeeef1defe66c6b1bb9133a4f040792b
React-bridging: 0fca0337cef9305026814907dd29254a833a2db7 React-bridging: e46911666b7ec19538a620a221d6396cd293d687
React-callinvoker: 700e6eb96b5f7f2fdd96d7263cd4627d2fa080ed React-callinvoker: 66b62e2c34546546b2f21ab0b7670346410a2b53
React-Codegen: fd21633c4b9f47d0681bbb54b173a203963a5e4d React-Codegen: b6999435966df3bdf82afa3f319ba0d6f9a8532a
React-Core: 8ec15c9727c8c01b1e4f14cad5bd21f7c1d56d49 React-Core: dabbc9d1fe0a11d884e6ee1599789cf8eb1058a5
React-CoreModules: 79486447bf901292a83df52d4f7acbecda296723 React-CoreModules: 5b6b7668f156f73a56420df9ec68ca2ec8f2e818
React-cxxreact: 9022135650dd9960a60a1361e9add424c6c37ab9 React-cxxreact: c7ca2baee46db22a30fce9e639277add3c3f6ad1
React-hermes: b5ce7fb460ff6d39e7bb9bbe1f523272c4b85c0b React-hermes: c93e1d759ad5560dfea54d233013d7d2c725c286
React-jsi: 4ccb3599c422ad071e3895c5feab9b0afc40505d React-jsi: a565dcb49130ed20877a9bb1105ffeecbb93d02d
React-jsiexecutor: c61b60de03b3474e5749b8a8fd8e6507630d62c4 React-jsiexecutor: 31564fa6912459921568e8b0e49024285a4d584b
React-jsinspector: eaacb698c5af7a99131bc1933806372c20222dfd React-jsinspector: badd81696361249893a80477983e697aab3c1a34
React-logger: ebb4d31bbbe4f1a8a1a9b658d7429210b8f68160 React-logger: fdda34dd285bdb0232e059b19d9606fa0ec3bb9c
react-native-document-picker: f5ec1a712ca2a975c233117f044817bb8393cad4 react-native-document-picker: f5ec1a712ca2a975c233117f044817bb8393cad4
react-native-safe-area-context: 6c12e3859b6f27b25de4fee8201cfb858432d8de react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
React-perflogger: 1fb1ad5333b43a5137afd7608695f7a42c5efd27 React-perflogger: e68d3795cf5d247a0379735cbac7309adf2fb931
React-RCTActionSheet: a435bd67689433575a1e5d7614b021d2c17f0726 React-RCTActionSheet: 05452c3b281edb27850253db13ecd4c5a65bc247
React-RCTAnimation: d097c5ed2d00735958508617555abd85183b94e2 React-RCTAnimation: 578eebac706428e68466118e84aeacf3a282b4da
React-RCTBlob: f43a0fceb328e1a40aa52701a4eba955635444ab React-RCTBlob: f47a0aa61e7d1fb1a0e13da832b0da934939d71a
React-RCTImage: 08f4428e931efe0eefb94443c8ca08cfb250a556 React-RCTImage: 60f54b66eed65d86b6dffaf4733d09161d44929d
React-RCTLinking: 3a8851e818652582f87e5a7577302e6ad7e1de3e React-RCTLinking: 91073205aeec4b29450ca79b709277319368ac9e
React-RCTNetwork: 19f7c66b612e2336eefdfbc7ab3a9bd8ca4e21cf React-RCTNetwork: ca91f2c9465a7e335c8a5fae731fd7f10572213b
React-RCTSettings: 9324e718a865ff01e4a96be4c65923581b2d5170 React-RCTSettings: 1a9a5d01337d55c18168c1abe0f4a589167d134a
React-RCTText: 9cadcd5d982c1d25f7439f47354b1c1b75e60105 React-RCTText: c591e8bd9347a294d8416357ca12d779afec01d5
React-RCTVibration: 285f8538386c660e6b9497e204636acd93bf7fcc React-RCTVibration: 8e5c8c5d17af641f306d7380d8d0fe9b3c142c48
React-runtimeexecutor: 0af71c94f968fa10015bf0119951bccd2e4d8865 React-runtimeexecutor: 7401c4a40f8728fd89df4a56104541b760876117
ReactCommon: fe7580b9d10f00249facf25659e0ec051320cc8a ReactCommon: c9246996e73bf75a2c6c3ff15f1e16707cdc2da9
RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
RNFlashList: 18d906a373da5ff16776e5013df9495d826edc7e RNFlashList: 18d906a373da5ff16776e5013df9495d826edc7e
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50 RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
RNReanimated: 7faa787e8d4493fbc95fab2ad331fa7625828cfa RNReanimated: 2a91e85fcd343f8af3c58d3425b99fdd285590a5
RNScreens: 4a1af06327774490d97342c00aee0c2bafb497b7 RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
RNSVG: 42a0c731b11179ebbd27a3eeeafa7201ebb476ff RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
Yoga: 75bf4b0131cfb46a659cd0c13309b79a6fcff66d Yoga: eca980a5771bf114c41a754098cd85e6e0d90ed7
PODFILE CHECKSUM: b77befb1871220c1a94408eeae0857d78b685698 PODFILE CHECKSUM: 144e94249445b1054f55a9e9533c8e6e80450047
COCOAPODS: 1.11.3 COCOAPODS: 1.11.3

View file

@ -361,7 +361,9 @@
"FB_SONARKIT_ENABLED=1", "FB_SONARKIT_ENABLED=1",
); );
INFOPLIST_FILE = Spacedrive/Info.plist; INFOPLIST_FILE = Spacedrive/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; INFOPLIST_KEY_CFBundleDisplayName = Spacedrive;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -454,7 +456,9 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = H7MGS2DHHJ; DEVELOPMENT_TEAM = H7MGS2DHHJ;
INFOPLIST_FILE = Spacedrive/Info.plist; INFOPLIST_FILE = Spacedrive/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; INFOPLIST_KEY_CFBundleDisplayName = Spacedrive;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -581,7 +585,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift, /usr/lib/swift,
"$(inherited)", "$(inherited)",
@ -635,7 +639,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift, /usr/lib/swift,
"$(inherited)", "$(inherited)",

View file

@ -15,7 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@gorhom/bottom-sheet": "^4.4.5", "@gorhom/bottom-sheet": "^4.4.5",
"@react-native-async-storage/async-storage": "~1.17.10", "@react-native-async-storage/async-storage": "~1.17.3",
"@react-native-masked-view/masked-view": "0.2.8", "@react-native-masked-view/masked-view": "0.2.8",
"@react-navigation/bottom-tabs": "^6.4.0", "@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/drawer": "^6.5.0", "@react-navigation/drawer": "^6.5.0",
@ -25,41 +25,45 @@
"@rspc/react": "^0.0.0-main-7c0a67c1", "@rspc/react": "^0.0.0-main-7c0a67c1",
"@sd/assets": "workspace:*", "@sd/assets": "workspace:*",
"@sd/client": "workspace:*", "@sd/client": "workspace:*",
"@shopify/flash-list": "^1.3.1", "@shopify/flash-list": "1.3.1",
"@tanstack/react-query": "^4.12.0", "@tanstack/react-query": "^4.12.0",
"byte-size": "^8.1.0", "byte-size": "^8.1.0",
"class-variance-authority": "^0.2.3", "class-variance-authority": "^0.2.3",
"dayjs": "^1.11.6", "dayjs": "^1.11.6",
"expo": "~46.0.15", "expo": "^47.0.0",
"expo-linking": "~3.2.2", "expo-linking": "~3.2.3",
"expo-media-library": "~14.2.0", "expo-media-library": "~15.0.0",
"expo-splash-screen": "~0.16.2", "expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.0", "expo-status-bar": "~1.4.2",
"intl": "^1.2.5", "intl": "^1.2.5",
"lottie-react-native": "^5.1.4", "lottie-react-native": "5.1.4",
"moti": "^0.20.0", "moti": "^0.20.0",
"phosphor-react-native": "^1.1.2", "phosphor-react-native": "^1.1.2",
"react": "18.0.0", "react": "18.1.0",
"react-native": "0.69.6", "react-hook-form": "^7.36.1",
"react-native": "0.70.5",
"react-native-document-picker": "^8.1.1", "react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0", "react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.5.0", "react-native-gesture-handler": "~2.8.0",
"react-native-reanimated": "~2.10.0", "react-native-reanimated": "~2.12.0",
"react-native-safe-area-context": "4.3.1", "react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.15.0", "react-native-screens": "~3.18.0",
"react-native-svg": "13.0.0", "react-native-svg": "13.4.0",
"react-native-wheel-color-picker": "^1.2.0",
"twrnc": "^3.4.1", "twrnc": "^3.4.1",
"use-count-up": "^3.0.1", "use-count-up": "^3.0.1",
"use-debounce": "^8.0.4",
"valtio": "^1.7.1" "valtio": "^1.7.1"
}, },
"devDependencies": { "devDependencies": {
"@rnx-kit/metro-config": "^1.3.2", "@rnx-kit/metro-config": "^1.3.2",
"@types/react": "~18.0.0", "@sd/config": "workspace:*",
"@types/react-native": "~0.69.5", "@types/react": "~18.0.24",
"@types/react-native": "~0.70.6",
"babel-plugin-module-resolver": "^4.1.0", "babel-plugin-module-resolver": "^4.1.0",
"eslint-plugin-react-native": "^4.0.0", "eslint-plugin-react-native": "^4.0.0",
"metro-minify-terser": "^0.73.1", "metro-minify-terser": "^0.73.1",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"typescript": "^4.8.4" "typescript": "^4.6.3"
} }
} }

View file

@ -16,7 +16,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useDeviceContext } from 'twrnc'; import { useDeviceContext } from 'twrnc';
import { GlobalModals } from './containers/modals/GlobalModals'; import { GlobalModals } from './containers/modal/GlobalModals';
import { reactNativeLink } from './lib/rspcReactNativeTransport'; import { reactNativeLink } from './lib/rspcReactNativeTransport';
import tw from './lib/tailwind'; import tw from './lib/tailwind';
import RootNavigator from './navigation'; import RootNavigator from './navigation';
@ -26,7 +26,8 @@ const NavigatorTheme: Theme = {
...DefaultTheme, ...DefaultTheme,
colors: { colors: {
...DefaultTheme.colors, ...DefaultTheme.colors,
background: tw.color('gray-650') // Default screen background
background: tw.color('app')
} }
}; };
@ -38,7 +39,7 @@ function AppContainer() {
const { library } = useCurrentLibrary(); const { library } = useCurrentLibrary();
return ( return (
<SafeAreaProvider style={tw`flex-1 bg-gray-650`}> <SafeAreaProvider style={tw`flex-1 bg-app`}>
<GestureHandlerRootView style={tw`flex-1`}> <GestureHandlerRootView style={tw`flex-1`}>
<BottomSheetModalProvider> <BottomSheetModalProvider>
<StatusBar style="light" /> <StatusBar style="light" />

View file

@ -90,13 +90,13 @@ const placeholderFileItems: ExplorerItem[] = [
} }
]; ];
export interface DeviceProps { type DeviceProps = {
name: string; name: string;
size: string; size: string;
type: keyof typeof DeviceIcon; type: keyof typeof DeviceIcon;
locations: { name: string; folder?: boolean; format?: string; icon?: string }[]; locations: { name: string; folder?: boolean; format?: string; icon?: string }[];
runningJob?: { amount: number; task: string }; runningJob?: { amount: number; task: string };
} };
const DeviceIcon = { const DeviceIcon = {
phone: <DeviceMobileCamera color="white" weight="fill" size={18} style={tw`mr-2`} />, phone: <DeviceMobileCamera color="white" weight="fill" size={18} style={tw`mr-2`} />,
@ -107,21 +107,20 @@ const DeviceIcon = {
const Device = ({ name, locations, size, type }: DeviceProps) => { const Device = ({ name, locations, size, type }: DeviceProps) => {
return ( return (
<View style={tw`my-2 bg-gray-600 border rounded-md border-gray-550`}> <View style={tw`my-2 bg-app-overlay border rounded-md border-app-line`}>
<View style={tw`flex flex-row items-center px-3.5 pt-3 pb-2`}> <View style={tw`flex flex-row items-center px-3.5 pt-3 pb-2`}>
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
{DeviceIcon[type]} {DeviceIcon[type]}
<Text style={tw`text-base font-semibold text-white`}>{name || 'Unnamed Device'}</Text> <Text style={tw`text-base font-semibold text-ink`}>{name || 'Unnamed Device'}</Text>
{/* P2P Lock */} {/* P2P Lock */}
<View style={tw`flex flex-row rounded items-center ml-2 bg-gray-500 py-[1px] px-[4px]`}> <View style={tw`flex flex-row rounded items-center ml-2 bg-app-box py-[1px] px-[4px]`}>
<Lock weight="bold" size={12} color={tw.color('gray-400')} /> <Lock weight="bold" size={12} color={tw.color('ink-dull')} />
<Text style={tw`text-gray-400 font-semibold ml-0.5 text-xs`}>P2P</Text> <Text style={tw`text-ink-dull font-semibold ml-0.5 text-xs`}>P2P</Text>
</View> </View>
</View> </View>
{/* Size */} {/* Size */}
<Text style={tw`ml-2 text-sm font-semibold text-gray-400`}>{size}</Text> <Text style={tw`ml-2 text-sm font-semibold text-ink-dull`}>{size}</Text>
</View> </View>
{/* Locations/Files TODO: Maybe use FlashList? */}
<FlatList <FlatList
data={placeholderFileItems} data={placeholderFileItems}
renderItem={({ item }) => <FileItem data={item} />} renderItem={({ item }) => <FileItem data={item} />}

View file

@ -5,12 +5,12 @@ import tw from '~/lib/tailwind';
import FileItem from './FileItem'; import FileItem from './FileItem';
type Props = { type ExplorerProps = {
locationId: number; locationId: number;
path?: string; path?: string;
}; };
const Explorer = ({ locationId, path }: Props) => { const Explorer = ({ locationId, path }: ExplorerProps) => {
const { data } = useLibraryQuery([ const { data } = useLibraryQuery([
'locations.getExplorerData', 'locations.getExplorerData',
{ {
@ -23,7 +23,7 @@ const Explorer = ({ locationId, path }: Props) => {
return ( return (
<View style={tw`flex-1`}> <View style={tw`flex-1`}>
<Text style={tw`text-xl font-bold text-white mt-4`}>Location id:{locationId}</Text> <Text style={tw`text-xl font-bold text-ink mt-4`}>Location id:{locationId}</Text>
{data && ( {data && (
<FlashList <FlashList
data={data.items} data={data.items}

View file

@ -1,6 +1,7 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { ExplorerItem } from '@sd/client'; import { ExplorerItem, isVideoExt } from '@sd/client';
import { Pressable, Text, View } from 'react-native'; import { Pressable, Text, View } from 'react-native';
import { isPath } from '~/types/helper';
import tw from '../../lib/tailwind'; import tw from '../../lib/tailwind';
import { SharedScreenProps } from '../../navigation/SharedScreens'; import { SharedScreenProps } from '../../navigation/SharedScreens';
@ -17,13 +18,12 @@ const FileItem = ({ data }: FileItemProps) => {
const navigation = useNavigation<SharedScreenProps<'Location'>['navigation']>(); const navigation = useNavigation<SharedScreenProps<'Location'>['navigation']>();
function handlePress() { function handlePress() {
// if (!data) return; if (isPath(data) && data.is_dir) {
// if (data.is_dir) { navigation.navigate('Location', { id: data.location_id });
// navigation.navigate('Location', { id: data.location_id }); } else {
// } else { setData(data);
// setData(data); fileRef.current.present();
// fileRef.current.present(); }
// }
} }
return ( return (
@ -31,10 +31,10 @@ const FileItem = ({ data }: FileItemProps) => {
<View style={tw`w-[90px] h-[80px] items-center`}> <View style={tw`w-[90px] h-[80px] items-center`}>
<FileThumb <FileThumb
data={data} data={data}
kind={data.extension === 'zip' ? 'zip' : isVideo(data.extension) ? 'video' : 'other'} kind={data.extension === 'zip' ? 'zip' : isVideoExt(data.extension) ? 'video' : 'other'}
/> />
<View style={tw`px-1.5 py-[1px] mt-1`}> <View style={tw`px-1.5 py-[1px] mt-1`}>
<Text numberOfLines={1} style={tw`text-xs font-medium text-center text-gray-300`}> <Text numberOfLines={1} style={tw`text-xs font-medium text-center text-ink-dull`}>
{data?.name} {data?.name}
</Text> </Text>
</View> </View>
@ -44,36 +44,3 @@ const FileItem = ({ data }: FileItemProps) => {
}; };
export default FileItem; export default FileItem;
// Copied from FileItem.tsx (interface/src/components/explorer/FileItem.tsx)
function isVideo(extension: string) {
return [
'avi',
'asf',
'mpeg',
'mts',
'mpe',
'vob',
'qt',
'mov',
'asf',
'asx',
'mjpeg',
'ts',
'mxf',
'm2ts',
'f4v',
'wm',
'3gp',
'm4v',
'wmv',
'mp4',
'webm',
'flv',
'mpg',
'hevc',
'ogv',
'swf',
'wtv'
].includes(extension);
}

View file

@ -84,7 +84,7 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
<Svg <Svg
// Background // Background
style={tw`absolute top-0 left-0`} style={tw`absolute top-0 left-0`}
fill={tw.color('gray-550')} fill={tw.color('app-box')}
width={45 * size} width={45 * size}
height={60 * size} height={60 * size}
viewBox="0 0 65 81" viewBox="0 0 65 81"
@ -94,7 +94,7 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
<Svg <Svg
// Peel // Peel
style={tw`absolute top-[2px] -right-[0.6px]`} style={tw`absolute top-[2px] -right-[0.6px]`}
fill={tw.color('gray-500')} fill={tw.color('app-highlight')}
width={15 * size} width={15 * size}
height={15 * size} height={15 * size}
viewBox="0 0 41 41" viewBox="0 0 41 41"

View file

@ -7,7 +7,8 @@ import { Pressable, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import tw from '~/lib/tailwind'; import tw from '~/lib/tailwind';
const Header = () => { // Default header with search bar and button to open drawer
export default function Header() {
const navigation = useNavigation<DrawerNavigationHelpers>(); const navigation = useNavigation<DrawerNavigationHelpers>();
const { top } = useSafeAreaInsets(); const { top } = useSafeAreaInsets();
@ -16,7 +17,7 @@ const Header = () => {
return ( return (
<View <View
style={tw.style('mx-4 bg-gray-500 border border-[#333949] bg-opacity-40 rounded', { style={tw.style('mx-4 bg-app-overlay border border-app-line rounded', {
marginTop: top + 10 marginTop: top + 10
})} })}
> >
@ -26,18 +27,16 @@ const Header = () => {
animate={{ rotate: isDrawerOpen ? '90deg' : '0deg' }} animate={{ rotate: isDrawerOpen ? '90deg' : '0deg' }}
transition={{ type: 'timing' }} transition={{ type: 'timing' }}
> >
<List size={20} color={tw.color('gray-300')} weight="fill" /> <List size={20} color={tw.color('ink-faint')} weight="fill" />
</MotiView> </MotiView>
</Pressable> </Pressable>
<Pressable <Pressable
style={tw`flex-1 h-full justify-center`} style={tw`flex-1 h-full justify-center`}
onPress={() => navigation.navigate('Search')} onPress={() => navigation.navigate('Search')}
> >
<Text style={tw`text-gray-300 font-medium text-sm`}>Search</Text> <Text style={tw`text-ink-dull font-medium text-sm`}>Search</Text>
</Pressable> </Pressable>
</View> </View>
</View> </View>
); );
}; }
export default Header;

View file

@ -0,0 +1,25 @@
import React from 'react';
import { View, ViewProps } from 'react-native';
import tw from '~/lib/tailwind';
type CardProps = {
children: React.ReactNode;
} & ViewProps;
const Card = ({ children, ...props }: CardProps) => {
const { style, ...otherProps } = props;
return (
<View
style={tw.style(
'px-4 py-3 border border-app-line rounded-lg bg-app-overlay',
style as string
)}
{...otherProps}
>
{children}
</View>
);
};
export default Card;

View file

@ -29,7 +29,7 @@ const CollapsibleView = ({ title, titleStyle, containerStyle, children }: Collap
}} }}
transition={{ type: 'timing' }} transition={{ type: 'timing' }}
> >
<CaretRight color={tw.color('gray-200')} size={16} style={tw`mr-3`} /> <CaretRight color={tw.color('ink-dull')} size={16} style={tw`mr-3`} />
</MotiView> </MotiView>
</Pressable> </Pressable>
<AnimatedHeight hide={hide}>{children}</AnimatedHeight> <AnimatedHeight hide={hide}>{children}</AnimatedHeight>

View file

@ -57,7 +57,7 @@ const Dialog = (props: DialogProps) => {
<Modal renderToHardwareTextureAndroid transparent visible={props.isVisible ?? visible}> <Modal renderToHardwareTextureAndroid transparent visible={props.isVisible ?? visible}>
{/* Backdrop */} {/* Backdrop */}
<Pressable <Pressable
style={tw`bg-black bg-opacity-50 absolute inset-0`} style={tw`bg-app-box/40 absolute inset-0`}
onPress={handleCloseDialog} onPress={handleCloseDialog}
disabled={props.disableBackdropClose || props.loading} disabled={props.disableBackdropClose || props.loading}
/> />
@ -73,15 +73,16 @@ const Dialog = (props: DialogProps) => {
animate={{ translateY: 0 }} animate={{ translateY: 0 }}
transition={{ type: 'timing', duration: 200 }} transition={{ type: 'timing', duration: 200 }}
> >
{/* TODO: Blur may look cool here */}
<View <View
style={tw`min-w-[360px] max-w-[380px] rounded-md bg-gray-650 border border-gray-550 shadow-md overflow-hidden`} style={tw`min-w-[360px] max-w-[380px] rounded-md bg-app border border-app-line shadow shadow-app-shade overflow-hidden`}
> >
<View style={tw`p-5`}> <View style={tw`p-5`}>
{/* Title */} {/* Title */}
<Text style={tw`font-bold text-white text-base`}>{props.title}</Text> <Text style={tw`font-bold text-ink text-base`}>{props.title}</Text>
{/* Description */} {/* Description */}
{props.description && ( {props.description && (
<Text style={tw`text-sm text-gray-300 mt-2 leading-normal`}> <Text style={tw`text-sm text-ink-dull mt-2 leading-normal`}>
{props.description} {props.description}
</Text> </Text>
)} )}
@ -90,7 +91,7 @@ const Dialog = (props: DialogProps) => {
</View> </View>
{/* Actions */} {/* Actions */}
<View <View
style={tw`flex flex-row items-center px-3 py-3 bg-gray-600 border-t border-gray-550`} style={tw`flex flex-row items-center px-3 py-3 bg-app-highlight border-t border-app-line`}
> >
{props.loading && <PulseAnimation style={tw`h-7`} />} {props.loading && <PulseAnimation style={tw`h-7`} />}
<View style={tw`flex-grow`} /> <View style={tw`flex-grow`} />
@ -100,17 +101,17 @@ const Dialog = (props: DialogProps) => {
disabled={props.loading} // Disables Close button if loading disabled={props.loading} // Disables Close button if loading
onPress={handleCloseDialog} onPress={handleCloseDialog}
> >
<Text style={tw`text-white text-sm`}>Close</Text> <Text style={tw`text-ink text-sm`}>Close</Text>
</Button> </Button>
{props.ctaAction && ( {props.ctaAction && (
<Button <Button
style={tw`ml-2`} style={tw`ml-2`}
variant={props.ctaDanger ? 'danger' : 'primary'} variant={props.ctaDanger ? 'danger' : 'accent'}
size="md" size="md"
onPress={props.ctaAction} onPress={props.ctaAction}
disabled={props.ctaDisabled || props.loading} disabled={props.ctaDisabled || props.loading}
> >
<Text style={tw`text-white text-sm`}>{props.ctaLabel}</Text> <Text style={tw`text-ink text-sm`}>{props.ctaLabel}</Text>
</Button> </Button>
)} )}
</View> </View>

View file

@ -17,8 +17,8 @@ const ModalBackdrop = (props: BottomSheetBackdropProps) => (
const ModalHandle = (props: BottomSheetHandleProps) => ( const ModalHandle = (props: BottomSheetHandleProps) => (
<BottomSheetHandle <BottomSheetHandle
{...props} {...props}
style={tw`bg-gray-600 rounded-t-xl`} style={tw`bg-app rounded-t-xl`}
indicatorStyle={tw`bg-gray-550`} indicatorStyle={tw`bg-app-highlight`}
/> />
); );
@ -27,7 +27,7 @@ export const Modal = forwardRef<BottomSheetModalMethods, BottomSheetModalProps>(
ref={ref} ref={ref}
backdropComponent={ModalBackdrop} backdropComponent={ModalBackdrop}
handleComponent={ModalHandle} handleComponent={ModalHandle}
backgroundStyle={tw.style('bg-gray-600')} backgroundStyle={tw`bg-app`}
{...props} {...props}
/> />
)); ));

View file

@ -7,11 +7,10 @@ import tw from '~/lib/tailwind';
const button = cva(['border rounded-md items-center justify-center shadow-sm'], { const button = cva(['border rounded-md items-center justify-center shadow-sm'], {
variants: { variants: {
variant: { variant: {
default: 'bg-gray-50 border-gray-100', danger: ['bg-red-600 border-red-800'],
primary: ['bg-primary-600'], gray: ['bg-app-button border-app-line'],
danger: ['bg-red-600'], dark_gray: ['bg-app border-app-box'],
gray: ['bg-gray-100 border-gray-200'], accent: ['bg-accent border-accent-deep shadow-md shadow-app-shade/10']
dark_gray: ['bg-gray-500 border-gray-600']
}, },
size: { size: {
default: ['py-1', 'px-3'], default: ['py-1', 'px-3'],
@ -24,7 +23,7 @@ const button = cva(['border rounded-md items-center justify-center shadow-sm'],
} }
}, },
defaultVariants: { defaultVariants: {
variant: 'default', variant: 'gray',
size: 'default' size: 'default'
} }
}); });

View file

@ -6,7 +6,7 @@ type DividerProps = {
}; };
const Divider = ({ style }: DividerProps) => { const Divider = ({ style }: DividerProps) => {
return <View style={[tw`w-full my-1 h-[1px] bg-gray-550`, style]} />; return <View style={[tw`w-full my-1 h-[1px] bg-app-line`, style]} />;
}; };
export default Divider; export default Divider;

View file

@ -1,12 +1,12 @@
import { VariantProps, cva } from 'class-variance-authority'; import { VariantProps, cva } from 'class-variance-authority';
import { FC } from 'react'; import { FC } from 'react';
import { TextInput as RNTextInput, TextInputProps as RNTextInputProps } from 'react-native'; import { TextInputProps as RNTextInputProps, TextInput } from 'react-native';
import tw from '~/lib/tailwind'; import tw from '~/lib/tailwind';
const input = cva(['text-sm rounded-md border shadow-sm'], { const input = cva(['text-sm rounded-md border shadow-sm'], {
variants: { variants: {
variant: { variant: {
default: 'bg-gray-550 border-gray-500 text-white' default: 'bg-app border-app-line text-ink'
}, },
size: { size: {
default: ['py-2', 'px-3'] default: ['py-2', 'px-3']
@ -20,11 +20,11 @@ const input = cva(['text-sm rounded-md border shadow-sm'], {
type InputProps = VariantProps<typeof input> & RNTextInputProps; type InputProps = VariantProps<typeof input> & RNTextInputProps;
export const TextInput: FC<InputProps> = ({ variant, ...props }) => { export const Input: FC<InputProps> = ({ variant, ...props }) => {
const { style, ...otherProps } = props; const { style, ...otherProps } = props;
return ( return (
<RNTextInput <TextInput
placeholderTextColor={tw.color('gray-300')} placeholderTextColor={tw.color('ink-dull')}
style={tw.style(input({ variant }), style as string)} style={tw.style(input({ variant }), style as string)}
{...otherProps} {...otherProps}
/> />

View file

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

View file

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

View file

@ -0,0 +1,32 @@
import { CaretRight, Icon } from 'phosphor-react-native';
import { Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
type SettingsItemProps = {
title: string;
onPress?: () => void;
leftIcon?: Icon;
rightArea?: React.ReactNode;
};
export function SettingsItem(props: SettingsItemProps) {
return (
<Pressable onPress={props.onPress}>
<View style={tw`flex flex-row items-center justify-between px-3 bg-app-overlay`}>
<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-sm text-ink`}>{props.title}</Text>
</View>
{props.rightArea ? props.rightArea : <CaretRight size={20} color={tw.color('ink-faint')} />}
</View>
</Pressable>
);
}
export function SettingsItemDivider() {
return (
<View style={tw`bg-app-overlay`}>
<View style={tw`mx-3 border-b border-b-app-line`} />
</View>
);
}

View file

@ -1,27 +0,0 @@
import { FC } from 'react';
import { Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import FolderIcon from '../../components/icons/FolderIcon';
interface BrowseLocationItemProps {
folderName: string;
onPress: () => void;
}
const BrowseLocationItem: FC<BrowseLocationItemProps> = (props) => {
const { folderName, onPress } = props;
return (
<Pressable onPress={onPress}>
<View style={tw.style('flex mb-[4px] flex-row items-center py-2 px-2 rounded')}>
<FolderIcon size={18} />
<Text style={tw.style('text-gray-300 text-sm font-medium ml-2')} numberOfLines={1}>
{folderName}
</Text>
</View>
</Pressable>
);
};
export default BrowseLocationItem;

View file

@ -1,25 +0,0 @@
import { FC } from 'react';
import { ColorValue, Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
type BrowseTagItemProps = {
tagName: string;
tagColor: ColorValue;
onPress: () => void;
};
const BrowseTagItem: FC<BrowseTagItemProps> = (props) => {
const { tagName, tagColor, onPress } = props;
return (
<Pressable onPress={onPress}>
<View style={tw.style('flex mb-[4px] flex-row items-center py-2 px-2 rounded')}>
<View style={tw.style('w-3 h-3 rounded-full', { backgroundColor: tagColor })} />
<Text style={tw.style('text-gray-300 text-sm font-medium ml-2')} numberOfLines={1}>
{tagName}
</Text>
</View>
</Pressable>
);
};
export default BrowseTagItem;

View file

@ -1,7 +1,7 @@
import { queryClient, useBridgeMutation, useCurrentLibrary } from '@sd/client'; import { queryClient, useBridgeMutation, useCurrentLibrary } from '@sd/client';
import { useState } from 'react'; import { useState } from 'react';
import Dialog from '~/components/layout/Dialog'; import Dialog from '~/components/layout/Dialog';
import { TextInput } from '~/components/primitive/Input'; import { Input } from '~/components/primitive/Input';
type Props = { type Props = {
onSubmit?: () => void; onSubmit?: () => void;
@ -11,7 +11,7 @@ type Props = {
const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props) => { const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props) => {
const [libName, setLibName] = useState(''); const [libName, setLibName] = useState('');
const [createLibOpen, setCreateLibOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { switchLibrary } = useCurrentLibrary(); const { switchLibrary } = useCurrentLibrary();
@ -22,6 +22,7 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
// Reset form // Reset form
setLibName(''); setLibName('');
// We do this instead of invalidating the query because it triggers a full app re-render??
queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]); queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]);
// Switch to the new library // Switch to the new library
@ -31,14 +32,14 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
}, },
onSettled: () => { onSettled: () => {
// Close create lib dialog // Close create lib dialog
setCreateLibOpen(false); setIsOpen(false);
} }
} }
); );
return ( return (
<Dialog <Dialog
isVisible={createLibOpen} isVisible={isOpen}
setIsVisible={setCreateLibOpen} setIsVisible={setIsOpen}
title="Create New Library" title="Create New Library"
description="Choose a name for your new library, you can configure this and more settings from the library settings later on." description="Choose a name for your new library, you can configure this and more settings from the library settings later on."
ctaLabel="Create" ctaLabel="Create"
@ -47,9 +48,9 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
ctaDisabled={libName.length === 0} ctaDisabled={libName.length === 0}
trigger={children} trigger={children}
disableBackdropClose={disableBackdropClose} disableBackdropClose={disableBackdropClose}
onClose={() => setLibName('')} // Reset form onClose onClose={() => setLibName('')} // Resets form onClose
> >
<TextInput <Input
value={libName} value={libName}
onChangeText={(text) => setLibName(text)} onChangeText={(text) => setLibName(text)}
placeholder="My Cool Library" placeholder="My Cool Library"

View file

@ -0,0 +1,42 @@
import { queryClient, useBridgeMutation } from '@sd/client';
import { useState } from 'react';
import Dialog from '~/components/layout/Dialog';
type Props = {
libraryUuid: string;
onSubmit?: () => void;
children: React.ReactNode;
};
const DeleteLibraryDialog = ({ children, onSubmit, libraryUuid }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const { mutate: deleteLibrary, isLoading: deleteLibLoading } = useBridgeMutation(
'library.delete',
{
onSuccess: () => {
queryClient.invalidateQueries(['library.list']);
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
}
}
);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
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}
ctaDanger
/>
);
};
export default DeleteLibraryDialog;

View file

@ -0,0 +1,41 @@
import { useLibraryMutation } from '@sd/client';
import { useState } from 'react';
import Dialog from '~/components/layout/Dialog';
type Props = {
locationId: number;
onSubmit?: () => void;
children: React.ReactNode;
};
const DeleteLocationDialog = ({ children, onSubmit, locationId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const { mutate: deleteLoc, isLoading: deleteLocLoading } = useLibraryMutation(
'locations.delete',
{
onSuccess: () => {
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
}
}
);
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
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}
ctaDanger
/>
);
};
export default DeleteLocationDialog;

View file

@ -0,0 +1,98 @@
import { queryClient, useLibraryMutation } from '@sd/client';
import React, { useState } from 'react';
import { Pressable, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import Dialog from '~/components/layout/Dialog';
import { Input } from '~/components/primitive/Input';
import tw 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={tw.style({ backgroundColor: tagColor }, 'w-5 h-5 rounded-full')}
/>
<Input
style={tw`flex-1 ml-2`}
value={tagName}
onChangeText={(text) => setTagName(text)}
placeholder="Name"
/>
</View>
{showPicker && (
<View style={tw`h-64 mt-4`}>
<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

@ -0,0 +1,38 @@
import { useLibraryMutation } from '@sd/client';
import { useState } from 'react';
import Dialog from '~/components/layout/Dialog';
type Props = {
tagId: number;
onSubmit?: () => void;
children: React.ReactNode;
};
const DeleteTagDialog = ({ children, onSubmit, tagId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const { mutate: deleteTag, isLoading: deleteTagLoading } = useLibraryMutation('tags.delete', {
onSuccess: () => {
onSubmit?.();
},
onSettled: () => {
// Close dialog
setIsOpen(false);
}
});
return (
<Dialog
isVisible={isOpen}
setIsVisible={setIsOpen}
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}
ctaDanger
/>
);
};
export default DeleteTagDialog;

View file

@ -0,0 +1,92 @@
import { Tag, queryClient, useLibraryMutation } from '@sd/client';
import React, { useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import Dialog from '~/components/layout/Dialog';
import { Input } from '~/components/primitive/Input';
import tw 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`mb-1 text-xs font-medium text-ink-dull ml-1 mt-3`}>Name</Text>
<Input value={tagName} onChangeText={(t) => setTagName(t)} />
<Text style={tw`mb-1 text-xs font-medium text-ink-dull ml-1 mt-3`}>Color</Text>
<View style={tw`flex flex-row items-center ml-2`}>
<Pressable
onPress={() => setShowPicker((v) => !v)}
style={tw.style({ backgroundColor: tagColor }, 'w-5 h-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`flex-1 ml-2`} />
</View>
{showPicker && (
<View style={tw`h-64 mt-4`}>
<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

@ -25,7 +25,7 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
<View> <View>
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
<Image source={require('@sd/assets/images/logo.png')} style={tw`w-[35px] h-[35px]`} /> <Image source={require('@sd/assets/images/logo.png')} style={tw`w-[35px] h-[35px]`} />
<Text style={tw`text-base font-bold text-white ml-2`}>Spacedrive</Text> <Text style={tw`text-base font-bold text-ink ml-2`}>Spacedrive</Text>
</View> </View>
<Divider style={tw`my-4`} /> <Divider style={tw`my-4`} />
{/* Library Manager */} {/* Library Manager */}
@ -37,7 +37,7 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
</View> </View>
{/* Settings */} {/* Settings */}
<Pressable onPress={() => navigation.navigate('Settings')}> <Pressable onPress={() => navigation.navigate('Settings')}>
<Gear color="white" size={24} /> <Gear color={tw.color('ink')} size={24} />
</Pressable> </Pressable>
</View> </View>
</DrawerContentScrollView> </DrawerContentScrollView>

View file

@ -1,4 +1,5 @@
import { useDrawerStatus } from '@react-navigation/drawer'; import { useDrawerStatus } from '@react-navigation/drawer';
import { useNavigation } from '@react-navigation/native';
import { MotiView } from 'moti'; import { MotiView } from 'moti';
import { CaretRight, Gear, Lock, Plus } from 'phosphor-react-native'; import { CaretRight, Gear, Lock, Plus } from 'phosphor-react-native';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@ -21,62 +22,73 @@ const DrawerLibraryManager = () => {
const { library: currentLibrary, libraries, switchLibrary } = useCurrentLibrary(); const { library: currentLibrary, libraries, switchLibrary } = useCurrentLibrary();
const navigation = useNavigation();
return ( return (
<View> <View>
<Pressable onPress={() => setDropdownClosed((v) => !v)}> <Pressable onPress={() => setDropdownClosed((v) => !v)}>
<View <View
style={tw.style( style={tw.style(
'flex flex-row justify-between items-center px-3 h-10 w-full bg-gray-500 border border-[#333949] bg-opacity-40 shadow-sm', 'flex flex-row justify-between items-center px-3 h-10 w-full bg-app-box border border-app-darkLine shadow-sm',
dropdownClosed ? 'rounded' : 'rounded-t border-b-gray-550' dropdownClosed ? 'rounded' : 'rounded-t border-b-app-box'
)} )}
> >
<Text style={tw`text-gray-200 text-sm font-semibold`}>{currentLibrary?.config.name}</Text> <Text style={tw`text-ink text-sm font-semibold`}>{currentLibrary?.config.name}</Text>
<MotiView <MotiView
animate={{ animate={{
rotateZ: dropdownClosed ? '0deg' : '90deg' rotateZ: dropdownClosed ? '0deg' : '90deg'
}} }}
transition={{ type: 'timing' }} transition={{ type: 'timing' }}
> >
<CaretRight color={tw.color('text-gray-200')} size={16} style={tw`ml-2`} /> <CaretRight color={tw.color('text-ink')} size={16} style={tw`ml-2`} />
</MotiView> </MotiView>
</View> </View>
</Pressable> </Pressable>
<AnimatedHeight hide={dropdownClosed}> <AnimatedHeight hide={dropdownClosed}>
<View <View
style={tw`py-2 px-2 bg-gray-500 border-l border-b border-r border-[#333949] bg-opacity-40 rounded-b`} style={tw`py-2 px-2 bg-app-box border-l border-b border-r border-app-darkLine rounded-b`}
> >
{/* Libraries */} {/* Libraries */}
{libraries?.map((library) => ( {libraries?.map((library) => (
<Pressable key={library.uuid} onPress={() => switchLibrary(library.uuid)}> <Pressable key={library.uuid} onPress={() => switchLibrary(library.uuid)}>
<View <View
style={tw.style( style={tw.style(
'p-2', 'p-2 mt-1',
currentLibrary.uuid === library.uuid && 'bg-gray-500 bg-opacity-70 rounded' currentLibrary.uuid === library.uuid && 'bg-accent rounded'
)} )}
> >
<Text style={tw`text-sm text-gray-200 font-semibold`}>{library.config.name}</Text> <Text
style={tw.style(
'text-sm text-ink font-semibold',
currentLibrary.uuid === library.uuid && 'text-white'
)}
>
{library.config.name}
</Text>
</View> </View>
</Pressable> </Pressable>
))} ))}
<Divider style={tw`mt-2 mb-2`} /> <Divider style={tw`mt-2 mb-2`} />
{/* Menu */} {/* Menu */}
<Pressable onPress={() => console.log('settings')}> <Pressable
onPress={() => navigation.navigate('Settings', { screen: 'LibraryGeneralSettings' })}
>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}> <View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Gear size={16} color={tw.color('gray-100')} style={tw`mr-2`} /> <Gear size={16} color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Library Settings</Text> <Text style={tw`text-sm text-ink font-semibold`}>Library Settings</Text>
</View> </View>
</Pressable> </Pressable>
{/* Create Library */} {/* Create Library */}
<CreateLibraryDialog> <CreateLibraryDialog>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}> <View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Plus size={16} weight="bold" color={tw.color('gray-100')} style={tw`mr-2`} /> <Plus size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Add Library</Text> <Text style={tw`text-sm text-ink font-semibold`}>Add Library</Text>
</View> </View>
</CreateLibraryDialog> </CreateLibraryDialog>
<Pressable onPress={() => console.log('lock')}> <Pressable onPress={() => console.log('TODO: lock')}>
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}> <View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
<Lock size={16} weight="bold" color={tw.color('gray-100')} style={tw`mr-2`} /> <Lock size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Lock</Text> <Text style={tw`text-sm text-ink font-semibold`}>Lock</Text>
</View> </View>
</Pressable> </Pressable>
</View> </View>

View file

@ -8,12 +8,12 @@ import tw from '~/lib/tailwind';
import FolderIcon from '../../components/icons/FolderIcon'; import FolderIcon from '../../components/icons/FolderIcon';
import CollapsibleView from '../../components/layout/CollapsibleView'; import CollapsibleView from '../../components/layout/CollapsibleView';
import ImportModal from '../modals/ImportModal'; import ImportModal from '../modal/ImportModal';
interface DrawerLocationItemProps { type DrawerLocationItemProps = {
folderName: string; folderName: string;
onPress: () => void; onPress: () => void;
} };
const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => { const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
const { folderName, onPress } = props; const { folderName, onPress } = props;
@ -30,9 +30,9 @@ const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
); );
}; };
interface DrawerLocationsProp { type DrawerLocationsProp = {
stackName: string; stackName: string;
} };
const DrawerLocations = ({ stackName }: DrawerLocationsProp) => { const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
const navigation = useNavigation<DrawerNavigationHelpers>(); const navigation = useNavigation<DrawerNavigationHelpers>();
@ -64,7 +64,7 @@ const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
</View> </View>
{/* Add Location */} {/* Add Location */}
<Pressable onPress={() => importModalRef.current.present()}> <Pressable onPress={() => importModalRef.current.present()}>
<View style={tw`border border-dashed rounded border-gray-450 border-opacity-60 mt-1`}> <View style={tw`border border-dashed rounded border-app-line border-opacity-80 mt-1`}>
<Text style={tw`text-xs font-bold text-center text-gray-400 px-2 py-2`}> <Text style={tw`text-xs font-bold text-center text-gray-400 px-2 py-2`}>
Add Location Add Location
</Text> </Text>

View file

@ -5,6 +5,7 @@ import { ColorValue, Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind'; import tw from '~/lib/tailwind';
import CollapsibleView from '../../components/layout/CollapsibleView'; import CollapsibleView from '../../components/layout/CollapsibleView';
import CreateTagDialog from '../dialog/tag/CreateTagDialog';
type DrawerTagItemProps = { type DrawerTagItemProps = {
tagName: string; tagName: string;
@ -56,6 +57,12 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
/> />
))} ))}
</View> </View>
{/* Add Tag */}
<CreateTagDialog>
<View style={tw`border border-dashed rounded border-app-line border-opacity-80 mt-1`}>
<Text style={tw`text-xs font-bold text-center text-gray-400 px-2 py-2`}>Add Tag</Text>
</View>
</CreateTagDialog>
</CollapsibleView> </CollapsibleView>
); );
}; };

View file

@ -10,10 +10,10 @@ import Divider from '../../components/primitive/Divider';
import tw from '../../lib/tailwind'; import tw from '../../lib/tailwind';
import { useFileModalStore } from '../../stores/modalStore'; import { useFileModalStore } from '../../stores/modalStore';
interface MetaItemProps { type MetaItemProps = {
title: string; title: string;
value: string; value: string;
} };
function MetaItem({ title, value }: MetaItemProps) { function MetaItem({ title, value }: MetaItemProps) {
return ( return (
@ -33,7 +33,7 @@ export const FileModal = () => {
<> <>
<Modal ref={fileRef} snapPoints={['60%', '90%']}> <Modal ref={fileRef} snapPoints={['60%', '90%']}>
{data && ( {data && (
<View style={tw`flex-1 p-4 bg-gray-600`}> <View style={tw`flex-1 p-4 bg-app`}>
{/* File Icon / Name */} {/* File Icon / Name */}
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
<FileIcon data={data} size={1.6} /> <FileIcon data={data} size={1.6} />
@ -48,7 +48,7 @@ export const FileModal = () => {
<Text style={tw`ml-1 text-xs text-gray-400`}>15 Aug</Text> <Text style={tw`ml-1 text-xs text-gray-400`}>15 Aug</Text>
</View> </View>
<Pressable style={tw`mt-2`} onPress={() => fileDetailsRef.current.present()}> <Pressable style={tw`mt-2`} onPress={() => fileDetailsRef.current.present()}>
<Text style={tw`text-sm text-primary-500`}>More</Text> <Text style={tw`text-sm text-accent`}>More</Text>
</Pressable> </Pressable>
</View> </View>
</View> </View>
@ -70,10 +70,10 @@ export const FileModal = () => {
snapPoints={['70%']} snapPoints={['70%']}
> >
{data && ( {data && (
<BottomSheetScrollView style={tw`flex-1 p-4 bg-gray-600`}> <BottomSheetScrollView style={tw`flex-1 p-4 bg-app`}>
{/* Back Button */} {/* Back Button */}
<Pressable style={tw`w-full ml-4`} onPress={() => fileDetailsRef.current.close()}> <Pressable style={tw`w-full ml-4`} onPress={() => fileDetailsRef.current.close()}>
<CaretLeft color={tw.color('primary-500')} size={20} /> <CaretLeft color={tw.color('accent')} size={20} />
</Pressable> </Pressable>
{/* File Icon / Name */} {/* File Icon / Name */}
<View style={tw`items-center`}> <View style={tw`items-center`}>

View file

@ -10,14 +10,15 @@ import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef'; 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<BottomSheetModal, unknown>((_, ref) => {
const modalRef = useForwardedRef(ref); const modalRef = useForwardedRef(ref);
const { mutate: createLocation } = useLibraryMutation('locations.create', { const { mutate: createLocation } = useLibraryMutation('locations.create', {
onError: (error, variables, context) => { onError: (error) => {
console.error(error); console.error(error);
}, },
onSettled: (data, error, variables, context) => { onSettled: () => {
// Close the modal // Close the modal
modalRef.current?.close(); modalRef.current?.close();
} }
@ -89,7 +90,6 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
// Gets Actual Path // Gets Actual Path
const path = (await ML.getAssetInfoAsync(assetId)).localUri; const path = (await ML.getAssetInfoAsync(assetId)).localUri;
// Permission Granted
const libraryPath = Platform.select({ const libraryPath = Platform.select({
android: '', android: '',
ios: path.replace('file://', '').split('Media/DCIM/')[0] + 'Media/DCIM/' ios: path.replace('file://', '').split('Media/DCIM/')[0] + 'Media/DCIM/'
@ -100,10 +100,10 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
indexer_rules_ids: [] indexer_rules_ids: []
}); });
const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo }); // const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo });
assets.assets.map(async (i) => { // assets.assets.map(async (i) => {
console.log((await ML.getAssetInfoAsync(i)).localUri); // console.log((await ML.getAssetInfoAsync(i)).localUri);
}); // });
}, [createLocation]); }, [createLocation]);
// const testFN = useCallback(async () => { // const testFN = useCallback(async () => {
@ -126,14 +126,14 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
return ( return (
<Modal ref={modalRef} snapPoints={['20%']}> <Modal ref={modalRef} snapPoints={['20%']}>
<View style={tw`flex-1 px-6 pt-1 pb-2 bg-gray-600`}> <View style={tw`flex-1 px-6 pt-1 pb-2 bg-app-box`}>
{/* <Button size="md" variant="primary" style={tw`my-2`} onPress={testFN}> {/* <Button size="md" variant="accent" style={tw`my-2`} onPress={testFN}>
<Text>TEST</Text> <Text>TEST</Text>
</Button> */} </Button> */}
<Button size="md" variant="primary" style={tw`my-2`} onPress={handleFilesButton}> <Button size="md" variant="accent" style={tw`my-2`} onPress={handleFilesButton}>
<Text>Import from Files</Text> <Text>Import from Files</Text>
</Button> </Button>
<Button size="md" variant="primary" onPress={handlePhotosButton}> <Button size="md" variant="accent" onPress={handlePhotosButton}>
<Text>Import from Photos</Text> <Text>Import from Photos</Text>
</Button> </Button>
</View> </View>

View file

@ -0,0 +1,21 @@
import { useEffect } from 'react';
import { FieldValues, UseFormReturn } from 'react-hook-form';
import { useDebouncedCallback } from 'use-debounce';
// Same as useDebouncedForm, just a bit more general to use it with all forms.
export function useAutoForm<TFieldValues extends FieldValues = FieldValues, TContext = any>(
form: UseFormReturn<TFieldValues, TContext>,
callback: (data: any) => void,
/**
*Wait time in miliseconds
*/
waitTime = 500
) {
const debounced = useDebouncedCallback(callback, waitTime);
// listen for any form changes
form.watch(debounced);
// persist unchanged data when the component is unmounted
useEffect(() => () => debounced.flush(), [debounced]);
}

View file

@ -1,7 +1,6 @@
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SplashScreen from 'expo-splash-screen'; import * as SplashScreen from 'expo-splash-screen';
import { lazy, useEffect } from 'react'; import { Suspense, lazy } from 'react';
import { Suspense } from 'react';
import { Platform } from 'react-native'; import { Platform } from 'react-native';
// Enable the splash screen // Enable the splash screen

View file

@ -2,6 +2,7 @@ import { DrawerScreenProps, createDrawerNavigator } from '@react-navigation/draw
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native'; import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
import { StackScreenProps } from '@react-navigation/stack'; import { StackScreenProps } from '@react-navigation/stack';
import DrawerContent from '~/containers/drawer/DrawerContent'; import DrawerContent from '~/containers/drawer/DrawerContent';
import tw from '~/lib/tailwind';
import type { RootStackParamList } from '.'; import type { RootStackParamList } from '.';
import type { TabParamList } from './TabNavigator'; import type { TabParamList } from './TabNavigator';
@ -16,7 +17,7 @@ export default function DrawerNavigator() {
screenOptions={{ screenOptions={{
headerShown: false, headerShown: false,
drawerStyle: { drawerStyle: {
backgroundColor: 'rgb(10,10,12)', backgroundColor: tw.color('app-darkBox'),
width: '75%' width: '75%'
}, },
overlayColor: 'transparent', overlayColor: 'transparent',

View file

@ -1,14 +1,13 @@
/**
* Learn more about deep linking with React Navigation
* https://reactnavigation.org/docs/deep-linking
* https://reactnavigation.org/docs/configuring-links
*/
import { LinkingOptions } from '@react-navigation/native'; import { LinkingOptions } from '@react-navigation/native';
import * as Linking from 'expo-linking'; import * as Linking from 'expo-linking';
import { RootStackParamList } from '.'; import { RootStackParamList } from '.';
// TODO: Deep linking for React Navigation. It will allow us to do spacedrive://tags/{id} etc. /**
* TODO: Deep linking for React Navigation. It will allow us to do spacedrive://tags/{id} etc.
* https://reactnavigation.org/docs/deep-linking
* https://reactnavigation.org/docs/configuring-links
*/
const linking: LinkingOptions<RootStackParamList> = { const linking: LinkingOptions<RootStackParamList> = {
prefixes: [Linking.createURL('/')], prefixes: [Linking.createURL('/')],
config: { config: {

View file

@ -0,0 +1,125 @@
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
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';
import GeneralSettingsScreen from '~/screens/settings/client/GeneralSettings';
import LibrarySettingsScreen from '~/screens/settings/client/LibrarySettings';
import PrivacySettingsScreen from '~/screens/settings/client/PrivacySettings';
import AboutScreen from '~/screens/settings/info/About';
import SupportScreen from '~/screens/settings/info/Support';
import KeysSettingsScreen from '~/screens/settings/library/KeysSettings';
import LibraryGeneralSettingsScreen from '~/screens/settings/library/LibraryGeneralSettings';
import LocationSettingsScreen from '~/screens/settings/library/LocationSettings';
import NodesSettingsScreen from '~/screens/settings/library/NodesSettings';
import TagsSettingsScreen from '~/screens/settings/library/TagsSettings';
const SettingsStack = createStackNavigator<SettingsStackParamList>();
export default function SettingsNavigator() {
return (
<SettingsStack.Navigator
initialRouteName="Home"
screenOptions={{
headerBackTitleVisible: false,
headerStyle: tw`bg-app`,
headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base`
// headerShadowVisible: false // will disable the white line under
}}
>
<SettingsStack.Screen
name="Home"
component={SettingsScreen}
options={{ headerTitle: 'Settings' }}
/>
{/* Client */}
<SettingsStack.Screen
name="GeneralSettings"
component={GeneralSettingsScreen}
options={{ headerTitle: 'General Settings' }}
/>
<SettingsStack.Screen
name="LibrarySettings"
component={LibrarySettingsScreen}
options={{ headerTitle: 'Libraries' }}
/>
<SettingsStack.Screen
name="AppearanceSettings"
component={AppearanceSettingsScreen}
options={{ headerTitle: 'Appearance' }}
/>
<SettingsStack.Screen
name="PrivacySettings"
component={PrivacySettingsScreen}
options={{ headerTitle: 'Privacy' }}
/>
<SettingsStack.Screen
name="ExtensionsSettings"
component={ExtensionsSettingsScreen}
options={{ headerTitle: 'Extensions' }}
/>
{/* Library */}
<SettingsStack.Screen
name="LibraryGeneralSettings"
component={LibraryGeneralSettingsScreen}
options={{ headerTitle: 'Library Settings' }}
/>
<SettingsStack.Screen
name="LocationSettings"
component={LocationSettingsScreen}
options={{ headerTitle: 'Locations' }}
/>
<SettingsStack.Screen
name="NodesSettings"
component={NodesSettingsScreen}
options={{ headerTitle: 'Nodes' }}
/>
<SettingsStack.Screen
name="TagsSettings"
component={TagsSettingsScreen}
options={{ headerTitle: 'Tags' }}
/>
<SettingsStack.Screen
name="KeysSettings"
component={KeysSettingsScreen}
options={{ headerTitle: 'Keys' }}
/>
{/* Info */}
<SettingsStack.Screen
name="About"
component={AboutScreen}
options={{ headerTitle: 'About' }}
/>
<SettingsStack.Screen
name="Support"
component={SupportScreen}
options={{ headerTitle: 'Support' }}
/>
</SettingsStack.Navigator>
);
}
export type SettingsStackParamList = {
// Home screen for the Settings stack.
Home: undefined;
// Client
GeneralSettings: undefined;
LibrarySettings: undefined;
AppearanceSettings: undefined;
PrivacySettings: undefined;
ExtensionsSettings: undefined;
// Library
LibraryGeneralSettings: undefined;
LocationSettings: undefined;
NodesSettings: undefined;
TagsSettings: undefined;
KeysSettings: undefined;
// Info
About: undefined;
Support: undefined;
};
export type SettingsStackScreenProps<Screen extends keyof SettingsStackParamList> =
StackScreenProps<SettingsStackParamList, Screen>;

View file

@ -7,6 +7,7 @@ import {
import LocationScreen from '~/screens/Location'; import LocationScreen from '~/screens/Location';
import TagScreen from '~/screens/Tag'; import TagScreen from '~/screens/Tag';
// Mounted on all the tabs, so we can navigate to it from any tab
export function SharedScreens( export function SharedScreens(
Stack: TypedNavigator< Stack: TypedNavigator<
SharedScreensParamList, SharedScreensParamList,

View file

@ -1,12 +1,12 @@
import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native'; import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
import { Camera, CirclesFour, Folder, Planet } from 'phosphor-react-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 type { HomeDrawerScreenProps } from './DrawerNavigator';
import BrowseStack, { BrowseStackParamList } from './tabs/BrowseStack'; import NodesStack, { NodesStackParamList } from './tabs/NodesStack';
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack'; import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
import PhotosStack, { PhotosStackParamList } from './tabs/PhotosStack';
import SpacesStack, { SpacesStackParamList } from './tabs/SpacesStack'; import SpacesStack, { SpacesStackParamList } from './tabs/SpacesStack';
const Tab = createBottomTabNavigator<TabParamList>(); const Tab = createBottomTabNavigator<TabParamList>();
@ -17,11 +17,11 @@ export default function TabNavigator() {
initialRouteName="OverviewStack" initialRouteName="OverviewStack"
screenOptions={{ screenOptions={{
headerShown: false, headerShown: false,
tabBarActiveTintColor: tw.color('primary'), tabBarActiveTintColor: tw.color('accent'),
tabBarInactiveTintColor: 'white', tabBarInactiveTintColor: tw.color('ink'),
tabBarStyle: { tabBarStyle: {
backgroundColor: tw.color('gray-650'), backgroundColor: tw.color('app'),
borderTopColor: tw.color('gray-600') borderTopColor: tw.color('app-shade')
} }
}} }}
> >
@ -30,19 +30,29 @@ export default function TabNavigator() {
component={OverviewStack} component={OverviewStack}
options={{ options={{
tabBarIcon: ({ focused }) => ( tabBarIcon: ({ focused }) => (
<Planet size={22} weight="bold" color={focused ? tw.color('bg-primary') : 'white'} /> <Planet
size={22}
weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('accent') : tw.color('ink')}
/>
), ),
tabBarLabel: 'Overview' tabBarLabel: 'Overview',
tabBarLabelStyle: tw`font-semibold text-tiny`
}} }}
/> />
<Tab.Screen <Tab.Screen
name="BrowseStack" name="NodesStack"
component={BrowseStack} component={NodesStack}
options={{ options={{
tabBarIcon: ({ focused }) => ( tabBarIcon: ({ focused }) => (
<Folder size={22} weight="bold" color={focused ? tw.color('bg-primary') : 'white'} /> <ShareNetwork
size={22}
weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('accent') : tw.color('ink')}
/>
), ),
tabBarLabel: 'Browse' tabBarLabel: 'Nodes',
tabBarLabelStyle: tw`font-semibold text-tiny`
}} }}
/> />
<Tab.Screen <Tab.Screen
@ -52,21 +62,12 @@ export default function TabNavigator() {
tabBarIcon: ({ focused }) => ( tabBarIcon: ({ focused }) => (
<CirclesFour <CirclesFour
size={22} size={22}
weight="bold" weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('bg-primary') : 'white'} color={focused ? tw.color('accent') : tw.color('ink')}
/> />
), ),
tabBarLabel: 'Spaces' tabBarLabel: 'Spaces',
}} tabBarLabelStyle: tw`font-semibold text-tiny`
/>
<Tab.Screen
name="PhotosStack"
component={PhotosStack}
options={{
tabBarIcon: ({ focused }) => (
<Camera size={22} weight="bold" color={focused ? tw.color('bg-primary') : 'white'} />
),
tabBarLabel: 'Photos'
}} }}
/> />
</Tab.Navigator> </Tab.Navigator>
@ -75,9 +76,8 @@ export default function TabNavigator() {
export type TabParamList = { export type TabParamList = {
OverviewStack: NavigatorScreenParams<OverviewStackParamList>; OverviewStack: NavigatorScreenParams<OverviewStackParamList>;
BrowseStack: NavigatorScreenParams<BrowseStackParamList>; NodesStack: NavigatorScreenParams<NodesStackParamList>;
SpacesStack: NavigatorScreenParams<SpacesStackParamList>; SpacesStack: NavigatorScreenParams<SpacesStackParamList>;
PhotosStack: NavigatorScreenParams<PhotosStackParamList>;
}; };
export type TabScreenProps<Screen extends keyof TabParamList> = CompositeScreenProps< export type TabScreenProps<Screen extends keyof TabParamList> = CompositeScreenProps<

View file

@ -2,11 +2,11 @@ import { NavigatorScreenParams } from '@react-navigation/native';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack'; import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import tw from '~/lib/tailwind'; import tw from '~/lib/tailwind';
import NotFoundScreen from '~/screens/NotFound'; import NotFoundScreen from '~/screens/NotFound';
import SearchScreen from '~/screens/modals/Search'; import SearchScreen from '~/screens/Search';
import SettingsScreen from '~/screens/modals/settings/Settings';
import type { DrawerNavParamList } from './DrawerNavigator'; import type { DrawerNavParamList } from './DrawerNavigator';
import DrawerNavigator from './DrawerNavigator'; import DrawerNavigator from './DrawerNavigator';
import SettingsNavigator, { SettingsStackParamList } from './SettingsNavigator';
const Stack = createStackNavigator<RootStackParamList>(); const Stack = createStackNavigator<RootStackParamList>();
@ -24,16 +24,17 @@ export default function RootNavigator() {
{/* Modals */} {/* Modals */}
<Stack.Group <Stack.Group
screenOptions={{ screenOptions={{
headerShown: false,
presentation: 'modal', presentation: 'modal',
headerBackTitleVisible: false, headerBackTitleVisible: false,
headerStyle: tw`bg-gray-650`, headerStyle: tw`bg-app`,
headerTintColor: tw.color('gray-200'), headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`, headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base` headerBackTitleStyle: tw`text-base`
// headerShadowVisible: false // will disable the white line under // headerShadowVisible: false // will disable the white line under
}} }}
> >
<Stack.Screen name="Settings" component={SettingsScreen} /> <Stack.Screen name="Settings" component={SettingsNavigator} />
</Stack.Group> </Stack.Group>
</Stack.Navigator> </Stack.Navigator>
); );
@ -44,7 +45,7 @@ export type RootStackParamList = {
NotFound: undefined; NotFound: undefined;
// Modals // Modals
Search: undefined; Search: undefined;
Settings: undefined; Settings: NavigatorScreenParams<SettingsStackParamList>;
}; };
export type RootStackScreenProps<Screen extends keyof RootStackParamList> = StackScreenProps< export type RootStackScreenProps<Screen extends keyof RootStackParamList> = StackScreenProps<

View file

@ -1,37 +0,0 @@
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 BrowseScreen from '~/screens/Browse';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';
const Stack = createStackNavigator<BrowseStackParamList>();
export default function BrowseStack() {
return (
<Stack.Navigator
initialRouteName="Browse"
screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') },
headerTintColor: tw.color('gray-200'),
headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base`
}}
>
<Stack.Screen name="Browse" component={BrowseScreen} options={{ header: Header }} />
{SharedScreens(Stack as any)}
</Stack.Navigator>
);
}
export type BrowseStackParamList = {
Browse: undefined;
} & SharedScreensParamList;
export type BrowseStackScreenProps<Screen extends keyof BrowseStackParamList> =
CompositeScreenProps<
StackScreenProps<BrowseStackParamList, Screen>,
TabScreenProps<'BrowseStack'>
>;

View file

@ -0,0 +1,36 @@
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 NodesScreen from '~/screens/Nodes';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';
const Stack = createStackNavigator<NodesStackParamList>();
export default function NodesStack() {
return (
<Stack.Navigator
initialRouteName="Nodes"
screenOptions={{
headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base`
}}
>
<Stack.Screen name="Nodes" component={NodesScreen} options={{ header: Header }} />
{SharedScreens(Stack as any)}
</Stack.Navigator>
);
}
export type NodesStackParamList = {
Nodes: undefined;
} & SharedScreensParamList;
export type NodesStackScreenProps<Screen extends keyof NodesStackParamList> = CompositeScreenProps<
StackScreenProps<NodesStackParamList, Screen>,
TabScreenProps<'NodesStack'>
>;

View file

@ -14,8 +14,8 @@ export default function OverviewStack() {
<Stack.Navigator <Stack.Navigator
initialRouteName="Overview" initialRouteName="Overview"
screenOptions={{ screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') }, headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('gray-200'), headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`, headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base` headerBackTitleStyle: tw`text-base`
}} }}

View file

@ -1,37 +0,0 @@
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 PhotosScreen from '../../screens/Photos';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';
const Stack = createStackNavigator<PhotosStackParamList>();
export default function PhotosStack() {
return (
<Stack.Navigator
initialRouteName="Photos"
screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') },
headerTintColor: tw.color('gray-200'),
headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base`
}}
>
<Stack.Screen name="Photos" component={PhotosScreen} options={{ header: Header }} />
{SharedScreens(Stack as any)}
</Stack.Navigator>
);
}
export type PhotosStackParamList = {
Photos: undefined;
} & SharedScreensParamList;
export type PhotosStackScreenProps<Screen extends keyof PhotosStackParamList> =
CompositeScreenProps<
StackScreenProps<PhotosStackParamList, Screen>,
TabScreenProps<'PhotosStack'>
>;

View file

@ -14,8 +14,8 @@ export default function SpacesStack() {
<Stack.Navigator <Stack.Navigator
initialRouteName="Spaces" initialRouteName="Spaces"
screenOptions={{ screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') }, headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('gray-200'), headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`, headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base` headerBackTitleStyle: tw`text-base`
}} }}

View file

@ -1,77 +0,0 @@
import { ColorValue, Text, View } from 'react-native';
import CollapsibleView from '~/components/layout/CollapsibleView';
import BrowseLocationItem from '~/containers/browse/BrowseLocationItem';
import BrowseTagItem from '~/containers/browse/BrowseTagItem';
import tw from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
const placeholderLocationData = [
{
id: 1,
name: 'Spacedrive'
},
{
id: 2,
name: 'Classified'
}
];
const placeholderTagsData = [
{
id: 1,
name: 'Secret',
color: tw.color('blue-500')
},
{
id: 2,
name: 'OBS',
color: tw.color('purple-500')
},
{
id: 3,
name: 'BlackMagic',
color: tw.color('red-500')
}
];
// Will refactor this soon
const BrowseScreen = ({ navigation }: BrowseStackScreenProps<'Browse'>) => {
return (
<View style={tw`flex-1 p-4`}>
<CollapsibleView
title="Locations"
titleStyle={tw`mt-5 mb-3 ml-1 text-base font-semibold text-gray-300`}
>
{placeholderLocationData.map((location) => (
<BrowseLocationItem
key={location.id}
folderName={location.name}
onPress={() => navigation.navigate('Location', { id: location.id })}
/>
))}
{/* Add Location */}
<View style={tw`border border-dashed rounded border-gray-450 border-opacity-60 mt-1`}>
<Text style={tw`text-xs font-bold text-center text-gray-400 px-2 py-2`}>
Add Location
</Text>
</View>
</CollapsibleView>
{/* Tags */}
<View style={tw`mt-8`} />
<CollapsibleView
title="Tags"
titleStyle={tw`mt-5 mb-3 ml-1 text-base font-semibold text-gray-300`}
>
{placeholderTagsData.map((tag) => (
<BrowseTagItem
key={tag.id}
tagName={tag.name}
onPress={() => navigation.navigate('Tag', { id: tag.id })}
tagColor={tag.color as ColorValue}
/>
))}
</CollapsibleView>
</View>
);
};
export default BrowseScreen;

View file

@ -0,0 +1,11 @@
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { NodesStackScreenProps } from '~/navigation/tabs/NodesStack';
export default function NodesScreen({ navigation }: NodesStackScreenProps<'Nodes'>) {
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`font-bold text-xl text-ink`}>Nodes</Text>
</View>
);
}

View file

@ -7,7 +7,7 @@ export default function NotFoundScreen({ navigation }: RootStackScreenProps<'Not
<View style={tw`flex-1 items-center justify-center p-5`}> <View style={tw`flex-1 items-center justify-center p-5`}>
<Text style={tw`font-bold text-xl`}>This screen doesn&apos;t exist.</Text> <Text style={tw`font-bold text-xl`}>This screen doesn&apos;t exist.</Text>
<TouchableOpacity onPress={() => navigation.replace('Root')} style={tw`mt-4 py-4`}> <TouchableOpacity onPress={() => navigation.replace('Root')} style={tw`mt-4 py-4`}>
<Text style={tw`text-sm text-gray-250`}>Go to home screen!</Text> <Text style={tw`text-sm text-ink-dull`}>Go to home screen!</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );

View file

@ -1,11 +0,0 @@
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { PhotosStackScreenProps } from '~/navigation/tabs/PhotosStack';
export default function PhotosScreen({ navigation }: PhotosStackScreenProps<'Photos'>) {
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`font-bold text-xl text-white`}>Photos</Text>
</View>
);
}

View file

@ -18,23 +18,21 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
{/* Header */} {/* Header */}
<View style={tw`flex flex-row items-center mx-4`}> <View style={tw`flex flex-row items-center mx-4`}>
{/* Search Input */} {/* Search Input */}
<View <View style={tw`flex-1 bg-app-overlay border border-app-line rounded h-10 mr-3`}>
style={tw`flex-1 bg-gray-500 border border-[#333949] bg-opacity-40 rounded h-10 mr-3`}
>
<View style={tw`flex flex-row h-full items-center px-3`}> <View style={tw`flex flex-row h-full items-center px-3`}>
<View style={tw`mr-3`}> <View style={tw`mr-3`}>
{loading ? ( {loading ? (
<ActivityIndicator size={'small'} color={'white'} /> <ActivityIndicator size={'small'} color={'white'} />
) : ( ) : (
<MagnifyingGlass size={20} weight="light" color={tw.color('gray-300')} /> <MagnifyingGlass size={20} weight="light" color={tw.color('ink-faint')} />
)} )}
</View> </View>
<TextInput <TextInput
placeholder={'Search'} placeholder={'Search'}
clearButtonMode="never" // can't change the color?? clearButtonMode="never" // can't change the color??
underlineColorAndroid="transparent" underlineColorAndroid="transparent"
placeholderTextColor={tw.color('gray-300')} placeholderTextColor={tw.color('ink-dull')}
style={tw`flex-1 text-gray-300 font-medium text-sm`} style={tw`flex-1 text-ink font-medium text-sm`}
textContentType={'none'} textContentType={'none'}
autoFocus autoFocus
autoCapitalize="none" autoCapitalize="none"
@ -44,12 +42,12 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
</View> </View>
{/* Cancel Button */} {/* Cancel Button */}
<Pressable onPress={() => navigation.goBack()}> <Pressable onPress={() => navigation.goBack()}>
<Text style={tw`text-primary-500`}>Cancel</Text> <Text style={tw`text-accent`}>Cancel</Text>
</Pressable> </Pressable>
</View> </View>
{/* Content */} {/* Content */}
<View style={tw`flex-1 items-center mt-8`}> <View style={tw`flex-1 items-center mt-8`}>
<Button variant="primary" onPress={() => setLoading((v) => !v)}> <Button variant="accent" onPress={() => setLoading((v) => !v)}>
<Text>Toggle loading</Text> <Text>Toggle loading</Text>
</Button> </Button>
</View> </View>

View file

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

View file

@ -6,7 +6,7 @@ export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'
const { id } = route.params; const { id } = route.params;
return ( return (
<View style={tw`flex-1 items-center justify-center`}> <View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`font-bold text-xl text-white`}>Tag {id}</Text> <Text style={tw`font-bold text-xl text-ink`}>Tag {id}</Text>
</View> </View>
); );
} }

View file

@ -1,12 +0,0 @@
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { RootStackScreenProps } from '~/navigation';
export default function SettingsScreen({ navigation }: RootStackScreenProps<'Settings'>) {
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`font-bold text-xl text-white`}>Settings</Text>
<View style={tw`my-8 h-1 w-4/5`} />
</View>
);
}

View file

@ -1,5 +1,4 @@
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useSnapshot } from 'valtio';
import { AnimatedButton } from '~/components/primitive/Button'; import { AnimatedButton } from '~/components/primitive/Button';
import CreateLibraryDialog from '~/containers/dialog/CreateLibraryDialog'; import CreateLibraryDialog from '~/containers/dialog/CreateLibraryDialog';
import tw from '~/lib/tailwind'; import tw from '~/lib/tailwind';
@ -7,13 +6,13 @@ import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => { const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => {
return ( return (
<View style={tw`flex-1 items-center justify-center bg-gray-650 p-4`}> <View style={tw`flex-1 items-center justify-center bg-app p-4`}>
<Text style={tw`text-gray-450 text-center px-6 my-8 text-base leading-relaxed`}> <Text style={tw`text-ink-dull text-center px-6 my-8 text-base leading-relaxed`}>
Onboarding screen for users to create their first library Onboarding screen for users to create their first library
</Text> </Text>
<CreateLibraryDialog disableBackdropClose> <CreateLibraryDialog disableBackdropClose>
<AnimatedButton disabled variant="primary"> <AnimatedButton variant="accent">
<Text style={tw`text-white text-center px-6 py-2 text-base font-medium`}> <Text style={tw`text-ink text-center px-6 py-2 text-base font-medium`}>
Create Library Create Library
</Text> </Text>
</AnimatedButton> </AnimatedButton>

View file

@ -6,7 +6,7 @@ import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => { const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => {
return ( return (
<View style={tw`flex-1 items-center justify-around bg-gray-650 p-4 z-10`}> <View style={tw`flex-1 items-center justify-around bg-app p-4 z-10`}>
{/* Logo */} {/* Logo */}
<LogoAnimation> <LogoAnimation>
<View style={tw`items-center mt-2`}> <View style={tw`items-center mt-2`}>
@ -16,12 +16,12 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
{/* Text */} {/* Text */}
<View> <View>
<FadeInUpAnimation delay={500}> <FadeInUpAnimation delay={500}>
<Text style={tw`text-white text-center text-5xl font-black leading-tight`}> <Text style={tw`text-ink text-center text-5xl font-black leading-tight`}>
A file explorer from the future. A file explorer from the future.
</Text> </Text>
</FadeInUpAnimation> </FadeInUpAnimation>
<FadeInUpAnimation delay={800}> <FadeInUpAnimation delay={800}>
<Text style={tw`text-gray-450 text-center px-6 mt-8 text-base leading-relaxed`}> <Text style={tw`text-ink-dull text-center px-6 mt-8 text-base leading-relaxed`}>
Combine your drives and clouds into one database that you can organize and explore from Combine your drives and clouds into one database that you can organize and explore from
any device. any device.
</Text> </Text>
@ -29,10 +29,8 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
</View> </View>
{/* Get Started Button */} {/* Get Started Button */}
<FadeInUpAnimation delay={1200}> <FadeInUpAnimation delay={1200}>
<AnimatedButton variant="primary" onPress={() => navigation.navigate('CreateLibrary')}> <AnimatedButton variant="accent" onPress={() => navigation.navigate('CreateLibrary')}>
<Text style={tw`text-white text-center px-6 py-2 text-base font-medium`}> <Text style={tw`text-ink text-center px-6 py-2 text-base font-medium`}>Get Started</Text>
Get Started
</Text>
</AnimatedButton> </AnimatedButton>
</FadeInUpAnimation> </FadeInUpAnimation>
</View> </View>

View file

@ -0,0 +1,148 @@
import {
Books,
FlyingSaucer,
GearSix,
HardDrive,
Heart,
Icon,
Key,
PaintBrush,
PuzzlePiece,
ShareNetwork,
ShieldCheck,
TagSimple
} from 'phosphor-react-native';
import React from 'react';
import { SectionList, Text, View } from 'react-native';
import { SettingsItem, SettingsItemDivider } from '~/components/settings/SettingsItem';
import tw from '~/lib/tailwind';
import { SettingsStackParamList, SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
type SectionType = {
title: string;
data: {
title: string;
icon: Icon;
navigateTo: keyof SettingsStackParamList;
}[];
};
const sections: SectionType[] = [
{
title: 'Client',
data: [
{
icon: GearSix,
navigateTo: 'GeneralSettings',
title: 'General'
},
{
icon: Books,
navigateTo: 'LibrarySettings',
title: 'Libraries'
},
{
icon: PaintBrush,
navigateTo: 'AppearanceSettings',
title: 'Appearance'
},
{
icon: ShieldCheck,
navigateTo: 'PrivacySettings',
title: 'Privacy'
},
{
icon: PuzzlePiece,
navigateTo: 'ExtensionsSettings',
title: 'Extensions'
}
]
},
{
title: 'Library',
data: [
{
icon: GearSix,
navigateTo: 'LibraryGeneralSettings',
title: 'General'
},
{
icon: HardDrive,
navigateTo: 'LocationSettings',
title: 'Locations'
},
{
icon: ShareNetwork,
navigateTo: 'NodesSettings',
title: 'Nodes'
},
{
icon: TagSimple,
navigateTo: 'TagsSettings',
title: 'Tags'
},
{
icon: Key,
navigateTo: 'KeysSettings',
title: 'Keys'
}
]
},
{
title: 'Resources',
data: [
{
icon: FlyingSaucer,
navigateTo: 'About',
title: 'About'
},
{
icon: Heart,
navigateTo: 'Support',
title: 'Support'
}
]
}
];
function renderSectionHeader({ section }: { section: { title: string } }) {
return (
<Text
style={tw.style(
'mb-2 ml-2 text-sm font-semibold text-ink-dull',
section.title === 'Client' ? 'mt-2' : 'mt-5'
)}
>
{section.title}
</Text>
);
}
export default function SettingsScreen({ navigation }: SettingsStackScreenProps<'Home'>) {
return (
<View style={tw`flex-1`}>
<SectionList
sections={sections}
contentContainerStyle={tw`py-4`}
ItemSeparatorComponent={SettingsItemDivider}
renderItem={({ item }) => (
<SettingsItem
title={item.title}
leftIcon={item.icon}
onPress={() => navigation.navigate(item.navigateTo)}
/>
)}
renderSectionHeader={renderSectionHeader}
ListFooterComponent={
<View style={tw`items-center mt-6 mb-4`}>
<Text style={tw`text-sm font-bold text-ink`}>Spacedrive</Text>
<Text style={tw`text-ink-dull text-xs mt-0.5`}>v0.1.0</Text>
</View>
}
showsVerticalScrollIndicator={false}
stickySectionHeadersEnabled={false}
initialNumToRender={50}
/>
</View>
);
}

View file

@ -0,0 +1,16 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const AppearanceSettingsScreen = ({
navigation
}: SettingsStackScreenProps<'AppearanceSettings'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO: Theme Switch</Text>
</View>
);
};
export default AppearanceSettingsScreen;

View file

@ -0,0 +1,16 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const ExtensionsSettingsScreen = ({
navigation
}: SettingsStackScreenProps<'ExtensionsSettings'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default ExtensionsSettingsScreen;

View file

@ -0,0 +1,44 @@
import { useBridgeQuery } from '@sd/client';
import React from 'react';
import { Text, View } from 'react-native';
import Card from '~/components/layout/Card';
import Divider from '~/components/primitive/Divider';
import { Input } from '~/components/primitive/Input';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'GeneralSettings'>) => {
const { data: node } = useBridgeQuery(['nodeState']);
if (!node) return null;
return (
<View style={tw`flex-1 p-4`}>
<Card>
{/* Card Header */}
<View style={tw`flex flex-row justify-between`}>
<Text style={tw`font-semibold text-ink`}>Connected Node</Text>
<View style={tw`flex flex-row`}>
{/* Peers */}
<View style={tw`rounded bg-app-highlight self-start px-1.5 py-[2px] mr-2`}>
<Text style={tw`text-xs font-semibold text-ink`}>0 Peers</Text>
</View>
{/* Status */}
<View style={tw`px-1.5 py-[2px] rounded bg-accent`}>
<Text style={tw`text-xs font-semibold text-ink`}>Running</Text>
</View>
</View>
</View>
{/* Divider */}
<Divider style={tw`mt-2 mb-4`} />
{/* Node Name and Port */}
<Text style={tw`mb-1 text-xs font-medium text-ink-dull ml-1`}>Node Name</Text>
<Input value={node.name} />
<Text style={tw`mt-2 mb-1 text-xs font-medium text-ink-dull ml-1`}>Node Port</Text>
<Input value={node.p2p_port?.toString() ?? '5795'} keyboardType="numeric" />
</Card>
</View>
);
};
export default GeneralSettingsScreen;

View file

@ -0,0 +1,82 @@
import { LibraryConfigWrapped, useBridgeQuery } from '@sd/client';
import { CaretRight, Pen, Trash } from 'phosphor-react-native';
import React from 'react';
import { Animated, FlatList, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import { AnimatedButton } from '~/components/primitive/Button';
import DeleteLibraryDialog from '~/containers/dialog/DeleteLibraryDialog';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function LibraryItem({
library,
index,
navigation
}: {
library: LibraryConfigWrapped;
index: number;
navigation: SettingsStackScreenProps<'LibrarySettings'>['navigation'];
}) {
const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>,
dragX: Animated.AnimatedInterpolation<number>
) => {
const translate = progress.interpolate({
inputRange: [0, 1],
outputRange: [100, 0],
extrapolate: 'clamp'
});
return (
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<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>
</Animated.View>
);
};
return (
<Swipeable
containerStyle={tw.style(
index !== 0 && 'mt-2',
'bg-app-overlay border border-app-line rounded-lg px-4 py-3'
)}
enableTrackpadTwoFingerGesture
renderRightActions={renderRightActions}
>
<View style={tw`flex flex-row items-center justify-between`}>
<View>
<Text style={tw`font-semibold text-ink`}>{library.config.name}</Text>
<Text style={tw`mt-0.5 text-xs text-ink-dull`}>{library.uuid}</Text>
</View>
<CaretRight color={tw.color('ink-dull')} size={18} />
</View>
</Swipeable>
);
}
const LibrarySettingsScreen = ({ navigation }: SettingsStackScreenProps<'LibrarySettings'>) => {
const { data: libraries } = useBridgeQuery(['library.list']);
return (
<View style={tw`py-4 px-3 flex-1`}>
<FlatList
data={libraries}
keyExtractor={(item) => item.uuid}
renderItem={({ item, index }) => (
<LibraryItem navigation={navigation} library={item} index={index} />
)}
/>
</View>
);
};
export default LibrarySettingsScreen;

View file

@ -0,0 +1,14 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const PrivacySettingsScreen = ({ navigation }: SettingsStackScreenProps<'PrivacySettings'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default PrivacySettingsScreen;

View file

@ -0,0 +1,14 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const AboutScreen = ({ navigation }: SettingsStackScreenProps<'About'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default AboutScreen;

View file

@ -0,0 +1,14 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const SupportScreen = ({ navigation }: SettingsStackScreenProps<'Support'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default SupportScreen;

View file

@ -0,0 +1,14 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const KeysSettingsScreen = ({ navigation }: SettingsStackScreenProps<'KeysSettings'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default KeysSettingsScreen;

View file

@ -0,0 +1,85 @@
import { useBridgeMutation, useCurrentLibrary } from '@sd/client';
import { Trash } from 'phosphor-react-native';
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Alert, Text, View } from 'react-native';
import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
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 { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
type LibraryFormData = {
name: string;
description: string;
};
const LibraryGeneralSettingsScreen = ({
navigation
}: SettingsStackScreenProps<'LibraryGeneralSettings'>) => {
const { library } = useCurrentLibrary();
const form = useForm<LibraryFormData>({
defaultValues: { name: library.config.name, description: library.config.description }
});
const { mutate: editLibrary } = useBridgeMutation('library.edit');
useAutoForm(form, (value) => {
editLibrary({ description: value.description, name: value.name, id: library.uuid });
console.log('Updated', value);
// TODO: Show toast
});
return (
<View>
{/* This looks bad... */}
<View style={tw`mt-4 px-2 py-4 bg-app-overlay`}>
<Text style={tw`mb-1 text-xs font-medium text-ink-dull ml-1`}>Name</Text>
<Controller
name="name"
control={form.control}
render={({ field: { onBlur, onChange, value } }) => (
<Input onBlur={onBlur} onChangeText={onChange} value={value} />
)}
/>
{/* Description */}
<Text style={tw`mb-1 text-xs font-medium text-ink-dull ml-1 mt-3`}>Description</Text>
<Controller
name="description"
control={form.control}
render={({ field: { onBlur, onChange, value } }) => (
<Input onBlur={onBlur} onChangeText={onChange} value={value} />
)}
/>
</View>
{/* Encrypt */}
<View style={tw`mt-6`} />
<SettingsContainer description="Enable encryption for this library, this will only encrypt the Spacedrive database, not the files themselves.">
<SettingsItem title="Encrypt Library" rightArea={<Switch value={true} />} />
</SettingsContainer>
<View style={tw`mt-6`} />
{/* 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 ? */}
<SettingsContainer description="This is permanent, your files will not be deleted, only the Spacedrive library.">
<SettingsItem
title="Delete Library"
rightArea={
<Button size="sm" variant="danger" onPress={() => Alert.alert('TODO')}>
<Trash color={tw.color('ink')} size={20} />
</Button>
}
/>
</SettingsContainer>
</View>
);
};
export default LibraryGeneralSettingsScreen;

View file

@ -0,0 +1,100 @@
import { Location, Node, useLibraryMutation, useLibraryQuery } from '@sd/client';
import { CaretRight, Repeat, Trash } from 'phosphor-react-native';
import { Animated, FlatList, Pressable, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import FolderIcon from '~/components/icons/FolderIcon';
import DeleteLocationDialog from '~/containers/dialog/DeleteLocationDialog';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function LocationItem({ location, index }: { location: Location & { node: Node }; index: number }) {
const { mutate: fullRescan } = useLibraryMutation('locations.fullRescan', {
onMutate: () => {
// TODO: Show Toast
}
});
const renderRightActions = (progress: Animated.AnimatedInterpolation<number>) => {
const translate = progress.interpolate({
inputRange: [0, 1],
outputRange: [100, 0],
extrapolate: 'clamp'
});
return (
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<DeleteLocationDialog locationId={location.id}>
<View
style={tw`py-1.5 px-3 bg-app-button border-app-line border rounded-md items-center justify-center shadow-sm`}
>
<Trash size={18} color="white" />
</View>
</DeleteLocationDialog>
{/* Full Re-scan IS too much here */}
<Pressable
style={tw`py-1.5 px-3 bg-app-button border-app-line border rounded-md items-center justify-center shadow-sm mx-2`}
onPress={() => fullRescan(location.id)}
>
<Repeat size={18} color="white" />
</Pressable>
</Animated.View>
);
};
return (
<Swipeable
containerStyle={tw`bg-app-overlay border border-app-line rounded-lg`}
enableTrackpadTwoFingerGesture
renderRightActions={renderRightActions}
>
<View style={tw.style('px-4 py-3', index !== 0 && 'mt-2')}>
<View style={tw`flex flex-row items-center`}>
<View style={tw`relative`}>
<FolderIcon size={32} />
{/* Online/Offline Indicator */}
<View
style={tw.style(
'absolute w-2 h-2 right-0 bottom-0.5 rounded-full',
location.is_online ? 'bg-green-500' : 'bg-red-500'
)}
/>
</View>
<View style={tw`flex-1 mx-4`}>
<Text numberOfLines={1} style={tw`text-sm font-semibold text-ink`}>
{location.name}
</Text>
<View style={tw`self-start bg-app-highlight py-[1px] px-1 rounded mt-0.5`}>
<Text numberOfLines={1} style={tw`text-xs font-semibold text-ink-dull`}>
{location.node.name}
</Text>
</View>
<Text numberOfLines={1} style={tw`mt-0.5 text-[10px] font-semibold text-ink-dull`}>
{location.local_path}
</Text>
</View>
<CaretRight color={tw.color('ink-dull')} size={18} />
</View>
</View>
</Swipeable>
);
}
// TODO: Add new location from here (ImportModal)
const LocationSettingsScreen = ({ navigation }: SettingsStackScreenProps<'LocationSettings'>) => {
const { data: locations } = useLibraryQuery(['locations.list']);
return (
<View style={tw`flex-1 px-3 py-4`}>
<FlatList
data={locations}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item, index }) => <LocationItem location={item} index={index} />}
/>
</View>
);
};
export default LocationSettingsScreen;

View file

@ -0,0 +1,14 @@
import React from 'react';
import { Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const NodesSettingsScreen = ({ navigation }: SettingsStackScreenProps<'NodesSettings'>) => {
return (
<View>
<Text style={tw`text-ink`}>TODO</Text>
</View>
);
};
export default NodesSettingsScreen;

View file

@ -0,0 +1,76 @@
import { Tag, useLibraryQuery } from '@sd/client';
import { CaretRight, Pen, Trash } from 'phosphor-react-native';
import { Animated, FlatList, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import { AnimatedButton } from '~/components/primitive/Button';
import DeleteTagDialog from '~/containers/dialog/tag/DeleteTagDialog';
import UpdateTagDialog from '~/containers/dialog/tag/UpdateTagDialog';
import tw from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function TagItem({ tag, index }: { tag: Tag; index: number }) {
const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>,
_dragX: Animated.AnimatedInterpolation<number>,
swipeable: Swipeable
) => {
const translate = progress.interpolate({
inputRange: [0, 1],
outputRange: [100, 0],
extrapolate: 'clamp'
});
return (
<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>
</Animated.View>
);
};
return (
<Swipeable
containerStyle={tw`bg-app-overlay border border-app-line rounded-lg`}
enableTrackpadTwoFingerGesture
renderRightActions={renderRightActions}
>
<View style={tw.style('px-4 py-3', index !== 0 && 'mt-2')}>
<View style={tw`flex flex-row items-center justify-between`}>
<View style={tw`flex flex-row`}>
<View style={tw.style({ backgroundColor: tag.color }, 'w-4 h-4 rounded-full')} />
<Text style={tw`ml-3 text-ink`}>{tag.name}</Text>
</View>
<CaretRight color={tw.color('ink-dull')} size={18} />
</View>
</View>
</Swipeable>
);
}
// TODO: Add "New Tag" button
const TagsSettingsScreen = ({ navigation }: SettingsStackScreenProps<'TagsSettings'>) => {
const { data: tags } = useLibraryQuery(['tags.list']);
return (
<View style={tw`flex-1 px-3 py-4`}>
<FlatList
data={tags}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item, index }) => <TagItem tag={item} index={index} />}
/>
</View>
);
};
export default TagsSettingsScreen;

View file

@ -1,34 +1,63 @@
// Extented colors are copied from packages/ui/style/colors.scss
module.exports = { module.exports = {
content: ['./screens/**/*.{js,ts,jsx}', './components/**/*.{js,ts,jsx}', 'App.tsx'], content: ['./screens/**/*.{js,ts,jsx}', './components/**/*.{js,ts,jsx}', 'App.tsx'],
theme: { theme: {
fontSize: {
'tiny': '.65rem',
'xs': '.75rem',
'sm': '.84rem',
'base': '1rem',
'lg': '1.125rem',
'xl': '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
'5xl': '3rem',
'6xl': '4rem',
'7xl': '5rem'
},
extend: { extend: {
fontSize: {
tiny: '.65rem',
// Default: '0.875rem'
sm: '.85rem'
},
colors: { colors: {
primary: { // Brand blue
DEFAULT: '#2599FF', accent: {
50: '#FFFFFF', DEFAULT: 'hsla(208, 100%, 47%, 1)',
100: '#F1F8FF', faint: 'hsla(208, 100%, 64%, 1)',
200: '#BEE1FF', deep: 'hsla(208, 100%, 47%, 1)'
300: '#8BC9FF', },
400: '#58B1FF', ink: {
500: '#2599FF', DEFAULT: 'hsla(230, 0%, 100%, 1)',
600: '#0081F1', light: 'hsla(230, 0%, 82%, 1)',
700: '#0065BE', dull: 'hsla(230, 10%, 70%, 1)',
800: '#004A8B', faint: 'hsla(230, 10%, 55%, 1)'
900: '#002F58' },
// Brand gray
app: {
DEFAULT: 'hsla(230, 15%, 13%, 1)',
// background (dark)
box: 'hsla(230, 15%, 17%, 1)',
darkBox: 'hsla(230, 15%, 7%, 1)',
// foreground (light)
overlay: 'hsla(230, 15%, 19%, 1)',
// border
line: 'hsla(230, 15%, 25%, 1)',
darkLine: 'hsla(230, 15%, 7%, 1)',
// 'selected' on desktop
highlight: 'hsla(230, 15%, 27%, 1)',
// shadow
shade: 'hsla(230, 15%, 0%, 1)',
// button
button: 'hsla(230, 15%, 23%, 1)',
50: 'hsla(230, 15%, 5%, 1)',
100: 'hsla(230, 15%, 10%, 1)',
150: 'hsla(230, 15%, 15%, 1)',
200: 'hsla(230, 15%, 20%, 1)',
250: 'hsla(230, 15%, 30%, 1)',
300: 'hsla(230, 15%, 35%, 1)',
350: 'hsla(230, 15%, 40%, 1)',
450: 'hsla(230, 15%, 45%, 1)',
500: 'hsla(230, 15%, 50%, 1)',
550: 'hsla(230, 15%, 55%, 1)',
600: 'hsla(230, 15%, 60%, 1)',
650: 'hsla(230, 15%, 65%, 1)',
700: 'hsla(230, 15%, 70%, 1)',
750: 'hsla(230, 15%, 75%, 1)',
800: 'hsla(230, 15%, 80%, 1)',
850: 'hsla(230, 15%, 85%, 1)',
900: 'hsla(230, 15%, 90%, 1)',
950: 'hsla(230, 15%, 95%, 1)',
1000: 'hsla(230, 15%, 100%, 1)'
}, },
gray: { gray: {
DEFAULT: '#505468', DEFAULT: '#505468',

View file

@ -1,11 +1,11 @@
{ {
"extends": "expo/tsconfig.base", "extends": "expo/tsconfig.base",
"compilerOptions": { "compilerOptions": {
"baseUrl": "src", "baseUrl": ".",
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"paths": { "paths": {
"~/*": ["*"] "~/*": ["src/*"]
}, },
"jsx": "react-native" "jsx": "react-native"
} }

View file

@ -9,3 +9,4 @@ export * from './hooks';
export * from './stores'; export * from './stores';
export * from './rspc'; export * from './rspc';
export * from './core'; export * from './core';
export * from './utils';

View file

@ -0,0 +1 @@
export * from './isVideoExt';

View file

@ -0,0 +1,31 @@
export function isVideoExt(extension: string) {
return [
'avi',
'asf',
'mpeg',
'mts',
'mpe',
'vob',
'qt',
'mov',
'asf',
'asx',
'mjpeg',
'ts',
'mxf',
'm2ts',
'f4v',
'wm',
'3gp',
'm4v',
'wmv',
'mp4',
'webm',
'flv',
'mpg',
'hevc',
'ogv',
'swf',
'wtv'
].includes(extension);
}

View file

@ -0,0 +1,59 @@
module.exports = {
env: {
'react-native/react-native': true
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 12,
sourceType: 'module'
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended'
],
plugins: ['react', 'react-native'],
rules: {
'react/display-name': 'off',
'react/prop-types': 'off',
'react/no-unescaped-entities': 'off',
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-control-regex': 'off',
'no-mixed-spaces-and-tabs': ['warn', 'smart-tabs'],
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'react-native',
importNames: ['SafeAreaView'],
message: 'Import SafeAreaView from react-native-safe-area-context instead'
},
{
name: 'react-native',
importNames: ['Button'],
message: 'Import Button from ~/components instead.'
}
]
}
]
},
ignorePatterns: ['**/*.js', '**/*.json', 'node_modules', 'android', 'ios', '.expo'],
settings: {
react: {
version: 'detect'
}
}
};

View file

@ -1,4 +1,4 @@
import { ExplorerItem } from '@sd/client'; import { ExplorerItem, isVideoExt } from '@sd/client';
import { cva, tw } from '@sd/ui'; import { cva, tw } from '@sd/ui';
import clsx from 'clsx'; import clsx from 'clsx';
import { HTMLAttributes } from 'react'; import { HTMLAttributes } from 'react';
@ -27,7 +27,7 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
} }
function FileItem({ data, selected, index, ...rest }: Props) { function FileItem({ data, selected, index, ...rest }: Props) {
const isVid = isVideo(data.extension || ''); const isVid = isVideoExt(data.extension || '');
return ( return (
<div <div
@ -86,35 +86,3 @@ function FileItem({ data, selected, index, ...rest }: Props) {
} }
export default FileItem; export default FileItem;
function isVideo(extension: string) {
return [
'avi',
'asf',
'mpeg',
'mts',
'mpe',
'vob',
'qt',
'mov',
'asf',
'asx',
'mjpeg',
'ts',
'mxf',
'm2ts',
'f4v',
'wm',
'3gp',
'm4v',
'wmv',
'mp4',
'webm',
'flv',
'mpg',
'hevc',
'ogv',
'swf',
'wtv'
].includes(extension);
}

File diff suppressed because it is too large Load diff