diff --git a/.gitignore b/.gitignore index 8297efc2..3c7af134 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,10 @@ web-build/ # macOS .DS_Store -.env +.envrc coverage/ builds/ + # @generated expo-cli sync-28e2ab0e9ece60556eaf932abe52d017ec33db50 # The following patterns were generated by expo-cli diff --git a/android/app/build.gradle b/android/app/build.gradle index c409f52f..2f3bfb6c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -78,7 +78,7 @@ import com.android.build.OutputFile */ project.ext.react = [ - enableHermes: false + enableHermes: true ] apply from: '../../node_modules/react-native-unimodules/gradle.groovy' diff --git a/android/app/src/main/java/com/xmflsct/app/tooot/MainApplication.java b/android/app/src/main/java/com/xmflsct/app/tooot/MainApplication.java index 7c5f1e8a..7bf44f4e 100644 --- a/android/app/src/main/java/com/xmflsct/app/tooot/MainApplication.java +++ b/android/app/src/main/java/com/xmflsct/app/tooot/MainApplication.java @@ -28,6 +28,9 @@ import java.util.Arrays; import java.util.List; import javax.annotation.Nullable; + import com.facebook.react.bridge.JSIModulePackage; // <- react-native-reanimated-v2 + import com.swmansion.reanimated.ReanimatedJSIModulePackage; // <- react-native-reanimated-v2 + public class MainApplication extends Application implements ReactApplication { private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider( new BasePackageList().getPackageList() @@ -51,6 +54,11 @@ public class MainApplication extends Application implements ReactApplication { return "index"; } + @Override // <- react-native-reanimated-v2 + protected JSIModulePackage getJSIModulePackage() { + return new ReanimatedJSIModulePackage(); + } + @Override protected @Nullable String getJSBundleFile() { if (BuildConfig.DEBUG) { diff --git a/app.config.ts b/app.config.ts index b233ea7e..4880dc5e 100644 --- a/app.config.ts +++ b/app.config.ts @@ -3,7 +3,7 @@ import 'dotenv/config' export default (): ExpoConfig => ({ name: 'tooot', - description: 'tooot for mastodon', + description: 'tooot for Mastodon', slug: 'tooot', privacy: 'hidden', sdkVersion: '40.0.0', diff --git a/build/android.sh b/build/android.sh deleted file mode 100755 index 35d32812..00000000 --- a/build/android.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ $# -ne 1 ]; then - echo "Arguments incorrect. Use 'xxx'" - exit 1 -fi - -JAVA_HOME=`/usr/libexec/java_home -v 1.8.0` \ -EXPO_USERNAME='xmflsct' \ -EXPO_PASSWORD=',8d_AJ1HmYJo8lbve&QoB40t3ImGdF)Dd' \ -EXPO_ANDROID_KEYSTORE_PASSWORD="9c54265087704801ba5d3d88809110a1" \ -EXPO_ANDROID_KEY_PASSWORD="748bb2e11529497dad7831c409175b94" \ -turtle build:android \ - --release-channel $1 \ - --type app-bundle \ - --keystore-path ./tooot.jks \ - --keystore-alias "QHhtZmxzY3QvdG9vb3Q=" \ - --build-dir ./builds \ No newline at end of file diff --git a/index.js b/index.js index 9d5d25a6..5c94d2cb 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ import { registerRootComponent } from 'expo'; -import App from './App'; +import App from '@root/App'; // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in the Expo client or in a native build, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1e7db969..2e4d6e59 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -7,14 +7,12 @@ PODS: - UMCore - UMFileSystemInterface - UMPermissionsInterface - - EXBlur (8.2.2): - - UMCore - EXConstants (10.0.1): - UMConstantsInterface - UMCore - EXCrypto (8.4.0): - UMCore - - EXDevice (2.4.0): + - EXDevice (3.1.1): - UMCore - EXErrorRecovery (1.4.0): - UMCore @@ -25,7 +23,7 @@ PODS: - EXFirebaseCore - Firebase/Core (= 6.14.0) - UMCore - - EXFirebaseCore (1.3.0): + - EXFirebaseCore (1.2.0): - Firebase/Core (= 6.14.0) - UMCore - EXFont (8.4.0): @@ -66,8 +64,8 @@ PODS: - UMFileSystemInterface - EXStoreReview (2.3.0): - UMCore - - EXUpdates (0.4.1): - - React-Core + - EXUpdates (0.3.5): + - React - UMCore - EXVideoThumbnails (4.4.0): - UMCore @@ -332,15 +330,17 @@ PODS: - React-cxxreact (= 0.63.4) - React-jsi (= 0.63.4) - React-jsinspector (0.63.4) + - react-native-blur (0.8.0): + - React - react-native-blurhash (1.0.29): - React - react-native-netinfo (5.9.10): - React-Core - react-native-safe-area-context (3.1.9): - React-Core - - react-native-segmented-control (2.2.1): + - react-native-segmented-control (2.2.2): - React-Core - - react-native-viewpager (4.2.0): + - react-native-viewpager (4.2.2): - React-Core - React-RCTActionSheet (0.63.4): - React-Core/RCTActionSheetHeaders (= 0.63.4) @@ -410,9 +410,9 @@ PODS: - React-Core - SDWebImage (~> 5.8) - SDWebImageWebPCoder (~> 0.6.1) - - RNGestureHandler (1.8.0): - - React - - RNReanimated (2.0.0-rc.0): + - RNGestureHandler (1.9.0): + - React-Core + - RNReanimated (2.0.0-rc.2): - DoubleConversion - FBLazyVector - FBReactNativeSpec @@ -441,11 +441,13 @@ PODS: - React-RCTVibration - ReactCommon/turbomodule/core - Yoga - - RNScreens (2.15.2): + - RNScreens (2.17.1): - React-Core - RNSentry (2.1.1): - React-Core - Sentry (= 6.0.9) + - RNSharedElement (0.7.0): + - React - RNSVG (12.1.0): - React - SDWebImage (5.10.3): @@ -480,8 +482,7 @@ DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXApplication (from `../node_modules/expo-application/ios`) - EXAV (from `../node_modules/expo-av/ios`) - - EXBlur (from `../node_modules/expo-blur/ios`) - - EXConstants (from `../node_modules/expo-constants/ios`) + - EXConstants (from `../node_modules/sentry-expo/node_modules/expo-constants/ios`) - EXCrypto (from `../node_modules/expo-crypto/ios`) - EXDevice (from `../node_modules/expo-device/ios`) - EXErrorRecovery (from `../node_modules/expo-error-recovery/ios`) @@ -521,6 +522,7 @@ DEPENDENCIES: - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-blurhash (from `../node_modules/react-native-blurhash`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) @@ -543,6 +545,7 @@ DEPENDENCIES: - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) - "RNSentry (from `../node_modules/@sentry/react-native`)" + - RNSharedElement (from `../node_modules/react-native-shared-element`) - RNSVG (from `../node_modules/react-native-svg`) - UMAppLoader (from `../node_modules/unimodules-app-loader/ios`) - UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`) @@ -586,10 +589,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-application/ios" EXAV: :path: "../node_modules/expo-av/ios" - EXBlur: - :path: "../node_modules/expo-blur/ios" EXConstants: - :path: "../node_modules/expo-constants/ios" + :path: "../node_modules/sentry-expo/node_modules/expo-constants/ios" EXCrypto: :path: "../node_modules/expo-crypto/ios" EXDevice: @@ -664,6 +665,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../node_modules/react-native/ReactCommon/jsinspector" + react-native-blur: + :path: "../node_modules/@react-native-community/blur" react-native-blurhash: :path: "../node_modules/react-native-blurhash" react-native-netinfo: @@ -708,6 +711,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-screens" RNSentry: :path: "../node_modules/@sentry/react-native" + RNSharedElement: + :path: "../node_modules/react-native-shared-element" RNSVG: :path: "../node_modules/react-native-svg" UMAppLoader: @@ -744,14 +749,13 @@ SPEC CHECKSUMS: DoubleConversion: cde416483dac037923206447da6e1454df403714 EXApplication: e3c201e7b913d081bbd37bd3c5d0e2cdc21733b4 EXAV: e32adf44c10a82f723172d5b01d5693890888dd6 - EXBlur: 569d38c2b0b7b000866d928bc36acadfdc02ea8e EXConstants: d9483a8fe0a963dd7ee3389f371303773e6ecd37 EXCrypto: 81948191c3d2caf18fb32f18ee88d87d42532d62 - EXDevice: 01f54314f618aa4098893f66cd8f2a8a411f33ee + EXDevice: 101eddd9bc28faba8246ba0c499a65a4821a6b5e EXErrorRecovery: b46af4b91e2b4ec598ab1fa51d2cf088aaf5511f EXFileSystem: 0e4974ab77bff04cda68d2886d070cbe64b93a6b EXFirebaseAnalytics: d72f553dc0c0a8ee451714499fac21883c3f7369 - EXFirebaseCore: f986c71bdd1cb5941c9cfa686b2395fe588a53d0 + EXFirebaseCore: d5befdb22015f9b71168ed89dda0b50dbb07fed1 EXFont: 30c64ed8735a180e3f20046e4fdac4ea074d71d3 EXHaptics: 6d2e09387f0eb4d3c8dc97ae905cbea8afe33651 EXImageLoader: f96ec9992733a4224418bbd9382e5485c8948944 @@ -766,7 +770,7 @@ SPEC CHECKSUMS: EXSplashScreen: 8c7c1112ce7611a853486af4737fe2298eda7657 EXSQLite: bda6a286dded0637bb312ee781239dcca163ff4b EXStoreReview: 9b161bd86c172a9755c787982bfc32c7193cf803 - EXUpdates: 28368049118dbe4ceaf4422ec72e0ad5f770df1f + EXUpdates: 74b39409f68eca207075d87b0077bdf37865a8bf EXVideoThumbnails: 8ff241784f1d89fdd451bcdb7f733d06361d44a8 EXWebBrowser: cb4811e6c883876d2e2ba1c10527de96963d410a FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e @@ -796,11 +800,12 @@ SPEC CHECKSUMS: React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31 React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949 React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a + react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c react-native-blurhash: 90886ae897cafbbdf2773cb3654656bcb34e8f43 react-native-netinfo: 30fb89fa913c342be82a887b56e96be6d71201dd react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94 - react-native-segmented-control: eb9e25fbfbce226ecf66d643428fbe97601e691a - react-native-viewpager: c1a686e7b5e164a52387f1522358623c4f52070f + react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097 + react-native-viewpager: ea945e2881ce9a4a8bcdc84de4ec65ff23c90f6e React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336 React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0 @@ -814,10 +819,11 @@ SPEC CHECKSUMS: RNCAsyncStorage: da95b83e241a7f5efe3da1a949b3ec3175380be0 RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f RNFastImage: d4870d58f5936111c56218dbd7fcfc18e65b58ff - RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39 - RNReanimated: b9c929bfff7dedc9c89ab1875f1c6151023358d9 - RNScreens: 3d682bcaba69a4f8e55543d90818704f34338db1 + RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b + RNReanimated: e8a1520b15df106c96214161078c69e4a23b8b29 + RNScreens: b6c9607e6fe47c1b6e2f1910d2acd46dd7ecea3a RNSentry: 6b46b6fc1d715a378fbaa5d7d43bc9ce99b500e5 + RNSharedElement: 00b1a1420d213a34459bb9a5aacabb38107d7948 RNSVG: ce9d996113475209013317e48b05c21ee988d42e SDWebImage: e378178472b735e84b007bfb55514c97948a0598 SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 diff --git a/ios/tooot.xcodeproj/project.pbxproj b/ios/tooot.xcodeproj/project.pbxproj index 266106d6..89650a2d 100644 --- a/ios/tooot.xcodeproj/project.pbxproj +++ b/ios/tooot.xcodeproj/project.pbxproj @@ -177,6 +177,7 @@ LastUpgradeCheck = 1130; TargetAttributes = { 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = 8EGBLQ2MA6; LastSwiftMigration = 1120; }; }; @@ -229,7 +230,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n../node_modules/expo-updates/scripts/create-manifest-ios.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n../node_modules/expo-updates/scripts/create-manifest-ios.sh\n"; }; 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -325,6 +326,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8EGBLQ2MA6; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -332,7 +334,7 @@ "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = tooot/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", @@ -356,9 +358,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8EGBLQ2MA6; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = tooot/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", @@ -421,7 +424,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = ( "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", @@ -475,7 +478,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = ( "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", diff --git a/ios/tooot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/tooot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/tooot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/tooot.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/tooot.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/ios/tooot.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/package.json b/package.json index 5c0cd633..7e4a0dd7 100644 --- a/package.json +++ b/package.json @@ -6,95 +6,91 @@ "web": "expo start --web", "eject": "expo eject", "test": "jest --watchAll", - "release": "./publish/publish.sh" + "release": "scripts/release.sh" }, "dependencies": { "@expo/react-native-action-sheet": "^3.8.0", "@neverdull-agency/expo-unlimited-secure-store": "^1.0.10", - "@react-native-async-storage/async-storage": "^1.13.2", + "@react-native-async-storage/async-storage": "^1.13.3", + "@react-native-community/blur": "^3.6.0", "@react-native-community/masked-view": "0.1.10", - "@react-native-community/netinfo": "^5.9.7", - "@react-native-community/segmented-control": "2.2.1", - "@react-native-community/viewpager": "4.2.0", - "@react-navigation/bottom-tabs": "^5.11.2", - "@react-navigation/native": "^5.8.10", + "@react-native-community/netinfo": "^5.9.10", + "@react-native-community/segmented-control": "2.2.2", + "@react-native-community/viewpager": "4.2.2", + "@react-navigation/bottom-tabs": "^5.11.7", + "@react-navigation/native": "^5.9.2", + "@react-navigation/stack": "^5.14.2", "@reduxjs/toolkit": "^1.5.0", "@sentry/react-native": "^2.1.1", - "@sharcoux/slider": "^5.0.1", + "@sharcoux/slider": "^5.0.4", "axios": "^0.21.1", - "expo": "^40.0.0", + "expo": "^40.0.1", "expo-auth-session": "~3.0.0", "expo-av": "~8.7.0", - "expo-blur": "~8.2.2", - "expo-constants": "~10.0.1", "expo-crypto": "~8.4.0", "expo-firebase-analytics": "~2.6.0", - "expo-firebase-core": "~1.3.0", "expo-haptics": "~8.4.0", "expo-image-picker": "~9.2.0", "expo-linear-gradient": "~8.4.0", - "expo-linking": "~2.0.0", + "expo-linking": "~2.0.1", "expo-localization": "~9.1.0", "expo-random": "~10.0.0", "expo-secure-store": "~9.3.0", "expo-splash-screen": "~0.8.1", "expo-status-bar": "~1.0.3", "expo-store-review": "~2.3.0", - "expo-updates": "~0.4.0", "expo-video-thumbnails": "~4.4.0", "expo-web-browser": "~8.6.0", - "i18next": "^19.8.4", + "i18next": "^19.8.5", "lodash": "^4.17.20", "react": "16.13.1", "react-dom": "16.13.1", "react-i18next": "^11.8.5", "react-native": "~0.63.4", - "react-native-animated-spinkit": "^1.4.2", + "react-native-animated-spinkit": "^1.5.1", "react-native-blurhash": "^1.0.29", "react-native-fast-image": "^8.3.4", "react-native-feather": "^1.0.2", - "react-native-gesture-handler": "~1.8.0", + "react-native-gesture-handler": "~1.9.0", "react-native-htmlview": "^0.16.0", "react-native-image-zoom-viewer": "^3.0.1", - "react-native-reanimated": "2.0.0-rc.0", + "react-native-reanimated": "^2.0.0-rc.2", "react-native-safe-area-context": "3.1.9", - "react-native-screens": "~2.15.0", + "react-native-screens": "~2.17.1", + "react-native-shared-element": "^0.7.0", "react-native-svg": "12.1.0", "react-native-tab-view": "^2.15.2", "react-native-tab-view-viewpager-adapter": "^1.1.0", - "react-native-toast-message": "^1.4.2", + "react-native-toast-message": "^1.4.3", "react-native-unimodules": "~0.12.0", - "react-native-web": "~0.13.12", - "react-navigation": "^4.4.3", - "react-query": "^3.5.6", + "react-navigation-shared-element": "^3.0.0", + "react-query": "^3.6.0", "react-redux": "^7.2.2", "react-timeago": "^5.2.0", "redux-persist": "^6.0.0", "rn-placeholder": "^3.0.3", "sentry-expo": "^3.0.4", - "tslib": "^2.0.3" + "tslib": "^2.1.0" }, "devDependencies": { - "@babel/core": "~7.9.0", - "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/core": "~7.12.10", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", "@babel/preset-typescript": "^7.12.7", - "@expo/config": "^3.3.15", + "@expo/config": "^3.3.26", "@jest/types": "^26.6.2", - "@react-navigation/stack": "^5.12.8", "@testing-library/jest-native": "^3.4.3", "@testing-library/react-hooks": "^3.7.0", "@testing-library/react-native": "^7.1.0", - "@types/crypto-js": "^4.0.1", "@types/jest": "^26.0.19", "@types/lodash": "^4.14.164", "@types/react": "~16.9.35", "@types/react-dom": "~16.9.8", - "@types/react-native": "~0.63.2", + "@types/react-native": "~0.63.46", "@types/react-navigation": "^3.4.0", - "@types/react-redux": "^7.1.12", + "@types/react-redux": "^7.1.16", "@types/react-test-renderer": "^17.0.0", "@types/react-timeago": "^4.1.2", - "@welldone-software/why-did-you-render": "^6.0.4", + "@welldone-software/why-did-you-render": "^6.0.5", "babel-jest": "~25.2.6", "babel-plugin-module-resolver": "^4.1.0", "chalk": "^4.1.0", @@ -102,6 +98,8 @@ "jest": "^26.6.3", "jest-expo": "^40.0.1", "nock": "^13.0.5", + "react-navigation": "^4.4.3", + "react-navigation-stack": "^2.10.2", "react-test-renderer": "^16.13.1", "typescript": "~4.1.3" }, diff --git a/publish/publish.sh b/scripts/release.sh similarity index 63% rename from publish/publish.sh rename to scripts/release.sh index 26d261ba..8ba2bfd1 100755 --- a/publish/publish.sh +++ b/scripts/release.sh @@ -5,4 +5,4 @@ if [ $# -ne 1 ]; then exit 1 fi -expo publish --release-channel=$1 \ No newline at end of file +expo publish --target bare --release-channel=$1 \ No newline at end of file diff --git a/src/@types/react-navigation.d.ts b/src/@types/react-navigation.d.ts index 23c36e7e..9c0a9187 100644 --- a/src/@types/react-navigation.d.ts +++ b/src/@types/react-navigation.d.ts @@ -1,19 +1,22 @@ +interface IImageInfo { + url: string + width?: number + height?: number + originUrl?: string + props?: any +} + declare namespace Nav { type RootStackParamList = { - 'Screen-Local': undefined - 'Screen-Public': undefined - 'Screen-Post': undefined - 'Screen-Notifications': undefined - 'Screen-Me': undefined - } - - type SharedStackParamList = { - 'Screen-Shared-Account': { - account: Mastodon.Account | Mastodon.Mention + 'Screen-Tabs': undefined + 'Screen-Actions': { + queryKey: QueryKeyTimeline + status: Mastodon.Status + url?: string + type?: 'status' | 'account' } - 'Screen-Shared-Announcements': { showAll?: boolean } - 'Screen-Shared-Attachments': { account: Mastodon.Account } - 'Screen-Shared-Compose': + 'Screen-Announcements': { showAll: boolean } + 'Screen-Compose': | { type: 'edit' incomingStatus: Mastodon.Status @@ -48,54 +51,71 @@ declare namespace Nav { accts: Mastodon.Account['acct'][] } | undefined - 'Screen-Shared-Hashtag': { - hashtag: Mastodon.Tag['name'] - } - 'Screen-Shared-ImagesViewer': { - imageUrls: ({ - url: string - width?: number - height?: number - originUrl?: string - props?: any - } & { + 'Screen-ImagesViewer': { + imageUrls: (IImageInfo & { preview_url: Mastodon.AttachmentImage['preview_url'] - remote_url: Mastodon.AttachmentImage['remote_url'] + remote_url?: Mastodon.AttachmentImage['remote_url'] imageIndex: number })[] imageIndex: number } - 'Screen-Shared-Relationships': { + } + + type ScreenTabsStackParamList = { + 'Tab-Local': undefined + 'Tab-Public': undefined + 'Tab-Compose': undefined + 'Tab-Notifications': undefined + 'Tab-Me': undefined + } + + type TabSharedStackParamList = { + 'Tab-Shared-Account': { + account: Mastodon.Account | Mastodon.Mention + } + 'Tab-Shared-Attachments': { account: Mastodon.Account } + 'Tab-Shared-Hashtag': { + hashtag: Mastodon.Tag['name'] + } + 'Tab-Shared-Relationships': { account: Mastodon.Account initialType: 'following' | 'followers' } - 'Screen-Shared-Search': undefined - 'Screen-Shared-Toot': { + 'Tab-Shared-Search': undefined + 'Tab-Shared-Toot': { toot: Mastodon.Status } } - type LocalStackParamList = { - 'Screen-Local-Root': undefined - } & SharedStackParamList + type TabLocalStackParamList = { + 'Tab-Local-Root': undefined + } & TabSharedStackParamList - type RemoteStackParamList = { - 'Screen-Remote-Root': undefined - } & SharedStackParamList + type TabPublicStackParamList = { + 'Tab-Public-Root': undefined + } & TabSharedStackParamList - type NotificationsStackParamList = { - 'Screen-Notifications-Root': undefined - } & SharedStackParamList + type TabComposeStackParamList = { + 'Tab-Compose-Root': undefined + 'Tab-Compose-EditAttachment': unknown + } - type MeStackParamList = { - 'Screen-Me-Root': { navigateAway?: 'Screen-Me-Settings-UpdateRemote' } - 'Screen-Me-Bookmarks': undefined - 'Screen-Me-Conversations': undefined - 'Screen-Me-Favourites': undefined - 'Screen-Me-Lists': undefined - 'Screen-Me-Lists-List': undefined - 'Screen-Me-Settings': undefined - 'Screen-Me-Settings-UpdateRemote': undefined - 'Screen-Me-Switch': undefined - } & SharedStackParamList + type TabNotificationsStackParamList = { + 'Tab-Notifications-Root': undefined + } & TabSharedStackParamList + + type TabMeStackParamList = { + 'Tab-Me-Root': undefined + 'Tab-Me-Bookmarks': undefined + 'Tab-Me-Conversations': undefined + 'Tab-Me-Favourites': undefined + 'Tab-Me-Lists': undefined + 'Tab-Me-Lists-List': { + list: Mastodon.List['id'] + title: Mastodon.List['title'] + } + 'Tab-Me-Settings': undefined + 'Tab-Me-Settings-UpdateRemote': undefined + 'Tab-Me-Switch': undefined + } & TabSharedStackParamList } diff --git a/App.tsx b/src/App.tsx similarity index 98% rename from App.tsx rename to src/App.tsx index 102ac0b8..55498b1b 100644 --- a/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { ActionSheetProvider } from '@expo/react-native-action-sheet' import i18n from '@root/i18n/i18n' -import Index from '@root/Index' +import Index from '@root/Screens' import audio from '@root/startup/audio' import dev from '@root/startup/dev' import log from '@root/startup/log' diff --git a/src/Index.tsx b/src/Index.tsx deleted file mode 100644 index 81eae379..00000000 --- a/src/Index.tsx +++ /dev/null @@ -1,324 +0,0 @@ -import client from '@api/client' -import haptics from '@components/haptics' -import Icon from '@components/Icon' -import { toast, toastConfig } from '@components/toast' -import { - BottomTabNavigationOptions, - createBottomTabNavigator -} from '@react-navigation/bottom-tabs' -import { - getFocusedRouteNameFromRoute, - NavigationContainer, - NavigationContainerRef -} from '@react-navigation/native' -import ScreenLocal from '@screens/Local' -import ScreenMe from '@screens/Me' -import ScreenNotifications from '@screens/Notifications' -import ScreenPublic from '@screens/Public' -import { useTimelineQuery } from '@utils/queryHooks/timeline' -import { - getLocalAccount, - getLocalActiveIndex, - getLocalNotification, - localUpdateAccountPreferences, - localUpdateNotification -} from '@utils/slices/instancesSlice' -import { useTheme } from '@utils/styles/ThemeManager' -import { themes } from '@utils/styles/themes' -import * as Analytics from 'expo-firebase-analytics' -import React, { - createRef, - useCallback, - useEffect, - useMemo, - useRef -} from 'react' -import { useTranslation } from 'react-i18next' -import { Image, Platform, StatusBar } from 'react-native' -import Toast from 'react-native-toast-message' -import { useDispatch, useSelector } from 'react-redux' - -const Tab = createBottomTabNavigator() - -export interface Props { - localCorrupt?: string -} - -export const navigationRef = createRef() - -const Index: React.FC = ({ localCorrupt }) => { - const dispatch = useDispatch() - const localActiveIndex = useSelector(getLocalActiveIndex) - const { mode, theme } = useTheme() - enum barStyle { - light = 'dark-content', - dark = 'light-content' - } - - const routeNameRef = useRef() - - // const isConnected = useNetInfo().isConnected - // const [firstRender, setFirstRender] = useState(false) - // useEffect(() => { - // if (firstRender) { - // // bug in netInfo on first render as false - // if (isConnected !== false) { - // toast({ type: 'error', content: 'ζ‰‹ζœΊπŸˆšοΈη½‘η»œ', autoHide: false }) - // } - // } else { - // setFirstRender(true) - // } - // }, [isConnected, firstRender]) - - // On launch display login credentials corrupt information - const { t } = useTranslation('common') - useEffect(() => { - const showLocalCorrect = localCorrupt - ? toast({ - type: 'error', - message: t('index.localCorrupt'), - description: localCorrupt.length ? localCorrupt : undefined, - autoHide: false - }) - : undefined - return showLocalCorrect - }, [localCorrupt]) - - // On launch check if there is any unread announcements - useEffect(() => { - console.log('Checking announcements') - localActiveIndex !== null && - client({ - method: 'get', - instance: 'local', - url: `announcements` - }) - .then(res => { - if (res?.filter(announcement => !announcement.read).length) { - navigationRef.current?.navigate('Screen-Shared-Announcements', { - showAll: false - }) - } - }) - .catch(() => {}) - }, []) - - // On launch check if there is any unread noficiations - const queryNotification = useTimelineQuery({ - page: 'Notifications', - options: { - enabled: localActiveIndex !== null ? true : false, - refetchInterval: 1000 * 60, - refetchIntervalInBackground: true - } - }) - const prevNotification = useSelector(getLocalNotification) - useEffect(() => { - if (queryNotification.data?.pages) { - const flattenData = queryNotification.data.pages.flatMap(d => [...d]) - const latestNotificationTime = flattenData.length - ? (flattenData[0] as Mastodon.Notification).created_at - : undefined - - if (!prevNotification || !prevNotification.latestTime) { - dispatch( - localUpdateNotification({ - unread: false - }) - ) - } else if ( - latestNotificationTime && - new Date(prevNotification.latestTime) < new Date(latestNotificationTime) - ) { - dispatch( - localUpdateNotification({ - unread: true, - latestTime: latestNotificationTime - }) - ) - } - } - }, [queryNotification.data?.pages]) - - // Lazily update users's preferences, for e.g. composing default visibility - useEffect(() => { - if (localActiveIndex !== null) { - dispatch(localUpdateAccountPreferences()) - } - }, []) - - // Callbacks - const navigationContainerOnReady = useCallback( - () => - (routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name), - [] - ) - const navigationContainerOnStateChange = useCallback(() => { - const previousRouteName = routeNameRef.current - const currentRouteName = navigationRef.current?.getCurrentRoute()?.name - - if (previousRouteName !== currentRouteName) { - Analytics.setCurrentScreen(currentRouteName) - } - - routeNameRef.current = currentRouteName - }, []) - const localAccount = useSelector(getLocalAccount) - const tabNavigatorScreenOptions = useCallback( - ({ route }): BottomTabNavigationOptions => ({ - tabBarIcon: ({ - focused, - color, - size - }: { - focused: boolean - color: string - size: number - }) => { - switch (route.name) { - case 'Screen-Local': - return - case 'Screen-Public': - return - case 'Screen-Post': - return - case 'Screen-Notifications': - return - case 'Screen-Me': - return localActiveIndex !== null ? ( - - ) : ( - - ) - default: - return - } - }, - ...(Platform.OS === 'android' && { - tabBarVisible: - getFocusedRouteNameFromRoute(route) !== 'Screen-Shared-Compose' && - getFocusedRouteNameFromRoute(route) !== - 'Screen-Shared-Announcements' && - getFocusedRouteNameFromRoute(route) !== - 'Screen-Shared-ImagesViewer' && - getFocusedRouteNameFromRoute(route) !== 'Screen-Me-Switch' - }) - }), - [localActiveIndex, localAccount] - ) - const tabNavigatorTabBarOptions = useMemo( - () => ({ - activeTintColor: theme.primary, - inactiveTintColor: - localActiveIndex !== null ? theme.secondary : theme.disabled, - showLabel: false, - ...(Platform.OS === 'android' && { keyboardHidesTabBar: true }) - }), - [theme, localActiveIndex] - ) - const tabScreenLocalListeners = useCallback( - () => ({ - tabPress: (e: any) => { - if (!(localActiveIndex !== null)) { - e.preventDefault() - } - } - }), - [localActiveIndex] - ) - const tabScreenComposeListeners = useMemo( - () => ({ - tabPress: (e: any) => { - e.preventDefault() - if (localActiveIndex !== null) { - haptics('Medium') - navigationRef.current?.navigate('Screen-Shared-Compose') - } - } - }), - [localActiveIndex] - ) - const tabScreenComposeComponent = useCallback(() => null, []) - const tabScreenNotificationsListeners = useCallback( - () => ({ - tabPress: (e: any) => { - if (!(localActiveIndex !== null)) { - e.preventDefault() - } - } - }), - [localActiveIndex] - ) - return ( - <> - - - - - - - - - - - {Platform.OS === 'ios' ? ( - - ) : null} - - - ) -} - -export default React.memo(Index, () => true) diff --git a/src/Screens.tsx b/src/Screens.tsx new file mode 100644 index 00000000..322e8580 --- /dev/null +++ b/src/Screens.tsx @@ -0,0 +1,216 @@ +import client from '@api/client' +import { HeaderLeft } from '@components/Header' +import { toast, toastConfig } from '@components/toast' +import { + NavigationContainer, + NavigationContainerRef +} from '@react-navigation/native' +import ScreenActions from '@screens/Actions' +import ScreenAnnouncements from '@screens/Announcements' +import ScreenCompose from '@screens/Compose' +import ScreenImagesViewer from '@screens/ImagesViewer' +import ScreenTabs from '@screens/Tabs' +import { + getLocalActiveIndex, + localUpdateAccountPreferences +} from '@utils/slices/instancesSlice' +import { useTheme } from '@utils/styles/ThemeManager' +import { themes } from '@utils/styles/themes' +import * as Analytics from 'expo-firebase-analytics' +import React, { createRef, useCallback, useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { Platform, StatusBar } from 'react-native' +import Toast from 'react-native-toast-message' +import { createSharedElementStackNavigator } from 'react-navigation-shared-element' +import { useDispatch, useSelector } from 'react-redux' + +const Stack = createSharedElementStackNavigator() + +export interface Props { + localCorrupt?: string +} + +export const navigationRef = createRef() + +const Index: React.FC = ({ localCorrupt }) => { + const dispatch = useDispatch() + const localActiveIndex = useSelector(getLocalActiveIndex) + const { mode, theme } = useTheme() + enum barStyle { + light = 'dark-content', + dark = 'light-content' + } + + const routeNameRef = useRef() + + // const isConnected = useNetInfo().isConnected + // const [firstRender, setFirstRender] = useState(false) + // useEffect(() => { + // if (firstRender) { + // // bug in netInfo on first render as false + // if (isConnected !== false) { + // toast({ type: 'error', content: 'ζ‰‹ζœΊπŸˆšοΈη½‘η»œ', autoHide: false }) + // } + // } else { + // setFirstRender(true) + // } + // }, [isConnected, firstRender]) + + // On launch display login credentials corrupt information + const { t } = useTranslation('common') + useEffect(() => { + const showLocalCorrect = localCorrupt + ? toast({ + type: 'error', + message: t('index.localCorrupt'), + description: localCorrupt.length ? localCorrupt : undefined, + autoHide: false + }) + : undefined + return showLocalCorrect + }, [localCorrupt]) + + // On launch check if there is any unread announcements + useEffect(() => { + localActiveIndex !== null && + client({ + method: 'get', + instance: 'local', + url: `announcements` + }) + .then(res => { + if (res?.filter(announcement => !announcement.read).length) { + navigationRef.current?.navigate('Screen-Announcements', { + showAll: false + }) + } + }) + .catch(() => {}) + }, []) + + // Lazily update users's preferences, for e.g. composing default visibility + useEffect(() => { + if (localActiveIndex !== null) { + dispatch(localUpdateAccountPreferences()) + } + }, []) + + // Callbacks + const navigationContainerOnReady = useCallback( + () => + (routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name), + [] + ) + const navigationContainerOnStateChange = useCallback(() => { + const previousRouteName = routeNameRef.current + const currentRouteName = navigationRef.current?.getCurrentRoute()?.name + + if (previousRouteName !== currentRouteName) { + Analytics.setCurrentScreen(currentRouteName) + } + + routeNameRef.current = currentRouteName + }, []) + + return ( + <> + + + + + + ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1] + }) + } + }) + }} + /> + ( + navigationRef.current?.goBack()} + /> + ), + animationTypeForReplace: 'pop', + cardStyle: { backgroundColor: 'transparent' }, + cardStyleInterpolator: ({ current: { progress } }) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1] + }) + } + }) + }} + /> + + ( + navigationRef.current?.goBack()} + /> + ), + cardStyle: { backgroundColor: 'transparent' }, + cardStyleInterpolator: ({ current: { progress } }) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1] + }) + } + }) + }} + sharedElements={route => { + const { imageIndex, imageUrls } = route.params + return [{ id: `image.${imageUrls[imageIndex].url}`, debug: true }] + }} + /> + + + {Platform.OS === 'ios' ? ( + + ) : null} + + + ) +} + +export default React.memo(Index, () => true) diff --git a/src/components/Account.tsx b/src/components/Account.tsx index 8ad34895..dfff5f0f 100644 --- a/src/components/Account.tsx +++ b/src/components/Account.tsx @@ -21,12 +21,12 @@ const ComponentAccount: React.FC = ({ }) => { const { theme } = useTheme() const navigation = useNavigation< - StackNavigationProp + StackNavigationProp >() const onPress = useCallback(() => { analytics('search_account_press', { page: origin }) - navigation.push('Screen-Shared-Account', { account }) + navigation.push('Tab-Shared-Account', { account }) }, []) return ( diff --git a/src/components/BottomSheet.tsx b/src/components/BottomSheet.tsx deleted file mode 100644 index 52feccb2..00000000 --- a/src/components/BottomSheet.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React from 'react' -import { Dimensions, Modal, StyleSheet, View } from 'react-native' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTheme } from '@utils/styles/ThemeManager' -import { StyleConstants } from '@utils/styles/constants' -import Button from '@components/Button' -import { - PanGestureHandler, - State, - TapGestureHandler -} from 'react-native-gesture-handler' -import Animated, { - Extrapolate, - interpolate, - runOnJS, - useAnimatedGestureHandler, - useAnimatedStyle, - useSharedValue, - withTiming -} from 'react-native-reanimated' -import analytics from './analytics' - -export interface Props { - children: React.ReactNode - visible: boolean - handleDismiss: () => void -} - -const BottomSheet: React.FC = ({ children, visible, handleDismiss }) => { - const { theme } = useTheme() - const insets = useSafeAreaInsets() - - const screenHeight = Dimensions.get('screen').height - const panY = useSharedValue(0) - const styleTop = useAnimatedStyle(() => { - return { - top: interpolate( - panY.value, - [0, screenHeight], - [0, screenHeight], - Extrapolate.CLAMP - ) - } - }) - const callDismiss = () => { - analytics('bottomsheet_swipe_close') - handleDismiss() - } - const onGestureEvent = useAnimatedGestureHandler({ - onActive: ({ translationY }) => { - panY.value = translationY - }, - onEnd: ({ velocityY }) => { - if (velocityY > 500) { - runOnJS(callDismiss)() - } else { - panY.value = withTiming(0) - } - } - }) - - return ( - - { - if (nativeEvent.state === State.ACTIVE) { - callDismiss() - } - }} - > - - - - - {children} -