Merge pull request #373 from spacedriveapp/mobile-library

Mobile library
This commit is contained in:
Utku
2022-09-07 13:03:58 +03:00
committed by GitHub
65 changed files with 820 additions and 615 deletions

View File

@@ -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:*",

View File

@@ -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;

View File

@@ -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';

View File

@@ -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'
}
}
]
]
};
};

View File

@@ -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

View File

@@ -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>

View File

@@ -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",

View File

Binary file not shown.

View File

@@ -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;

View File

@@ -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 }) => (

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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
}
];

View File

@@ -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 */}

View 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;

View File

@@ -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}

View File

@@ -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;

View File

@@ -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}

View File

@@ -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 }]}>

View File

@@ -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']>();

View File

@@ -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>

View File

@@ -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>

View 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;

View File

@@ -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

View File

@@ -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']
}
},

View File

@@ -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>;

View 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}
/>
);
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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),

View File

@@ -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)} />}
>

View File

@@ -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> =

View File

@@ -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<

View File

@@ -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')
}
}}
>

View File

@@ -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} />

View File

@@ -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 }} />

View File

@@ -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 }} />

View File

@@ -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 }} />

View File

@@ -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 }} />

View File

@@ -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 = [
{

View File

@@ -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;

View File

@@ -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 (

View File

@@ -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}

View File

@@ -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 (

View File

@@ -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 (

View File

@@ -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;

View File

@@ -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"

View File

@@ -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 (

View 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;

View File

@@ -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>

View 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 };
};

View 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;
}
});

View 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
});

View File

@@ -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 }));
}
}));

View File

@@ -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 }))
}));

View File

@@ -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 }))
}));

View 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
View 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;
}

View File

@@ -1,4 +1,9 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {}
"compilerOptions": {
"baseUrl": "src",
"paths": {
"~/*": ["*"]
}
}
}

View File

@@ -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:*",

View File

@@ -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(

View File

@@ -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;

View File

@@ -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",

View File

@@ -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
View File

Binary file not shown.