diff --git a/README.md b/README.md index ed0da06d..b1bcf7e3 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,20 @@ Please **do not** create a pull request to update translation. tooot's translati ## Special thanks -[@amrtf](https://crowdin.com/profile/amrtf) for Spanish translation - -[@pat](https://piaille.fr/@pat) for French translation +[@amrtf](https://crowdin.com/profile/amrtf) for Catalan and Spanish translation [@forenta](https://github.com/forenta) for German translation +[@pat](https://piaille.fr/@pat) for French translation + [@andrigamerita](https://github.com/andrigamerita) for Italian translation [@Hikaru](https://github.com/Hikali-47041) and [@la_la](https://mstdn.jp/@la_la_la) for Japanese translation [@hellojaccc](https://github.com/hellojaccc) for Korean translation +[@jan-vandenberg](https://crowdin.com/profile/jan-vandenberg) for Dutch translation + [@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese [@janlindblom](https://github.com/janlindblom) for Swedish diff --git a/android/app/build.gradle b/android/app/build.gradle index a2231784..9826e70f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -337,5 +337,3 @@ def isNewArchitectureEnabled() { // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" } - -apply plugin: 'com.google.gms.google-services' diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index d833db99..00000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "project_info": { - "project_number": "661638997772", - "project_id": "xmflsct-mastodon-app", - "storage_bucket": "xmflsct-mastodon-app.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:661638997772:android:4fd02851f757f8fa9f8b29", - "android_client_info": { - "package_name": "com.xmflsct.app.tooot" - } - }, - "oauth_client": [ - { - "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDUw4s-mhQsHvs4hdIsldsi68ZIygM5MC4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "661638997772-sqa4raeghhrieqt9guljhcul9b51dvna.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.xmflsct.app.mastodon" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 0872442c..e90d3c65 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,6 @@ buildscript { jcenter() } dependencies { - classpath 'com.google.gms:google-services:4.3.3' classpath("com.android.tools.build:gradle:7.2.1") classpath("com.facebook.react:react-native-gradle-plugin") classpath("de.undercouch:gradle-download-task:5.0.1") diff --git a/app.config.ts b/app.config.ts index 4b34421b..0afae03f 100644 --- a/app.config.ts +++ b/app.config.ts @@ -15,7 +15,6 @@ export default (): ExpoConfig => ({ }, android: { package: 'com.xmflsct.app.tooot', - googleServicesFile: './configs/google-services.json', permissions: ['CAMERA', 'VIBRATE'], blockedPermissions: ['USE_BIOMETRIC', 'USE_FINGERPRINT'] }, diff --git a/configs/GoogleService-Info.plist b/configs/GoogleService-Info.plist deleted file mode 100644 index 553df7ab..00000000 --- a/configs/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - 661638997772-65g8ce369ugck3ii4ulk6jhb3ijg51kl.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.661638997772-65g8ce369ugck3ii4ulk6jhb3ijg51kl - API_KEY - AIzaSyAOS1Yq_uNVctG89LB6Dl1PVhb_FAQRbRg - GCM_SENDER_ID - 661638997772 - PLIST_VERSION - 1 - BUNDLE_ID - com.xmflsct.app.tooot - PROJECT_ID - xmflsct-mastodon-app - STORAGE_BUCKET - xmflsct-mastodon-app.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:661638997772:ios:c8d2e09264a344b09f8b29 - - \ No newline at end of file diff --git a/configs/google-services.json b/configs/google-services.json deleted file mode 100644 index d833db99..00000000 --- a/configs/google-services.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "project_info": { - "project_number": "661638997772", - "project_id": "xmflsct-mastodon-app", - "storage_bucket": "xmflsct-mastodon-app.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:661638997772:android:4fd02851f757f8fa9f8b29", - "android_client_info": { - "package_name": "com.xmflsct.app.tooot" - } - }, - "oauth_client": [ - { - "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDUw4s-mhQsHvs4hdIsldsi68ZIygM5MC4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "661638997772-sqa4raeghhrieqt9guljhcul9b51dvna.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.xmflsct.app.mastodon" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 98b9127d..ce23340c 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -15,12 +15,6 @@ target 'tooot' do # Flags change depending on the env values. flags = get_default_flags() - # https://stackoverflow.com/questions/72289521/swift-pods-cannot-yet-be-integrated-as-static-libraries-firebasecoreinternal-lib/72969220#72969220 - pod 'Firebase', :modular_headers => true - pod 'FirebaseCore', :modular_headers => true - pod 'GoogleUtilities', :modular_headers => true - $RNFirebaseAsStaticFramework = true - use_react_native!( :path => config[:reactNativePath], :hermes_enabled => true, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c57eaa54..82d8d8f0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,7 +3,7 @@ PODS: - DoubleConversion (1.1.6) - EXApplication (5.0.1): - ExpoModulesCore - - EXAV (13.0.1): + - EXAV (13.0.2): - ExpoModulesCore - ReactCommon/turbomodule/core - EXConstants (14.0.2): @@ -12,18 +12,11 @@ PODS: - ExpoModulesCore - EXFileSystem (15.1.1): - ExpoModulesCore - - EXFirebaseAnalytics (8.0.0): - - EXFirebaseCore - - ExpoModulesCore - - Firebase/Core (= 9.5.0) - - EXFirebaseCore (6.0.0): - - ExpoModulesCore - - Firebase/Core (= 9.5.0) - EXFont (11.0.1): - ExpoModulesCore - EXNotifications (0.17.0): - ExpoModulesCore - - Expo (47.0.7): + - Expo (47.0.8): - ExpoModulesCore - ExpoCrypto (12.0.0): - ExpoModulesCore @@ -59,107 +52,8 @@ PODS: - React-Core (= 0.70.6) - React-jsi (= 0.70.6) - ReactCommon/turbomodule/core (= 0.70.6) - - Firebase (9.5.0): - - Firebase/Core (= 9.5.0) - - Firebase/Core (9.5.0): - - Firebase/CoreOnly - - FirebaseAnalytics (~> 9.5.0) - - Firebase/CoreOnly (9.5.0): - - FirebaseCore (= 9.5.0) - - FirebaseAnalytics (9.5.0): - - FirebaseAnalytics/AdIdSupport (= 9.5.0) - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseAnalytics/AdIdSupport (9.5.0): - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleAppMeasurement (= 9.5.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCore (9.5.0): - - FirebaseCoreDiagnostics (~> 9.0) - - FirebaseCoreInternal (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - FirebaseCoreDiagnostics (9.6.0): - - GoogleDataTransport (< 10.0.0, >= 9.1.4) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCoreInternal (9.6.0): - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - FirebaseInstallations (9.6.0): - - FirebaseCore (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/UserDefaults (~> 7.7) - - PromisesObjC (~> 2.1) - fmt (6.2.1) - glog (0.3.5) - - GoogleAppMeasurement (9.5.0): - - GoogleAppMeasurement/AdIdSupport (= 9.5.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (9.5.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 9.5.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (9.5.0): - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleDataTransport (9.2.0): - - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities (7.10.0): - - GoogleUtilities/AppDelegateSwizzler (= 7.10.0) - - GoogleUtilities/Environment (= 7.10.0) - - GoogleUtilities/ISASwizzler (= 7.10.0) - - GoogleUtilities/Logger (= 7.10.0) - - GoogleUtilities/MethodSwizzler (= 7.10.0) - - GoogleUtilities/Network (= 7.10.0) - - "GoogleUtilities/NSData+zlib (= 7.10.0)" - - GoogleUtilities/Reachability (= 7.10.0) - - GoogleUtilities/SwizzlerTestHelpers (= 7.10.0) - - GoogleUtilities/UserDefaults (= 7.10.0) - - GoogleUtilities/AppDelegateSwizzler (7.10.0): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (7.10.0): - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/ISASwizzler (7.10.0) - - GoogleUtilities/Logger (7.10.0): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.10.0): - - GoogleUtilities/Logger - - GoogleUtilities/Network (7.10.0): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.10.0)" - - GoogleUtilities/Reachability (7.10.0): - - GoogleUtilities/Logger - - GoogleUtilities/SwizzlerTestHelpers (7.10.0): - - GoogleUtilities/MethodSwizzler - - GoogleUtilities/UserDefaults (7.10.0): - - GoogleUtilities/Logger - hermes-engine (0.70.6) - libevent (2.1.12) - libwebp (1.2.4): @@ -171,12 +65,6 @@ PODS: - libwebp/mux (1.2.4): - libwebp/demux - libwebp/webp (1.2.4) - - nanopb (2.30909.0): - - nanopb/decode (= 2.30909.0) - - nanopb/encode (= 2.30909.0) - - nanopb/decode (2.30909.0) - - nanopb/encode (2.30909.0) - - PromisesObjC (2.1.1) - RCT-Folly (2021.07.22.00): - boost - DoubleConversion @@ -409,17 +297,19 @@ PODS: - React-Core - react-native-cameraroll (5.1.0): - React-Core - - react-native-context-menu-view (1.5.4): - - React - - react-native-image-picker (4.10.1): + - react-native-image-picker (4.10.2): + - React-Core + - react-native-ios-context-menu (1.15.1): - React-Core - react-native-language-detection (0.1.0): - React - react-native-live-text-image-view (0.4.0): - React-Core - - react-native-netinfo (9.3.6): + - react-native-menu (0.7.2): + - React + - react-native-netinfo (9.3.7): - React-Core - - react-native-pager-view (6.1.1): + - react-native-pager-view (6.1.2): - React-Core - react-native-paste-input (0.5.1): - React-Core @@ -538,9 +428,9 @@ PODS: - RNScreens (3.18.2): - React-Core - React-RCTImage - - RNSentry (4.8.0): + - RNSentry (4.10.1): - React-Core - - Sentry (= 7.29.0) + - Sentry/HybridSDK (= 7.31.2) - RNShareMenu (6.0.0): - React - RNSVG (13.6.0): @@ -551,9 +441,7 @@ PODS: - SDWebImageWebPCoder (0.9.1): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.13) - - Sentry (7.29.0): - - Sentry/Core (= 7.29.0) - - Sentry/Core (7.29.0) + - Sentry/HybridSDK (7.31.2) - Swime (3.0.6) - Yoga (1.14.0) @@ -565,8 +453,6 @@ DEPENDENCIES: - EXConstants (from `../node_modules/expo-constants/ios`) - EXErrorRecovery (from `../node_modules/expo-error-recovery/ios`) - EXFileSystem (from `../node_modules/expo-file-system/ios`) - - EXFirebaseAnalytics (from `../node_modules/expo-firebase-analytics/ios`) - - EXFirebaseCore (from `../node_modules/expo-firebase-core/ios`) - EXFont (from `../node_modules/expo-font/ios`) - EXNotifications (from `../node_modules/expo-notifications/ios`) - Expo (from `../node_modules/expo`) @@ -584,10 +470,7 @@ DEPENDENCIES: - EXVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - - Firebase - - FirebaseCore - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - GoogleUtilities - hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`) - libevent (~> 2.1.12) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) @@ -609,10 +492,11 @@ DEPENDENCIES: - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-blurhash (from `../node_modules/react-native-blurhash`) - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)" - - react-native-context-menu-view (from `../node_modules/react-native-context-menu-view`) - react-native-image-picker (from `../node_modules/react-native-image-picker`) + - react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`) - react-native-language-detection (from `../node_modules/react-native-language-detection`) - react-native-live-text-image-view (from `../node_modules/react-native-live-text-image-view`) + - "react-native-menu (from `../node_modules/@react-native-menu/menu`)" - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-pager-view (from `../node_modules/react-native-pager-view`) - "react-native-paste-input (from `../node_modules/@mattermost/react-native-paste-input`)" @@ -643,20 +527,9 @@ DEPENDENCIES: SPEC REPOS: trunk: - - Firebase - - FirebaseAnalytics - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreInternal - - FirebaseInstallations - fmt - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleUtilities - libevent - libwebp - - nanopb - - PromisesObjC - SDWebImage - SDWebImageWebPCoder - Sentry @@ -677,10 +550,6 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-error-recovery/ios" EXFileSystem: :path: "../node_modules/expo-file-system/ios" - EXFirebaseAnalytics: - :path: "../node_modules/expo-firebase-analytics/ios" - EXFirebaseCore: - :path: "../node_modules/expo-firebase-core/ios" EXFont: :path: "../node_modules/expo-font/ios" EXNotifications: @@ -755,14 +624,16 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-blurhash" react-native-cameraroll: :path: "../node_modules/@react-native-camera-roll/camera-roll" - react-native-context-menu-view: - :path: "../node_modules/react-native-context-menu-view" react-native-image-picker: :path: "../node_modules/react-native-image-picker" + react-native-ios-context-menu: + :path: "../node_modules/react-native-ios-context-menu" react-native-language-detection: :path: "../node_modules/react-native-language-detection" react-native-live-text-image-view: :path: "../node_modules/react-native-live-text-image-view" + react-native-menu: + :path: "../node_modules/@react-native-menu/menu" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-pager-view: @@ -822,15 +693,13 @@ SPEC CHECKSUMS: boost: a7c83b31436843459a1961bfd74b96033dc77234 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 EXApplication: 034b1c40a8e9fe1bff76a1e511ee90dff64ad834 - EXAV: 766516466675fc5fdd7c500acced5934e8b00de2 + EXAV: 9a45d37772c5329294c054a041dcc39931fc5032 EXConstants: 3c86653c422dd77e40d10cbbabb3025003977415 EXErrorRecovery: ae43433feb0608a64dc5b1c8363b3e7769a9ea24 EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6 - EXFirebaseAnalytics: 58d70e698859b070b2450ad8664d7b5bc6c6e3e1 - EXFirebaseCore: d0d88cb904e893af07f809ab08c0892489bc6956 EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80 EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a - Expo: a37d568e9ae87645b74ed597dd0f0fd89e2daf2d + Expo: 36b5f625d36728adbdd1934d4d57182f319ab832 ExpoCrypto: 51e7662c7f5bfeab25b7909b8a5d545ec15d4877 ExpoHaptics: 5a56d30a87ea213dd00b09566dc4b441a4dff97f ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318 @@ -845,22 +714,11 @@ SPEC CHECKSUMS: EXVideoThumbnails: 8b3e48f3716679dd0cbf949217a31eab5c555799 FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4 FBReactNativeSpec: dd1186fd05255e3457baa2f4ca65e94c2cd1e3ac - Firebase: 800f16f07af493d98d017446a315c27af0552f41 - FirebaseAnalytics: 1b60984a408320dda637306f3f733699ef8473d7 - FirebaseCore: 25c0400b670fd1e2f2104349cd3b5dcce8d9418f - FirebaseCoreDiagnostics: 99a495094b10a57eeb3ae8efa1665700ad0bdaa6 - FirebaseCoreInternal: bca76517fe1ed381e989f5e7d8abb0da8d85bed3 - FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - GoogleAppMeasurement: 6ee231473fbd75c11221dfce489894334024eead - GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f - GoogleUtilities: bad72cb363809015b1f7f19beb1f1cd23c589f95 hermes-engine: 2af7b7a59128f250adfd86f15aa1d5a2ecd39995 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef - nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 - PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda RCTRequired: e1866f61af7049eb3d8e08e8b133abd38bc1ca7a RCTTypeSafety: 27c2ac1b00609a432ced1ae701247593f07f901e @@ -879,12 +737,13 @@ SPEC CHECKSUMS: react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3 react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7 react-native-cameraroll: a40b082318eb1ecd0336a2f29d9f74b7f2c8cae8 - react-native-context-menu-view: b0beca02aad4bd9f9d7d932bf437e0a03baa69ef - react-native-image-picker: f2ab1215d17bcfe27b0eb6417cc236fd1f4775e7 + react-native-image-picker: bf34f3f516d139ed3e24c5f5a381a91819e349ea + react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac react-native-language-detection: 0e43195ad014974f1b7a31b64820eff34a243f2d react-native-live-text-image-view: 483bacfdba464162b8cf176bba555364f18b584c - react-native-netinfo: f80db8cac2151405633324cb645c60af098ee461 - react-native-pager-view: 3c66c4e2f3ab423643d07b2c7041f8ac48395f72 + react-native-menu: 8e172cfcf0e42e92f028e7781eddf84d430cae24 + react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983 + react-native-pager-view: 54bed894cecebe28cede54c01038d9d1e122de43 react-native-paste-input: 183ad7dc224e192719616f4258dde5b548627d08 react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097 @@ -906,15 +765,15 @@ SPEC CHECKSUMS: RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3 RNReanimated: ce445c233a6ff5600223484a88ad5704945d972a RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d - RNSentry: db7fd7b66efda28885e4e904a8b5e7349aec61c1 + RNSentry: 3c27f3c57f16bab9835d9555add298571077e0c1 RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3 RNSVG: 3a79c0c4992213e4f06c08e62730c5e7b9e4dc17 SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0 - Sentry: 4272663eb0eda312024d795ca3f5a562a8ce5e18 + Sentry: b15765d11769852fe78c9add942f7df60ed5dbf5 Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b Yoga: 99caf8d5ab45e9d637ee6e0174ec16fbbb01bcfc -PODFILE CHECKSUM: e4191b63c8f15031b2365226730770e7978dca41 +PODFILE CHECKSUM: 05bf71d31ba782dfda5a6b47d38e98a6f6bc079a COCOAPODS: 1.11.3 diff --git a/ios/ca.lproj/InfoPlist.strings b/ios/ca.lproj/InfoPlist.strings new file mode 100644 index 00000000..873ce7f9 --- /dev/null +++ b/ios/ca.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"NSPhotoLibraryAddUsageDescription" = "Permet que tooot desi imatges al carret de la càmera"; +"NSPhotoLibraryUsageDescription" = "Permet que tooot desi imatges al carret de la càmera"; diff --git a/ios/nl.lproj/InfoPlist.strings b/ios/nl.lproj/InfoPlist.strings new file mode 100644 index 00000000..8251d31e --- /dev/null +++ b/ios/nl.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"NSPhotoLibraryAddUsageDescription" = "Sta tooot toe om afbeeldingen op te slaan in je filmrol"; +"NSPhotoLibraryUsageDescription" = "Sta tooot toe om afbeeldingen op te slaan in je filmrol"; diff --git a/ios/tooot.xcodeproj/project.pbxproj b/ios/tooot.xcodeproj/project.pbxproj index 6af87ad9..6d8698dd 100644 --- a/ios/tooot.xcodeproj/project.pbxproj +++ b/ios/tooot.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 5EE088C926297820007E5FEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EE088CB26297820007E5FEC /* InfoPlist.strings */; }; 5EE44DD62600124E00A9BCED /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EE44DD52600124E00A9BCED /* File.swift */; }; 96905EF65AED1B983A6B3ABC /* libPods-tooot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */; }; - DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */; }; E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */; }; E613A80B28282A01003C97D6 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = E613A80A28282A01003C97D6 /* AppDelegate.mm */; }; E633A42B281EAEAB000E540F /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E633A420281EAEAB000E540F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -69,9 +68,9 @@ 7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-tooot.release.xcconfig"; path = "Target Support Files/Pods-tooot/Pods-tooot.release.xcconfig"; sourceTree = ""; }; 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-tooot/ExpoModulesProvider.swift"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = ""; }; - B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "tooot/GoogleService-Info.plist"; sourceTree = ""; }; DF8133F098604A10B0D94952 /* boop.mp3 */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = boop.mp3; path = tooot/boop.mp3; sourceTree = ""; }; E613A80A28282A01003C97D6 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = tooot/AppDelegate.mm; sourceTree = ""; }; + E6217B7E293C1EBF00B1755E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; E633A420281EAEAB000E540F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; E633A427281EAEAB000E540F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E633A42F281EAF38000E540F /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = ""; }; @@ -85,6 +84,7 @@ E69EBACC28DF28420057EDEC /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; E69EBACD28DF284D0057EDEC /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; E69EBACE28DF28560057EDEC /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = ""; }; + E6A4895D293C1F740047951A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = ""; }; E6C8B26628F5F9FC0062CF2E /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; @@ -122,7 +122,6 @@ 13B07FB71A68108700A75B9A /* main.m */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */, - B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */, 5EE088CB26297820007E5FEC /* InfoPlist.strings */, DF8133F098604A10B0D94952 /* boop.mp3 */, ); @@ -297,6 +296,8 @@ fr, es, sv, + nl, + ca, ); mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; @@ -319,7 +320,6 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, - DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */, 4986628FD0DD4630BFE5F388 /* boop.mp3 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -528,6 +528,8 @@ E66C0842291F095800DFFF60 /* fr */, E690AF692926B737002C38A8 /* es */, E63E7FF0292A828100C76FD4 /* sv */, + E6217B7E293C1EBF00B1755E /* nl */, + E6A4895D293C1F740047951A /* ca */, ); name = InfoPlist.strings; sourceTree = ""; diff --git a/ios/tooot/GoogleService-Info.plist b/ios/tooot/GoogleService-Info.plist deleted file mode 100644 index 553df7ab..00000000 --- a/ios/tooot/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - 661638997772-65g8ce369ugck3ii4ulk6jhb3ijg51kl.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.661638997772-65g8ce369ugck3ii4ulk6jhb3ijg51kl - API_KEY - AIzaSyAOS1Yq_uNVctG89LB6Dl1PVhb_FAQRbRg - GCM_SENDER_ID - 661638997772 - PLIST_VERSION - 1 - BUNDLE_ID - com.xmflsct.app.tooot - PROJECT_ID - xmflsct-mastodon-app - STORAGE_BUCKET - xmflsct-mastodon-app.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:661638997772:ios:c8d2e09264a344b09f8b29 - - \ No newline at end of file diff --git a/package.json b/package.json index f40d4351..12581c74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.6.4", + "version": "4.6.5", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", @@ -19,35 +19,35 @@ }, "dependencies": { "@expo/react-native-action-sheet": "^4.0.1", - "@formatjs/intl-datetimeformat": "^6.3.1", - "@formatjs/intl-getcanonicallocales": "^2.0.4", - "@formatjs/intl-locale": "^3.0.7", - "@formatjs/intl-numberformat": "^8.2.0", - "@formatjs/intl-pluralrules": "^5.1.4", - "@formatjs/intl-relativetimeformat": "^11.1.4", + "@formatjs/intl-datetimeformat": "^6.4.3", + "@formatjs/intl-getcanonicallocales": "^2.0.5", + "@formatjs/intl-locale": "^3.0.11", + "@formatjs/intl-numberformat": "^8.3.3", + "@formatjs/intl-pluralrules": "^5.1.8", + "@formatjs/intl-relativetimeformat": "^11.1.8", "@mattermost/react-native-paste-input": "^0.5.1", "@neverdull-agency/expo-unlimited-secure-store": "^1.0.10", "@react-native-async-storage/async-storage": "~1.17.11", "@react-native-camera-roll/camera-roll": "^5.1.0", "@react-native-clipboard/clipboard": "^1.11.1", "@react-native-community/blur": "^4.3.0", - "@react-native-community/netinfo": "9.3.6", + "@react-native-community/netinfo": "9.3.7", "@react-native-community/segmented-control": "^2.2.2", - "@react-navigation/bottom-tabs": "^6.4.1", - "@react-navigation/native": "^6.0.14", - "@react-navigation/native-stack": "^6.9.2", - "@react-navigation/stack": "^6.3.5", - "@reduxjs/toolkit": "^1.9.0", - "@sentry/react-native": "4.8.0", - "@sharcoux/slider": "^6.0.3", + "@react-native-menu/menu": "^0.7.2", + "@react-navigation/bottom-tabs": "^6.4.3", + "@react-navigation/native": "^6.0.16", + "@react-navigation/native-stack": "^6.9.4", + "@react-navigation/stack": "^6.3.7", + "@reduxjs/toolkit": "^1.9.1", + "@sentry/react-native": "4.10.1", + "@sharcoux/slider": "^6.1.1", "axios": "^0.27.2", - "expo": "^47.0.7", - "expo-auth-session": "^3.7.2", - "expo-av": "^13.0.1", + "expo": "^47.0.8", + "expo-auth-session": "^3.7.3", + "expo-av": "^13.0.2", "expo-constants": "^14.0.2", "expo-crypto": "^12.0.0", "expo-file-system": "^15.1.1", - "expo-firebase-analytics": "^8.0.0", "expo-haptics": "^12.0.1", "expo-linking": "^3.2.3", "expo-localization": "^14.0.0", @@ -66,21 +66,21 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^12.0.0", - "react-intl": "^6.2.1", + "react-intl": "^6.2.5", "react-native": "0.70.6", "react-native-animated-spinkit": "^1.5.2", "react-native-base64": "^0.2.1", "react-native-blurhash": "^1.1.10", - "react-native-context-menu-view": "xmflsct/react-native-context-menu-view", "react-native-fast-image": "^8.6.3", "react-native-feather": "^1.1.2", "react-native-flash-message": "^0.3.1", "react-native-gesture-handler": "~2.8.0", "react-native-htmlview": "^0.16.0", - "react-native-image-picker": "^4.10.1", + "react-native-image-picker": "^4.10.2", + "react-native-ios-context-menu": "^1.15.1", "react-native-language-detection": "^0.1.0", "react-native-live-text-image-view": "^0.4.0", - "react-native-pager-view": "^6.1.1", + "react-native-pager-view": "^6.1.2", "react-native-reanimated": "^2.13.0", "react-native-reanimated-zoom": "^0.3.3", "react-native-safe-area-context": "^4.4.1", @@ -88,24 +88,25 @@ "react-native-share-menu": "^6.0.0", "react-native-svg": "^13.6.0", "react-native-swipe-list-view": "^3.2.9", - "react-native-tab-view": "^3.3.0", + "react-native-tab-view": "^3.3.2", "react-query": "^3.39.2", "react-redux": "^8.0.5", "redux-persist": "^6.0.0", "rn-placeholder": "^3.0.3", - "valid-url": "^1.0.9" + "valid-url": "^1.0.9", + "zeego": "^0.5.0" }, "devDependencies": { - "@babel/core": "^7.20.2", + "@babel/core": "^7.20.5", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@expo/config": "^7.0.3", "@types/linkify-it": "^3.0.2", - "@types/lodash": "^4.14.189", - "@types/react": "~18.0.25", + "@types/lodash": "^4.14.191", + "@types/react": "~18.0.26", "@types/react-dom": "~18.0.9", - "@types/react-native": "~0.70.6", + "@types/react-native": "~0.70.7", "@types/react-native-base64": "^0.2.0", "@types/react-native-share-menu": "^5.0.2", "@types/react-timeago": "^4.1.3", diff --git a/src/@types/mastodon.d.ts b/src/@types/mastodon.d.ts index 0e452f3a..54bc1571 100644 --- a/src/@types/mastodon.d.ts +++ b/src/@types/mastodon.d.ts @@ -274,6 +274,7 @@ declare namespace Mastodon { type List = { id: string title: string + replies_policy: 'none' | 'list' | 'followed' } type Instance = { diff --git a/src/App.tsx b/src/App.tsx index 2f26fdb8..6575b3e1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,4 @@ import { ActionSheetProvider } from '@expo/react-native-action-sheet' -import getLanguage from '@helpers/getLanguage' import queryClient from '@helpers/queryClient' import i18n from '@root/i18n/i18n' import Screens from '@root/Screens' @@ -13,11 +12,12 @@ import timezone from '@root/startup/timezone' import { persistor, store } from '@root/store' import * as Sentry from '@sentry/react-native' import AccessibilityManager from '@utils/accessibility/AccessibilityManager' -import { changeLanguage } from '@utils/slices/settingsSlice' +import { changeLanguage, getSettingsLanguage } from '@utils/slices/settingsSlice' import ThemeManager from '@utils/styles/ThemeManager' import * as Localization from 'expo-localization' import * as SplashScreen from 'expo-splash-screen' import React, { useCallback, useEffect, useState } from 'react' +import { IntlProvider } from 'react-intl' import { LogBox, Platform } from 'react-native' import { GestureHandlerRootView } from 'react-native-gesture-handler' import { SafeAreaProvider } from 'react-native-safe-area-context' @@ -85,7 +85,7 @@ const App: React.FC = () => { if (bootstrapped) { log('log', 'App', 'loading actual app :)') log('log', 'App', `Locale: ${Localization.locale}`) - const language = getLanguage() + const language = getSettingsLanguage(store.getState()) if (!language) { if (Platform.OS !== 'ios') { store.dispatch(changeLanguage('en')) @@ -96,15 +96,17 @@ const App: React.FC = () => { } return ( - - - - - - - - - + + + + + + + + + + + ) } else { return null diff --git a/src/Screens.tsx b/src/Screens.tsx index 82e09f8c..c88da00b 100644 --- a/src/Screens.tsx +++ b/src/Screens.tsx @@ -1,4 +1,3 @@ -import analytics from '@components/analytics' import { HeaderLeft } from '@components/Header' import { displayMessage, Message } from '@components/Message' import navigationRef from '@helpers/navigationRef' @@ -28,7 +27,6 @@ import * as Linking from 'expo-linking' import { addScreenshotListener } from 'expo-screen-capture' import React, { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { IntlProvider } from 'react-intl' import { Alert, Platform, StatusBar } from 'react-native' import ShareMenu from 'react-native-share-menu' import { useSelector } from 'react-redux' @@ -113,7 +111,6 @@ const Screens: React.FC = ({ localCorrupt }) => { } if (previousRoute?.name !== currentRoute?.name) { - analytics('screen_view', { screen_name: currentRoute?.name }) Sentry.setContext('page', { previous: previousRoute, current: currentRoute @@ -273,7 +270,7 @@ const Screens: React.FC = ({ localCorrupt }) => { }, []) return ( - + <> = ({ localCorrupt }) => { - + ) } diff --git a/src/api/handleError.ts b/src/api/handleError.ts index 6f984b78..efbff696 100644 --- a/src/api/handleError.ts +++ b/src/api/handleError.ts @@ -7,30 +7,23 @@ const handleError = (error: any) => { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.error( - ctx.bold(' API instance '), + ctx.bold(' API '), ctx.bold('response'), error.response.status, error?.response.data?.error || error?.response.message || 'Unknown error' ) return Promise.reject({ status: error?.response.status, - message: - error?.response.data?.error || - error?.response.message || - 'Unknown error' + message: error?.response.data?.error || error?.response.message || 'Unknown error' }) } else if (error?.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js - console.error(ctx.bold(' API instance '), ctx.bold('request'), error) + console.error(ctx.bold(' API '), ctx.bold('request'), error) return Promise.reject() } else { - console.error( - ctx.bold(' API instance '), - ctx.bold('internal'), - error?.message - ) + console.error(ctx.bold(' API '), ctx.bold('internal'), error?.message) return Promise.reject() } } diff --git a/src/api/instance.ts b/src/api/instance.ts index 6df9974d..0126e4b1 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -13,10 +13,7 @@ export type Params = { } headers?: { [key: string]: string } body?: FormData - extras?: Omit< - AxiosRequestConfig, - 'method' | 'url' | 'params' | 'headers' | 'data' - > + extras?: Omit } export type InstanceResponse = { @@ -35,9 +32,7 @@ const apiInstance = async ({ }: Params): Promise> => { const { store } = require('@root/store') const state = store.getState() as RootState - const instanceActive = state.instances.instances.findIndex( - instance => instance.active - ) + const instanceActive = state.instances.instances.findIndex(instance => instance.active) let domain let token @@ -45,21 +40,19 @@ const apiInstance = async ({ domain = state.instances.instances[instanceActive].url token = state.instances.instances[instanceActive].token } else { - console.warn( - ctx.bgRed.white.bold(' API ') + ' ' + 'No instance domain is provided' - ) + console.warn(ctx.bgRed.white.bold(' API ') + ' ' + 'No instance domain is provided') return Promise.reject() } console.log( ctx.bgGreen.bold(' API instance ') + - ' ' + - domain + - ' ' + - method + - ctx.green(' -> ') + - `/${url}` + - (params ? ctx.green(' -> ') : ''), + ' ' + + domain + + ' ' + + method + + ctx.green(' -> ') + + `/${url}` + + (params ? ctx.green(' -> ') : ''), params ? params : '' ) @@ -70,10 +63,7 @@ const apiInstance = async ({ url, params, headers: { - 'Content-Type': - body && body instanceof FormData - ? 'multipart/form-data' - : 'application/json', + 'Content-Type': body && body instanceof FormData ? 'multipart/form-data' : 'application/json', Accept: '*/*', ...userAgent, ...headers, @@ -87,10 +77,10 @@ const apiInstance = async ({ .then(response => { let prev let next - if (response.headers.link) { - const headersLinks = li.parse(response.headers.link) - prev = headersLinks.prev?.match(/_id=([0-9]*)/)[1] - next = headersLinks.next?.match(/_id=([0-9]*)/)[1] + if (response.headers?.link) { + const headersLinks = li.parse(response.headers?.link) + prev = headersLinks.prev?.match(/_id=([0-9]*)/)?.[1] + next = headersLinks.next?.match(/_id=([0-9]*)/)?.[1] } return Promise.resolve({ body: response.data, diff --git a/src/components/Account.tsx b/src/components/Account.tsx index 103ded66..a65827dd 100644 --- a/src/components/Account.tsx +++ b/src/components/Account.tsx @@ -4,49 +4,47 @@ import { StackNavigationProp } from '@react-navigation/stack' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useCallback } from 'react' +import React, { PropsWithChildren } from 'react' import { Pressable, View } from 'react-native' -import analytics from './analytics' import GracefullyImage from './GracefullyImage' import CustomText from './Text' export interface Props { account: Mastodon.Account - onPress?: () => void - origin?: string + Component?: typeof View | typeof Pressable + props?: {} } -const ComponentAccount: React.FC = ({ +const ComponentAccount: React.FC = ({ account, - onPress: customOnPress, - origin + Component, + props, + children }) => { const { colors } = useTheme() - const navigation = - useNavigation>() + const navigation = useNavigation>() - const onPress = useCallback(() => { - analytics('search_account_press', { page: origin }) - navigation.push('Tab-Shared-Account', { account }) - }, []) + if (!props) { + props = { onPress: () => navigation.push('Tab-Shared-Account', { account }) } + } - return ( - + } + }, + = ({ @{account.acct} - + , + children ) } diff --git a/src/components/ContextMenu/account.ts b/src/components/ContextMenu/account.ts deleted file mode 100644 index cd5e4793..00000000 --- a/src/components/ContextMenu/account.ts +++ /dev/null @@ -1,185 +0,0 @@ -import analytics from '@components/analytics' -import { displayMessage } from '@components/Message' -import { useRelationshipQuery } from '@utils/queryHooks/relationship' -import { - MutationVarsTimelineUpdateAccountProperty, - QueryKeyTimeline, - useTimelineMutation -} from '@utils/queryHooks/timeline' -import { getInstanceAccount } from '@utils/slices/instancesSlice' -import { useTheme } from '@utils/styles/ThemeManager' -import { useTranslation } from 'react-i18next' -import { Platform } from 'react-native' -import { ContextMenuAction } from 'react-native-context-menu-view' -import { useQueryClient } from 'react-query' -import { useSelector } from 'react-redux' - -export interface Props { - actions: ContextMenuAction[] - type: 'status' | 'account' // Do not need to fetch relationship in timeline - queryKey?: QueryKeyTimeline - rootQueryKey?: QueryKeyTimeline - id: Mastodon.Account['id'] -} - -const contextMenuAccount = ({ actions, type, queryKey, rootQueryKey, id: accountId }: Props) => { - const { theme } = useTheme() - const { t } = useTranslation('componentContextMenu') - - const queryClient = useQueryClient() - const mutation = useTimelineMutation({ - onSuccess: (_, params) => { - queryClient.refetchQueries(['Relationship', { id: accountId }]) - const theParams = params as MutationVarsTimelineUpdateAccountProperty - displayMessage({ - theme, - type: 'success', - message: t('common:message.success.message', { - function: t(`account.${theParams.payload.property}.action`, { - ...(theParams.payload.property !== 'reports' && { - context: (theParams.payload.currentValue || false).toString() - }) - }) - }) - }) - }, - onError: (err: any, params) => { - const theParams = params as MutationVarsTimelineUpdateAccountProperty - displayMessage({ - theme, - type: 'error', - message: t('common:message.error.message', { - function: t(`account.${theParams.payload.property}.action`, { - ...(theParams.payload.property !== 'reports' && { - context: (theParams.payload.currentValue || false).toString() - }) - }) - }), - ...(err.status && - typeof err.status === 'number' && - err.data && - err.data.error && - typeof err.data.error === 'string' && { - description: err.data.error - }) - }) - }, - onSettled: () => { - queryKey && queryClient.invalidateQueries(queryKey) - rootQueryKey && queryClient.invalidateQueries(rootQueryKey) - } - }) - - const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id) - const ownAccount = instanceAccount?.id === accountId - - const { data: relationship } = useRelationshipQuery({ - id: accountId, - options: { enabled: type === 'account' } - }) - - if (!ownAccount) { - actions.push({ - id: 'account-mute', - title: t('account.mute.action', { - context: (relationship?.muting || false).toString() - }), - systemIcon: 'eye.slash' - }) - switch (Platform.OS) { - case 'ios': - actions.push({ - id: 'account', - title: t('account.title'), - actions: [ - { - id: 'account-block', - title: t('account.block.action', { - context: (relationship?.blocking || false).toString() - }), - systemIcon: 'xmark.circle', - destructive: true - }, - { - id: 'account-reports', - title: t('account.reports.action'), - systemIcon: 'flag', - destructive: true - } - ] - }) - break - default: - actions.push( - { - id: 'account-block', - title: t('account.block.action', { - context: (relationship?.blocking || false).toString() - }), - systemIcon: 'xmark.circle', - destructive: true - }, - { - id: 'account-reports', - title: t('account.reports.action'), - systemIcon: 'flag', - destructive: true - } - ) - break - } - } - - return (index: number) => { - if (typeof index !== 'number' || !actions[index]) { - return // For Android - } - if (actions[index].id === 'account-mute') { - analytics('timeline_shared_headeractions_account_mute_press', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: accountId, - payload: { property: 'mute', currentValue: relationship?.muting } - }) - } - if ( - actions[index].id === 'account-block' || - (actions[index].id === 'account' && actions[index].actions?.[0].id === 'account-block') - ) { - analytics('timeline_shared_headeractions_account_block_press', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: accountId, - payload: { property: 'block', currentValue: relationship?.blocking } - }) - } - if ( - actions[index].id === 'account-reports' || - (actions[index].id === 'account' && actions[index].actions?.[0].id === 'account-reports') - ) { - analytics('timeline_shared_headeractions_account_reports_press', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: accountId, - payload: { property: 'reports' } - }) - mutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: accountId, - payload: { property: 'block', currentValue: false } - }) - } - } -} - -export default contextMenuAccount diff --git a/src/components/ContextMenu/instance.ts b/src/components/ContextMenu/instance.ts index 92df562e..b152779a 100644 --- a/src/components/ContextMenu/instance.ts +++ b/src/components/ContextMenu/instance.ts @@ -1,27 +1,25 @@ -import analytics from '@components/analytics' import { displayMessage } from '@components/Message' import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' import { getInstanceUrl } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' -import { Alert, Platform } from 'react-native' -import { ContextMenuAction } from 'react-native-context-menu-view' +import { Alert } from 'react-native' import { useQueryClient } from 'react-query' import { useSelector } from 'react-redux' -export interface Props { - actions: ContextMenuAction[] - status: Mastodon.Status - queryKey: QueryKeyTimeline +const menuInstance = ({ + status, + queryKey, + rootQueryKey +}: { + status?: Mastodon.Status + queryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline -} +}): ContextMenu[][] => { + if (!status || !queryKey) return [] -const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props) => { - const { t } = useTranslation('componentContextMenu') const { theme } = useTheme() - - const currentInstance = useSelector(getInstanceUrl) - const instance = status?.uri && status.uri.split(new RegExp(/\/\/(.*?)\//))[1] + const { t } = useTranslation('componentContextMenu') const queryClient = useQueryClient() const mutation = useTimelineMutation({ @@ -38,67 +36,48 @@ const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props) } }) + const menus: ContextMenu[][] = [] + + const currentInstance = useSelector(getInstanceUrl) + const instance = status.uri && status.uri.split(new RegExp(/\/\/(.*?)\//))[1] + if (currentInstance !== instance && instance) { - switch (Platform.OS) { - case 'ios': - actions.push({ - id: 'instance', - title: t('instance.title'), - actions: [ - { - id: 'instance-block', - title: t('instance.block.action', { instance }), - destructive: true - } - ] - }) - break - default: - actions.push({ - id: 'instance-block', - title: t('instance.block.action', { instance }), - destructive: true - }) - break - } + menus.push([ + { + key: 'instance-block', + item: { + onSelect: () => + Alert.alert( + t('instance.block.alert.title', { instance }), + t('instance.block.alert.message'), + [ + { + text: t('instance.block.alert.buttons.confirm'), + style: 'destructive', + onPress: () => { + mutation.mutate({ + type: 'domainBlock', + queryKey, + domain: instance + }) + } + }, + { + text: t('common:buttons.cancel') + } + ] + ), + disabled: false, + destructive: true, + hidden: false + }, + title: t('instance.block.action', { instance }), + icon: '' + } + ]) } - return (index: number) => { - if (typeof index !== 'number' || !actions[index]) { - return // For Android - } - if ( - actions[index].id === 'instance-block' || - (actions[index].id === 'instance' && actions[index].actions?.[0].id === 'instance-block') - ) { - analytics('timeline_shared_headeractions_domain_block_press', { - page: queryKey[1].page - }) - Alert.alert( - t('instance.block.alert.title', { instance }), - t('instance.block.alert.message'), - [ - { - text: t('instance.block.alert.buttons.confirm'), - style: 'destructive', - onPress: () => { - analytics('timeline_shared_headeractions_domain_block_confirm', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'domainBlock', - queryKey, - domain: instance - }) - } - }, - { - text: t('common:buttons.cancel') - } - ] - ) - } - } + return menus } -export default contextMenuInstance +export default menuInstance diff --git a/src/components/ContextMenu/share.ts b/src/components/ContextMenu/share.ts index 03401876..961bc091 100644 --- a/src/components/ContextMenu/share.ts +++ b/src/components/ContextMenu/share.ts @@ -1,64 +1,76 @@ -import analytics from '@components/analytics' import { displayMessage } from '@components/Message' import Clipboard from '@react-native-clipboard/clipboard' import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' import { Platform, Share } from 'react-native' -import { ContextMenuAction } from 'react-native-context-menu-view' -export interface Props { - copiableContent?: React.MutableRefObject<{ - content?: string | undefined - complete: boolean - }> - actions: ContextMenuAction[] - type: 'status' | 'account' - url: string -} +const menuShare = ( + params: + | { + visibility?: Mastodon.Status['visibility'] + copiableContent?: React.MutableRefObject<{ + content?: string | undefined + complete: boolean + }> + type: 'status' + url?: string + } + | { + type: 'account' + url: string + } +): ContextMenu[][] => { + if (params.type === 'status' && params.visibility === 'direct') return [] -const contextMenuShare = ({ copiableContent, actions, type, url }: Props) => { const { theme } = useTheme() const { t } = useTranslation('componentContextMenu') - actions.push({ - id: 'share', - title: t(`share.${type}.action`), - systemIcon: 'square.and.arrow.up' - }) - Platform.OS !== 'android' && - type === 'status' && - actions.push({ - id: 'copy', - title: t(`copy.action`), - systemIcon: 'doc.on.doc', - disabled: !copiableContent?.current.content?.length + const menus: ContextMenu[][] = [[]] + + if (params.url) { + const url = params.url + menus[0].push({ + key: 'share', + item: { + onSelect: () => { + switch (Platform.OS) { + case 'ios': + Share.share({ url }) + break + case 'android': + Share.share({ message: url }) + break + } + }, + disabled: false, + destructive: false, + hidden: false + }, + title: t(`share.${params.type}.action`), + icon: 'square.and.arrow.up' + }) + } + if (params.type === 'status' && Platform.OS === 'ios') + menus[0].push({ + key: 'copy', + item: { + onSelect: () => { + Clipboard.setString(params.copiableContent?.current.content || '') + displayMessage({ + theme, + type: 'success', + message: t(`copy.succeed`) + }) + }, + disabled: false, + destructive: false, + hidden: !params.copiableContent?.current.content?.length + }, + title: t('copy.action'), + icon: 'doc.on.doc' }) - return (index: number) => { - if (typeof index !== 'number' || !actions[index]) { - return // For Android - } - if (actions[index].id === 'copy') { - analytics('timeline_shared_headeractions_copy_press') - Clipboard.setString(copiableContent?.current.content || '') - displayMessage({ - theme, - type: 'success', - message: t(`copy.succeed`) - }) - } - if (actions[index].id === 'share') { - analytics('timeline_shared_headeractions_share_press') - switch (Platform.OS) { - case 'ios': - Share.share({ url }) - break - case 'android': - Share.share({ message: url }) - break - } - } - } + return menus } -export default contextMenuShare +export default menuShare diff --git a/src/components/ContextMenu/status.ts b/src/components/ContextMenu/status.ts index 8e98f7fc..9223d640 100644 --- a/src/components/ContextMenu/status.ts +++ b/src/components/ContextMenu/status.ts @@ -1,5 +1,4 @@ import apiInstance from '@api/instance' -import analytics from '@components/analytics' import { displayMessage } from '@components/Message' import { useNavigation } from '@react-navigation/native' import { NativeStackNavigationProp } from '@react-navigation/native-stack' @@ -13,18 +12,20 @@ import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instance import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' -import { ContextMenuAction } from 'react-native-context-menu-view' import { useQueryClient } from 'react-query' import { useSelector } from 'react-redux' -export interface Props { - actions: ContextMenuAction[] - status: Mastodon.Status - queryKey: QueryKeyTimeline +const menuStatus = ({ + status, + queryKey, + rootQueryKey +}: { + status?: Mastodon.Status + queryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline -} +}): ContextMenu[][] => { + if (!status || !queryKey) return [] -const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) => { const navigation = useNavigation>() const { theme } = useTheme() const { t } = useTranslation('componentContextMenu') @@ -54,96 +55,19 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) = } }) + const menus: ContextMenu[][] = [] + const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id) - const ownAccount = instanceAccount?.id === status?.account?.id + const ownAccount = instanceAccount?.id === status.account?.id + + const canEditPost = useSelector(checkInstanceFeature('edit_post')) if (ownAccount) { - const accountMenuItems: ContextMenuAction[] = [ + menus.push([ { - id: 'status-delete', - title: t('status.delete.action'), - systemIcon: 'trash', - destructive: true - }, - { - id: 'status-delete-edit', - title: t('status.deleteEdit.action'), - systemIcon: 'pencil.and.outline', - destructive: true - }, - { - id: 'status-mute', - title: t('status.mute.action', { - context: (status.muted || false).toString() - }), - systemIcon: status.muted ? 'speaker' : 'speaker.slash' - } - ] - - const canEditPost = useSelector(checkInstanceFeature('edit_post')) - if (canEditPost) { - accountMenuItems.unshift({ - id: 'status-edit', - title: t('status.edit.action'), - systemIcon: 'square.and.pencil' - }) - } - - if (status.visibility === 'public' || status.visibility === 'unlisted') { - accountMenuItems.push({ - id: 'status-pin', - title: t('status.pin.action', { - context: (status.pinned || false).toString() - }), - systemIcon: status.pinned ? 'pin.slash' : 'pin' - }) - } - - actions.push(...accountMenuItems) - } - - return async (index: number) => { - if (typeof index !== 'number' || !actions[index]) { - return // For Android - } - if (actions[index].id === 'status-delete') { - analytics('timeline_shared_headeractions_status_delete_press', { - page: queryKey && queryKey[1].page - }) - Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [ - { - text: t('status.delete.alert.buttons.confirm'), - style: 'destructive', - onPress: async () => { - analytics('timeline_shared_headeractions_status_delete_confirm', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'deleteItem', - source: 'statuses', - queryKey, - rootQueryKey, - id: status.id - }) - } - }, - { - text: t('common:buttons.cancel') - } - ]) - } - if (actions[index].id === 'status-delete-edit') { - analytics('timeline_shared_headeractions_status_deleteedit_press', { - page: queryKey && queryKey[1].page - }) - Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [ - { - text: t('status.deleteEdit.alert.buttons.confirm'), - style: 'destructive', - onPress: async () => { - analytics('timeline_shared_headeractions_status_deleteedit_confirm', { - page: queryKey && queryKey[1].page - }) + key: 'status-edit', + item: { + onSelect: async () => { let replyToStatus: Mastodon.Status | undefined = undefined if (status.in_reply_to_id) { replyToStatus = await apiInstance({ @@ -151,96 +75,166 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) = url: `statuses/${status.in_reply_to_id}` }).then(res => res.body) } - mutation - .mutateAsync({ - type: 'deleteItem', - source: 'statuses', + apiInstance<{ + id: Mastodon.Status['id'] + text: NonNullable + spoiler_text: Mastodon.Status['spoiler_text'] + }>({ + method: 'get', + url: `statuses/${status.id}/source` + }).then(res => { + navigation.navigate('Screen-Compose', { + type: 'edit', + incomingStatus: { + ...status, + text: res.body.text, + spoiler_text: res.body.spoiler_text + }, + ...(replyToStatus && { replyToStatus }), queryKey, - id: status.id + rootQueryKey }) - .then(res => { - navigation.navigate('Screen-Compose', { - type: 'deleteEdit', - incomingStatus: res.body as Mastodon.Status, - ...(replyToStatus && { replyToStatus }), - queryKey - }) - }) - } - }, - { - text: t('common:buttons.cancel') - } - ]) - } - if (actions[index].id === 'status-mute') { - analytics('timeline_shared_headeractions_status_mute_press', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'updateStatusProperty', - queryKey, - rootQueryKey, - id: status.id, - payload: { - property: 'muted', - currentValue: status.muted, - propertyCount: undefined, - countValue: undefined - } - }) - } - if (actions[index].id === 'status-edit') { - analytics('timeline_shared_headeractions_status_edit_press', { - page: queryKey && queryKey[1].page - }) - let replyToStatus: Mastodon.Status | undefined = undefined - if (status.in_reply_to_id) { - replyToStatus = await apiInstance({ - method: 'get', - url: `statuses/${status.in_reply_to_id}` - }).then(res => res.body) - } - apiInstance<{ - id: Mastodon.Status['id'] - text: NonNullable - spoiler_text: Mastodon.Status['spoiler_text'] - }>({ - method: 'get', - url: `statuses/${status.id}/source` - }).then(res => { - navigation.navigate('Screen-Compose', { - type: 'edit', - incomingStatus: { - ...status, - text: res.body.text, - spoiler_text: res.body.spoiler_text + }) }, - ...(replyToStatus && { replyToStatus }), - queryKey, - rootQueryKey - }) - }) - } - if (actions[index].id === 'status-pin') { - // Also note that reblogs cannot be pinned. - analytics('timeline_shared_headeractions_status_pin_press', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'updateStatusProperty', - queryKey, - rootQueryKey, - id: status.id, - payload: { - property: 'pinned', - currentValue: status.pinned, - propertyCount: undefined, - countValue: undefined - } - }) - } + disabled: false, + destructive: false, + hidden: !canEditPost + }, + title: t('status.edit.action'), + icon: 'square.and.pencil' + }, + { + key: 'status-delete-edit', + item: { + onSelect: () => + Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [ + { + text: t('status.deleteEdit.alert.buttons.confirm'), + style: 'destructive', + onPress: async () => { + let replyToStatus: Mastodon.Status | undefined = undefined + if (status.in_reply_to_id) { + replyToStatus = await apiInstance({ + method: 'get', + url: `statuses/${status.in_reply_to_id}` + }).then(res => res.body) + } + mutation + .mutateAsync({ + type: 'deleteItem', + source: 'statuses', + queryKey, + id: status.id + }) + .then(res => { + navigation.navigate('Screen-Compose', { + type: 'deleteEdit', + incomingStatus: res.body as Mastodon.Status, + ...(replyToStatus && { replyToStatus }), + queryKey + }) + }) + } + }, + { + text: t('common:buttons.cancel') + } + ]), + disabled: false, + destructive: true, + hidden: false + }, + title: t('status.deleteEdit.action'), + icon: 'pencil.and.outline' + }, + { + key: 'status-delete', + item: { + onSelect: () => + Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [ + { + text: t('status.delete.alert.buttons.confirm'), + style: 'destructive', + onPress: async () => { + mutation.mutate({ + type: 'deleteItem', + source: 'statuses', + queryKey, + rootQueryKey, + id: status.id + }) + } + }, + { + text: t('common:buttons.cancel'), + style: 'default' + } + ]), + disabled: false, + destructive: true, + hidden: false + }, + title: t('status.delete.action'), + icon: 'trash' + } + ]) + + menus.push([ + { + key: 'status-mute', + item: { + onSelect: () => + mutation.mutate({ + type: 'updateStatusProperty', + queryKey, + rootQueryKey, + id: status.id, + payload: { + property: 'muted', + currentValue: status.muted, + propertyCount: undefined, + countValue: undefined + } + }), + disabled: false, + destructive: false, + hidden: false + }, + title: t('status.mute.action', { + context: (status.muted || false).toString() + }), + icon: status.muted ? 'speaker' : 'speaker.slash' + }, + { + key: 'status-pin', + item: { + onSelect: () => + // Also note that reblogs cannot be pinned. + mutation.mutate({ + type: 'updateStatusProperty', + queryKey, + rootQueryKey, + id: status.id, + payload: { + property: 'pinned', + currentValue: status.pinned, + propertyCount: undefined, + countValue: undefined + } + }), + disabled: false, + destructive: false, + hidden: status.visibility !== 'public' && status.visibility !== 'unlisted' + }, + title: t('status.pin.action', { + context: (status.pinned || false).toString() + }), + icon: status.pinned ? 'pin.slash' : 'pin' + } + ]) } + + return menus } -export default contextMenuStatus +export default menuStatus diff --git a/src/components/Hashtag.tsx b/src/components/Hashtag.tsx index 7efccdf3..2d05edc9 100644 --- a/src/components/Hashtag.tsx +++ b/src/components/Hashtag.tsx @@ -3,40 +3,60 @@ import { StackNavigationProp } from '@react-navigation/stack' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useCallback } from 'react' -import { Pressable } from 'react-native' -import analytics from './analytics' +import React, { useCallback, useState } from 'react' +import { Dimensions, Pressable } from 'react-native' +import Sparkline from './Sparkline' import CustomText from './Text' export interface Props { hashtag: Mastodon.Tag onPress?: () => void - origin?: string } -const ComponentHashtag: React.FC = ({ - hashtag, - onPress: customOnPress, - origin -}) => { +const ComponentHashtag: React.FC = ({ hashtag, onPress: customOnPress }) => { const { colors } = useTheme() - const navigation = - useNavigation>() + const navigation = useNavigation>() const onPress = useCallback(() => { - analytics('search_account_press', { page: origin }) navigation.push('Tab-Shared-Hashtag', { hashtag: hashtag.name }) }, []) + const padding = StyleConstants.Spacing.Global.PagePadding + const width = Dimensions.get('window').width / 4 + const [height, setHeight] = useState(0) + return ( setHeight(height - padding * 2 - 1)} > - + #{hashtag.name} + parseInt(h.uses)).reverse()} + width={width} + height={height} + /> ) } diff --git a/src/components/Instance.tsx b/src/components/Instance.tsx index b37361c2..b0039b37 100644 --- a/src/components/Instance.tsx +++ b/src/components/Instance.tsx @@ -1,5 +1,6 @@ import Button from '@components/Button' import Icon from '@components/Icon' +import browserPackage from '@helpers/browserPackage' import { useAppsQuery } from '@utils/queryHooks/apps' import { useInstanceQuery } from '@utils/queryHooks/instance' import { getInstances } from '@utils/slices/instancesSlice' @@ -9,18 +10,10 @@ import * as WebBrowser from 'expo-web-browser' import { debounce } from 'lodash' import React, { RefObject, useCallback, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { - Alert, - Image, - KeyboardAvoidingView, - Platform, - TextInput, - View -} from 'react-native' +import { Alert, Image, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' import { useSelector } from 'react-redux' import { Placeholder } from 'rn-placeholder' -import analytics from './analytics' import InstanceAuth from './Instance/Auth' import InstanceInfo from './Instance/Info' import CustomText from './Text' @@ -65,18 +58,14 @@ const ComponentInstance: React.FC = ({ const processUpdate = useCallback(() => { if (domain) { - analytics('instance_login') - if ( - instances && - instances.filter(instance => instance.url === domain).length - ) { + if (instances && instances.filter(instance => instance.url === domain).length) { Alert.alert(t('update.alert.title'), t('update.alert.message'), [ { - text: t('update.alert.buttons.cancel'), + text: t('common:buttons.cancel'), style: 'cancel' }, { - text: t('update.alert.buttons.continue'), + text: t('common:buttons.continue'), onPress: () => { appsQuery.refetch() } @@ -142,9 +131,7 @@ const ComponentInstance: React.FC = ({ borderBottomWidth: 1, ...StyleConstants.FontStyle.M, color: colors.primaryDefault, - borderBottomColor: instanceQuery.isError - ? colors.red - : colors.border + borderBottomColor: instanceQuery.isError ? colors.red : colors.border }} editable={false} defaultValue='https://' @@ -156,9 +143,7 @@ const ComponentInstance: React.FC = ({ ...StyleConstants.FontStyle.M, marginRight: StyleConstants.Spacing.M, color: colors.primaryDefault, - borderBottomColor: instanceQuery.isError - ? colors.red - : colors.border + borderBottomColor: instanceQuery.isError ? colors.red : colors.border }} onChangeText={onChangeText} autoCapitalize='none' @@ -166,7 +151,6 @@ const ComponentInstance: React.FC = ({ keyboardType='url' textContentType='URL' onSubmitEditing={({ nativeEvent: { text } }) => { - analytics('instance_textinput_submit', { match: text === domain }) if ( text === domain && instanceQuery.isSuccess && @@ -182,11 +166,7 @@ const ComponentInstance: React.FC = ({ keyboardAppearance={mode} {...(scrollViewRef && { onFocus: () => - setTimeout( - () => - scrollViewRef.current?.scrollTo({ y: 0, animated: true }), - 150 - ) + setTimeout(() => scrollViewRef.current?.scrollTo({ y: 0, animated: true }), 150) })} autoCorrect={false} spellCheck={false} @@ -211,27 +191,19 @@ const ComponentInstance: React.FC = ({ @@ -248,17 +220,11 @@ const ComponentInstance: React.FC = ({ size={StyleConstants.Font.Size.S} color={colors.secondary} style={{ - marginTop: - (StyleConstants.Font.LineHeight.S - - StyleConstants.Font.Size.S) / - 2, + marginTop: (StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2, marginRight: StyleConstants.Spacing.XS }} /> - + {t('server.disclaimer.base')} @@ -274,10 +240,7 @@ const ComponentInstance: React.FC = ({ size={StyleConstants.Font.Size.S} color={colors.secondary} style={{ - marginTop: - (StyleConstants.Font.LineHeight.S - - StyleConstants.Font.Size.S) / - 2, + marginTop: (StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2, marginRight: StyleConstants.Spacing.XS }} /> @@ -292,22 +255,20 @@ const ComponentInstance: React.FC = ({ { - analytics('view_privacy') - WebBrowser.openBrowserAsync( - 'https://tooot.app/privacy-policy' - ) - }} + onPress={async () => + WebBrowser.openBrowserAsync('https://tooot.app/privacy-policy', { + browserPackage: await browserPackage() + }) + } />, { - analytics('view_tos') - WebBrowser.openBrowserAsync( - 'https://tooot.app/terms-of-service' - ) - }} + onPress={async () => + WebBrowser.openBrowserAsync('https://tooot.app/terms-of-service', { + browserPackage: await browserPackage() + }) + } /> ]} /> diff --git a/src/components/Instance/Auth.tsx b/src/components/Instance/Auth.tsx index 16af0428..1a72e2b5 100644 --- a/src/components/Instance/Auth.tsx +++ b/src/components/Instance/Auth.tsx @@ -1,3 +1,4 @@ +import browserPackage from '@helpers/browserPackage' import { useNavigation } from '@react-navigation/native' import { useAppDispatch } from '@root/store' import { InstanceLatest } from '@utils/migrations/instances/migration' @@ -24,14 +25,11 @@ const InstanceAuth = React.memo( useProxy: false }) - const navigation = - useNavigation>() + const navigation = useNavigation>() const queryClient = useQueryClient() const dispatch = useAppDispatch() - const deprecateAuthFollow = useSelector( - checkInstanceFeature('deprecate_auth_follow') - ) + const deprecateAuthFollow = useSelector(checkInstanceFeature('deprecate_auth_follow')) const [request, response, promptAsync] = AuthSession.useAuthRequest( { clientId: appData.clientId, @@ -48,7 +46,7 @@ const InstanceAuth = React.memo( useEffect(() => { ;(async () => { if (request?.clientId) { - await promptAsync() + await promptAsync({ browserPackage: await browserPackage() }).catch(e => console.log(e)) } })() }, [request]) diff --git a/src/components/Menu/Container.tsx b/src/components/Menu/Container.tsx index 0a7910c0..10c942eb 100644 --- a/src/components/Menu/Container.tsx +++ b/src/components/Menu/Container.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { StyleSheet, View } from 'react-native' +import { View } from 'react-native' import { StyleConstants } from '@utils/styles/constants' export interface Props { @@ -7,14 +7,16 @@ export interface Props { } const MenuContainer: React.FC = ({ children }) => { - return {children} + return ( + + {children} + + ) } -const styles = StyleSheet.create({ - base: { - paddingHorizontal: StyleConstants.Spacing.Global.PagePadding, - marginBottom: StyleConstants.Spacing.Global.PagePadding - } -}) - export default MenuContainer diff --git a/src/components/Menu/Row.tsx b/src/components/Menu/Row.tsx index e5027ff1..3953e5c9 100644 --- a/src/components/Menu/Row.tsx +++ b/src/components/Menu/Row.tsx @@ -5,7 +5,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import { ColorDefinitions } from '@utils/styles/themes' import React, { useMemo } from 'react' -import { View } from 'react-native' +import { Text, View } from 'react-native' import { Flow } from 'react-native-animated-spinkit' import { State, Switch, TapGestureHandler } from 'react-native-gesture-handler' @@ -65,6 +65,7 @@ const MenuRow: React.FC = ({ > { + if (typeof iconBack !== 'string') return // Let icon back handles the gesture if (nativeEvent.state === State.ACTIVE && !loading) { if (screenReaderEnabled && switchOnValueChange) { switchOnValueChange() @@ -79,12 +80,13 @@ const MenuRow: React.FC = ({ style={{ flex: 1, flexDirection: 'row', - paddingTop: StyleConstants.Spacing.S + justifyContent: 'space-between', + marginTop: StyleConstants.Spacing.S }} > ((_, ref) => { shadowOpacity: theme === 'light' ? 0.16 : 0.24, shadowRadius: 4, paddingRight: StyleConstants.Spacing.M * 2, - marginTop: insets.top + marginTop: ref ? undefined : insets.top }} titleStyle={{ color: colors.primaryDefault, diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index e68b3d30..828ba608 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -1,4 +1,3 @@ -import analytics from '@components/analytics' import Icon from '@components/Icon' import openLink from '@components/openLink' import ParseEmojis from '@components/Parse/Emojis' @@ -63,7 +62,6 @@ const renderNode = ({ lineHeight: adaptedLineheight }} onPress={() => { - analytics('status_hashtag_press') !disableDetails && differentTag && navigation.push('Tab-Shared-Hashtag', { @@ -89,7 +87,6 @@ const renderNode = ({ lineHeight: adaptedLineheight }} onPress={() => { - analytics('status_mention_press') accountIndex !== -1 && !disableDetails && differentAccount && @@ -118,7 +115,6 @@ const renderNode = ({ lineHeight: adaptedLineheight }} onPress={async () => { - analytics('status_link_press') if (!disableDetails) { if (shouldBeTag) { navigation.push('Tab-Shared-Hashtag', { @@ -172,6 +168,7 @@ export interface Props { highlighted?: boolean disableDetails?: boolean selectable?: boolean + setSpoilerExpanded?: React.Dispatch> } const ParseHTML = React.memo( @@ -187,7 +184,8 @@ const ParseHTML = React.memo( expandHint, highlighted = false, disableDetails = false, - selectable = false + selectable = false, + setSpoilerExpanded }: Props) => { const adaptiveFontsize = useSelector(getSettingsFontsize) const adaptedFontsize = adaptiveScale( @@ -255,9 +253,11 @@ const ParseHTML = React.memo( { - analytics('status_readmore', { totalLines, expanded }) layoutAnimation() setExpanded(!expanded) + if (setSpoilerExpanded) { + setSpoilerExpanded(!expanded) + } }} style={{ flexDirection: 'row', diff --git a/src/components/Relationship/Incoming.tsx b/src/components/Relationship/Incoming.tsx index feb97340..d6eb633c 100644 --- a/src/components/Relationship/Incoming.tsx +++ b/src/components/Relationship/Incoming.tsx @@ -1,11 +1,7 @@ -import analytics from '@components/analytics' import Button from '@components/Button' import haptics from '@components/haptics' import { displayMessage } from '@components/Message' -import { - QueryKeyRelationship, - useRelationshipMutation -} from '@utils/queryHooks/relationship' +import { QueryKeyRelationship, useRelationshipMutation } from '@utils/queryHooks/relationship' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' @@ -23,17 +19,12 @@ const RelationshipIncoming: React.FC = ({ id }) => { const { t } = useTranslation() const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }] - const queryKeyNotification: QueryKeyTimeline = [ - 'Timeline', - { page: 'Notifications' } - ] + const queryKeyNotification: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }] const queryClient = useQueryClient() const mutation = useRelationshipMutation({ onSuccess: res => { haptics('Success') - queryClient.setQueryData(queryKeyRelationship, [ - res - ]) + queryClient.setQueryData(queryKeyRelationship, [res]) queryClient.refetchQueries(queryKeyNotification) }, onError: (err: any, { type }) => { @@ -62,28 +53,26 @@ const RelationshipIncoming: React.FC = ({ id }) => { type='icon' content='X' loading={mutation.isLoading} - onPress={() => { - analytics('relationship_incoming_press_reject') + onPress={() => mutation.mutate({ id, type: 'incoming', payload: { action: 'reject' } }) - }} + } />