diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 7b6dfae6f..3e300ad93 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -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:*", diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index b773aa813..9fb97486e 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -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; diff --git a/apps/desktop/src/index.tsx b/apps/desktop/src/index.tsx index 93a94fdf2..ad21450db 100644 --- a/apps/desktop/src/index.tsx +++ b/apps/desktop/src/index.tsx @@ -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'; diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js index 224bce79e..123f8b03f 100644 --- a/apps/mobile/babel.config.js +++ b/apps/mobile/babel.config.js @@ -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' + } + } + ] + ] }; }; diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index eb04e2bc1..5825a73f1 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -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 diff --git a/apps/mobile/ios/Spacedrive/Info.plist b/apps/mobile/ios/Spacedrive/Info.plist index 544e7b24b..fd2881b25 100644 --- a/apps/mobile/ios/Spacedrive/Info.plist +++ b/apps/mobile/ios/Spacedrive/Info.plist @@ -1,81 +1,83 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Spacedrive - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 0.0.1 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - spacedrive - com.spacedrive.app - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - NSAllowsArbitraryLoads - - NSExceptionDomains - - localhost + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Spacedrive + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 0.0.1 + CFBundleSignature + ???? + CFBundleURLTypes + - NSExceptionAllowsInsecureHTTPLoads - + CFBundleURLSchemes + + spacedrive + com.spacedrive.app + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + UIBackgroundModes + + remote-notification + + UIFileSharingEnabled + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + - UIBackgroundModes - - remote-notification - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - armv7 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - + \ No newline at end of file diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 711926bd7..f1c15ee93 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -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", diff --git a/apps/mobile/pnpm-lock.yaml b/apps/mobile/pnpm-lock.yaml index 75295d7a7..84ec59b3d 100644 Binary files a/apps/mobile/pnpm-lock.yaml and b/apps/mobile/pnpm-lock.yaml differ diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index 14794ffd7..2b1e45276 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -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({ @@ -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 ( - - <> - - - - - - - {showOnboarding ? : } - - - - - - - + + + + + + {showOnboarding ? : } + + + + + ); } } +export default function App() { + return ( + + <> + + + + + ); +} + function InvalidateQuery() { useInvalidateQuery(); return null; diff --git a/apps/mobile/src/components/animation/layout.tsx b/apps/mobile/src/components/animation/layout.tsx index 78253f6c4..f1dabe3ca 100644 --- a/apps/mobile/src/components/animation/layout.tsx +++ b/apps/mobile/src/components/animation/layout.tsx @@ -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 }) => ( diff --git a/apps/mobile/src/components/browse/BrowseLocationItem.tsx b/apps/mobile/src/components/browse/BrowseLocationItem.tsx index 2298925c9..49c271b1d 100644 --- a/apps/mobile/src/components/browse/BrowseLocationItem.tsx +++ b/apps/mobile/src/components/browse/BrowseLocationItem.tsx @@ -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 { diff --git a/apps/mobile/src/components/browse/BrowseTagItem.tsx b/apps/mobile/src/components/browse/BrowseTagItem.tsx index 2f8508008..e89477f1e 100644 --- a/apps/mobile/src/components/browse/BrowseTagItem.tsx +++ b/apps/mobile/src/components/browse/BrowseTagItem.tsx @@ -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; diff --git a/apps/mobile/src/components/device/Device.tsx b/apps/mobile/src/components/device/Device.tsx index d8a24afad..bcf1e246b 100644 --- a/apps/mobile/src/components/device/Device.tsx +++ b/apps/mobile/src/components/device/Device.tsx @@ -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 } ]; diff --git a/apps/mobile/src/components/drawer/DrawerContent.tsx b/apps/mobile/src/components/drawer/DrawerContent.tsx index 2b93328ac..49c0b59c8 100644 --- a/apps/mobile/src/components/drawer/DrawerContent.tsx +++ b/apps/mobile/src/components/drawer/DrawerContent.tsx @@ -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) => { - - TODO: Library Selection + + + Spacedrive + + + {/* Library Manager */} + {/* Locations */} - {placeholderLocationData.map((location) => ( - - navigation.navigate(stackName, { - screen: 'Location', - params: { id: location.id } - }) - } - /> - ))} + + {placeholderLocationData.map((location) => ( + + navigation.navigate(stackName, { + screen: 'Location', + params: { id: location.id } + }) + } + /> + ))} + {/* Add Location */} - - + + Add Location @@ -89,21 +98,24 @@ const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => { {/* Tags */} - {placeholderTagsData.map((tag) => ( - - navigation.navigate(stackName, { - screen: 'Tag', - params: { id: tag.id } - }) - } - tagColor={tag.color as ColorValue} - /> - ))} + + {placeholderTagsData.map((tag) => ( + + navigation.navigate(stackName, { + screen: 'Tag', + params: { id: tag.id } + }) + } + tagColor={tag.color as ColorValue} + /> + ))} + {/* Settings */} diff --git a/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx b/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx new file mode 100644 index 000000000..7fbd51daa --- /dev/null +++ b/apps/mobile/src/components/drawer/DrawerLibraryManager.tsx @@ -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 ( + + setDropdownClosed((v) => !v)}> + + {currentLibrary?.config.name} + + + + + + + + {/* Libraries */} + {libraries?.map((library) => ( + switchLibrary(library.uuid)}> + + {library.config.name} + + + ))} + + {/* Menu */} + console.log('settings')}> + + + Library Settings + + + {/* Create Library */} + createLibrary(libName)} + trigger={ + + + Add Library + + } + > + setLibName(text)} + placeholder="My Cool Library" + /> + + console.log('lock')}> + + + Lock + + + + + + ); +}; + +export default DrawerLibraryManager; diff --git a/apps/mobile/src/components/drawer/DrawerLocationItem.tsx b/apps/mobile/src/components/drawer/DrawerLocationItem.tsx index a9f74ae74..c950f2e82 100644 --- a/apps/mobile/src/components/drawer/DrawerLocationItem.tsx +++ b/apps/mobile/src/components/drawer/DrawerLocationItem.tsx @@ -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 = (props) => { const { folderName, onPress } = props; return ( - + {folderName} diff --git a/apps/mobile/src/components/drawer/DrawerLogo.tsx b/apps/mobile/src/components/drawer/DrawerLogo.tsx deleted file mode 100644 index afe1ce54c..000000000 --- a/apps/mobile/src/components/drawer/DrawerLogo.tsx +++ /dev/null @@ -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 ( - <> - - - Spacedrive - - - - ); -}; - -export default DrawerLogo; diff --git a/apps/mobile/src/components/drawer/DrawerTagItem.tsx b/apps/mobile/src/components/drawer/DrawerTagItem.tsx index 210b04876..d364e9845 100644 --- a/apps/mobile/src/components/drawer/DrawerTagItem.tsx +++ b/apps/mobile/src/components/drawer/DrawerTagItem.tsx @@ -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 = (props) => { const { tagName, tagColor, onPress } = props; return ( - + {tagName} diff --git a/apps/mobile/src/components/file/FileIcon.tsx b/apps/mobile/src/components/file/FileIcon.tsx index 101f78dc9..db646bebe 100644 --- a/apps/mobile/src/components/file/FileIcon.tsx +++ b/apps/mobile/src/components/file/FileIcon.tsx @@ -17,13 +17,16 @@ type FileIconProps = { }; const FileIcon = ({ file, size = 1 }: FileIconProps) => { + // Temp + const has_thumbnail = false; + return ( {file?.is_dir ? ( - ) : file?.file?.has_thumbnail ? ( + ) : has_thumbnail ? ( <>{/* TODO */} ) : ( diff --git a/apps/mobile/src/components/file/FileItem.tsx b/apps/mobile/src/components/file/FileItem.tsx index 5bc5d8a03..352f4e17a 100644 --- a/apps/mobile/src/components/file/FileItem.tsx +++ b/apps/mobile/src/components/file/FileItem.tsx @@ -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['navigation']>(); diff --git a/apps/mobile/src/components/header/Header.tsx b/apps/mobile/src/components/header/Header.tsx index 8838ade88..df139c78f 100644 --- a/apps/mobile/src/components/header/Header.tsx +++ b/apps/mobile/src/components/header/Header.tsx @@ -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(); @@ -17,7 +16,11 @@ const Header = () => { const isDrawerOpen = useDrawerStatus() === 'open'; return ( - + navigation.openDrawer()}> { style={tw`flex-1 h-full justify-center`} onPress={() => navigation.navigate('Search')} > - Search + Search diff --git a/apps/mobile/src/components/layout/CollapsibleView.tsx b/apps/mobile/src/components/layout/CollapsibleView.tsx index abdf28a68..0d55838ae 100644 --- a/apps/mobile/src/components/layout/CollapsibleView.tsx +++ b/apps/mobile/src/components/layout/CollapsibleView.tsx @@ -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 ( - + {title} @@ -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' }} > - + {children} diff --git a/apps/mobile/src/components/layout/Dialog.tsx b/apps/mobile/src/components/layout/Dialog.tsx new file mode 100644 index 000000000..4b6a634d7 --- /dev/null +++ b/apps/mobile/src/components/layout/Dialog.tsx @@ -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 ( + + {props.trigger && ( + (props.setIsVisible ? props.setIsVisible(true) : setVisible(true))} + > + {props.trigger} + + )} + + {/* Backdrop */} + (props.setIsVisible ? props.setIsVisible(false) : setVisible(false))} + disabled={props.disableBackdropClose} + /> + {/* Content */} + + + + + {/* Title */} + {props.title} + {/* Description */} + {props.description && ( + + {props.description} + + )} + {/* Children */} + {props.children} + + {/* Actions */} + + + {props.ctaAction && ( + + )} + + + + + + + ); +}; + +export default Dialog; diff --git a/apps/mobile/src/components/modals/FileModal.tsx b/apps/mobile/src/components/modals/FileModal.tsx index e5658ee1d..f46017c73 100644 --- a/apps/mobile/src/components/modals/FileModal.tsx +++ b/apps/mobile/src/components/modals/FileModal.tsx @@ -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(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 && ( @@ -72,7 +62,6 @@ export const FileModal = () => { {/* Divider */} {/* Buttons */} -