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

17
.vscode/settings.json vendored
View file

@ -37,9 +37,18 @@
"rust-analyzer.procMacro.enable": true,
"rust-analyzer.diagnostics.experimental.enable": false,
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
"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 = {
env: {
'react-native/react-native': true
},
parser: '@typescript-eslint/parser',
...require('@sd/config/eslint-react-native.js'),
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']
},
ignorePatterns: ['**/*.js', '**/*.json', 'node_modules', 'android', 'ios', '.expo'],
settings: {
react: {
version: 'detect'
}
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
};

View file

@ -1,3 +1,2 @@
Make sure to run `pnpm i` in this folder after making changes to the `packages`.
- Note: If you add/remove something from `packages/assets` folder, you need to delete node_modules and run `pnpm i` again to link it.
- 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.

View file

@ -36,6 +36,17 @@ target 'Spacedrive' do
post_install do |installer|
react_native_post_install(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
post_integrate do |installer|

View file

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

View file

@ -361,7 +361,9 @@
"FB_SONARKIT_ENABLED=1",
);
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 = (
"$(inherited)",
"@executable_path/Frameworks",
@ -454,7 +456,9 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = H7MGS2DHHJ;
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 = (
"$(inherited)",
"@executable_path/Frameworks",
@ -581,7 +585,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
@ -635,7 +639,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",

View file

@ -15,7 +15,7 @@
},
"dependencies": {
"@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-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/drawer": "^6.5.0",
@ -25,41 +25,45 @@
"@rspc/react": "^0.0.0-main-7c0a67c1",
"@sd/assets": "workspace:*",
"@sd/client": "workspace:*",
"@shopify/flash-list": "^1.3.1",
"@shopify/flash-list": "1.3.1",
"@tanstack/react-query": "^4.12.0",
"byte-size": "^8.1.0",
"class-variance-authority": "^0.2.3",
"dayjs": "^1.11.6",
"expo": "~46.0.15",
"expo-linking": "~3.2.2",
"expo-media-library": "~14.2.0",
"expo-splash-screen": "~0.16.2",
"expo-status-bar": "~1.4.0",
"expo": "^47.0.0",
"expo-linking": "~3.2.3",
"expo-media-library": "~15.0.0",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2",
"intl": "^1.2.5",
"lottie-react-native": "^5.1.4",
"lottie-react-native": "5.1.4",
"moti": "^0.20.0",
"phosphor-react-native": "^1.1.2",
"react": "18.0.0",
"react-native": "0.69.6",
"react": "18.1.0",
"react-hook-form": "^7.36.1",
"react-native": "0.70.5",
"react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.5.0",
"react-native-reanimated": "~2.10.0",
"react-native-safe-area-context": "4.3.1",
"react-native-screens": "~3.15.0",
"react-native-svg": "13.0.0",
"react-native-gesture-handler": "~2.8.0",
"react-native-reanimated": "~2.12.0",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-native-svg": "13.4.0",
"react-native-wheel-color-picker": "^1.2.0",
"twrnc": "^3.4.1",
"use-count-up": "^3.0.1",
"use-debounce": "^8.0.4",
"valtio": "^1.7.1"
},
"devDependencies": {
"@rnx-kit/metro-config": "^1.3.2",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.5",
"@sd/config": "workspace:*",
"@types/react": "~18.0.24",
"@types/react-native": "~0.70.6",
"babel-plugin-module-resolver": "^4.1.0",
"eslint-plugin-react-native": "^4.0.0",
"metro-minify-terser": "^0.73.1",
"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 { useDeviceContext } from 'twrnc';
import { GlobalModals } from './containers/modals/GlobalModals';
import { GlobalModals } from './containers/modal/GlobalModals';
import { reactNativeLink } from './lib/rspcReactNativeTransport';
import tw from './lib/tailwind';
import RootNavigator from './navigation';
@ -26,7 +26,8 @@ const NavigatorTheme: Theme = {
...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();
return (
<SafeAreaProvider style={tw`flex-1 bg-gray-650`}>
<SafeAreaProvider style={tw`flex-1 bg-app`}>
<GestureHandlerRootView style={tw`flex-1`}>
<BottomSheetModalProvider>
<StatusBar style="light" />

View file

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

View file

@ -5,12 +5,12 @@ import tw from '~/lib/tailwind';
import FileItem from './FileItem';
type Props = {
type ExplorerProps = {
locationId: number;
path?: string;
};
const Explorer = ({ locationId, path }: Props) => {
const Explorer = ({ locationId, path }: ExplorerProps) => {
const { data } = useLibraryQuery([
'locations.getExplorerData',
{
@ -23,7 +23,7 @@ const Explorer = ({ locationId, path }: Props) => {
return (
<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 && (
<FlashList
data={data.items}

View file

@ -1,6 +1,7 @@
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 { isPath } from '~/types/helper';
import tw from '../../lib/tailwind';
import { SharedScreenProps } from '../../navigation/SharedScreens';
@ -17,13 +18,12 @@ const FileItem = ({ data }: FileItemProps) => {
const navigation = useNavigation<SharedScreenProps<'Location'>['navigation']>();
function handlePress() {
// if (!data) return;
// if (data.is_dir) {
// navigation.navigate('Location', { id: data.location_id });
// } else {
// setData(data);
// fileRef.current.present();
// }
if (isPath(data) && data.is_dir) {
navigation.navigate('Location', { id: data.location_id });
} else {
setData(data);
fileRef.current.present();
}
}
return (
@ -31,10 +31,10 @@ const FileItem = ({ data }: FileItemProps) => {
<View style={tw`w-[90px] h-[80px] items-center`}>
<FileThumb
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`}>
<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}
</Text>
</View>
@ -44,36 +44,3 @@ const FileItem = ({ data }: FileItemProps) => {
};
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
// Background
style={tw`absolute top-0 left-0`}
fill={tw.color('gray-550')}
fill={tw.color('app-box')}
width={45 * size}
height={60 * size}
viewBox="0 0 65 81"
@ -94,7 +94,7 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
<Svg
// Peel
style={tw`absolute top-[2px] -right-[0.6px]`}
fill={tw.color('gray-500')}
fill={tw.color('app-highlight')}
width={15 * size}
height={15 * size}
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 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 { top } = useSafeAreaInsets();
@ -16,7 +17,7 @@ const Header = () => {
return (
<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
})}
>
@ -26,18 +27,16 @@ const Header = () => {
animate={{ rotate: isDrawerOpen ? '90deg' : '0deg' }}
transition={{ type: 'timing' }}
>
<List size={20} color={tw.color('gray-300')} weight="fill" />
<List size={20} color={tw.color('ink-faint')} weight="fill" />
</MotiView>
</Pressable>
<Pressable
style={tw`flex-1 h-full justify-center`}
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>
</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' }}
>
<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>
</Pressable>
<AnimatedHeight hide={hide}>{children}</AnimatedHeight>

View file

@ -57,7 +57,7 @@ const Dialog = (props: DialogProps) => {
<Modal renderToHardwareTextureAndroid transparent visible={props.isVisible ?? visible}>
{/* Backdrop */}
<Pressable
style={tw`bg-black bg-opacity-50 absolute inset-0`}
style={tw`bg-app-box/40 absolute inset-0`}
onPress={handleCloseDialog}
disabled={props.disableBackdropClose || props.loading}
/>
@ -73,15 +73,16 @@ const Dialog = (props: DialogProps) => {
animate={{ translateY: 0 }}
transition={{ type: 'timing', duration: 200 }}
>
{/* TODO: Blur may look cool here */}
<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`}>
{/* 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 */}
{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}
</Text>
)}
@ -90,7 +91,7 @@ const Dialog = (props: DialogProps) => {
</View>
{/* Actions */}
<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`} />}
<View style={tw`flex-grow`} />
@ -100,17 +101,17 @@ const Dialog = (props: DialogProps) => {
disabled={props.loading} // Disables Close button if loading
onPress={handleCloseDialog}
>
<Text style={tw`text-white text-sm`}>Close</Text>
<Text style={tw`text-ink text-sm`}>Close</Text>
</Button>
{props.ctaAction && (
<Button
style={tw`ml-2`}
variant={props.ctaDanger ? 'danger' : 'primary'}
variant={props.ctaDanger ? 'danger' : 'accent'}
size="md"
onPress={props.ctaAction}
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>
)}
</View>

View file

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

View file

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

View file

@ -6,7 +6,7 @@ type 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;

View file

@ -1,12 +1,12 @@
import { VariantProps, cva } from 'class-variance-authority';
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';
const input = cva(['text-sm rounded-md border shadow-sm'], {
variants: {
variant: {
default: 'bg-gray-550 border-gray-500 text-white'
default: 'bg-app border-app-line text-ink'
},
size: {
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;
export const TextInput: FC<InputProps> = ({ variant, ...props }) => {
export const Input: FC<InputProps> = ({ variant, ...props }) => {
const { style, ...otherProps } = props;
return (
<RNTextInput
placeholderTextColor={tw.color('gray-300')}
<TextInput
placeholderTextColor={tw.color('ink-dull')}
style={tw.style(input({ variant }), style as string)}
{...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 { useState } from 'react';
import Dialog from '~/components/layout/Dialog';
import { TextInput } from '~/components/primitive/Input';
import { Input } from '~/components/primitive/Input';
type Props = {
onSubmit?: () => void;
@ -11,7 +11,7 @@ type Props = {
const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props) => {
const [libName, setLibName] = useState('');
const [createLibOpen, setCreateLibOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const { switchLibrary } = useCurrentLibrary();
@ -22,6 +22,7 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
// Reset form
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]);
// Switch to the new library
@ -31,14 +32,14 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
},
onSettled: () => {
// Close create lib dialog
setCreateLibOpen(false);
setIsOpen(false);
}
}
);
return (
<Dialog
isVisible={createLibOpen}
setIsVisible={setCreateLibOpen}
isVisible={isOpen}
setIsVisible={setIsOpen}
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."
ctaLabel="Create"
@ -47,9 +48,9 @@ const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props
ctaDisabled={libName.length === 0}
trigger={children}
disableBackdropClose={disableBackdropClose}
onClose={() => setLibName('')} // Reset form onClose
onClose={() => setLibName('')} // Resets form onClose
>
<TextInput
<Input
value={libName}
onChangeText={(text) => setLibName(text)}
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 style={tw`flex flex-row items-center`}>
<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>
<Divider style={tw`my-4`} />
{/* Library Manager */}
@ -37,7 +37,7 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
</View>
{/* Settings */}
<Pressable onPress={() => navigation.navigate('Settings')}>
<Gear color="white" size={24} />
<Gear color={tw.color('ink')} size={24} />
</Pressable>
</View>
</DrawerContentScrollView>

View file

@ -1,4 +1,5 @@
import { useDrawerStatus } from '@react-navigation/drawer';
import { useNavigation } from '@react-navigation/native';
import { MotiView } from 'moti';
import { CaretRight, Gear, Lock, Plus } from 'phosphor-react-native';
import { useEffect, useState } from 'react';
@ -21,62 +22,73 @@ const DrawerLibraryManager = () => {
const { library: currentLibrary, libraries, switchLibrary } = useCurrentLibrary();
const navigation = useNavigation();
return (
<View>
<Pressable onPress={() => setDropdownClosed((v) => !v)}>
<View
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',
dropdownClosed ? 'rounded' : 'rounded-t border-b-gray-550'
'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-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
animate={{
rotateZ: dropdownClosed ? '0deg' : '90deg'
}}
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>
</View>
</Pressable>
<AnimatedHeight hide={dropdownClosed}>
<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?.map((library) => (
<Pressable key={library.uuid} onPress={() => switchLibrary(library.uuid)}>
<View
style={tw.style(
'p-2',
currentLibrary.uuid === library.uuid && 'bg-gray-500 bg-opacity-70 rounded'
'p-2 mt-1',
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>
</Pressable>
))}
<Divider style={tw`mt-2 mb-2`} />
{/* 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]`}>
<Gear size={16} color={tw.color('gray-100')} style={tw`mr-2`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Library Settings</Text>
<Gear size={16} color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-ink font-semibold`}>Library Settings</Text>
</View>
</Pressable>
{/* Create Library */}
<CreateLibraryDialog>
<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`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Add Library</Text>
<Plus size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-ink font-semibold`}>Add Library</Text>
</View>
</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]`}>
<Lock size={16} weight="bold" color={tw.color('gray-100')} style={tw`mr-2`} />
<Text style={tw`text-sm text-gray-200 font-semibold`}>Lock</Text>
<Lock size={16} weight="bold" color={tw.color('ink-dull')} style={tw`mr-2`} />
<Text style={tw`text-sm text-ink font-semibold`}>Lock</Text>
</View>
</Pressable>
</View>

View file

@ -8,12 +8,12 @@ import tw from '~/lib/tailwind';
import FolderIcon from '../../components/icons/FolderIcon';
import CollapsibleView from '../../components/layout/CollapsibleView';
import ImportModal from '../modals/ImportModal';
import ImportModal from '../modal/ImportModal';
interface DrawerLocationItemProps {
type DrawerLocationItemProps = {
folderName: string;
onPress: () => void;
}
};
const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
const { folderName, onPress } = props;
@ -30,9 +30,9 @@ const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
);
};
interface DrawerLocationsProp {
type DrawerLocationsProp = {
stackName: string;
}
};
const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
const navigation = useNavigation<DrawerNavigationHelpers>();
@ -64,7 +64,7 @@ const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
</View>
{/* Add Location */}
<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`}>
Add Location
</Text>

View file

@ -5,6 +5,7 @@ import { ColorValue, Pressable, Text, View } from 'react-native';
import tw from '~/lib/tailwind';
import CollapsibleView from '../../components/layout/CollapsibleView';
import CreateTagDialog from '../dialog/tag/CreateTagDialog';
type DrawerTagItemProps = {
tagName: string;
@ -56,6 +57,12 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
/>
))}
</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>
);
};

View file

@ -10,10 +10,10 @@ import Divider from '../../components/primitive/Divider';
import tw from '../../lib/tailwind';
import { useFileModalStore } from '../../stores/modalStore';
interface MetaItemProps {
type MetaItemProps = {
title: string;
value: string;
}
};
function MetaItem({ title, value }: MetaItemProps) {
return (
@ -33,7 +33,7 @@ export const FileModal = () => {
<>
<Modal ref={fileRef} snapPoints={['60%', '90%']}>
{data && (
<View style={tw`flex-1 p-4 bg-gray-600`}>
<View style={tw`flex-1 p-4 bg-app`}>
{/* File Icon / Name */}
<View style={tw`flex flex-row items-center`}>
<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>
</View>
<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>
</View>
</View>
@ -70,10 +70,10 @@ export const FileModal = () => {
snapPoints={['70%']}
>
{data && (
<BottomSheetScrollView style={tw`flex-1 p-4 bg-gray-600`}>
<BottomSheetScrollView style={tw`flex-1 p-4 bg-app`}>
{/* Back Button */}
<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>
{/* File Icon / Name */}
<View style={tw`items-center`}>

View file

@ -10,14 +10,15 @@ import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef';
import tw from '~/lib/tailwind';
// WIP component
const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
const modalRef = useForwardedRef(ref);
const { mutate: createLocation } = useLibraryMutation('locations.create', {
onError: (error, variables, context) => {
onError: (error) => {
console.error(error);
},
onSettled: (data, error, variables, context) => {
onSettled: () => {
// Close the modal
modalRef.current?.close();
}
@ -89,7 +90,6 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
// Gets Actual Path
const path = (await ML.getAssetInfoAsync(assetId)).localUri;
// Permission Granted
const libraryPath = Platform.select({
android: '',
ios: path.replace('file://', '').split('Media/DCIM/')[0] + 'Media/DCIM/'
@ -100,10 +100,10 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
indexer_rules_ids: []
});
const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo });
assets.assets.map(async (i) => {
console.log((await ML.getAssetInfoAsync(i)).localUri);
});
// const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo });
// assets.assets.map(async (i) => {
// console.log((await ML.getAssetInfoAsync(i)).localUri);
// });
}, [createLocation]);
// const testFN = useCallback(async () => {
@ -126,14 +126,14 @@ const ImportModal = forwardRef<BottomSheetModal, unknown>((_, ref) => {
return (
<Modal ref={modalRef} snapPoints={['20%']}>
<View style={tw`flex-1 px-6 pt-1 pb-2 bg-gray-600`}>
{/* <Button size="md" variant="primary" style={tw`my-2`} onPress={testFN}>
<View style={tw`flex-1 px-6 pt-1 pb-2 bg-app-box`}>
{/* <Button size="md" variant="accent" style={tw`my-2`} onPress={testFN}>
<Text>TEST</Text>
</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>
</Button>
<Button size="md" variant="primary" onPress={handlePhotosButton}>
<Button size="md" variant="accent" onPress={handlePhotosButton}>
<Text>Import from Photos</Text>
</Button>
</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 * as SplashScreen from 'expo-splash-screen';
import { lazy, useEffect } from 'react';
import { Suspense } from 'react';
import { Suspense, lazy } from 'react';
import { Platform } from 'react-native';
// 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 { StackScreenProps } from '@react-navigation/stack';
import DrawerContent from '~/containers/drawer/DrawerContent';
import tw from '~/lib/tailwind';
import type { RootStackParamList } from '.';
import type { TabParamList } from './TabNavigator';
@ -16,7 +17,7 @@ export default function DrawerNavigator() {
screenOptions={{
headerShown: false,
drawerStyle: {
backgroundColor: 'rgb(10,10,12)',
backgroundColor: tw.color('app-darkBox'),
width: '75%'
},
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 * as Linking from 'expo-linking';
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> = {
prefixes: [Linking.createURL('/')],
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 TagScreen from '~/screens/Tag';
// Mounted on all the tabs, so we can navigate to it from any tab
export function SharedScreens(
Stack: TypedNavigator<
SharedScreensParamList,

View file

@ -1,12 +1,12 @@
import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
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 type { HomeDrawerScreenProps } from './DrawerNavigator';
import BrowseStack, { BrowseStackParamList } from './tabs/BrowseStack';
import NodesStack, { NodesStackParamList } from './tabs/NodesStack';
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
import PhotosStack, { PhotosStackParamList } from './tabs/PhotosStack';
import SpacesStack, { SpacesStackParamList } from './tabs/SpacesStack';
const Tab = createBottomTabNavigator<TabParamList>();
@ -17,11 +17,11 @@ export default function TabNavigator() {
initialRouteName="OverviewStack"
screenOptions={{
headerShown: false,
tabBarActiveTintColor: tw.color('primary'),
tabBarInactiveTintColor: 'white',
tabBarActiveTintColor: tw.color('accent'),
tabBarInactiveTintColor: tw.color('ink'),
tabBarStyle: {
backgroundColor: tw.color('gray-650'),
borderTopColor: tw.color('gray-600')
backgroundColor: tw.color('app'),
borderTopColor: tw.color('app-shade')
}
}}
>
@ -30,19 +30,29 @@ export default function TabNavigator() {
component={OverviewStack}
options={{
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
name="BrowseStack"
component={BrowseStack}
name="NodesStack"
component={NodesStack}
options={{
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
@ -52,21 +62,12 @@ export default function TabNavigator() {
tabBarIcon: ({ focused }) => (
<CirclesFour
size={22}
weight="bold"
color={focused ? tw.color('bg-primary') : 'white'}
weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('accent') : tw.color('ink')}
/>
),
tabBarLabel: 'Spaces'
}}
/>
<Tab.Screen
name="PhotosStack"
component={PhotosStack}
options={{
tabBarIcon: ({ focused }) => (
<Camera size={22} weight="bold" color={focused ? tw.color('bg-primary') : 'white'} />
),
tabBarLabel: 'Photos'
tabBarLabel: 'Spaces',
tabBarLabelStyle: tw`font-semibold text-tiny`
}}
/>
</Tab.Navigator>
@ -75,9 +76,8 @@ export default function TabNavigator() {
export type TabParamList = {
OverviewStack: NavigatorScreenParams<OverviewStackParamList>;
BrowseStack: NavigatorScreenParams<BrowseStackParamList>;
NodesStack: NavigatorScreenParams<NodesStackParamList>;
SpacesStack: NavigatorScreenParams<SpacesStackParamList>;
PhotosStack: NavigatorScreenParams<PhotosStackParamList>;
};
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 tw from '~/lib/tailwind';
import NotFoundScreen from '~/screens/NotFound';
import SearchScreen from '~/screens/modals/Search';
import SettingsScreen from '~/screens/modals/settings/Settings';
import SearchScreen from '~/screens/Search';
import type { DrawerNavParamList } from './DrawerNavigator';
import DrawerNavigator from './DrawerNavigator';
import SettingsNavigator, { SettingsStackParamList } from './SettingsNavigator';
const Stack = createStackNavigator<RootStackParamList>();
@ -24,16 +24,17 @@ export default function RootNavigator() {
{/* Modals */}
<Stack.Group
screenOptions={{
headerShown: false,
presentation: 'modal',
headerBackTitleVisible: false,
headerStyle: tw`bg-gray-650`,
headerTintColor: tw.color('gray-200'),
headerStyle: tw`bg-app`,
headerTintColor: tw.color('ink'),
headerTitleStyle: tw`text-base`,
headerBackTitleStyle: tw`text-base`
// headerShadowVisible: false // will disable the white line under
}}
>
<Stack.Screen name="Settings" component={SettingsScreen} />
<Stack.Screen name="Settings" component={SettingsNavigator} />
</Stack.Group>
</Stack.Navigator>
);
@ -44,7 +45,7 @@ export type RootStackParamList = {
NotFound: undefined;
// Modals
Search: undefined;
Settings: undefined;
Settings: NavigatorScreenParams<SettingsStackParamList>;
};
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
initialRouteName="Overview"
screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') },
headerTintColor: tw.color('gray-200'),
headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('ink'),
headerTitleStyle: 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
initialRouteName="Spaces"
screenOptions={{
headerStyle: { backgroundColor: tw.color('gray-650') },
headerTintColor: tw.color('gray-200'),
headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('ink'),
headerTitleStyle: 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`}>
<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`}>
<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>
</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 */}
<View style={tw`flex flex-row items-center mx-4`}>
{/* Search Input */}
<View
style={tw`flex-1 bg-gray-500 border border-[#333949] bg-opacity-40 rounded h-10 mr-3`}
>
<View style={tw`flex-1 bg-app-overlay border border-app-line rounded h-10 mr-3`}>
<View style={tw`flex flex-row h-full items-center px-3`}>
<View style={tw`mr-3`}>
{loading ? (
<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>
<TextInput
placeholder={'Search'}
clearButtonMode="never" // can't change the color??
underlineColorAndroid="transparent"
placeholderTextColor={tw.color('gray-300')}
style={tw`flex-1 text-gray-300 font-medium text-sm`}
placeholderTextColor={tw.color('ink-dull')}
style={tw`flex-1 text-ink font-medium text-sm`}
textContentType={'none'}
autoFocus
autoCapitalize="none"
@ -44,12 +42,12 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
</View>
{/* Cancel Button */}
<Pressable onPress={() => navigation.goBack()}>
<Text style={tw`text-primary-500`}>Cancel</Text>
<Text style={tw`text-accent`}>Cancel</Text>
</Pressable>
</View>
{/* Content */}
<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>
</Button>
</View>

View file

@ -5,7 +5,7 @@ import { SpacesStackScreenProps } from '~/navigation/tabs/SpacesStack';
export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) {
return (
<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 file

@ -6,7 +6,7 @@ export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'
const { id } = route.params;
return (
<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 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 { useSnapshot } from 'valtio';
import { AnimatedButton } from '~/components/primitive/Button';
import CreateLibraryDialog from '~/containers/dialog/CreateLibraryDialog';
import tw from '~/lib/tailwind';
@ -7,13 +6,13 @@ import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => {
return (
<View style={tw`flex-1 items-center justify-center bg-gray-650 p-4`}>
<Text style={tw`text-gray-450 text-center px-6 my-8 text-base leading-relaxed`}>
<View style={tw`flex-1 items-center justify-center bg-app p-4`}>
<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
</Text>
<CreateLibraryDialog disableBackdropClose>
<AnimatedButton disabled variant="primary">
<Text style={tw`text-white text-center px-6 py-2 text-base font-medium`}>
<AnimatedButton variant="accent">
<Text style={tw`text-ink text-center px-6 py-2 text-base font-medium`}>
Create Library
</Text>
</AnimatedButton>

View file

@ -6,7 +6,7 @@ import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => {
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 */}
<LogoAnimation>
<View style={tw`items-center mt-2`}>
@ -16,12 +16,12 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
{/* Text */}
<View>
<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.
</Text>
</FadeInUpAnimation>
<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
any device.
</Text>
@ -29,10 +29,8 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
</View>
{/* Get Started Button */}
<FadeInUpAnimation delay={1200}>
<AnimatedButton variant="primary" onPress={() => navigation.navigate('CreateLibrary')}>
<Text style={tw`text-white text-center px-6 py-2 text-base font-medium`}>
Get Started
</Text>
<AnimatedButton variant="accent" onPress={() => navigation.navigate('CreateLibrary')}>
<Text style={tw`text-ink text-center px-6 py-2 text-base font-medium`}>Get Started</Text>
</AnimatedButton>
</FadeInUpAnimation>
</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 = {
content: ['./screens/**/*.{js,ts,jsx}', './components/**/*.{js,ts,jsx}', 'App.tsx'],
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: {
fontSize: {
tiny: '.65rem',
// Default: '0.875rem'
sm: '.85rem'
},
colors: {
primary: {
DEFAULT: '#2599FF',
50: '#FFFFFF',
100: '#F1F8FF',
200: '#BEE1FF',
300: '#8BC9FF',
400: '#58B1FF',
500: '#2599FF',
600: '#0081F1',
700: '#0065BE',
800: '#004A8B',
900: '#002F58'
// Brand blue
accent: {
DEFAULT: 'hsla(208, 100%, 47%, 1)',
faint: 'hsla(208, 100%, 64%, 1)',
deep: 'hsla(208, 100%, 47%, 1)'
},
ink: {
DEFAULT: 'hsla(230, 0%, 100%, 1)',
light: 'hsla(230, 0%, 82%, 1)',
dull: 'hsla(230, 10%, 70%, 1)',
faint: 'hsla(230, 10%, 55%, 1)'
},
// 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: {
DEFAULT: '#505468',

View file

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

View file

@ -9,3 +9,4 @@ export * from './hooks';
export * from './stores';
export * from './rspc';
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 clsx from 'clsx';
import { HTMLAttributes } from 'react';
@ -27,7 +27,7 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
}
function FileItem({ data, selected, index, ...rest }: Props) {
const isVid = isVideo(data.extension || '');
const isVid = isVideoExt(data.extension || '');
return (
<div
@ -86,35 +86,3 @@ function FileItem({ data, selected, index, ...rest }: Props) {
}
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