From 62f2c77a52b38f1d6dcb31ae66164b97d3a89181 Mon Sep 17 00:00:00 2001 From: Utku <74243531+utkubakir@users.noreply.github.com> Date: Thu, 4 May 2023 11:10:31 +0300 Subject: [PATCH] [MOB-1] Theme support for Mobile (#755) * revert rspc changes and some theme stuff * Run onboarding test first * test adding a tag * handle keyboard on Create Tag Modal * listen system theme changes * fix delete tag button * wait add tag mutation * remove duplicate assert * fix edit location setting screen * select theme & fix add tag test * add how to run web app to contributing * add note about how to use stores correctly * use theme colors * system theme * remove metro-minify-terser * final tweaks * cleanup * cleanup --------- Co-authored-by: Oscar Beaumont --- .vscode/settings.json | 3 +- CONTRIBUTING.md | 7 +- apps/mobile/app.json | 7 +- apps/mobile/ios/Podfile | 2 - apps/mobile/ios/Podfile.lock | 10 +- .../ios/Spacedrive.xcodeproj/project.pbxproj | 6 +- apps/mobile/ios/Spacedrive/Info.plist | 2 + .../ios/Spacedrive/Spacedrive.entitlements | 16 +- apps/mobile/metro.config.js | 15 +- apps/mobile/package.json | 24 ++- apps/mobile/scripts/run-maestro-tests | 27 ++++ apps/mobile/src/App.tsx | 31 ++-- .../src/components/drawer/DrawerTags.tsx | 2 +- .../src/components/explorer/FileThumb.tsx | 2 +- apps/mobile/src/components/form/Input.tsx | 15 +- apps/mobile/src/components/layout/Modal.tsx | 2 +- .../src/components/modal/ImportModal.tsx | 2 +- .../modal/inspector/ActionsModal.tsx | 2 +- .../modal/inspector/FileInfoModal.tsx | 2 +- .../components/modal/tag/CreateTagModal.tsx | 24 ++- .../components/modal/tag/UpdateTagModal.tsx | 2 +- .../src/components/overview/OverviewStats.tsx | 28 +++- .../components/settings/SettingsContainer.tsx | 2 +- .../src/components/settings/SettingsItem.tsx | 8 +- apps/mobile/src/constants/style/Colors.js | 120 ++++++++++++++ apps/mobile/src/constants/style/tailwind.js | 16 ++ apps/mobile/src/hooks/useKeyboard.ts | 62 +++++++ apps/mobile/src/hooks/useTheme.ts | 38 +++++ apps/mobile/src/lib/tailwind.ts | 19 ++- apps/mobile/src/navigation/TabNavigator.tsx | 3 +- .../mobile/src/screens/onboarding/Privacy.tsx | 5 +- .../settings/client/AppearanceSettings.tsx | 152 +++++++++++++++++- .../settings/client/GeneralSettings.tsx | 6 +- .../settings/library/EditLocationSettings.tsx | 59 ++++--- .../library/LibraryGeneralSettings.tsx | 6 +- .../screens/settings/library/TagsSettings.tsx | 6 +- apps/mobile/tailwind.config.js | 97 +---------- apps/mobile/tests/add-tag.yml | 12 ++ apps/mobile/tests/onboarding.yml | 7 +- packages/client/src/hooks/usePlausible.tsx | 9 -- packages/client/src/stores/index.ts | 2 + packages/client/src/stores/themeStore.ts | 11 +- packages/config/package.json | 3 - pnpm-lock.yaml | 102 ++++++------ 44 files changed, 663 insertions(+), 313 deletions(-) create mode 100644 apps/mobile/src/constants/style/Colors.js create mode 100644 apps/mobile/src/constants/style/tailwind.js create mode 100644 apps/mobile/src/hooks/useKeyboard.ts create mode 100644 apps/mobile/src/hooks/useTheme.ts create mode 100644 apps/mobile/tests/add-tag.yml diff --git a/.vscode/settings.json b/.vscode/settings.json index f28cb9e02..7c929f596 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -48,8 +48,9 @@ "search.exclude": { "**/node_modules": true, "**/bower_components": true, - "**/*.code-search": true + "**/*.code-search": true, // Hiding these folders bcs they create a lot of noise in the search results + "apps/mobile/ios/Pods": true // "apps/mobile/android": true // "apps/mobile/ios": true }, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 15ab12e4f..5119e4e06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,9 +54,13 @@ To quickly run only the desktop app after `prep` you can use: If necessary, react-devtools can be launched using `pnpm react-devtools`. However, it must be executed before the desktop app for it to connect. +To run the web app: + +- `cargo run -p server` - runs the server +- `pnpm web dev` - runs the web embed server + To run the landing page: -- `pnpm web dev` - runs the web app for the embed - `pnpm landing dev` If you are having issues ensure you are using the following versions of Rust and Node: @@ -78,6 +82,7 @@ To run mobile app - You must also ensure [you must have NDK 23.1.7779620 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio - `pnpm android` - runs on Android Emulator - `pnpm ios` - runs on iOS Emulator +- `pnpm start` - runs the metro bundler ### Pull Request diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 02f0b25d9..6b807dd6c 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -18,11 +18,6 @@ "supportsTablet": false }, "android": {}, - "privacy": "hidden", - "extra": { - "eas": { - "projectId": "0cbf4456-87fb-499c-8dfa-554bfa5129f3" - } - } + "privacy": "hidden" } } diff --git a/apps/mobile/ios/Podfile b/apps/mobile/ios/Podfile index 5460dfed4..9ed9afb37 100644 --- a/apps/mobile/ios/Podfile +++ b/apps/mobile/ios/Podfile @@ -86,8 +86,6 @@ target 'Spacedrive' do target_installation_result.resource_bundle_targets.each do |resource_bundle_target| resource_bundle_target.build_configurations.each do |config| config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - # Spacedrive - Added to fix the issue with the deployment target - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = podfile_properties['ios.deploymentTarget'] end end end diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index c2a6e8cc5..86dc5e84c 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -603,8 +603,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: 57d2868c099736d80fcd648bf211b4431e51a558 - DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + boost: 20b6b3a53cb43939b6ffc2fd27d15ec7497167d0 + DoubleConversion: 2d248a4174d78beaaa49735340c9bcc564091245 EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9 EXFileSystem: 844e86ca9b5375486ecc4ef06d3838d5597d895d @@ -617,12 +617,12 @@ SPEC CHECKSUMS: FBLazyVector: 60195509584153283780abdac5569feffb8f08cc FBReactNativeSpec: c5a5c4f1b95ae42a17cd22c8c89c482a7b327fe3 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - hermes-engine: 38bfe887e456b33b697187570a08de33969f5db7 + glog: 5b8834f2f3f7d36c1c73e5a9837f53f03cfe84eb + hermes-engine: 2670551c21c6838c04f837c7c3db0ce55f5da525 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8 lottie-react-native: b702fab740cdb952a8e2354713d3beda63ff97b0 - RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCT-Folly: 30d165d2e977f66793419c3314296392cb793f38 RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a React: d7433ccb6a8c36e4cbed59a73c0700fc83c3e98a diff --git a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj index f17cc678a..5518d5bb0 100644 --- a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj @@ -350,7 +350,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = H7MGS2DHHJ; + DEVELOPMENT_TEAM = XF923AZS22; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", @@ -411,6 +411,7 @@ /usr/lib/swift, ../../../target, ); + MARKETING_VERSION = 0.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -451,7 +452,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = H7MGS2DHHJ; + DEVELOPMENT_TEAM = XF923AZS22; INFOPLIST_FILE = Spacedrive/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Spacedrive; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; @@ -507,6 +508,7 @@ /usr/lib/swift, ../../../target, ); + MARKETING_VERSION = 0.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/apps/mobile/ios/Spacedrive/Info.plist b/apps/mobile/ios/Spacedrive/Info.plist index 927095e38..ce8b50ec6 100644 --- a/apps/mobile/ios/Spacedrive/Info.plist +++ b/apps/mobile/ios/Spacedrive/Info.plist @@ -83,5 +83,7 @@ Automatic UIViewControllerBasedStatusBarAppearance + ITSAppUsesNonExemptEncryption + \ No newline at end of file diff --git a/apps/mobile/ios/Spacedrive/Spacedrive.entitlements b/apps/mobile/ios/Spacedrive/Spacedrive.entitlements index 018a6e20c..9e818d59c 100644 --- a/apps/mobile/ios/Spacedrive/Spacedrive.entitlements +++ b/apps/mobile/ios/Spacedrive/Spacedrive.entitlements @@ -1,8 +1,16 @@ - - aps-environment - development - + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + com.apple.developer.icloud-services + + CloudDocuments + + com.apple.developer.ubiquity-container-identifiers + + \ No newline at end of file diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 9195e2997..834a77396 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -59,22 +59,9 @@ const metroConfig = makeMetroConfig({ }, transformer: { ...expoDefaultConfig.transformer, - // Metro default is "uglify-es" but terser should be faster and has better defaults. - minifierPath: 'metro-minify-terser', - minifierConfig: { - compress: { - drop_console: true, - // Sometimes improves performance? - reduce_funcs: false - }, - format: { - ascii_only: true, - wrap_iife: true, - quote_style: 3 - } - }, getTransformOptions: async () => ({ transform: { + // What does this do? experimentalImportSupport: false, inlineRequires: true } diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 6a9c6792d..234f1bf84 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -18,7 +18,7 @@ "dependencies": { "@gorhom/bottom-sheet": "^4.4.5", "@hookform/resolvers": "^3.1.0", - "@react-native-async-storage/async-storage": "~1.17.11", + "@react-native-async-storage/async-storage": "~1.17.12", "@react-native-masked-view/masked-view": "0.2.8", "@react-navigation/bottom-tabs": "^6.5.7", "@react-navigation/drawer": "^6.6.2", @@ -28,12 +28,12 @@ "@rspc/react": "=0.0.0-main-a312a505", "@sd/assets": "workspace:*", "@sd/client": "workspace:*", - "@shopify/flash-list": "1.4.0", - "@tanstack/react-query": "^4.26.1", + "@shopify/flash-list": "1.4.2", + "@tanstack/react-query": "^4.29.1", "byte-size": "^8.1.0", - "class-variance-authority": "^0.4.0", + "class-variance-authority": "^0.5.2", "dayjs": "^1.11.5", - "expo": "~48.0.6", + "expo": "~48.0.10", "expo-linking": "~4.0.1", "expo-media-library": "~15.2.3", "expo-splash-screen": "~0.18.1", @@ -45,30 +45,28 @@ "react": "18.2.0", "react-hook-form": "^7.43.9", "react-native": "0.71.3", - "react-native-document-picker": "^8.1.1", + "react-native-document-picker": "^8.2.0", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.9.0", "react-native-popup-menu": "^0.16.1", "react-native-reanimated": "~2.14.4", - "react-native-safe-area-context": "4.5.0", + "react-native-safe-area-context": "4.5.1", "react-native-screens": "~3.20.0", "react-native-svg": "13.4.0", "react-native-wheel-color-picker": "^1.2.0", "twrnc": "^3.6.0", "use-count-up": "^3.0.1", - "use-debounce": "^9.0.2", - "valtio": "^1.10.3", + "use-debounce": "^9.0.4", + "valtio": "^1.10.4", "zod": "^3.21.4" }, "devDependencies": { - "@babel/core": "^7.21.0", + "@babel/core": "^7.21.4", "@rnx-kit/metro-config": "^1.3.5", "@sd/config": "workspace:*", "@types/react": "~18.0.27", "babel-plugin-module-resolver": "^5.0.0", "eslint-plugin-react-native": "^4.0.0", - "metro-minify-terser": "0.76.0", - "react-native-svg-transformer": "^1.0.0", - "typescript": "^4.9.5" + "react-native-svg-transformer": "^1.0.0" } } diff --git a/apps/mobile/scripts/run-maestro-tests b/apps/mobile/scripts/run-maestro-tests index 0c14cc7ae..2df41314a 100644 --- a/apps/mobile/scripts/run-maestro-tests +++ b/apps/mobile/scripts/run-maestro-tests @@ -22,9 +22,36 @@ else testFiles=$(ls apps/mobile/tests/*.yml apps/mobile/tests/android-only/*.yml) fi + +# Run onboarding first +onboardingFile="apps/mobile/tests/onboarding.yml" +if ! maestro test "$onboardingFile" +then + echo "Onboarding test failed. Retrying in 30 seconds..." + sleep 30 + if ! maestro test "$onboardingFile" + then + echo "Onboarding test failed again. Retrying for the last time in 120 seconds..." + sleep 120 + if ! maestro test "$onboardingFile" + then + echo "Onboarding test failed again. Exiting..." + exit 1 + fi + fi +fi + failedTests=() + +# Run the rest of the files for file in $testFiles do + # Skip onboarding.yml since it has already been run + if [ "$file" == "$onboardingFile" ] + then + continue + fi + if ! maestro test "$file" then echo "Test ${file} failed. Retrying in 30 seconds..." diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index 40e8461c0..b0a27fe23 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -2,7 +2,6 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import { DefaultTheme, NavigationContainer, - Theme, useNavigationContainerRef } from '@react-navigation/native'; import { QueryClient } from '@tanstack/react-query'; @@ -16,7 +15,6 @@ import { useEffect, useRef, useState } from 'react'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { MenuProvider } from 'react-native-popup-menu'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { useDeviceContext } from 'twrnc'; import { useSnapshot } from 'valtio'; import { ClientContextProvider, @@ -28,7 +26,8 @@ import { usePlausiblePageViewMonitor } from '@sd/client'; import { GlobalModals } from './components/modal/GlobalModals'; -import { tw } from './lib/tailwind'; +import { useTheme } from './hooks/useTheme'; +import { changeTwTheme, tw } from './lib/tailwind'; import RootNavigator from './navigation'; import OnboardingNavigator from './navigation/OnboardingNavigator'; import { currentLibraryStore } from './utils/nav'; @@ -37,16 +36,10 @@ dayjs.extend(advancedFormat); dayjs.extend(relativeTime); dayjs.extend(duration); -const NavigatorTheme: Theme = { - ...DefaultTheme, - colors: { - ...DefaultTheme.colors, - // Default screen background - background: tw.color('app')! - } -}; - initPlausible({ platformType: 'mobile' }); +// changeTwTheme(getThemeStore().theme); +// TODO: Use above when light theme is ready +changeTwTheme('dark'); function AppNavigation() { const { library } = useClientContext(); @@ -67,7 +60,14 @@ function AppNavigation() { onReady={() => { routeNameRef.current = navRef.getCurrentRoute()?.name; }} - theme={NavigatorTheme} + theme={{ + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + // Default screen background + background: tw.color('app')! + } + }} onStateChange={async () => { const previousRouteName = routeNameRef.current; const currentRouteName = navRef.getCurrentRoute()?.name; @@ -78,7 +78,6 @@ function AppNavigation() { if (navRef.getRootState().routeNames.includes('GetStarted')) { return; } - console.log(`Navigated from ${previousRouteName} to ${currentRouteName}`); currentRouteName && setCurrentPath(currentRouteName); } }} @@ -96,9 +95,7 @@ function AppNavigation() { } function AppContainer() { - // Enables dark mode, and screen size breakpoints, etc. for tailwind - useDeviceContext(tw, { withDeviceColorScheme: false }); - + useTheme(); useInvalidateQuery(); const { id } = useSnapshot(currentLibraryStore); diff --git a/apps/mobile/src/components/drawer/DrawerTags.tsx b/apps/mobile/src/components/drawer/DrawerTags.tsx index 813a7e678..551db4d05 100644 --- a/apps/mobile/src/components/drawer/DrawerTags.tsx +++ b/apps/mobile/src/components/drawer/DrawerTags.tsx @@ -17,7 +17,7 @@ type DrawerTagItemProps = { const DrawerTagItem: React.FC = (props) => { const { tagName, tagColor, onPress } = props; return ( - + diff --git a/apps/mobile/src/components/explorer/FileThumb.tsx b/apps/mobile/src/components/explorer/FileThumb.tsx index 8b8981f19..e7f4b230f 100644 --- a/apps/mobile/src/components/explorer/FileThumb.tsx +++ b/apps/mobile/src/components/explorer/FileThumb.tsx @@ -80,7 +80,7 @@ export default function FileThumb({ data, size = 1 }: FileThumbProps) { icon = icons[kind]; } - // TODO: Handle video thumbnails + // TODO: Handle video thumbnails (do we have ffmpeg on mobile?) // // 10 percent of the size // const videoBarsHeight = Math.floor(size / 10); diff --git a/apps/mobile/src/components/form/Input.tsx b/apps/mobile/src/components/form/Input.tsx index 151f3b2e3..0619792b5 100644 --- a/apps/mobile/src/components/form/Input.tsx +++ b/apps/mobile/src/components/form/Input.tsx @@ -1,3 +1,4 @@ +import { BottomSheetTextInput } from '@gorhom/bottom-sheet'; import { VariantProps, cva } from 'class-variance-authority'; import { Eye, EyeSlash } from 'phosphor-react-native'; import { useState } from 'react'; @@ -33,7 +34,19 @@ export const Input = ({ variant, size, ...props }: InputProps) => { ); }; -// Same as above but configured with password props & show/hide password button +// To use in modals (for keyboard handling) +export const ModalInput = ({ variant, size, ...props }: InputProps) => { + const { style, ...otherProps } = props; + return ( + + ); +}; + +// Same as Input but configured with password props & show/hide password button type PasswordInputProps = InputProps & { isNewPassword?: boolean; diff --git a/apps/mobile/src/components/layout/Modal.tsx b/apps/mobile/src/components/layout/Modal.tsx index 05fe0bb5c..d7ba8b5cf 100644 --- a/apps/mobile/src/components/layout/Modal.tsx +++ b/apps/mobile/src/components/layout/Modal.tsx @@ -110,7 +110,7 @@ export const ConfirmModal = forwardRef((props, ref) handleComponent={(props) => ModalHandle({ modalRef, showCloseButton: false, ...props }) } - snapPoints={props.snapPoints ?? ['25%']} + snapPoints={props.snapPoints ?? ['25']} > {/* Title */} {props.title && ( diff --git a/apps/mobile/src/components/modal/ImportModal.tsx b/apps/mobile/src/components/modal/ImportModal.tsx index ec37db9d9..6eba72f0b 100644 --- a/apps/mobile/src/components/modal/ImportModal.tsx +++ b/apps/mobile/src/components/modal/ImportModal.tsx @@ -131,7 +131,7 @@ const ImportModal = forwardRef((_, ref) => { // }, []); return ( - + {/*