mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-05 05:46:24 -04:00
Merge pull request #373 from spacedriveapp/mobile-library
Mobile library
This commit is contained in:
@@ -11,7 +11,8 @@
|
||||
"build": "tauri build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rspc/client": "^0.0.5",
|
||||
"@rspc/client": "^0.0.6",
|
||||
"@rspc/tauri": "^0.0.6",
|
||||
"@sd/client": "workspace:*",
|
||||
"@sd/core": "workspace:*",
|
||||
"@sd/interface": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// import Spacedrive JS client
|
||||
import { TauriTransport, createClient } from '@rspc/client';
|
||||
import { createClient } from '@rspc/client';
|
||||
import { TauriTransport } from '@rspc/tauri';
|
||||
import { Operations, queryClient, rspc } from '@sd/client';
|
||||
import SpacedriveInterface, { Platform } from '@sd/interface';
|
||||
import { dialog, invoke, os, shell } from '@tauri-apps/api';
|
||||
import { Event, listen } from '@tauri-apps/api/event';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
@@ -2,6 +2,27 @@ module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: ['react-native-reanimated/plugin']
|
||||
plugins: [
|
||||
'react-native-reanimated/plugin',
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
extensions: [
|
||||
'.js',
|
||||
'.jsx',
|
||||
'.ts',
|
||||
'.tsx',
|
||||
'.android.js',
|
||||
'.android.tsx',
|
||||
'.ios.js',
|
||||
'.ios.tsx'
|
||||
],
|
||||
root: ['src'],
|
||||
alias: {
|
||||
'~': './src'
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,11 +9,11 @@ PODS:
|
||||
- ExpoModulesCore
|
||||
- EXFont (10.2.0):
|
||||
- ExpoModulesCore
|
||||
- Expo (46.0.9):
|
||||
- Expo (46.0.10):
|
||||
- ExpoModulesCore
|
||||
- ExpoKeepAwake (10.2.0):
|
||||
- ExpoModulesCore
|
||||
- ExpoModulesCore (0.11.4):
|
||||
- ExpoModulesCore (0.11.5):
|
||||
- React-Core
|
||||
- ReactCommon/turbomodule/core
|
||||
- EXSplashScreen (0.16.2):
|
||||
@@ -31,6 +31,10 @@ PODS:
|
||||
- glog (0.3.5)
|
||||
- hermes-engine (0.69.4)
|
||||
- libevent (2.1.12)
|
||||
- lottie-ios (3.4.2)
|
||||
- lottie-react-native (5.1.4):
|
||||
- lottie-ios (~> 3.4.0)
|
||||
- React-Core
|
||||
- RCT-Folly (2021.06.28.00-v2):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -335,7 +339,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNGestureHandler (2.5.0):
|
||||
- React-Core
|
||||
- RNReanimated (2.9.1):
|
||||
- RNReanimated (2.10.0):
|
||||
- DoubleConversion
|
||||
- FBLazyVector
|
||||
- FBReactNativeSpec
|
||||
@@ -372,19 +376,20 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- "EXApplication (from `../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.9/node_modules/expo-application/ios`)"
|
||||
- "EXConstants (from `../node_modules/.pnpm/expo-constants@13.2.4_expo@46.0.9/node_modules/expo-constants/ios`)"
|
||||
- "EXFileSystem (from `../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.9/node_modules/expo-file-system/ios`)"
|
||||
- "EXFont (from `../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.9/node_modules/expo-font/ios`)"
|
||||
- "Expo (from `../node_modules/.pnpm/expo@46.0.9_@babel+core@7.18.10/node_modules/expo`)"
|
||||
- "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.9/node_modules/expo-keep-awake/ios`)"
|
||||
- "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@0.11.4/node_modules/expo-modules-core/ios`)"
|
||||
- "EXSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.9/node_modules/expo-splash-screen/ios`)"
|
||||
- "EXApplication (from `../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.10/node_modules/expo-application/ios`)"
|
||||
- "EXConstants (from `../node_modules/.pnpm/expo-constants@13.2.4_expo@46.0.10/node_modules/expo-constants/ios`)"
|
||||
- "EXFileSystem (from `../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.10/node_modules/expo-file-system/ios`)"
|
||||
- "EXFont (from `../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.10/node_modules/expo-font/ios`)"
|
||||
- "Expo (from `../node_modules/.pnpm/expo@46.0.10_@babel+core@7.18.10/node_modules/expo`)"
|
||||
- "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.10/node_modules/expo-keep-awake/ios`)"
|
||||
- "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@0.11.5/node_modules/expo-modules-core/ios`)"
|
||||
- "EXSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.10/node_modules/expo-splash-screen/ios`)"
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`)
|
||||
- libevent (~> 2.1.12)
|
||||
- lottie-react-native (from `../node_modules/lottie-react-native`)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
@@ -426,6 +431,7 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- fmt
|
||||
- libevent
|
||||
- lottie-ios
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@@ -433,21 +439,21 @@ EXTERNAL SOURCES:
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
EXApplication:
|
||||
:path: "../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.9/node_modules/expo-application/ios"
|
||||
:path: "../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.10/node_modules/expo-application/ios"
|
||||
EXConstants:
|
||||
:path: "../node_modules/.pnpm/expo-constants@13.2.4_expo@46.0.9/node_modules/expo-constants/ios"
|
||||
:path: "../node_modules/.pnpm/expo-constants@13.2.4_expo@46.0.10/node_modules/expo-constants/ios"
|
||||
EXFileSystem:
|
||||
:path: "../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.9/node_modules/expo-file-system/ios"
|
||||
:path: "../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.10/node_modules/expo-file-system/ios"
|
||||
EXFont:
|
||||
:path: "../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.9/node_modules/expo-font/ios"
|
||||
:path: "../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.10/node_modules/expo-font/ios"
|
||||
Expo:
|
||||
:path: "../node_modules/.pnpm/expo@46.0.9_@babel+core@7.18.10/node_modules/expo"
|
||||
:path: "../node_modules/.pnpm/expo@46.0.10_@babel+core@7.18.10/node_modules/expo"
|
||||
ExpoKeepAwake:
|
||||
:path: "../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.9/node_modules/expo-keep-awake/ios"
|
||||
:path: "../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.10/node_modules/expo-keep-awake/ios"
|
||||
ExpoModulesCore:
|
||||
:path: "../node_modules/.pnpm/expo-modules-core@0.11.4/node_modules/expo-modules-core/ios"
|
||||
:path: "../node_modules/.pnpm/expo-modules-core@0.11.5/node_modules/expo-modules-core/ios"
|
||||
EXSplashScreen:
|
||||
:path: "../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.9/node_modules/expo-splash-screen/ios"
|
||||
:path: "../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.10/node_modules/expo-splash-screen/ios"
|
||||
FBLazyVector:
|
||||
:path: "../node_modules/react-native/Libraries/FBLazyVector"
|
||||
FBReactNativeSpec:
|
||||
@@ -456,6 +462,8 @@ EXTERNAL SOURCES:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
hermes-engine:
|
||||
:podspec: "../node_modules/react-native/sdks/hermes/hermes-engine.podspec"
|
||||
lottie-react-native:
|
||||
:path: "../node_modules/lottie-react-native"
|
||||
RCT-Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
RCTRequired:
|
||||
@@ -534,9 +542,9 @@ SPEC CHECKSUMS:
|
||||
EXConstants: 7c44785d41d8e959d527d23d29444277a4d1ee73
|
||||
EXFileSystem: 927e0a8885aa9c49e50fc38eaba2c2389f2f1019
|
||||
EXFont: a5d80bd9b3452b2d5abbce2487da89b0150e6487
|
||||
Expo: 73412414e62f5cbc6e713def821de70b92cd3ad6
|
||||
Expo: fcdb32274e2ca9c7638d3b21b30fb665c6869219
|
||||
ExpoKeepAwake: 0e8f18142e71bbf2c7f6aa66ebed249ba1420320
|
||||
ExpoModulesCore: e281bb7b78ea47e227dd5af94d04b24d8b2e1255
|
||||
ExpoModulesCore: 5a973701f4400d70254bc836305228731c829010
|
||||
EXSplashScreen: 799bece80089219b2c989c1082d70f3b00995cda
|
||||
FBLazyVector: c71b8c429a8af2aff1013934a7152e9d9d0c937d
|
||||
FBReactNativeSpec: 3cc5cff7d792e74a875be91e56d6242335016f50
|
||||
@@ -544,6 +552,8 @@ SPEC CHECKSUMS:
|
||||
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
|
||||
hermes-engine: 761a544537e62df2a37189389b9d2654dc1f75af
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
lottie-ios: 6bbc53eef6957e4744a50321507015fba72d8ca6
|
||||
lottie-react-native: b702fab740cdb952a8e2354713d3beda63ff97b0
|
||||
RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a
|
||||
RCTRequired: bd9d2ab0fda10171fcbcf9ba61a7df4dc15a28f4
|
||||
RCTTypeSafety: e44e139bf6ec8042db396201834fc2372f6a21cd
|
||||
@@ -575,7 +585,7 @@ SPEC CHECKSUMS:
|
||||
RNCAsyncStorage: d81ee5c3db1060afd49ea7045ad460eff82d2b7d
|
||||
RNCMaskedView: cb9670ea9239998340eaab21df13fa12a1f9de15
|
||||
RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50
|
||||
RNReanimated: 2cf7451318bb9cc430abeec8d67693f9cf4e039c
|
||||
RNReanimated: 7faa787e8d4493fbc95fab2ad331fa7625828cfa
|
||||
RNScreens: 4a1af06327774490d97342c00aee0c2bafb497b7
|
||||
RNSVG: 42a0c731b11179ebbd27a3eeeafa7201ebb476ff
|
||||
Yoga: ff994563b2fd98c982ca58e8cd9db2cdaf4dda74
|
||||
|
||||
@@ -1,81 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spacedrive</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>spacedrive</string>
|
||||
<string>com.spacedrive.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spacedrive</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>spacedrive</string>
|
||||
<string>com.spacedrive.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true />
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true />
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false />
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false />
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -10,7 +10,6 @@
|
||||
"lint": "eslint src/**/*.{ts,tsx} && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^13.0.0",
|
||||
"@gorhom/bottom-sheet": "^4.4.3",
|
||||
"@react-native-async-storage/async-storage": "~1.17.3",
|
||||
"@react-native-masked-view/masked-view": "0.2.7",
|
||||
@@ -18,41 +17,45 @@
|
||||
"@react-navigation/drawer": "^6.4.4",
|
||||
"@react-navigation/native": "^6.0.12",
|
||||
"@react-navigation/stack": "^6.2.3",
|
||||
"@rspc/client": "^0.0.5",
|
||||
"@rspc/client": "^0.0.6",
|
||||
"@rspc/react": "^0.0.6",
|
||||
"@sd/assets": "file:../../packages/assets",
|
||||
"@tanstack/react-query": "^4.2.3",
|
||||
"byte-size": "^8.1.0",
|
||||
"class-variance-authority": "^0.2.3",
|
||||
"date-fns": "^2.29.2",
|
||||
"expo": "~46.0.9",
|
||||
"expo-font": "~10.2.0",
|
||||
"expo": "~46.0.10",
|
||||
"expo-linking": "~3.2.2",
|
||||
"expo-splash-screen": "~0.16.2",
|
||||
"expo-status-bar": "~1.4.0",
|
||||
"immer": "^9.0.15",
|
||||
"intl": "^1.2.5",
|
||||
"lottie-react-native": "^5.1.4",
|
||||
"moti": "^0.18.0",
|
||||
"phosphor-react-native": "^1.1.2",
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.4",
|
||||
"react-native-gesture-handler": "~2.5.0",
|
||||
"react-native-heroicons": "^2.2.0",
|
||||
"react-native-reanimated": "~2.9.1",
|
||||
"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",
|
||||
"twrnc": "^3.4.0",
|
||||
"use-count-up": "^3.0.1",
|
||||
"zustand": "^4.1.1"
|
||||
"valtio": "^1.7.0",
|
||||
"valtio-persist": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.6",
|
||||
"@babel/runtime": "^7.18.9",
|
||||
"@rnx-kit/metro-config": "^1.2.36",
|
||||
"@rnx-kit/metro-config": "^1.2.37",
|
||||
"@rnx-kit/metro-resolver-symlinks": "^0.1.21",
|
||||
"@types/react": "~18.0.0",
|
||||
"@types/react-native": "~0.69.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||
"@typescript-eslint/parser": "^5.30.7",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
|
||||
BIN
apps/mobile/pnpm-lock.yaml
generated
BIN
apps/mobile/pnpm-lock.yaml
generated
Binary file not shown.
@@ -6,15 +6,22 @@ import React, { useEffect } from 'react';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { useDeviceContext } from 'twrnc';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import { GlobalModals } from './components/modals/GlobalModals';
|
||||
import { ReactNativeTransport, queryClient, rspc, useInvalidateQuery } from './hooks/rspc';
|
||||
import {
|
||||
ReactNativeTransport,
|
||||
queryClient,
|
||||
rspc,
|
||||
useBridgeQuery,
|
||||
useInvalidateQuery
|
||||
} from './hooks/rspc';
|
||||
import useCachedResources from './hooks/useCachedResources';
|
||||
import { getItemFromStorage } from './lib/storage';
|
||||
import tw from './lib/tailwind';
|
||||
import RootNavigator from './navigation';
|
||||
import OnboardingNavigator from './navigation/OnboardingNavigator';
|
||||
import { useOnboardingStore } from './stores/useOnboardingStore';
|
||||
import { libraryStore } from './stores/libraryStore';
|
||||
import { onboardingStore } from './stores/onboardingStore';
|
||||
import type { Operations } from './types/bindings';
|
||||
|
||||
const client = createClient<Operations>({
|
||||
@@ -25,50 +32,69 @@ const NavigatorTheme: Theme = {
|
||||
...DefaultTheme,
|
||||
colors: {
|
||||
...DefaultTheme.colors,
|
||||
background: '#08090D'
|
||||
background: tw.color('gray-650')
|
||||
}
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
function AppContainer() {
|
||||
// Enables dark mode, and screen size breakpoints, etc. for tailwind
|
||||
useDeviceContext(tw, { withDeviceColorScheme: false });
|
||||
|
||||
const isLoadingComplete = useCachedResources();
|
||||
|
||||
const { showOnboarding, hideOnboarding } = useOnboardingStore();
|
||||
const { showOnboarding } = useSnapshot(onboardingStore);
|
||||
|
||||
const { data: libraries } = useBridgeQuery(['library.list'], {
|
||||
onError(err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
const { _persist, switchLibrary } = useSnapshot(libraryStore);
|
||||
|
||||
console.log('persisted?', _persist.loaded);
|
||||
|
||||
// Runs when the app is launched
|
||||
useEffect(() => {
|
||||
getItemFromStorage('@onboarding').then((value) => {
|
||||
value && hideOnboarding();
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
// Temporarly set the first library to be the current library
|
||||
if (!showOnboarding) {
|
||||
if (libraries && libraries.length > 0) {
|
||||
switchLibrary(libraries[0].uuid);
|
||||
}
|
||||
}
|
||||
}, [libraries, showOnboarding, switchLibrary]);
|
||||
|
||||
if (!isLoadingComplete) {
|
||||
// Might need to move _persist.loaded to useCacheResources hook.
|
||||
if (!isLoadingComplete || !_persist.loaded) {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<rspc.Provider client={client} queryClient={queryClient}>
|
||||
<>
|
||||
<InvalidateQuery />
|
||||
<SafeAreaProvider style={tw`flex-1 bg-black`}>
|
||||
<GestureHandlerRootView style={tw`flex-1`}>
|
||||
<BottomSheetModalProvider>
|
||||
<StatusBar style="light" />
|
||||
<NavigationContainer theme={NavigatorTheme}>
|
||||
{showOnboarding ? <OnboardingNavigator /> : <RootNavigator />}
|
||||
</NavigationContainer>
|
||||
<GlobalModals />
|
||||
</BottomSheetModalProvider>
|
||||
</GestureHandlerRootView>
|
||||
</SafeAreaProvider>
|
||||
</>
|
||||
</rspc.Provider>
|
||||
<SafeAreaProvider style={tw`flex-1 bg-gray-650`}>
|
||||
<GestureHandlerRootView style={tw`flex-1`}>
|
||||
<BottomSheetModalProvider>
|
||||
<StatusBar style="light" />
|
||||
<NavigationContainer theme={NavigatorTheme}>
|
||||
{showOnboarding ? <OnboardingNavigator /> : <RootNavigator />}
|
||||
</NavigationContainer>
|
||||
<GlobalModals />
|
||||
</BottomSheetModalProvider>
|
||||
</GestureHandlerRootView>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<rspc.Provider client={client} queryClient={queryClient}>
|
||||
<>
|
||||
<InvalidateQuery />
|
||||
<AppContainer />
|
||||
</>
|
||||
</rspc.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidateQuery() {
|
||||
useInvalidateQuery();
|
||||
return null;
|
||||
|
||||
@@ -2,9 +2,8 @@ import { MotiView, useDynamicAnimation } from 'moti';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
||||
|
||||
import Layout from '../../constants/Layout';
|
||||
import tw from '../../lib/tailwind';
|
||||
import Layout from '~/constants/Layout';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
// Anything wrapped with FadeIn will fade in on mount.
|
||||
export const FadeInAnimation = ({ children, delay }: { children: any; delay?: number }) => (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import FolderIcon from '../icons/FolderIcon';
|
||||
|
||||
interface BrowseLocationItemProps {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ColorValue, Pressable, Text, View } from 'react-native';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
type BrowseTagItemProps = {
|
||||
tagName: string;
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Cloud, Desktop, DeviceMobileCamera, Laptop } from 'phosphor-react-nativ
|
||||
import React from 'react';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { LockClosedIcon } from 'react-native-heroicons/solid';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { FilePath } from '~/types/bindings';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import { FilePath } from '../../types/bindings';
|
||||
import FileItem from '../file/FileItem';
|
||||
|
||||
const placeholderFileItems: FilePath[] = [
|
||||
@@ -16,48 +16,11 @@ const placeholderFileItems: FilePath[] = [
|
||||
extension: '',
|
||||
file_id: 1,
|
||||
id: 1,
|
||||
key: null,
|
||||
location_id: 1,
|
||||
materialized_path: '',
|
||||
name: 'Minecraft',
|
||||
parent_id: 0,
|
||||
key_id: null,
|
||||
location: null,
|
||||
file: {
|
||||
id: 1,
|
||||
key_id: 1,
|
||||
albums: [],
|
||||
comments: [],
|
||||
key: {
|
||||
algorithm: null,
|
||||
checksum: '',
|
||||
date_created: null,
|
||||
file_paths: [],
|
||||
files: [],
|
||||
id: 1,
|
||||
name: 'Hello world'
|
||||
},
|
||||
labels: [],
|
||||
media_data: null,
|
||||
spaces: [],
|
||||
tags: [],
|
||||
cas_id: '',
|
||||
ipfs_id: '',
|
||||
has_thumbnail: false,
|
||||
favorite: false,
|
||||
has_thumbstrip: false,
|
||||
has_video_preview: false,
|
||||
hidden: false,
|
||||
important: false,
|
||||
integrity_checksum: '',
|
||||
kind: 1,
|
||||
note: '',
|
||||
paths: [],
|
||||
size_in_bytes: '555',
|
||||
date_created: '',
|
||||
date_indexed: '',
|
||||
date_modified: ''
|
||||
}
|
||||
key_id: null
|
||||
},
|
||||
{
|
||||
is_dir: true,
|
||||
@@ -67,48 +30,11 @@ const placeholderFileItems: FilePath[] = [
|
||||
extension: '',
|
||||
file_id: 2,
|
||||
id: 2,
|
||||
key: null,
|
||||
location_id: 2,
|
||||
materialized_path: '',
|
||||
name: 'Documents',
|
||||
parent_id: 0,
|
||||
key_id: null,
|
||||
location: null,
|
||||
file: {
|
||||
id: 2,
|
||||
key_id: 2,
|
||||
albums: [],
|
||||
comments: [],
|
||||
key: {
|
||||
algorithm: null,
|
||||
checksum: '',
|
||||
date_created: null,
|
||||
file_paths: [],
|
||||
files: [],
|
||||
id: 1,
|
||||
name: 'Hello world'
|
||||
},
|
||||
labels: [],
|
||||
media_data: null,
|
||||
spaces: [],
|
||||
tags: [],
|
||||
cas_id: '',
|
||||
ipfs_id: '',
|
||||
has_thumbnail: false,
|
||||
favorite: false,
|
||||
has_thumbstrip: false,
|
||||
has_video_preview: false,
|
||||
hidden: false,
|
||||
important: false,
|
||||
integrity_checksum: '',
|
||||
kind: 1,
|
||||
note: '',
|
||||
paths: [],
|
||||
size_in_bytes: '555',
|
||||
date_created: '',
|
||||
date_indexed: '',
|
||||
date_modified: ''
|
||||
}
|
||||
key_id: null
|
||||
},
|
||||
{
|
||||
is_dir: false,
|
||||
@@ -118,48 +44,11 @@ const placeholderFileItems: FilePath[] = [
|
||||
extension: 'tsx',
|
||||
file_id: 3,
|
||||
id: 3,
|
||||
key: null,
|
||||
location_id: 3,
|
||||
materialized_path: '',
|
||||
name: 'App.tsx',
|
||||
parent_id: 0,
|
||||
key_id: null,
|
||||
location: null,
|
||||
file: {
|
||||
id: 3,
|
||||
key_id: 3,
|
||||
albums: [],
|
||||
comments: [],
|
||||
key: {
|
||||
algorithm: null,
|
||||
checksum: '',
|
||||
date_created: null,
|
||||
file_paths: [],
|
||||
files: [],
|
||||
id: 1,
|
||||
name: 'Hello world'
|
||||
},
|
||||
labels: [],
|
||||
media_data: null,
|
||||
spaces: [],
|
||||
tags: [],
|
||||
cas_id: '',
|
||||
ipfs_id: '',
|
||||
has_thumbnail: false,
|
||||
favorite: false,
|
||||
has_thumbstrip: false,
|
||||
has_video_preview: false,
|
||||
hidden: false,
|
||||
important: false,
|
||||
integrity_checksum: '',
|
||||
kind: 1,
|
||||
note: '',
|
||||
paths: [],
|
||||
size_in_bytes: '555',
|
||||
date_created: '',
|
||||
date_indexed: '',
|
||||
date_modified: ''
|
||||
}
|
||||
key_id: null
|
||||
},
|
||||
{
|
||||
is_dir: false,
|
||||
@@ -169,48 +58,11 @@ const placeholderFileItems: FilePath[] = [
|
||||
extension: 'vite',
|
||||
file_id: 4,
|
||||
id: 4,
|
||||
key: null,
|
||||
location_id: 4,
|
||||
materialized_path: '',
|
||||
name: 'vite.config.js',
|
||||
parent_id: 0,
|
||||
key_id: null,
|
||||
location: null,
|
||||
file: {
|
||||
id: 4,
|
||||
key_id: 4,
|
||||
albums: [],
|
||||
comments: [],
|
||||
key: {
|
||||
algorithm: null,
|
||||
checksum: '',
|
||||
date_created: null,
|
||||
file_paths: [],
|
||||
files: [],
|
||||
id: 1,
|
||||
name: 'Hello world'
|
||||
},
|
||||
labels: [],
|
||||
media_data: null,
|
||||
spaces: [],
|
||||
tags: [],
|
||||
cas_id: '',
|
||||
ipfs_id: '',
|
||||
has_thumbnail: false,
|
||||
favorite: false,
|
||||
has_thumbstrip: false,
|
||||
has_video_preview: false,
|
||||
hidden: false,
|
||||
important: false,
|
||||
integrity_checksum: '',
|
||||
kind: 1,
|
||||
note: '',
|
||||
paths: [],
|
||||
size_in_bytes: '555',
|
||||
date_created: '',
|
||||
date_indexed: '',
|
||||
date_modified: ''
|
||||
}
|
||||
key_id: null
|
||||
},
|
||||
{
|
||||
is_dir: false,
|
||||
@@ -220,48 +72,11 @@ const placeholderFileItems: FilePath[] = [
|
||||
extension: 'docker',
|
||||
file_id: 5,
|
||||
id: 5,
|
||||
key: null,
|
||||
location_id: 5,
|
||||
materialized_path: '',
|
||||
name: 'Dockerfile',
|
||||
parent_id: 0,
|
||||
key_id: null,
|
||||
location: null,
|
||||
file: {
|
||||
id: 5,
|
||||
key_id: 5,
|
||||
albums: [],
|
||||
comments: [],
|
||||
key: {
|
||||
algorithm: null,
|
||||
checksum: '',
|
||||
date_created: null,
|
||||
file_paths: [],
|
||||
files: [],
|
||||
id: 1,
|
||||
name: 'Hello world'
|
||||
},
|
||||
labels: [],
|
||||
media_data: null,
|
||||
spaces: [],
|
||||
tags: [],
|
||||
cas_id: '',
|
||||
ipfs_id: '',
|
||||
has_thumbnail: false,
|
||||
favorite: false,
|
||||
has_thumbstrip: false,
|
||||
has_video_preview: false,
|
||||
hidden: false,
|
||||
important: false,
|
||||
integrity_checksum: '',
|
||||
kind: 1,
|
||||
note: '',
|
||||
paths: [],
|
||||
size_in_bytes: '555',
|
||||
date_created: '',
|
||||
date_indexed: '',
|
||||
date_modified: ''
|
||||
}
|
||||
key_id: null
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { DrawerContentComponentProps } from '@react-navigation/drawer/lib/typescript/src/types';
|
||||
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import { ColorValue, Platform, Pressable, Text, View } from 'react-native';
|
||||
import { ColorValue, Image, Platform, Pressable, Text, View } from 'react-native';
|
||||
import { CogIcon } from 'react-native-heroicons/solid';
|
||||
import Layout from '~/constants/Layout';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import Layout from '../../constants/Layout';
|
||||
import tw from '../../lib/tailwind';
|
||||
import CollapsibleView from '../layout/CollapsibleView';
|
||||
import Divider from '../primitive/Divider';
|
||||
import DrawerLibraryManager from './DrawerLibraryManager';
|
||||
import DrawerLocationItem from './DrawerLocationItem';
|
||||
import DrawerLogo from './DrawerLogo';
|
||||
import DrawerTagItem from './DrawerTagItem';
|
||||
|
||||
const placeholderLocationData = [
|
||||
@@ -60,28 +61,36 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
|
||||
<DrawerContentScrollView style={tw`flex-1 px-4 py-2`} scrollEnabled={false}>
|
||||
<View style={tw.style('justify-between', { height: drawerHeight })}>
|
||||
<View>
|
||||
<DrawerLogo />
|
||||
<Text style={tw`my-4 text-xs text-white`}>TODO: Library Selection</Text>
|
||||
<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>
|
||||
</View>
|
||||
<Divider style={tw`my-4`} />
|
||||
{/* Library Manager */}
|
||||
<DrawerLibraryManager />
|
||||
{/* Locations */}
|
||||
<CollapsibleView
|
||||
title="Locations"
|
||||
titleStyle={tw`mt-4 mb-3 ml-1 text-sm font-semibold text-gray-300`}
|
||||
titleStyle={tw`text-sm font-semibold text-gray-300`}
|
||||
containerStyle={tw`mt-6 mb-3`}
|
||||
>
|
||||
{placeholderLocationData.map((location) => (
|
||||
<DrawerLocationItem
|
||||
key={location.id}
|
||||
folderName={location.name}
|
||||
onPress={() =>
|
||||
navigation.navigate(stackName, {
|
||||
screen: 'Location',
|
||||
params: { id: location.id }
|
||||
})
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<View style={tw`mt-2`}>
|
||||
{placeholderLocationData.map((location) => (
|
||||
<DrawerLocationItem
|
||||
key={location.id}
|
||||
folderName={location.name}
|
||||
onPress={() =>
|
||||
navigation.navigate(stackName, {
|
||||
screen: 'Location',
|
||||
params: { id: location.id }
|
||||
})
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
{/* Add Location */}
|
||||
<View style={tw`mt-1 border border-dashed rounded border-gray-450 border-opacity-60`}>
|
||||
<Text style={tw`px-2 py-2 text-xs font-bold text-center text-gray-400`}>
|
||||
<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>
|
||||
@@ -89,21 +98,24 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
|
||||
{/* Tags */}
|
||||
<CollapsibleView
|
||||
title="Tags"
|
||||
titleStyle={tw`mt-6 mb-3 ml-1 text-sm font-semibold text-gray-300`}
|
||||
titleStyle={tw`text-sm font-semibold text-gray-300`}
|
||||
containerStyle={tw`mt-6 mb-3`}
|
||||
>
|
||||
{placeholderTagsData.map((tag) => (
|
||||
<DrawerTagItem
|
||||
key={tag.id}
|
||||
tagName={tag.name}
|
||||
onPress={() =>
|
||||
navigation.navigate(stackName, {
|
||||
screen: 'Tag',
|
||||
params: { id: tag.id }
|
||||
})
|
||||
}
|
||||
tagColor={tag.color as ColorValue}
|
||||
/>
|
||||
))}
|
||||
<View style={tw`mt-2`}>
|
||||
{placeholderTagsData.map((tag) => (
|
||||
<DrawerTagItem
|
||||
key={tag.id}
|
||||
tagName={tag.name}
|
||||
onPress={() =>
|
||||
navigation.navigate(stackName, {
|
||||
screen: 'Tag',
|
||||
params: { id: tag.id }
|
||||
})
|
||||
}
|
||||
tagColor={tag.color as ColorValue}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</CollapsibleView>
|
||||
</View>
|
||||
{/* Settings */}
|
||||
|
||||
126
apps/mobile/src/components/drawer/DrawerLibraryManager.tsx
Normal file
126
apps/mobile/src/components/drawer/DrawerLibraryManager.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import { MotiView } from 'moti';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { LockClosedIcon } from 'react-native-heroicons/outline';
|
||||
import { ChevronRightIcon, CogIcon, PlusIcon } from 'react-native-heroicons/solid';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import { useBridgeMutation } from '~/hooks/rspc';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { libraryStore, useCurrentLibrary } from '~/stores/libraryStore';
|
||||
|
||||
import { AnimatedHeight } from '../animation/layout';
|
||||
import Dialog from '../layout/Dialog';
|
||||
import Divider from '../primitive/Divider';
|
||||
import { TextInput } from '../primitive/Input';
|
||||
|
||||
// TODO: Maybe minimize this when drawer is closed?
|
||||
const DrawerLibraryManager = () => {
|
||||
const [dropdownClosed, setDropdownClosed] = useState(true);
|
||||
|
||||
// Init Libraries
|
||||
const { initLibraries, switchLibrary } = useSnapshot(libraryStore);
|
||||
const { currentLibrary, libraries, currentLibraryUuid } = useCurrentLibrary();
|
||||
|
||||
useEffect(() => {
|
||||
if (libraries && !currentLibraryUuid) initLibraries(libraries);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [libraries, currentLibraryUuid]);
|
||||
|
||||
// Create Library
|
||||
const [libName, setLibName] = useState('');
|
||||
|
||||
const [createLibOpen, setCreateLibOpen] = useState(false);
|
||||
|
||||
const { mutate: createLibrary, isLoading: createLibLoading } = useBridgeMutation(
|
||||
'library.create',
|
||||
{
|
||||
onSuccess: () => {
|
||||
// Reset form
|
||||
setLibName('');
|
||||
},
|
||||
onSettled: () => {
|
||||
// Close create lib dialog
|
||||
setCreateLibOpen(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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'
|
||||
)}
|
||||
>
|
||||
<Text style={tw`text-gray-200 text-sm font-semibold`}>{currentLibrary?.config.name}</Text>
|
||||
<MotiView
|
||||
animate={{
|
||||
rotateZ: dropdownClosed ? '0deg' : '90deg'
|
||||
}}
|
||||
transition={{ type: 'timing' }}
|
||||
>
|
||||
<ChevronRightIcon size={18} style={tw`text-gray-200 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`}
|
||||
>
|
||||
{/* Libraries */}
|
||||
{libraries?.map((library) => (
|
||||
<Pressable key={library.uuid} onPress={() => switchLibrary(library.uuid)}>
|
||||
<View
|
||||
style={tw.style(
|
||||
'p-2',
|
||||
library.uuid === currentLibraryUuid && 'bg-gray-500 bg-opacity-70 rounded'
|
||||
)}
|
||||
>
|
||||
<Text style={tw`text-sm text-gray-200 font-semibold`}>{library.config.name}</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
))}
|
||||
<Divider style={tw`mt-2 mb-2`} />
|
||||
{/* Menu */}
|
||||
<Pressable onPress={() => console.log('settings')}>
|
||||
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
|
||||
<CogIcon size={18} style={tw`text-gray-100 mr-2`} />
|
||||
<Text style={tw`text-sm text-gray-200 font-semibold`}>Library Settings</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
{/* Create Library */}
|
||||
<Dialog
|
||||
isVisible={createLibOpen}
|
||||
setIsVisible={setCreateLibOpen}
|
||||
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"
|
||||
ctaAction={() => createLibrary(libName)}
|
||||
trigger={
|
||||
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
|
||||
<PlusIcon size={18} style={tw`text-gray-100 mr-2`} />
|
||||
<Text style={tw`text-sm text-gray-200 font-semibold`}>Add Library</Text>
|
||||
</View>
|
||||
}
|
||||
>
|
||||
<TextInput
|
||||
value={libName}
|
||||
onChangeText={(text) => setLibName(text)}
|
||||
placeholder="My Cool Library"
|
||||
/>
|
||||
</Dialog>
|
||||
<Pressable onPress={() => console.log('lock')}>
|
||||
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
|
||||
<LockClosedIcon size={18} style={tw`text-gray-100 mr-2`} />
|
||||
<Text style={tw`text-sm text-gray-200 font-semibold`}>Lock</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
</View>
|
||||
</AnimatedHeight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerLibraryManager;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import FolderIcon from '../icons/FolderIcon';
|
||||
|
||||
interface DrawerLocationItemProps {
|
||||
@@ -13,7 +13,7 @@ const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (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')}>
|
||||
<View style={tw.style('flex mb-[4px] flex-row items-center py-2 px-1 rounded')}>
|
||||
<FolderIcon size={18} />
|
||||
<Text style={tw.style('text-gray-300 text-sm font-medium ml-2')} numberOfLines={1}>
|
||||
{folderName}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import Divider from '../base/Divider';
|
||||
|
||||
const DrawerLogo = () => {
|
||||
return (
|
||||
<>
|
||||
<View style={tw`flex flex-row items-center`}>
|
||||
<Image source={require('@sd/assets/images/logo.png')} style={tw`w-9 h-9`} />
|
||||
<Text style={tw`text-base font-bold text-white ml-2`}>Spacedrive</Text>
|
||||
</View>
|
||||
<Divider style={tw`mt-4`} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerLogo;
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ColorValue, Pressable, Text, View } from 'react-native';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
type DrawerTagItemProps = {
|
||||
tagName: string;
|
||||
@@ -13,7 +12,7 @@ const DrawerTagItem: React.FC<DrawerTagItemProps> = (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('flex mb-[4px] flex-row items-center py-2 px-1 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}
|
||||
|
||||
@@ -17,13 +17,16 @@ type FileIconProps = {
|
||||
};
|
||||
|
||||
const FileIcon = ({ file, size = 1 }: FileIconProps) => {
|
||||
// Temp
|
||||
const has_thumbnail = false;
|
||||
|
||||
return (
|
||||
<View style={[tw`justify-center`, { width: 60 * size, height: 60 * size }]}>
|
||||
{file?.is_dir ? (
|
||||
<View style={tw`items-center`}>
|
||||
<FolderIcon size={50 * size} />
|
||||
</View>
|
||||
) : file?.file?.has_thumbnail ? (
|
||||
) : has_thumbnail ? (
|
||||
<>{/* TODO */}</>
|
||||
) : (
|
||||
<View style={[tw`m-auto relative`, { width: 45 * size, height: 60 * size }]}>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import { SharedScreenProps } from '../../navigation/SharedScreens';
|
||||
import { useFileModalStore } from '../../stores/useModalStore';
|
||||
import { fileModalStore } from '../../stores/modalStore';
|
||||
import { FilePath } from '../../types/bindings';
|
||||
import FileIcon from './FileIcon';
|
||||
|
||||
@@ -15,8 +16,7 @@ type FileItemProps = {
|
||||
// TODO: Menu for file actions (File details, Share etc.)
|
||||
|
||||
const FileItem = ({ file }: FileItemProps) => {
|
||||
const fileRef = useFileModalStore((state) => state.fileRef);
|
||||
const setData = useFileModalStore((state) => state.setData);
|
||||
const { fileRef, setData } = useSnapshot(fileModalStore);
|
||||
|
||||
const navigation = useNavigation<SharedScreenProps<'Location'>['navigation']>();
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import { List } from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
const Header = () => {
|
||||
const navigation = useNavigation<DrawerNavigationHelpers>();
|
||||
@@ -17,7 +16,11 @@ const Header = () => {
|
||||
const isDrawerOpen = useDrawerStatus() === 'open';
|
||||
|
||||
return (
|
||||
<View style={tw.style('mx-4 bg-gray-550 rounded-md', { marginTop: top + 10 })}>
|
||||
<View
|
||||
style={tw.style('mx-4 bg-gray-500 border border-[#333949] bg-opacity-40 rounded', {
|
||||
marginTop: top + 10
|
||||
})}
|
||||
>
|
||||
<View style={tw`flex flex-row items-center h-10`}>
|
||||
<Pressable style={tw`px-3 h-full justify-center`} onPress={() => navigation.openDrawer()}>
|
||||
<MotiView
|
||||
@@ -31,7 +34,7 @@ const Header = () => {
|
||||
style={tw`flex-1 h-full justify-center`}
|
||||
onPress={() => navigation.navigate('Search')}
|
||||
>
|
||||
<Text style={tw`text-gray-300 font-semibold text-sm`}>Search</Text>
|
||||
<Text style={tw`text-gray-300 font-medium text-sm`}>Search</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { MotiView } from 'moti';
|
||||
import React, { useReducer } from 'react';
|
||||
import { Pressable, StyleProp, Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { ChevronRightIcon } from 'react-native-heroicons/solid';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import { AnimatedHeight } from '../animation/layout';
|
||||
|
||||
type CollapsibleViewProps = {
|
||||
@@ -18,7 +18,7 @@ const CollapsibleView = ({ title, titleStyle, containerStyle, children }: Collap
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<Pressable onPress={toggle} style={tw`flex flex-row justify-between items-baseline`}>
|
||||
<Pressable onPress={toggle} style={tw`flex flex-row justify-between items-center`}>
|
||||
<Text style={titleStyle} selectable={false}>
|
||||
{title}
|
||||
</Text>
|
||||
@@ -28,14 +28,9 @@ const CollapsibleView = ({ title, titleStyle, containerStyle, children }: Collap
|
||||
translateX: hide ? 0 : 5,
|
||||
translateY: hide ? 0 : 5
|
||||
}}
|
||||
transition={{ type: 'spring' }}
|
||||
transition={{ type: 'timing' }}
|
||||
>
|
||||
<Ionicons
|
||||
style={tw`mr-2`}
|
||||
name="chevron-forward"
|
||||
color={tw.color('gray-300')}
|
||||
size={14}
|
||||
/>
|
||||
<ChevronRightIcon size={18} style={tw`text-gray-200 mr-3`} />
|
||||
</MotiView>
|
||||
</Pressable>
|
||||
<AnimatedHeight hide={hide}>{children}</AnimatedHeight>
|
||||
|
||||
110
apps/mobile/src/components/layout/Dialog.tsx
Normal file
110
apps/mobile/src/components/layout/Dialog.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { MotiView } from 'moti';
|
||||
import React, { useState } from 'react';
|
||||
import { KeyboardAvoidingView, Modal, Platform, Pressable, Text, View } from 'react-native';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import { Button } from '../primitive/Button';
|
||||
|
||||
type DialogProps = {
|
||||
title: string;
|
||||
description?: string;
|
||||
trigger?: React.ReactNode;
|
||||
/**
|
||||
* if `true`, dialog will be visible when mounted.
|
||||
* It can be used when trigger is not provided and/or you need to open the dialog programmatically
|
||||
*/
|
||||
isVisible?: boolean;
|
||||
/**
|
||||
* Like above, it will override the default dialog state for opening/closing the dialog.
|
||||
* It can be used to control dialog state from outside
|
||||
*/
|
||||
setIsVisible?: (v: boolean) => void;
|
||||
children?: React.ReactNode;
|
||||
ctaAction?: () => void;
|
||||
ctaLabel?: string;
|
||||
ctaDanger?: boolean;
|
||||
/**
|
||||
* Disables backdrop press to close the modal.
|
||||
*/
|
||||
disableBackdropClose?: boolean;
|
||||
};
|
||||
|
||||
const Dialog = (props: DialogProps) => {
|
||||
const [visible, setVisible] = useState(props.isVisible ?? false);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{props.trigger && (
|
||||
<Pressable
|
||||
onPress={() => (props.setIsVisible ? props.setIsVisible(true) : setVisible(true))}
|
||||
>
|
||||
{props.trigger}
|
||||
</Pressable>
|
||||
)}
|
||||
<Modal renderToHardwareTextureAndroid transparent visible={props.isVisible ?? visible}>
|
||||
{/* Backdrop */}
|
||||
<Pressable
|
||||
style={tw`bg-black bg-opacity-50 absolute inset-0`}
|
||||
onPress={() => (props.setIsVisible ? props.setIsVisible(false) : setVisible(false))}
|
||||
disabled={props.disableBackdropClose}
|
||||
/>
|
||||
{/* Content */}
|
||||
<KeyboardAvoidingView
|
||||
pointerEvents="box-none"
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={Platform.OS === 'ios' ? 40 : undefined}
|
||||
style={tw`flex-1 items-center justify-center`}
|
||||
>
|
||||
<MotiView
|
||||
from={{ translateY: 40 }}
|
||||
animate={{ translateY: 0 }}
|
||||
transition={{ type: 'timing', duration: 200 }}
|
||||
>
|
||||
<View
|
||||
style={tw`min-w-[360px] max-w-[380px] rounded-md bg-gray-650 border border-gray-550 shadow-md overflow-hidden`}
|
||||
>
|
||||
<View style={tw`p-5`}>
|
||||
{/* Title */}
|
||||
<Text style={tw`font-bold text-white text-base`}>{props.title}</Text>
|
||||
{/* Description */}
|
||||
{props.description && (
|
||||
<Text style={tw`text-sm text-gray-300 mt-2 leading-normal`}>
|
||||
{props.description}
|
||||
</Text>
|
||||
)}
|
||||
{/* Children */}
|
||||
<View style={tw`mt-3`}>{props.children}</View>
|
||||
</View>
|
||||
{/* Actions */}
|
||||
<View
|
||||
style={tw`flex flex-row justify-end px-3 py-3 bg-gray-600 border-t border-gray-550`}
|
||||
>
|
||||
<Button
|
||||
variant="dark_gray"
|
||||
size="md"
|
||||
onPress={() =>
|
||||
props.setIsVisible ? props.setIsVisible(false) : setVisible(false)
|
||||
}
|
||||
>
|
||||
<Text style={tw`text-white text-sm`}>Close</Text>
|
||||
</Button>
|
||||
{props.ctaAction && (
|
||||
<Button
|
||||
style={tw`ml-2`}
|
||||
variant={props.ctaDanger ? 'danger' : 'primary'}
|
||||
size="md"
|
||||
onPress={props.ctaAction}
|
||||
>
|
||||
<Text style={tw`text-white text-sm`}>{props.ctaLabel}</Text>
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</MotiView>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dialog;
|
||||
@@ -3,23 +3,15 @@ import { format } from 'date-fns';
|
||||
import React, { useRef } from 'react';
|
||||
import { Button, Pressable, Text, View } from 'react-native';
|
||||
import { ChevronLeftIcon } from 'react-native-heroicons/outline';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import { useFileModalStore } from '../../stores/useModalStore';
|
||||
import Divider from '../base/Divider';
|
||||
import { fileModalStore } from '../../stores/modalStore';
|
||||
import FileIcon from '../file/FileIcon';
|
||||
import Divider from '../primitive/Divider';
|
||||
import ModalBackdrop from './layout/ModalBackdrop';
|
||||
import ModalHandle from './layout/ModalHandle';
|
||||
|
||||
/*
|
||||
https://github.com/software-mansion/react-native-reanimated/issues/3296
|
||||
https://github.com/gorhom/react-native-bottom-sheet/issues/925
|
||||
https://github.com/gorhom/react-native-bottom-sheet/issues/1036
|
||||
|
||||
Reanimated has a bug where it sometimes doesn't animate on mount (IOS only?), doing a console.log() seems to do a re-render and fix the issue.
|
||||
We can't do this for production obvs but until then they might fix it so, let's not try weird hacks for now and live with the logs.
|
||||
*/
|
||||
|
||||
interface MetaItemProps {
|
||||
title: string;
|
||||
value: string;
|
||||
@@ -35,7 +27,7 @@ function MetaItem({ title, value }: MetaItemProps) {
|
||||
}
|
||||
|
||||
export const FileModal = () => {
|
||||
const { fileRef, data } = useFileModalStore();
|
||||
const { fileRef, data } = useSnapshot(fileModalStore);
|
||||
|
||||
const fileDetailsRef = useRef<BottomSheetModal>(null);
|
||||
|
||||
@@ -46,8 +38,6 @@ export const FileModal = () => {
|
||||
snapPoints={['60%', '90%']}
|
||||
backdropComponent={ModalBackdrop}
|
||||
handleComponent={ModalHandle}
|
||||
// Do not remove!
|
||||
onAnimate={(from, to) => console.log(from, to)}
|
||||
>
|
||||
{data && (
|
||||
<View style={tw`flex-1 p-4 bg-gray-600`}>
|
||||
@@ -72,7 +62,6 @@ export const FileModal = () => {
|
||||
{/* Divider */}
|
||||
<Divider style={tw`my-6`} />
|
||||
{/* Buttons */}
|
||||
|
||||
<Button onPress={() => fileRef.current.close()} title="Copy" color="white" />
|
||||
<Button onPress={() => fileRef.current.close()} title="Move" color="white" />
|
||||
<Button onPress={() => fileRef.current.close()} title="Share" color="white" />
|
||||
@@ -88,8 +77,6 @@ export const FileModal = () => {
|
||||
snapPoints={['70%']}
|
||||
backdropComponent={ModalBackdrop}
|
||||
handleComponent={ModalHandle}
|
||||
// Do not remove!
|
||||
onAnimate={(from, to) => console.log(from, to)}
|
||||
>
|
||||
{data && (
|
||||
<BottomSheetScrollView style={tw`flex-1 p-4 bg-gray-600`}>
|
||||
@@ -105,9 +92,8 @@ export const FileModal = () => {
|
||||
{/* Details */}
|
||||
<Divider style={tw`mt-6 mb-4`} />
|
||||
<>
|
||||
{data?.file?.cas_id && (
|
||||
<MetaItem title="Unique Content ID" value={data.file.cas_id as string} />
|
||||
)}
|
||||
{/* Temp, we need cas id */}
|
||||
{data?.id && <MetaItem title="Unique Content ID" value={'555555555'} />}
|
||||
<MetaItem title="URI" value={`/Users/utku/Somewhere/vite.config.js`} />
|
||||
<Divider style={tw`my-4`} />
|
||||
<MetaItem
|
||||
|
||||
@@ -2,19 +2,21 @@ import { VariantProps, cva } from 'class-variance-authority';
|
||||
import { MotiPressable, MotiPressableProps } from 'moti/interactions';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Pressable, PressableProps } from 'react-native';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
const button = cva(['border rounded-md items-center shadow-sm'], {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-gray-50 border-gray-100',
|
||||
primary: ['bg-primary-600'],
|
||||
gray: ['bg-gray-100 border-gray-200']
|
||||
danger: ['bg-red-600'],
|
||||
gray: ['bg-gray-100 border-gray-200'],
|
||||
dark_gray: ['bg-gray-500 border-gray-600']
|
||||
},
|
||||
size: {
|
||||
default: ['py-1', 'px-3'],
|
||||
sm: ['py-1', 'px-2'],
|
||||
md: ['py-1.5', 'px-3'],
|
||||
lg: ['py-2', 'px-4']
|
||||
}
|
||||
},
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { StyleProp, Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import tw from '../../lib/tailwind';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
type DividerProps = {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
32
apps/mobile/src/components/primitive/Input.tsx
Normal file
32
apps/mobile/src/components/primitive/Input.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import React from 'react';
|
||||
import { TextInput as RNTextInput, TextInputProps as RNTextInputProps } 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'
|
||||
},
|
||||
size: {
|
||||
default: ['py-2', 'px-3']
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
type InputProps = VariantProps<typeof input> & RNTextInputProps;
|
||||
|
||||
export const TextInput: React.FC<InputProps> = ({ variant, ...props }) => {
|
||||
const { style, ...otherProps } = props;
|
||||
return (
|
||||
<RNTextInput
|
||||
placeholderTextColor={tw.color('gray-300')}
|
||||
style={tw.style(input({ variant }), style as string)}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
ClientTransformer,
|
||||
OperationKey,
|
||||
OperationType,
|
||||
RSPCError,
|
||||
Transport,
|
||||
createReactQueryHooks
|
||||
} from '@rspc/client';
|
||||
import { ClientTransformer, OperationKey, OperationType, RSPCError, Transport } from '@rspc/client';
|
||||
import { createReactQueryHooks } from '@rspc/react';
|
||||
import {
|
||||
QueryClient,
|
||||
UseMutationOptions,
|
||||
@@ -15,8 +9,9 @@ import {
|
||||
useMutation as _useMutation
|
||||
} from '@tanstack/react-query';
|
||||
import { NativeEventEmitter, NativeModules } from 'react-native';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import { useLibraryStore } from '../stores/useLibraryStore';
|
||||
import { libraryStore } from '../stores/libraryStore';
|
||||
import type { LibraryArgs, Operations } from '../types/bindings';
|
||||
|
||||
export const queryClient = new QueryClient();
|
||||
@@ -92,7 +87,8 @@ export function useLibraryQuery<K extends LibraryQueryKey>(
|
||||
key: LibraryQueryArgs<K> extends null | undefined ? [K] : [K, LibraryQueryArgs<K>],
|
||||
options?: UseQueryOptions<LibraryQueryResult<K>, RSPCError>
|
||||
): UseQueryResult<LibraryQueryResult<K>, RSPCError> {
|
||||
const library_id = useLibraryStore((state) => state.currentLibraryUuid);
|
||||
const store = useSnapshot(libraryStore);
|
||||
const library_id = store.currentLibraryUuid;
|
||||
if (!library_id) throw new Error(`Attempted to do library query with no library set!`);
|
||||
// @ts-ignore
|
||||
return rspc.useQuery([key[0], { library_id: library_id || '', arg: key[1] || null }], options);
|
||||
@@ -109,7 +105,8 @@ export function useLibraryMutation<K extends LibraryMutationKey>(
|
||||
options?: UseMutationOptions<LibraryMutationResult<K>, RSPCError>
|
||||
) {
|
||||
const ctx = rspc.useContext();
|
||||
const library_id = useLibraryStore((state) => state.currentLibraryUuid);
|
||||
const store = useSnapshot(libraryStore);
|
||||
const library_id = store.currentLibraryUuid;
|
||||
if (!library_id) throw new Error(`Attempted to do library query with no library set!`);
|
||||
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { FontAwesome } from '@expo/vector-icons';
|
||||
import * as Font from 'expo-font';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
@@ -28,9 +26,6 @@ export default function useCachedResources() {
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
|
||||
// Load fonts, icons etc.
|
||||
await Font.loadAsync({
|
||||
...FontAwesome.font
|
||||
});
|
||||
} catch (e) {
|
||||
// We might want to provide this error information to an error reporting service
|
||||
console.warn(e);
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useCountUp } from 'use-count-up';
|
||||
import create from 'zustand';
|
||||
import { proxy, useSnapshot } from 'valtio';
|
||||
|
||||
const useCounterStore = create<{
|
||||
counterLastValue: Map<string, number>;
|
||||
setCounterLastValue: (key: string, value: number) => void;
|
||||
}>((set) => ({
|
||||
// const useCounterStore = create<{
|
||||
// counterLastValue: Map<string, number>;
|
||||
// setCounterLastValue: (key: string, value: number) => void;
|
||||
// }>((set) => ({
|
||||
// counterLastValue: new Map<string, number>(),
|
||||
// setCounterLastValue: (name, lastValue) =>
|
||||
// set((state) => ({
|
||||
// ...state,
|
||||
// counterLastValue: state.counterLastValue.set(name, lastValue)
|
||||
// }))
|
||||
// }));
|
||||
|
||||
const counterStore = proxy({
|
||||
counterLastValue: new Map<string, number>(),
|
||||
setCounterLastValue: (name, lastValue) =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
counterLastValue: state.counterLastValue.set(name, lastValue)
|
||||
}))
|
||||
}));
|
||||
setCounterLastValue: (key: string, value: number) => {
|
||||
counterStore.counterLastValue.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
const useCounterState = (key: string) => {
|
||||
const { counterLastValue, setCounterLastValue } = useCounterStore();
|
||||
const { counterLastValue, setCounterLastValue } = useSnapshot(counterStore);
|
||||
|
||||
return {
|
||||
lastValue: counterLastValue.get(key),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { DrawerScreenProps, createDrawerNavigator } from '@react-navigation/drawer';
|
||||
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { StackScreenProps } from '@react-navigation/stack';
|
||||
import DrawerContent from '~/components/drawer/DrawerContent';
|
||||
|
||||
import type { RootStackParamList } from '.';
|
||||
import DrawerContent from '../components/drawer/DrawerContent';
|
||||
import type { TabParamList } from './TabNavigator';
|
||||
import TabNavigator from './TabNavigator';
|
||||
|
||||
@@ -13,19 +13,15 @@ export default function DrawerNavigator() {
|
||||
return (
|
||||
<Drawer.Navigator
|
||||
initialRouteName="Home"
|
||||
screenOptions={({ route }) => {
|
||||
return {
|
||||
headerShown: false,
|
||||
drawerStyle: {
|
||||
backgroundColor: '#08090D',
|
||||
width: '75%'
|
||||
},
|
||||
overlayColor: 'transparent',
|
||||
drawerType: 'slide'
|
||||
// swipeEnabled: false
|
||||
// drawerHideStatusBarOnOpen: true,
|
||||
// drawerStatusBarAnimation: 'slide'
|
||||
};
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
drawerStyle: {
|
||||
backgroundColor: 'rgb(10,10,12)',
|
||||
width: '75%'
|
||||
},
|
||||
overlayColor: 'transparent',
|
||||
drawerType: 'slide',
|
||||
swipeEdgeWidth: 50
|
||||
}}
|
||||
drawerContent={(props) => <DrawerContent {...(props as any)} />}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
|
||||
|
||||
import OnboardingScreen from '../screens/onboarding/Onboarding';
|
||||
import CreateLibraryScreen from '~/screens/onboarding/CreateLibrary';
|
||||
import OnboardingScreen from '~/screens/onboarding/Onboarding';
|
||||
|
||||
const OnboardingStack = createStackNavigator<OnboardingStackParamList>();
|
||||
|
||||
@@ -8,12 +8,14 @@ export default function OnboardingNavigator() {
|
||||
return (
|
||||
<OnboardingStack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<OnboardingStack.Screen name="Onboarding" component={OnboardingScreen} />
|
||||
<OnboardingStack.Screen name="CreateLibrary" component={CreateLibraryScreen} />
|
||||
</OnboardingStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
export type OnboardingStackParamList = {
|
||||
Onboarding: undefined;
|
||||
CreateLibrary: undefined;
|
||||
};
|
||||
|
||||
export type OnboardingStackScreenProps<Screen extends keyof OnboardingStackParamList> =
|
||||
|
||||
@@ -4,9 +4,8 @@ import {
|
||||
StackNavigationOptions,
|
||||
StackScreenProps
|
||||
} from '@react-navigation/stack';
|
||||
|
||||
import LocationScreen from '../screens/Location';
|
||||
import TagScreen from '../screens/Tag';
|
||||
import LocationScreen from '~/screens/Location';
|
||||
import TagScreen from '~/screens/Tag';
|
||||
|
||||
export function SharedScreens(
|
||||
Stack: TypedNavigator<
|
||||
|
||||
@@ -2,8 +2,8 @@ import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigatio
|
||||
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { CirclesFour, Folder, Planet } from 'phosphor-react-native';
|
||||
import { PhotographIcon } from 'react-native-heroicons/outline';
|
||||
import tw from '~/lib/tailwind';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import type { HomeDrawerScreenProps } from './DrawerNavigator';
|
||||
import BrowseStack, { BrowseStackParamList } from './tabs/BrowseStack';
|
||||
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
|
||||
@@ -18,11 +18,11 @@ export default function TabNavigator() {
|
||||
initialRouteName="OverviewStack"
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarActiveTintColor: tw.color('bg-primary'),
|
||||
tabBarActiveTintColor: tw.color('primary'),
|
||||
tabBarInactiveTintColor: 'white',
|
||||
tabBarStyle: {
|
||||
backgroundColor: '#08090D',
|
||||
borderTopColor: 'transparent'
|
||||
backgroundColor: tw.color('gray-650'),
|
||||
borderTopColor: tw.color('gray-600')
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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 NotFoundScreen from '../screens/NotFound';
|
||||
import SearchScreen from '../screens/modals/Search';
|
||||
import SettingsScreen from '../screens/modals/settings/Settings';
|
||||
import type { DrawerNavParamList } from './DrawerNavigator';
|
||||
import DrawerNavigator from './DrawerNavigator';
|
||||
|
||||
@@ -15,15 +16,21 @@ export default function RootNavigator() {
|
||||
<Stack.Navigator initialRouteName="Root">
|
||||
<Stack.Screen name="Root" component={DrawerNavigator} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
|
||||
<Stack.Screen name="Search" component={SearchScreen} options={{ headerShown: false }} />
|
||||
<Stack.Screen
|
||||
name="Search"
|
||||
component={SearchScreen}
|
||||
options={{ headerShown: false, animationEnabled: false }}
|
||||
/>
|
||||
{/* Modals */}
|
||||
<Stack.Group
|
||||
screenOptions={{
|
||||
presentation: 'modal',
|
||||
headerBackTitleVisible: false,
|
||||
headerStyle: { backgroundColor: '#08090D' },
|
||||
// headerShadowVisible: false,
|
||||
headerTintColor: '#fff'
|
||||
headerStyle: tw`bg-gray-650`,
|
||||
headerTintColor: tw.color('gray-200'),
|
||||
headerTitleStyle: tw`text-base`,
|
||||
headerBackTitleStyle: tw`text-base`
|
||||
// headerShadowVisible: false // will disable the white line under
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Settings" component={SettingsScreen} />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { StackScreenProps, TransitionPresets, createStackNavigator } from '@react-navigation/stack';
|
||||
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import tw from '~/lib/tailwind';
|
||||
import BrowseScreen from '~/screens/Browse';
|
||||
|
||||
import Header from '../../components/header/Header';
|
||||
import BrowseScreen from '../../screens/Browse';
|
||||
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
@@ -13,9 +14,10 @@ export default function BrowseStack() {
|
||||
<Stack.Navigator
|
||||
initialRouteName="Browse"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#08090D' },
|
||||
headerTintColor: '#fff',
|
||||
...TransitionPresets.ModalFadeTransition
|
||||
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 }} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 OverviewScreen from '../../screens/Overview';
|
||||
@@ -13,9 +14,10 @@ export default function OverviewStack() {
|
||||
<Stack.Navigator
|
||||
initialRouteName="Overview"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#08090D' },
|
||||
headerTintColor: '#fff',
|
||||
...TransitionPresets.ModalFadeTransition
|
||||
headerStyle: { backgroundColor: tw.color('gray-650') },
|
||||
headerTintColor: tw.color('gray-200'),
|
||||
headerTitleStyle: tw`text-base`,
|
||||
headerBackTitleStyle: tw`text-base`
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Overview" component={OverviewScreen} options={{ header: Header }} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -13,9 +14,10 @@ export default function PhotosStack() {
|
||||
<Stack.Navigator
|
||||
initialRouteName="Photos"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#08090D' },
|
||||
headerTintColor: '#fff',
|
||||
...TransitionPresets.ModalFadeTransition
|
||||
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 }} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 SpacesScreen from '../../screens/Spaces';
|
||||
@@ -13,9 +14,10 @@ export default function SpacesStack() {
|
||||
<Stack.Navigator
|
||||
initialRouteName="Spaces"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#08090D' },
|
||||
headerTintColor: '#fff',
|
||||
...TransitionPresets.ModalFadeTransition
|
||||
headerStyle: { backgroundColor: tw.color('gray-650') },
|
||||
headerTintColor: tw.color('gray-200'),
|
||||
headerTitleStyle: tw`text-base`,
|
||||
headerBackTitleStyle: tw`text-base`
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Spaces" component={SpacesScreen} options={{ header: Header }} />
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import { ColorValue, Text, View } from 'react-native';
|
||||
|
||||
import BrowseLocationItem from '../components/browse/BrowseLocationItem';
|
||||
import BrowseTagItem from '../components/browse/BrowseTagItem';
|
||||
import CollapsibleView from '../components/layout/CollapsibleView';
|
||||
import tw from '../lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '../navigation/tabs/BrowseStack';
|
||||
import BrowseLocationItem from '~/components/browse/BrowseLocationItem';
|
||||
import BrowseTagItem from '~/components/browse/BrowseTagItem';
|
||||
import CollapsibleView from '~/components/layout/CollapsibleView';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
||||
const placeholderLocationData = [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import { SharedScreenProps } from '../navigation/SharedScreens';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { SharedScreenProps } from '~/navigation/SharedScreens';
|
||||
|
||||
export default function LocationScreen({ navigation, route }: SharedScreenProps<'Location'>) {
|
||||
const { id } = route.params;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import type { RootStackScreenProps } from '../navigation';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { RootStackScreenProps } from '~/navigation';
|
||||
|
||||
export default function NotFoundScreen({ navigation }: RootStackScreenProps<'NotFound'>) {
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import { FlatList, View } from 'react-native';
|
||||
|
||||
import Device from '../components/device/Device';
|
||||
import VirtualizedListWrapper from '../components/layout/VirtualizedListWrapper';
|
||||
import OverviewStats from '../containers/OverviewStats';
|
||||
import tw from '../lib/tailwind';
|
||||
import { OverviewStackScreenProps } from '../navigation/tabs/OverviewStack';
|
||||
import Device from '~/components/device/Device';
|
||||
import VirtualizedListWrapper from '~/components/layout/VirtualizedListWrapper';
|
||||
import OverviewStats from '~/containers/OverviewStats';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
|
||||
|
||||
const placeholderOverviewStats = {
|
||||
id: 1,
|
||||
@@ -52,8 +51,6 @@ export default function OverviewScreen({ navigation }: OverviewStackScreenProps<
|
||||
<View style={tw`px-4 mt-4`}>
|
||||
{/* Stats */}
|
||||
<OverviewStats stats={placeholderOverviewStats} />
|
||||
{/* Spacing */}
|
||||
<View style={tw`mt-4`} />
|
||||
{/* Devices */}
|
||||
<FlatList
|
||||
data={placeholderDevices}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import { PhotosStackScreenProps } from '../navigation/tabs/PhotosStack';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { PhotosStackScreenProps } from '~/navigation/tabs/PhotosStack';
|
||||
|
||||
export default function PhotosScreen({ navigation }: PhotosStackScreenProps<'Photos'>) {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import { SpacesStackScreenProps } from '../navigation/tabs/SpacesStack';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { SpacesStackScreenProps } from '~/navigation/tabs/SpacesStack';
|
||||
|
||||
export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import tw from '../lib/tailwind';
|
||||
import { SharedScreenProps } from '../navigation/SharedScreens';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { SharedScreenProps } from '~/navigation/SharedScreens';
|
||||
|
||||
export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'>) {
|
||||
const { id } = route.params;
|
||||
|
||||
@@ -2,22 +2,25 @@ import { MagnifyingGlass } from 'phosphor-react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { ActivityIndicator, Pressable, Text, TextInput, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import { Button } from '../../components/base/Button';
|
||||
import tw from '../../lib/tailwind';
|
||||
import { RootStackScreenProps } from '../../navigation';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { RootStackScreenProps } from '~/navigation';
|
||||
|
||||
const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
|
||||
const { top } = useSafeAreaInsets();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// TODO: Animations!
|
||||
|
||||
return (
|
||||
<View style={tw.style('flex-1', { marginTop: top + 10 })}>
|
||||
{/* Header */}
|
||||
<View style={tw`flex flex-row items-center mx-4`}>
|
||||
{/* Search Input */}
|
||||
<View style={tw`flex-1 bg-gray-550 rounded-md h-9 mr-3`}>
|
||||
<View
|
||||
style={tw`flex-1 bg-gray-500 border border-[#333949] bg-opacity-40 rounded h-10 mr-3`}
|
||||
>
|
||||
<View style={tw`flex flex-row h-full items-center px-3`}>
|
||||
<View style={tw`mr-3`}>
|
||||
{loading ? (
|
||||
@@ -31,7 +34,7 @@ const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => {
|
||||
clearButtonMode="never" // can't change the color??
|
||||
underlineColorAndroid="transparent"
|
||||
placeholderTextColor={tw.color('gray-300')}
|
||||
style={tw`text-white flex-1 text-sm`}
|
||||
style={tw`flex-1 text-gray-300 font-medium text-sm`}
|
||||
textContentType={'none'}
|
||||
autoFocus
|
||||
autoCapitalize="none"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import tw from '../../../lib/tailwind';
|
||||
import { RootStackScreenProps } from '../../../navigation';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { RootStackScreenProps } from '~/navigation';
|
||||
|
||||
export default function SettingsScreen({ navigation }: RootStackScreenProps<'Settings'>) {
|
||||
return (
|
||||
|
||||
30
apps/mobile/src/screens/onboarding/CreateLibrary.tsx
Normal file
30
apps/mobile/src/screens/onboarding/CreateLibrary.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import { AnimatedButton } from '~/components/primitive/Button';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
import { onboardingStore } from '~/stores/onboardingStore';
|
||||
|
||||
const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => {
|
||||
const { hideOnboarding } = useSnapshot(onboardingStore);
|
||||
|
||||
function onButtonPress() {
|
||||
// TODO: Add a loading indicator to button as this takes a second or so.
|
||||
hideOnboarding();
|
||||
}
|
||||
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`}>
|
||||
Onboarding screen for users to create their first library
|
||||
</Text>
|
||||
<AnimatedButton variant="primary" onPress={onButtonPress}>
|
||||
<Text style={tw`text-white text-center px-6 py-2 text-base font-medium`}>
|
||||
Create Library
|
||||
</Text>
|
||||
</AnimatedButton>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateLibraryScreen;
|
||||
@@ -1,24 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
|
||||
import { FadeInUpAnimation, LogoAnimation } from '../../components/animation/layout';
|
||||
import { AnimatedButton } from '../../components/base/Button';
|
||||
import { setItemToStorage } from '../../lib/storage';
|
||||
import tw from '../../lib/tailwind';
|
||||
import type { OnboardingStackScreenProps } from '../../navigation/OnboardingNavigator';
|
||||
import { useOnboardingStore } from '../../stores/useOnboardingStore';
|
||||
import { FadeInUpAnimation, LogoAnimation } from '~/components/animation/layout';
|
||||
import { AnimatedButton } from '~/components/primitive/Button';
|
||||
import tw from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
|
||||
const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => {
|
||||
const { hideOnboarding } = useOnboardingStore();
|
||||
|
||||
function onButtonPress() {
|
||||
setItemToStorage('@onboarding', '1');
|
||||
// TODO: Add a loading indicator to button as this takes a second or so.
|
||||
hideOnboarding();
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={tw`flex-1 items-center justify-around bg-black p-4 z-10`}>
|
||||
<View style={tw`flex-1 items-center justify-around bg-gray-650 p-4 z-10`}>
|
||||
{/* Logo */}
|
||||
<LogoAnimation>
|
||||
<View style={tw`items-center mt-2`}>
|
||||
@@ -26,7 +15,7 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
|
||||
</View>
|
||||
</LogoAnimation>
|
||||
{/* Text */}
|
||||
<View style={tw``}>
|
||||
<View>
|
||||
<FadeInUpAnimation delay={500}>
|
||||
<Text style={tw`text-white text-center text-5xl font-black leading-tight`}>
|
||||
A file explorer from the future.
|
||||
@@ -41,7 +30,7 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding
|
||||
</View>
|
||||
{/* Get Started Button */}
|
||||
<FadeInUpAnimation delay={1200}>
|
||||
<AnimatedButton variant="primary" onPress={onButtonPress}>
|
||||
<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>
|
||||
|
||||
47
apps/mobile/src/stores/libraryStore.ts
Normal file
47
apps/mobile/src/stores/libraryStore.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import proxyWithPersist, { PersistStrategy } from 'valtio-persist';
|
||||
import { useBridgeQuery } from '~/hooks/rspc';
|
||||
import { LibraryConfigWrapped } from '~/types/bindings';
|
||||
|
||||
import { StorageEngine } from './utils';
|
||||
|
||||
export const libraryStore = proxyWithPersist({
|
||||
initialState: {
|
||||
currentLibraryUuid: null as string | null,
|
||||
switchLibrary: (libraryUuid: string) => {
|
||||
libraryStore.currentLibraryUuid = libraryUuid;
|
||||
// Reset any other stores connected to library
|
||||
},
|
||||
initLibraries: async (libraries: LibraryConfigWrapped[]) => {
|
||||
// use first library default if none set
|
||||
if (!libraryStore.currentLibraryUuid) {
|
||||
libraryStore.currentLibraryUuid = libraries[0].uuid;
|
||||
}
|
||||
}
|
||||
},
|
||||
persistStrategies: PersistStrategy.SingleFile,
|
||||
name: 'sd-library-store',
|
||||
version: 0,
|
||||
migrations: {},
|
||||
getStorage: () => StorageEngine
|
||||
});
|
||||
|
||||
// this must be used at least once in the app to correct the initial state
|
||||
// is memorized and can be used safely in any component
|
||||
export const useCurrentLibrary = () => {
|
||||
const store = useSnapshot(libraryStore);
|
||||
const { data: libraries } = useBridgeQuery(['library.list']);
|
||||
|
||||
// memorize library to avoid re-running find function
|
||||
const currentLibrary = useMemo(() => {
|
||||
const current = libraries?.find((l: any) => l.uuid === store.currentLibraryUuid);
|
||||
// switch to first library if none set
|
||||
if (Array.isArray(libraries) && !current && libraries[0]?.uuid) {
|
||||
store.switchLibrary(libraries[0]?.uuid);
|
||||
}
|
||||
return current;
|
||||
}, [libraries, store]);
|
||||
|
||||
return { currentLibrary, libraries, currentLibraryUuid: store.currentLibraryUuid };
|
||||
};
|
||||
13
apps/mobile/src/stores/modalStore.ts
Normal file
13
apps/mobile/src/stores/modalStore.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
||||
import React from 'react';
|
||||
import { proxy } from 'valtio';
|
||||
|
||||
import { FilePath } from '../types/bindings';
|
||||
|
||||
export const fileModalStore = proxy({
|
||||
fileRef: null as React.RefObject<BottomSheetModalMethods>,
|
||||
data: null as FilePath | null,
|
||||
setData: (data: FilePath) => {
|
||||
fileModalStore.data = data;
|
||||
}
|
||||
});
|
||||
18
apps/mobile/src/stores/onboardingStore.ts
Normal file
18
apps/mobile/src/stores/onboardingStore.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import proxyWithPersist, { PersistStrategy } from 'valtio-persist';
|
||||
|
||||
import { StorageEngine } from './utils';
|
||||
|
||||
// Might wanna make this a `appStore` so we can add other stuff to it
|
||||
export const onboardingStore = proxyWithPersist({
|
||||
initialState: {
|
||||
showOnboarding: true,
|
||||
hideOnboarding: () => {
|
||||
onboardingStore.showOnboarding = false;
|
||||
}
|
||||
},
|
||||
persistStrategies: PersistStrategy.SingleFile,
|
||||
name: 'sd-onboarding-store',
|
||||
version: 0,
|
||||
migrations: {},
|
||||
getStorage: () => StorageEngine
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import create from 'zustand';
|
||||
|
||||
interface LibraryStore {
|
||||
currentLibraryUuid: string | null;
|
||||
switchLibrary: (id: string) => void;
|
||||
}
|
||||
|
||||
export const useLibraryStore = create<LibraryStore>()((set) => ({
|
||||
currentLibraryUuid: null,
|
||||
switchLibrary: (uuid) => {
|
||||
set((state) => ({ currentLibraryUuid: uuid }));
|
||||
}
|
||||
}));
|
||||
@@ -1,19 +0,0 @@
|
||||
import { BottomSheetModal } from '@gorhom/bottom-sheet';
|
||||
import React from 'react';
|
||||
import create from 'zustand';
|
||||
|
||||
import { FilePath } from '../types/bindings';
|
||||
|
||||
interface FileModalState {
|
||||
fileRef: React.RefObject<BottomSheetModal>;
|
||||
data: FilePath | null;
|
||||
setData: (data: FilePath) => void;
|
||||
clearData: () => void;
|
||||
}
|
||||
|
||||
export const useFileModalStore = create<FileModalState>((set) => ({
|
||||
fileRef: React.createRef<BottomSheetModal>(),
|
||||
data: null,
|
||||
setData: (data: FilePath) => set((_) => ({ data })),
|
||||
clearData: () => set((_) => ({ data: null }))
|
||||
}));
|
||||
@@ -1,11 +0,0 @@
|
||||
import create from 'zustand';
|
||||
|
||||
interface OnboardingState {
|
||||
showOnboarding: boolean;
|
||||
hideOnboarding: () => void;
|
||||
}
|
||||
|
||||
export const useOnboardingStore = create<OnboardingState>((set) => ({
|
||||
showOnboarding: true,
|
||||
hideOnboarding: () => set((state) => ({ showOnboarding: false }))
|
||||
}));
|
||||
9
apps/mobile/src/stores/utils.ts
Normal file
9
apps/mobile/src/stores/utils.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import type { ProxyPersistStorageEngine } from 'valtio-persist';
|
||||
|
||||
export const StorageEngine: ProxyPersistStorageEngine = {
|
||||
getItem: (name) => AsyncStorage.getItem(name),
|
||||
setItem: (name, value) => AsyncStorage.setItem(name, value),
|
||||
removeItem: (name) => AsyncStorage.removeItem(name),
|
||||
getAllKeys: () => AsyncStorage.getAllKeys() as Promise<string[]>
|
||||
};
|
||||
6
apps/mobile/src/types/valtio.d.ts
vendored
Normal file
6
apps/mobile/src/types/valtio.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Loosen the type definition of the `useSnapshot` hook
|
||||
import 'valtio';
|
||||
|
||||
declare module 'valtio' {
|
||||
function useSnapshot<T extends object>(p: T): T;
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {}
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"~/*": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^4.5.11",
|
||||
"@rspc/client": "^0.0.5",
|
||||
"@rspc/client": "^0.0.6",
|
||||
"@sd/client": "workspace:*",
|
||||
"@sd/core": "workspace:*",
|
||||
"@sd/interface": "workspace:*",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WebsocketTransport, createClient } from '@rspc/client';
|
||||
import { Operations, queryClient, rspc } from '@sd/client';
|
||||
import SpacedriveInterface from '@sd/interface';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const client = createClient<Operations>({
|
||||
transport: new WebsocketTransport(
|
||||
|
||||
@@ -85,7 +85,7 @@ impl StatefulJob for FileIdentifierJob {
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_default();
|
||||
|
||||
let total_count = count_orphan_file_paths(&library, state.init.location_id.into()).await?;
|
||||
let total_count = count_orphan_file_paths(&library, state.init.location_id).await?;
|
||||
info!("Found {} orphan file paths", total_count);
|
||||
|
||||
let task_count = (total_count as f64 / CHUNK_SIZE as f64).ceil() as usize;
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
"preset": "scripts/jest/node"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rspc/client": "^0.0.5",
|
||||
"@sd/core": "workspace:*",
|
||||
"@rspc/client": "^0.0.6",
|
||||
"@rspc/react": "^0.0.6",
|
||||
"@sd/config": "workspace:*",
|
||||
"@sd/core": "workspace:*",
|
||||
"@sd/interface": "workspace:*",
|
||||
"@tanstack/react-query": "^4.0.10",
|
||||
"eventemitter3": "^4.0.7",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RSPCError, createReactQueryHooks } from '@rspc/client';
|
||||
import { RSPCError } from '@rspc/client';
|
||||
import { createReactQueryHooks } from '@rspc/react';
|
||||
import { LibraryArgs, Operations } from '@sd/core';
|
||||
import {
|
||||
QueryClient,
|
||||
|
||||
BIN
pnpm-lock.yaml
generated
BIN
pnpm-lock.yaml
generated
Binary file not shown.
Reference in New Issue
Block a user