mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
commit
d7d01f1300
4
index.share.js
Normal file
4
index.share.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { AppRegistry } from 'react-native'
|
||||||
|
import ShareExtension from './src/ShareExtension'
|
||||||
|
|
||||||
|
AppRegistry.registerComponent('ShareMenuModuleComponent', () => ShareExtension)
|
15
ios/Podfile
15
ios/Podfile
@ -24,5 +24,20 @@ target 'tooot' do
|
|||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
react_native_post_install(installer)
|
react_native_post_install(installer)
|
||||||
|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
target 'ShareExtension' do
|
||||||
|
use_react_native!(
|
||||||
|
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes'
|
||||||
|
)
|
||||||
|
|
||||||
|
pod 'RNShareMenu', :path => '../node_modules/react-native-share-menu'
|
||||||
|
pod 'RNFS', :path => '../node_modules/react-native-fs'
|
||||||
|
end
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- boost (1.76.0)
|
- boost (1.76.0)
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
|
- EXApplication (4.0.2):
|
||||||
|
- ExpoModulesCore
|
||||||
- EXAV (10.2.1):
|
- EXAV (10.2.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ReactCommon/turbomodule/core
|
- ReactCommon/turbomodule/core
|
||||||
@ -21,6 +23,8 @@ PODS:
|
|||||||
- EXFirebaseCore (4.1.1):
|
- EXFirebaseCore (4.1.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- Firebase/Core (= 7.7.0)
|
- Firebase/Core (= 7.7.0)
|
||||||
|
- EXFont (10.0.5):
|
||||||
|
- ExpoModulesCore
|
||||||
- EXImageLoader (3.1.1):
|
- EXImageLoader (3.1.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- React-Core
|
- React-Core
|
||||||
@ -30,6 +34,8 @@ PODS:
|
|||||||
- EXImagePicker (12.0.2):
|
- EXImagePicker (12.0.2):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- EXJSONUtils (0.2.1)
|
- EXJSONUtils (0.2.1)
|
||||||
|
- EXKeepAwake (10.0.2):
|
||||||
|
- ExpoModulesCore
|
||||||
- EXManifests (0.2.4):
|
- EXManifests (0.2.4):
|
||||||
- EXJSONUtils
|
- EXJSONUtils
|
||||||
- EXNotifications (0.14.1):
|
- EXNotifications (0.14.1):
|
||||||
@ -367,15 +373,15 @@ PODS:
|
|||||||
- glog
|
- glog
|
||||||
- react-native-blur (0.8.0):
|
- react-native-blur (0.8.0):
|
||||||
- React
|
- React
|
||||||
- react-native-blurhash (1.1.9):
|
- react-native-blurhash (1.1.10):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-cameraroll (4.1.2):
|
- react-native-cameraroll (4.1.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-netinfo (8.2.0):
|
- react-native-netinfo (8.3.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-pager-view (5.4.15):
|
- react-native-pager-view (5.4.11):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-safe-area-context (4.2.4):
|
- react-native-safe-area-context (4.2.5):
|
||||||
- RCT-Folly
|
- RCT-Folly
|
||||||
- RCTRequired
|
- RCTRequired
|
||||||
- RCTTypeSafety
|
- RCTTypeSafety
|
||||||
@ -448,15 +454,17 @@ PODS:
|
|||||||
- React-jsi (= 0.67.4)
|
- React-jsi (= 0.67.4)
|
||||||
- React-logger (= 0.67.4)
|
- React-logger (= 0.67.4)
|
||||||
- React-perflogger (= 0.67.4)
|
- React-perflogger (= 0.67.4)
|
||||||
- RNCAsyncStorage (1.17.0):
|
- RNCAsyncStorage (1.17.3):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNFastImage (8.5.11):
|
- RNFastImage (8.5.11):
|
||||||
- React-Core
|
- React-Core
|
||||||
- SDWebImage (~> 5.12.3)
|
- SDWebImage (~> 5.12.5)
|
||||||
- SDWebImageWebPCoder (~> 0.8.4)
|
- SDWebImageWebPCoder (~> 0.8.4)
|
||||||
- RNGestureHandler (2.3.2):
|
- RNFS (2.19.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNReanimated (2.5.0):
|
- RNGestureHandler (2.4.1):
|
||||||
|
- React-Core
|
||||||
|
- RNReanimated (2.8.0):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- FBLazyVector
|
- FBLazyVector
|
||||||
- FBReactNativeSpec
|
- FBReactNativeSpec
|
||||||
@ -464,7 +472,6 @@ PODS:
|
|||||||
- RCT-Folly
|
- RCT-Folly
|
||||||
- RCTRequired
|
- RCTRequired
|
||||||
- RCTTypeSafety
|
- RCTTypeSafety
|
||||||
- React
|
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
- React-Core/DevSupport
|
- React-Core/DevSupport
|
||||||
@ -487,9 +494,11 @@ PODS:
|
|||||||
- RNScreens (3.13.1):
|
- RNScreens (3.13.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- React-RCTImage
|
- React-RCTImage
|
||||||
- RNSentry (3.3.6):
|
- RNSentry (3.4.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- Sentry (= 7.11.0)
|
- Sentry (= 7.11.0)
|
||||||
|
- RNShareMenu (5.0.5):
|
||||||
|
- React
|
||||||
- RNSVG (12.3.0):
|
- RNSVG (12.3.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- SDWebImage (5.12.5):
|
- SDWebImage (5.12.5):
|
||||||
@ -508,6 +517,7 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- 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`)
|
- EXAV (from `../node_modules/expo-av/ios`)
|
||||||
- EXConstants (from `../node_modules/expo-constants/ios`)
|
- EXConstants (from `../node_modules/expo-constants/ios`)
|
||||||
- EXCrypto (from `../node_modules/expo-crypto/ios`)
|
- EXCrypto (from `../node_modules/expo-crypto/ios`)
|
||||||
@ -516,10 +526,12 @@ DEPENDENCIES:
|
|||||||
- EXFileSystem (from `../node_modules/expo-file-system/ios`)
|
- EXFileSystem (from `../node_modules/expo-file-system/ios`)
|
||||||
- EXFirebaseAnalytics (from `../node_modules/expo-firebase-analytics/ios`)
|
- EXFirebaseAnalytics (from `../node_modules/expo-firebase-analytics/ios`)
|
||||||
- EXFirebaseCore (from `../node_modules/expo-firebase-core/ios`)
|
- EXFirebaseCore (from `../node_modules/expo-firebase-core/ios`)
|
||||||
|
- EXFont (from `../node_modules/expo-font/ios`)
|
||||||
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
|
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
|
||||||
- EXImageManipulator (from `../node_modules/expo-image-manipulator/ios`)
|
- EXImageManipulator (from `../node_modules/expo-image-manipulator/ios`)
|
||||||
- EXImagePicker (from `../node_modules/expo-image-picker/ios`)
|
- EXImagePicker (from `../node_modules/expo-image-picker/ios`)
|
||||||
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
|
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
|
||||||
|
- EXKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||||
- EXManifests (from `../node_modules/expo-manifests/ios`)
|
- EXManifests (from `../node_modules/expo-manifests/ios`)
|
||||||
- EXNotifications (from `../node_modules/expo-notifications/ios`)
|
- EXNotifications (from `../node_modules/expo-notifications/ios`)
|
||||||
- Expo (from `../node_modules/expo/ios`)
|
- Expo (from `../node_modules/expo/ios`)
|
||||||
@ -577,10 +589,12 @@ DEPENDENCIES:
|
|||||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||||
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
||||||
|
- RNFS (from `../node_modules/react-native-fs`)
|
||||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||||
- RNScreens (from `../node_modules/react-native-screens`)
|
- RNScreens (from `../node_modules/react-native-screens`)
|
||||||
- "RNSentry (from `../node_modules/@sentry/react-native`)"
|
- "RNSentry (from `../node_modules/@sentry/react-native`)"
|
||||||
|
- RNShareMenu (from `../node_modules/react-native-share-menu`)
|
||||||
- RNSVG (from `../node_modules/react-native-svg`)
|
- RNSVG (from `../node_modules/react-native-svg`)
|
||||||
- UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`)
|
- UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`)
|
||||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||||
@ -610,6 +624,8 @@ EXTERNAL SOURCES:
|
|||||||
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
||||||
DoubleConversion:
|
DoubleConversion:
|
||||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||||
|
EXApplication:
|
||||||
|
:path: "../node_modules/expo-application/ios"
|
||||||
EXAV:
|
EXAV:
|
||||||
:path: "../node_modules/expo-av/ios"
|
:path: "../node_modules/expo-av/ios"
|
||||||
EXConstants:
|
EXConstants:
|
||||||
@ -626,6 +642,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-firebase-analytics/ios"
|
:path: "../node_modules/expo-firebase-analytics/ios"
|
||||||
EXFirebaseCore:
|
EXFirebaseCore:
|
||||||
:path: "../node_modules/expo-firebase-core/ios"
|
:path: "../node_modules/expo-firebase-core/ios"
|
||||||
|
EXFont:
|
||||||
|
:path: "../node_modules/expo-font/ios"
|
||||||
EXImageLoader:
|
EXImageLoader:
|
||||||
:path: "../node_modules/expo-image-loader/ios"
|
:path: "../node_modules/expo-image-loader/ios"
|
||||||
EXImageManipulator:
|
EXImageManipulator:
|
||||||
@ -634,6 +652,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-image-picker/ios"
|
:path: "../node_modules/expo-image-picker/ios"
|
||||||
EXJSONUtils:
|
EXJSONUtils:
|
||||||
:path: "../node_modules/expo-json-utils/ios"
|
:path: "../node_modules/expo-json-utils/ios"
|
||||||
|
EXKeepAwake:
|
||||||
|
:path: "../node_modules/expo-keep-awake/ios"
|
||||||
EXManifests:
|
EXManifests:
|
||||||
:path: "../node_modules/expo-manifests/ios"
|
:path: "../node_modules/expo-manifests/ios"
|
||||||
EXNotifications:
|
EXNotifications:
|
||||||
@ -740,6 +760,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/@react-native-async-storage/async-storage"
|
:path: "../node_modules/@react-native-async-storage/async-storage"
|
||||||
RNFastImage:
|
RNFastImage:
|
||||||
:path: "../node_modules/react-native-fast-image"
|
:path: "../node_modules/react-native-fast-image"
|
||||||
|
RNFS:
|
||||||
|
:path: "../node_modules/react-native-fs"
|
||||||
RNGestureHandler:
|
RNGestureHandler:
|
||||||
:path: "../node_modules/react-native-gesture-handler"
|
:path: "../node_modules/react-native-gesture-handler"
|
||||||
RNReanimated:
|
RNReanimated:
|
||||||
@ -748,6 +770,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native-screens"
|
:path: "../node_modules/react-native-screens"
|
||||||
RNSentry:
|
RNSentry:
|
||||||
:path: "../node_modules/@sentry/react-native"
|
:path: "../node_modules/@sentry/react-native"
|
||||||
|
RNShareMenu:
|
||||||
|
:path: "../node_modules/react-native-share-menu"
|
||||||
RNSVG:
|
RNSVG:
|
||||||
:path: "../node_modules/react-native-svg"
|
:path: "../node_modules/react-native-svg"
|
||||||
UMTaskManagerInterface:
|
UMTaskManagerInterface:
|
||||||
@ -758,6 +782,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||||
|
EXApplication: 54fe5bd6268d697771645e8f1aef8b806a65247a
|
||||||
EXAV: b9ed0c201092244c46aa78f907f5c66176bed236
|
EXAV: b9ed0c201092244c46aa78f907f5c66176bed236
|
||||||
EXConstants: 88bf79622fbd9b476c96d8ec57fe97ca44fe8e3c
|
EXConstants: 88bf79622fbd9b476c96d8ec57fe97ca44fe8e3c
|
||||||
EXCrypto: ab2ba0df3136a5f2407b8c7e70eb498ac20d704f
|
EXCrypto: ab2ba0df3136a5f2407b8c7e70eb498ac20d704f
|
||||||
@ -766,10 +791,12 @@ SPEC CHECKSUMS:
|
|||||||
EXFileSystem: 7bcd3c1428698150d5c8ca140c8183f2ee204048
|
EXFileSystem: 7bcd3c1428698150d5c8ca140c8183f2ee204048
|
||||||
EXFirebaseAnalytics: a7ec2dd1394ad0de5c0c63fac0deee496f798284
|
EXFirebaseAnalytics: a7ec2dd1394ad0de5c0c63fac0deee496f798284
|
||||||
EXFirebaseCore: 52151d0b008b99983e6a120cea94466ee760a4e9
|
EXFirebaseCore: 52151d0b008b99983e6a120cea94466ee760a4e9
|
||||||
|
EXFont: 2597c10ac85a69d348d44d7873eccf5a7576ef5e
|
||||||
EXImageLoader: 347b72c2ec2df65120ccec40ea65a4c4f24317ff
|
EXImageLoader: 347b72c2ec2df65120ccec40ea65a4c4f24317ff
|
||||||
EXImageManipulator: 60d1bf3f1d7709453b1feb38adf8594b7f58710f
|
EXImageManipulator: 60d1bf3f1d7709453b1feb38adf8594b7f58710f
|
||||||
EXImagePicker: bf4d62532cc2bf217edbe4abbb0014e73e079eac
|
EXImagePicker: bf4d62532cc2bf217edbe4abbb0014e73e079eac
|
||||||
EXJSONUtils: 5ee0d5cf76da70ad86f0be1a41cc70f47d69e06f
|
EXJSONUtils: 5ee0d5cf76da70ad86f0be1a41cc70f47d69e06f
|
||||||
|
EXKeepAwake: bf48d7f740a5cd2befed6cf9a49911d385c6c47d
|
||||||
EXManifests: d3464cd2278f4a19cd80c1aa673231570b534c11
|
EXManifests: d3464cd2278f4a19cd80c1aa673231570b534c11
|
||||||
EXNotifications: a7d582fa800d77f4a75bd22d52e84e2fbcee26df
|
EXNotifications: a7d582fa800d77f4a75bd22d52e84e2fbcee26df
|
||||||
Expo: 534e51e607aba8229293297da5585f4b26f50fa1
|
Expo: 534e51e607aba8229293297da5585f4b26f50fa1
|
||||||
@ -817,11 +844,11 @@ SPEC CHECKSUMS:
|
|||||||
React-jsinspector: f4775ea9118cbe1f72b834f0f842baa7a99508d8
|
React-jsinspector: f4775ea9118cbe1f72b834f0f842baa7a99508d8
|
||||||
React-logger: a1f028f6d8639a3f364ef80419e5e862e1115250
|
React-logger: a1f028f6d8639a3f364ef80419e5e862e1115250
|
||||||
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
||||||
react-native-blurhash: 80ed027224075ef708c3cac6ab116e6f8ee3272f
|
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
||||||
react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866
|
react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866
|
||||||
react-native-netinfo: e922cb2e3eaf9ccdf16b8d4744a89657377aa4a1
|
react-native-netinfo: 3671b091c4843fda5e153612866ef4024b8f5d62
|
||||||
react-native-pager-view: b1914469643f40042e65d78cbf3d3dfebd6fb0d9
|
react-native-pager-view: 7f00d63688f7df9fad86dfb0154814419cc5eb8d
|
||||||
react-native-safe-area-context: f98b0b16d1546d208fc293b4661e3f81a895afd9
|
react-native-safe-area-context: ebf8c413eb8b5f7c392a036a315eb7b46b96845f
|
||||||
react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097
|
react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097
|
||||||
React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f
|
React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f
|
||||||
React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2
|
React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2
|
||||||
@ -835,12 +862,14 @@ SPEC CHECKSUMS:
|
|||||||
React-RCTVibration: 3b52a7dced19cdb025b4f88ab26ceb2d85f30ba2
|
React-RCTVibration: 3b52a7dced19cdb025b4f88ab26ceb2d85f30ba2
|
||||||
React-runtimeexecutor: a9d3c82ddf7ffdad9fbe6a81c6d6f8c06385464d
|
React-runtimeexecutor: a9d3c82ddf7ffdad9fbe6a81c6d6f8c06385464d
|
||||||
ReactCommon: 07d0c460b9ba9af3eaf1b8f5abe7daaad28c9c4e
|
ReactCommon: 07d0c460b9ba9af3eaf1b8f5abe7daaad28c9c4e
|
||||||
RNCAsyncStorage: 4efdcd2f7378421b29467c9de58f43552b60cf5b
|
RNCAsyncStorage: 005c0e2f09575360f142d0d1f1f15e4ec575b1af
|
||||||
RNFastImage: cced864a4a2eac27c5c10ac16bd5e8b9d2be4504
|
RNFastImage: 945abf54742505d790d9024d230c69b1e866bc88
|
||||||
RNGestureHandler: 6e757e487a4834e7280e98e9bac66d2d9c575e9c
|
RNFS: fc610f78fdf8bfc89a9e5cc2f898519f4dba1002
|
||||||
RNReanimated: 190b6930d5d94832061278e1070bbe313e50c830
|
RNGestureHandler: 4f4986408310a43f1606c391f38f76e0d6e790d5
|
||||||
|
RNReanimated: 46cdb89ca59ab7181334f4ed05a70e82ddb36751
|
||||||
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
||||||
RNSentry: 6fbe6a4194cf632e5dc02a640741512faa943dbf
|
RNSentry: fbbdcd7213058e3de5fbaa452b25a06a16b4b382
|
||||||
|
RNShareMenu: c69282e50ac439737a86949a55c7b023b90027c8
|
||||||
RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8
|
RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8
|
||||||
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
|
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
|
||||||
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
||||||
@ -848,6 +877,6 @@ SPEC CHECKSUMS:
|
|||||||
UMTaskManagerInterface: 3184c93ecc290bd422c6e344badc89b601e9c29b
|
UMTaskManagerInterface: 3184c93ecc290bd422c6e344badc89b601e9c29b
|
||||||
Yoga: d6b6a80659aa3e91aaba01d0012e7edcbedcbecd
|
Yoga: d6b6a80659aa3e91aaba01d0012e7edcbedcbecd
|
||||||
|
|
||||||
PODFILE CHECKSUM: 9bf9d386bac4ff98f76fc93f120c9922660384b5
|
PODFILE CHECKSUM: 26ee7ffc1b88088246dc6a04f532230c6d5a5884
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.11.3
|
||||||
|
27
ios/ShareExtension/Base.lproj/MainInterface.storyboard
Normal file
27
ios/ShareExtension/Base.lproj/MainInterface.storyboard
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--React Share View Controller-->
|
||||||
|
<scene sceneID="ceB-am-kn3">
|
||||||
|
<objects>
|
||||||
|
<viewController id="j1y-V4-xli" customClass="ReactShareViewController" customModule="ShareExtension" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||||
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="122" y="105"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
70
ios/ShareExtension/Info.plist
Normal file
70
ios/ShareExtension/Info.plist
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<string>TRUEPREDICATE</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
<key>HostAppBundleIdentifier</key>
|
||||||
|
<string>com.xmflsct.app.tooot</string>
|
||||||
|
<key>HostAppURLScheme</key>
|
||||||
|
<string>tooot://</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<key>ReactShareViewBackgroundColor</key>
|
||||||
|
<dict>
|
||||||
|
<key>Red</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Green</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Blue</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Alpha</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Transparent</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
8
ios/ShareExtension/ShareExtension-Bridging-Header.h
Normal file
8
ios/ShareExtension/ShareExtension-Bridging-Header.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <React/RCTBridge.h>
|
||||||
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <React/RCTBridgeDelegate.h>
|
||||||
|
#import <React/RCTRootView.h>
|
@ -11,6 +11,7 @@
|
|||||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
|
34A37A6C820725DC6DDAA0EE /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */; };
|
||||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||||
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */; };
|
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */; };
|
||||||
5EE088C926297820007E5FEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EE088CB26297820007E5FEC /* InfoPlist.strings */; };
|
5EE088C926297820007E5FEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EE088CB26297820007E5FEC /* InfoPlist.strings */; };
|
||||||
@ -19,8 +20,36 @@
|
|||||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||||
DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */; };
|
DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */; };
|
||||||
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */; };
|
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */; };
|
||||||
|
E633A426281EAEAB000E540F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E633A424281EAEAB000E540F /* MainInterface.storyboard */; };
|
||||||
|
E633A42B281EAEAB000E540F /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E633A420281EAEAB000E540F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
E633A430281EAF38000E540F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E633A42F281EAF38000E540F /* ShareViewController.swift */; };
|
||||||
|
E633A437281EB5BC000E540F /* ReactShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E633A436281EB5BC000E540F /* ReactShareViewController.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
E633A428281EAEAB000E540F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = E633A41F281EAEAB000E540F;
|
||||||
|
remoteInfo = ShareExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
E633A42A281EAEAB000E540F /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
E633A42B281EAEAB000E540F /* ShareExtension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
||||||
13B07F961A680F5B00A75B9A /* tooot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tooot.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* tooot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tooot.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -30,6 +59,9 @@
|
|||||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = tooot/Images.xcassets; sourceTree = "<group>"; };
|
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = tooot/Images.xcassets; sourceTree = "<group>"; };
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tooot/Info.plist; sourceTree = "<group>"; };
|
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tooot/Info.plist; sourceTree = "<group>"; };
|
||||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = tooot/main.m; sourceTree = "<group>"; };
|
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = tooot/main.m; sourceTree = "<group>"; };
|
||||||
|
1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-tooot.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-tooot.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = RootViewColor.xcassets; path = tooot/RootViewColor.xcassets; sourceTree = "<group>"; };
|
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = RootViewColor.xcassets; path = tooot/RootViewColor.xcassets; sourceTree = "<group>"; };
|
||||||
5EE088CA26297820007E5FEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
5EE088CA26297820007E5FEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
@ -42,6 +74,12 @@
|
|||||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
|
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||||
|
E633A420281EAEAB000E540F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
E633A425281EAEAB000E540F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
|
E633A427281EAEAB000E540F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E633A42F281EAF38000E540F /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = "<group>"; };
|
||||||
|
E633A431281EB55C000E540F /* ShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ShareExtension-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
E633A436281EB5BC000E540F /* ReactShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReactShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ReactShareViewController.swift"; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
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; };
|
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; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@ -55,6 +93,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41D281EAEAB000E540F /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
34A37A6C820725DC6DDAA0EE /* libPods-ShareExtension.a in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@ -91,6 +137,7 @@
|
|||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
||||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */,
|
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */,
|
||||||
|
1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -116,6 +163,7 @@
|
|||||||
5EE44DD52600124E00A9BCED /* File.swift */,
|
5EE44DD52600124E00A9BCED /* File.swift */,
|
||||||
13B07FAE1A68108700A75B9A /* tooot */,
|
13B07FAE1A68108700A75B9A /* tooot */,
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
|
E633A421281EAEAB000E540F /* ShareExtension */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
D65327D7A22EEC0BE12398D9 /* Pods */,
|
D65327D7A22EEC0BE12398D9 /* Pods */,
|
||||||
@ -131,6 +179,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13B07F961A680F5B00A75B9A /* tooot.app */,
|
13B07F961A680F5B00A75B9A /* tooot.app */,
|
||||||
|
E633A420281EAEAB000E540F /* ShareExtension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -149,10 +198,24 @@
|
|||||||
children = (
|
children = (
|
||||||
6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */,
|
6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */,
|
||||||
7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */,
|
7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */,
|
||||||
|
49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */,
|
||||||
|
312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */,
|
||||||
);
|
);
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E633A421281EAEAB000E540F /* ShareExtension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E633A436281EB5BC000E540F /* ReactShareViewController.swift */,
|
||||||
|
E633A42F281EAF38000E540F /* ShareViewController.swift */,
|
||||||
|
E633A424281EAEAB000E540F /* MainInterface.storyboard */,
|
||||||
|
E633A427281EAEAB000E540F /* Info.plist */,
|
||||||
|
E633A431281EB55C000E540F /* ShareExtension-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = ShareExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -168,22 +231,45 @@
|
|||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||||
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */,
|
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */,
|
||||||
|
E633A42A281EAEAB000E540F /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
E633A429281EAEAB000E540F /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = tooot;
|
name = tooot;
|
||||||
productName = tooot;
|
productName = tooot;
|
||||||
productReference = 13B07F961A680F5B00A75B9A /* tooot.app */;
|
productReference = 13B07F961A680F5B00A75B9A /* tooot.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
E633A41F281EAEAB000E540F /* ShareExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = E633A42E281EAEAB000E540F /* Build configuration list for PBXNativeTarget "ShareExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
8E32C2F04B8226F2A839525E /* [CP] Check Pods Manifest.lock */,
|
||||||
|
E633A41C281EAEAB000E540F /* Sources */,
|
||||||
|
E633A41D281EAEAB000E540F /* Frameworks */,
|
||||||
|
E633A41E281EAEAB000E540F /* Resources */,
|
||||||
|
9620878489526FB1EDDF9FB7 /* [CP] Copy Pods Resources */,
|
||||||
|
E633A438281EB628000E540F /* Bundle React Native code and images */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = ShareExtension;
|
||||||
|
productName = ShareExtension;
|
||||||
|
productReference = E633A420281EAEAB000E540F /* ShareExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1330;
|
||||||
LastUpgradeCheck = 1320;
|
LastUpgradeCheck = 1320;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
@ -191,6 +277,12 @@
|
|||||||
LastSwiftMigration = 1240;
|
LastSwiftMigration = 1240;
|
||||||
ProvisioningStyle = Manual;
|
ProvisioningStyle = Manual;
|
||||||
};
|
};
|
||||||
|
E633A41F281EAEAB000E540F = {
|
||||||
|
CreatedOnToolsVersion = 13.3.1;
|
||||||
|
DevelopmentTeam = 8EGBLQ2MA6;
|
||||||
|
LastSwiftMigration = 1330;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "tooot" */;
|
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "tooot" */;
|
||||||
@ -208,6 +300,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
13B07F861A680F5B00A75B9A /* tooot */,
|
13B07F861A680F5B00A75B9A /* tooot */,
|
||||||
|
E633A41F281EAEAB000E540F /* ShareExtension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@ -227,6 +320,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41E281EAEAB000E540F /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
E633A426281EAEAB000E540F /* MainInterface.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
@ -306,6 +407,64 @@
|
|||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
8E32C2F04B8226F2A839525E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
9620878489526FB1EDDF9FB7 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-resources.sh",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
E633A438281EB628000E540F /* Bundle React Native code and images */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Bundle React Native code and images";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "export NODE_BINARY=node\nexport ENTRY_FILE=index.share.js\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||||
|
};
|
||||||
FD10A7F022414F080027D42C /* Start Packager */ = {
|
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -339,8 +498,25 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41C281EAEAB000E540F /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
E633A437281EB5BC000E540F /* ReactShareViewController.swift in Sources */,
|
||||||
|
E633A430281EAF38000E540F /* ShareViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
E633A429281EAEAB000E540F /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = E633A41F281EAEAB000E540F /* ShareExtension */;
|
||||||
|
targetProxy = E633A428281EAEAB000E540F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
|
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@ -360,6 +536,14 @@
|
|||||||
name = InfoPlist.strings;
|
name = InfoPlist.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E633A424281EAEAB000E540F /* MainInterface.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
E633A425281EAEAB000E540F /* Base */,
|
||||||
|
);
|
||||||
|
name = MainInterface.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
@ -367,6 +551,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */;
|
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
@ -405,6 +590,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */;
|
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
@ -553,6 +739,85 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
E633A42C281EAEAB000E540F /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
E633A42D281EAEAB000E540F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@ -574,6 +839,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
E633A42E281EAEAB000E540F /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
E633A42C281EAEAB000E540F /* Debug */,
|
||||||
|
E633A42D281EAEAB000E540F /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
@ -25,8 +25,10 @@
|
|||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
<key>CFBundleURLName</key>
|
<key>CFBundleURLName</key>
|
||||||
<string>gizmos</string>
|
<string>com.xmflsct.app.tooot</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>tooot</string>
|
<string>tooot</string>
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
<string>development</string>
|
<string>development</string>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.xmflsct.app.tooot</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.device.audio-input</key>
|
<key>com.apple.security.device.audio-input</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.device.camera</key>
|
<key>com.apple.security.device.camera</key>
|
||||||
|
63
package.json
63
package.json
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "tooot",
|
"name": "tooot",
|
||||||
"versions": {
|
"versions": {
|
||||||
"native": "220328",
|
"native": "220428",
|
||||||
"major": 3,
|
"major": 4,
|
||||||
"minor": 6,
|
"minor": 0,
|
||||||
"patch": 0,
|
"patch": 0,
|
||||||
"expo": "44.0.0"
|
"expo": "44.0.0"
|
||||||
},
|
},
|
||||||
@ -27,19 +27,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/react-native-action-sheet": "3.13.0",
|
"@expo/react-native-action-sheet": "3.13.0",
|
||||||
"@neverdull-agency/expo-unlimited-secure-store": "1.0.10",
|
"@neverdull-agency/expo-unlimited-secure-store": "1.0.10",
|
||||||
"@react-native-async-storage/async-storage": "1.17.0",
|
"@react-native-async-storage/async-storage": "1.17.3",
|
||||||
"@react-native-community/blur": "3.6.0",
|
"@react-native-community/blur": "3.6.0",
|
||||||
"@react-native-community/cameraroll": "4.1.2",
|
"@react-native-community/cameraroll": "4.1.2",
|
||||||
"@react-native-community/netinfo": "8.2.0",
|
"@react-native-community/netinfo": "8.3.0",
|
||||||
"@react-native-community/segmented-control": "2.2.2",
|
"@react-native-community/segmented-control": "2.2.2",
|
||||||
"@react-navigation/bottom-tabs": "6.2.0",
|
"@react-navigation/bottom-tabs": "6.3.1",
|
||||||
"@react-navigation/native": "6.0.8",
|
"@react-navigation/native": "6.0.10",
|
||||||
"@react-navigation/native-stack": "6.5.2",
|
"@react-navigation/native-stack": "6.6.2",
|
||||||
"@react-navigation/stack": "6.1.1",
|
"@react-navigation/stack": "6.2.1",
|
||||||
"@reduxjs/toolkit": "1.8.0",
|
"@reduxjs/toolkit": "1.8.1",
|
||||||
"@sentry/react-native": "3.3.6",
|
"@sentry/react-native": "3.4.1",
|
||||||
"@sharcoux/slider": "5.6.5",
|
"@sharcoux/slider": "6.0.2",
|
||||||
"axios": "0.26.1",
|
"axios": "0.27.2",
|
||||||
"expo": "44.0.6",
|
"expo": "44.0.6",
|
||||||
"expo-auth-session": "3.5.0",
|
"expo-auth-session": "3.5.0",
|
||||||
"expo-av": "10.2.1",
|
"expo-av": "10.2.1",
|
||||||
@ -62,51 +62,54 @@
|
|||||||
"expo-updates": "0.11.6",
|
"expo-updates": "0.11.6",
|
||||||
"expo-video-thumbnails": "6.2.0",
|
"expo-video-thumbnails": "6.2.0",
|
||||||
"expo-web-browser": "10.1.1",
|
"expo-web-browser": "10.1.1",
|
||||||
"i18next": "21.6.14",
|
"i18next": "21.6.16",
|
||||||
"li": "1.3.0",
|
"li": "1.3.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-i18next": "11.16.2",
|
"react-i18next": "11.16.7",
|
||||||
"react-native": "0.67.4",
|
"react-native": "0.67.4",
|
||||||
"react-native-animated-spinkit": "1.5.2",
|
"react-native-animated-spinkit": "1.5.2",
|
||||||
"react-native-base64": "^0.2.1",
|
"react-native-base64": "^0.2.1",
|
||||||
"react-native-blurhash": "1.1.9",
|
"react-native-blurhash": "1.1.10",
|
||||||
"react-native-fast-image": "8.5.11",
|
"react-native-fast-image": "8.5.11",
|
||||||
"react-native-feather": "1.1.2",
|
"react-native-feather": "1.1.2",
|
||||||
"react-native-flash-message": "0.2.1",
|
"react-native-flash-message": "0.2.1",
|
||||||
"react-native-gesture-handler": "2.3.2",
|
"react-native-fs": "^2.19.0",
|
||||||
|
"react-native-gesture-handler": "2.4.1",
|
||||||
"react-native-htmlview": "0.16.0",
|
"react-native-htmlview": "0.16.0",
|
||||||
"react-native-pager-view": "5.4.15",
|
"react-native-pager-view": "5.4.11",
|
||||||
"react-native-reanimated": "2.5.0",
|
"react-native-reanimated": "2.8.0",
|
||||||
"react-native-safe-area-context": "4.2.4",
|
"react-native-safe-area-context": "4.2.5",
|
||||||
"react-native-screens": "3.13.1",
|
"react-native-screens": "3.13.1",
|
||||||
|
"react-native-share-menu": "^5.0.5",
|
||||||
"react-native-svg": "12.3.0",
|
"react-native-svg": "12.3.0",
|
||||||
"react-native-swipe-list-view": "3.2.9",
|
"react-native-swipe-list-view": "3.2.9",
|
||||||
"react-native-tab-view": "3.1.1",
|
"react-native-tab-view": "3.1.1",
|
||||||
"react-query": "3.34.19",
|
"react-native-uuid": "^2.0.1",
|
||||||
"react-redux": "7.2.6",
|
"react-query": "3.38.0",
|
||||||
|
"react-redux": "8.0.1",
|
||||||
"react-timeago": "6.2.1",
|
"react-timeago": "6.2.1",
|
||||||
"redux-persist": "6.0.0",
|
"redux-persist": "6.0.0",
|
||||||
"rn-placeholder": "3.0.3",
|
"rn-placeholder": "3.0.3",
|
||||||
"sentry-expo": "4.1.0",
|
"sentry-expo": "4.1.1",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.4.0",
|
||||||
"valid-url": "1.0.9"
|
"valid-url": "1.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.17.8",
|
"@babel/core": "7.17.9",
|
||||||
"@babel/plugin-proposal-optional-chaining": "7.16.7",
|
"@babel/plugin-proposal-optional-chaining": "7.16.7",
|
||||||
"@babel/preset-typescript": "7.16.7",
|
"@babel/preset-typescript": "7.16.7",
|
||||||
"@expo/config": "6.0.19",
|
"@expo/config": "6.0.23",
|
||||||
"@types/lodash": "4.14.180",
|
"@types/lodash": "4.14.182",
|
||||||
"@types/react": "17.0.43",
|
"@types/react": "17.0.43",
|
||||||
"@types/react-dom": "17.0.14",
|
"@types/react-dom": "17.0.14",
|
||||||
"@types/react-native": "0.67.3",
|
"@types/react-native": "0.67.6",
|
||||||
"@types/react-native-base64": "^0.2.0",
|
"@types/react-native-base64": "^0.2.0",
|
||||||
"@types/react-redux": "7.1.23",
|
"@types/react-native-share-menu": "^5.0.2",
|
||||||
"@types/react-timeago": "4.1.3",
|
"@types/react-timeago": "4.1.3",
|
||||||
"@types/valid-url": "1.0.3",
|
"@types/valid-url": "1.0.3",
|
||||||
"@welldone-software/why-did-you-render": "6.2.3",
|
"@welldone-software/why-did-you-render": "7.0.1",
|
||||||
"babel-plugin-module-resolver": "4.1.0",
|
"babel-plugin-module-resolver": "4.1.0",
|
||||||
"babel-plugin-transform-remove-console": "6.9.4",
|
"babel-plugin-transform-remove-console": "6.9.4",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
|
25
patches/@types+react-native-share-menu+5.0.2.patch
Normal file
25
patches/@types+react-native-share-menu+5.0.2.patch
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
diff --git a/node_modules/@types/react-native-share-menu/index.d.ts b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
index f52822c..23d9f99 100755
|
||||||
|
--- a/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
+++ b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
@@ -6,9 +6,8 @@
|
||||||
|
// Minimum TypeScript Version: 3.7
|
||||||
|
|
||||||
|
export interface ShareData {
|
||||||
|
- mimeType: string;
|
||||||
|
- data: string | string[];
|
||||||
|
- extraData?: object | undefined;
|
||||||
|
+ data: {data: {mimeType: string; data: string}[]};
|
||||||
|
+ extraData?: {share: {mimeType: string; data: string}[]} | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ShareCallback = (share?: ShareData) => void;
|
||||||
|
@@ -28,7 +27,7 @@ interface ShareMenuReactView {
|
||||||
|
dismissExtension(error?: string): void;
|
||||||
|
openApp(): void;
|
||||||
|
continueInApp(extraData?: object): void;
|
||||||
|
- data(): Promise<{mimeType: string, data: string}>;
|
||||||
|
+ data(): Promise<{data: {mimeType: string; data: string}[]}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShareMenuReactView: ShareMenuReactView;
|
@ -1,5 +1,5 @@
|
|||||||
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec
|
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
index db0fada..9a2457c 100644
|
index db0fada..54d8d5b 100644
|
||||||
--- a/node_modules/react-native-fast-image/RNFastImage.podspec
|
--- a/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec
|
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
@@ -16,6 +16,6 @@ Pod::Spec.new do |s|
|
@@ -16,6 +16,6 @@ Pod::Spec.new do |s|
|
||||||
@ -7,7 +7,7 @@ index db0fada..9a2457c 100644
|
|||||||
|
|
||||||
s.dependency 'React-Core'
|
s.dependency 'React-Core'
|
||||||
- s.dependency 'SDWebImage', '~> 5.11.1'
|
- s.dependency 'SDWebImage', '~> 5.11.1'
|
||||||
+ s.dependency 'SDWebImage', '~> 5.12.3'
|
+ s.dependency 'SDWebImage', '~> 5.12.5'
|
||||||
s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
|
s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
|
||||||
end
|
end
|
||||||
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle
|
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle
|
||||||
|
479
patches/react-native-share-menu+5.0.5.patch
Normal file
479
patches/react-native-share-menu+5.0.5.patch
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Constants.swift b/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
index 2811008..63761c3 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
@@ -23,7 +23,7 @@ public let COULD_NOT_PARSE_IMG_ERROR = "Couldn't parse image"
|
||||||
|
public let COULD_NOT_SAVE_FILE_ERROR = "Couldn't save file on disk"
|
||||||
|
public let NO_EXTENSION_CONTEXT_ERROR = "No extension context attached"
|
||||||
|
public let NO_DELEGATE_ERROR = "No ReactShareViewDelegate attached"
|
||||||
|
-public let COULD_NOT_FIND_ITEM_ERROR = "Couldn't find item attached to this share"
|
||||||
|
+public let COULD_NOT_FIND_ITEMS_ERROR = "Couldn't find items attached to this share"
|
||||||
|
|
||||||
|
// MARK: Keys
|
||||||
|
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
index 6c4922a..1277df2 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
@@ -9,7 +9,7 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- var sharedData: [String:String]?
|
||||||
|
+ var sharedData: [[String:String]?]?
|
||||||
|
|
||||||
|
static var initialShare: (UIApplication, URL, [UIApplication.OpenURLOptionsKey : Any])?
|
||||||
|
|
||||||
|
@@ -91,7 +91,7 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
|
||||||
|
let extraData = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any]
|
||||||
|
|
||||||
|
- if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [String:String] {
|
||||||
|
+ if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [[String:String]] {
|
||||||
|
sharedData = data
|
||||||
|
dispatchEvent(with: data, and: extraData)
|
||||||
|
userDefaults.removeObject(forKey: USER_DEFAULTS_KEY)
|
||||||
|
@@ -100,25 +100,22 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
|
||||||
|
@objc(getSharedText:)
|
||||||
|
func getSharedText(callback: RCTResponseSenderBlock) {
|
||||||
|
- guard var data: [String:Any] = sharedData else {
|
||||||
|
- callback([])
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
+ var data = [DATA_KEY: sharedData] as [String: Any]
|
||||||
|
|
||||||
|
if let bundleId = Bundle.main.bundleIdentifier, let userDefaults = UserDefaults(suiteName: "group.\(bundleId)") {
|
||||||
|
- data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any]
|
||||||
|
+ data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String: Any]
|
||||||
|
} else {
|
||||||
|
print("Error: \(NO_APP_GROUP_ERROR)")
|
||||||
|
}
|
||||||
|
|
||||||
|
callback([data as Any])
|
||||||
|
- sharedData = nil
|
||||||
|
+ sharedData = []
|
||||||
|
}
|
||||||
|
|
||||||
|
- func dispatchEvent(with data: [String:String], and extraData: [String:Any]?) {
|
||||||
|
+ func dispatchEvent(with data: [[String:String]], and extraData: [String:Any]?) {
|
||||||
|
guard hasListeners else { return }
|
||||||
|
|
||||||
|
- var finalData = data as [String:Any]
|
||||||
|
+ var finalData = [DATA_KEY: data] as [String: Any]
|
||||||
|
if (extraData != nil) {
|
||||||
|
finalData[EXTRA_DATA_KEY] = extraData
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
index 5d21773..d8a0847 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
@@ -3,8 +3,9 @@
|
||||||
|
// RNShareMenu
|
||||||
|
//
|
||||||
|
// Created by Gustavo Parreira on 28/07/2020.
|
||||||
|
-//
|
||||||
|
+// Modified by Veselin Stoyanov on 17/04/2021.
|
||||||
|
|
||||||
|
+import Foundation
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
|
@objc(ShareMenuReactView)
|
||||||
|
@@ -65,12 +66,12 @@ public class ShareMenuReactView: NSObject {
|
||||||
|
|
||||||
|
let extensionContext = viewDelegate.loadExtensionContext()
|
||||||
|
|
||||||
|
- guard let item = extensionContext.inputItems.first as? NSExtensionItem else {
|
||||||
|
- print("Error: \(COULD_NOT_FIND_ITEM_ERROR)")
|
||||||
|
+ guard let items = extensionContext.inputItems as? [NSExtensionItem] else {
|
||||||
|
+ print("Error: \(COULD_NOT_FIND_ITEMS_ERROR)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- viewDelegate.continueInApp(with: item, and: extraData)
|
||||||
|
+ viewDelegate.continueInApp(with: items, and: extraData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(data:reject:)
|
||||||
|
@@ -82,91 +83,96 @@ public class ShareMenuReactView: NSObject {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- extractDataFromContext(context: extensionContext) { (data, mimeType, error) in
|
||||||
|
+ extractDataFromContext(context: extensionContext) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
reject("error", error?.description, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- resolve([MIME_TYPE_KEY: mimeType, DATA_KEY: data])
|
||||||
|
+ resolve([DATA_KEY: data])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping (String?, String?, NSException?) -> Void) {
|
||||||
|
- let item:NSExtensionItem! = context.inputItems.first as? NSExtensionItem
|
||||||
|
- let attachments:[AnyObject]! = item.attachments
|
||||||
|
-
|
||||||
|
- var urlProvider:NSItemProvider! = nil
|
||||||
|
- var imageProvider:NSItemProvider! = nil
|
||||||
|
- var textProvider:NSItemProvider! = nil
|
||||||
|
- var dataProvider:NSItemProvider! = nil
|
||||||
|
-
|
||||||
|
- for provider in attachments {
|
||||||
|
- if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||||
|
- urlProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||||
|
- textProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||||
|
- imageProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) {
|
||||||
|
- dataProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping ([Any]?, NSException?) -> Void) {
|
||||||
|
+ DispatchQueue.global().async {
|
||||||
|
+ let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
+ let items:[NSExtensionItem]! = context.inputItems as? [NSExtensionItem]
|
||||||
|
+ var results: [[String: String]] = []
|
||||||
|
|
||||||
|
- if (urlProvider != nil) {
|
||||||
|
- urlProvider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in
|
||||||
|
- let url: URL! = item as? URL
|
||||||
|
+ for item in items {
|
||||||
|
+ guard let attachments = item.attachments else {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find attachments", userInfo:nil))
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- callback(url.absoluteString, "text/plain", nil)
|
||||||
|
- }
|
||||||
|
- } else if (imageProvider != nil) {
|
||||||
|
- imageProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in
|
||||||
|
- let imageUrl: URL! = item as? URL
|
||||||
|
+ for provider in attachments {
|
||||||
|
+ if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in
|
||||||
|
+ let url: URL! = item as? URL
|
||||||
|
|
||||||
|
- if (imageUrl != nil) {
|
||||||
|
- if let imageData = try? Data(contentsOf: imageUrl) {
|
||||||
|
- callback(imageUrl.absoluteString, self.extractMimeType(from: imageUrl), nil)
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- let image: UIImage! = item as? UIImage
|
||||||
|
+ results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
|
||||||
|
- if (image != nil) {
|
||||||
|
- let imageData: Data! = image.pngData();
|
||||||
|
+ semaphore.signal()
|
||||||
|
+ }
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in
|
||||||
|
+ let text:String! = item as? String
|
||||||
|
+
|
||||||
|
+ results.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
|
||||||
|
- // Creating temporary URL for image data (UIImage)
|
||||||
|
- guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else {
|
||||||
|
- return
|
||||||
|
+ semaphore.signal()
|
||||||
|
+ }
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in
|
||||||
|
+ let imageUrl: URL! = item as? URL
|
||||||
|
+
|
||||||
|
+ if (imageUrl != nil) {
|
||||||
|
+ if let imageData = try? Data(contentsOf: imageUrl) {
|
||||||
|
+ results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: imageUrl)])
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ let image: UIImage! = item as? UIImage
|
||||||
|
+
|
||||||
|
+ if (image != nil) {
|
||||||
|
+ let imageData: Data! = image.pngData();
|
||||||
|
+
|
||||||
|
+ // Creating temporary URL for image data (UIImage)
|
||||||
|
+ guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ do {
|
||||||
|
+ // Writing the image to the URL
|
||||||
|
+ try imageData.write(to: imageURL)
|
||||||
|
+
|
||||||
|
+ results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])
|
||||||
|
+ } catch {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in
|
||||||
|
+ let url: URL! = item as? URL
|
||||||
|
|
||||||
|
- do {
|
||||||
|
- // Writing the image to the URL
|
||||||
|
- try imageData.write(to: imageURL)
|
||||||
|
+ results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: url)])
|
||||||
|
|
||||||
|
- callback(imageURL.absoluteString, imageURL.extractMimeType(), nil)
|
||||||
|
- } catch {
|
||||||
|
- callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- } else if (textProvider != nil) {
|
||||||
|
- textProvider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in
|
||||||
|
- let text:String! = item as? String
|
||||||
|
|
||||||
|
- callback(text, "text/plain", nil)
|
||||||
|
- }
|
||||||
|
- } else if (dataProvider != nil) {
|
||||||
|
- dataProvider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in
|
||||||
|
- let url: URL! = item as? URL
|
||||||
|
-
|
||||||
|
- callback(url.absoluteString, self.extractMimeType(from: url), nil)
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil))
|
||||||
|
+ callback(results, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
index 0189ef6..e620257 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
@@ -62,7 +62,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha
|
||||||
|
self.openHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
- func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?) {
|
||||||
|
- handlePost(item, extraData: extraData)
|
||||||
|
+ func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?) {
|
||||||
|
+ handlePost(items, extraData: extraData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
index 0aa4c58..d2bc970 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
@@ -10,5 +10,5 @@ public protocol ReactShareViewDelegate {
|
||||||
|
|
||||||
|
func openApp()
|
||||||
|
|
||||||
|
- func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?)
|
||||||
|
+ func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?)
|
||||||
|
}
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
index 7faf6e4..81aef73 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
@@ -6,7 +6,9 @@
|
||||||
|
//
|
||||||
|
// Created by Gustavo Parreira on 26/07/2020.
|
||||||
|
//
|
||||||
|
+// Modified by Veselin Stoyanov on 17/04/2021.
|
||||||
|
|
||||||
|
+import Foundation
|
||||||
|
import MobileCoreServices
|
||||||
|
import UIKit
|
||||||
|
import Social
|
||||||
|
@@ -15,6 +17,7 @@ import RNShareMenu
|
||||||
|
class ShareViewController: SLComposeServiceViewController {
|
||||||
|
var hostAppId: String?
|
||||||
|
var hostAppUrlScheme: String?
|
||||||
|
+ var sharedItems: [Any] = []
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
@@ -39,12 +42,12 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
|
||||||
|
override func didSelectPost() {
|
||||||
|
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
|
||||||
|
- guard let item = extensionContext?.inputItems.first as? NSExtensionItem else {
|
||||||
|
+ guard let items = extensionContext?.inputItems as? [NSExtensionItem] else {
|
||||||
|
cancelRequest()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- handlePost(item)
|
||||||
|
+ handlePost(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func configurationItems() -> [Any]! {
|
||||||
|
@@ -52,24 +55,50 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
- func handlePost(_ item: NSExtensionItem, extraData: [String:Any]? = nil) {
|
||||||
|
- guard let provider = item.attachments?.first else {
|
||||||
|
- cancelRequest()
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
+ func handlePost(_ items: [NSExtensionItem], extraData: [String:Any]? = nil) {
|
||||||
|
+ DispatchQueue.global().async {
|
||||||
|
+ guard let hostAppId = self.hostAppId else {
|
||||||
|
+ self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+ guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
+ self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if let data = extraData {
|
||||||
|
- storeExtraData(data)
|
||||||
|
- } else {
|
||||||
|
- removeExtraData()
|
||||||
|
- }
|
||||||
|
+ if let data = extraData {
|
||||||
|
+ self.storeExtraData(data)
|
||||||
|
+ } else {
|
||||||
|
+ self.removeExtraData()
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if provider.isText {
|
||||||
|
- storeText(withProvider: provider)
|
||||||
|
- } else if provider.isURL {
|
||||||
|
- storeUrl(withProvider: provider)
|
||||||
|
- } else {
|
||||||
|
- storeFile(withProvider: provider)
|
||||||
|
+ let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
+ var results: [Any] = []
|
||||||
|
+
|
||||||
|
+ for item in items {
|
||||||
|
+ guard let attachments = item.attachments else {
|
||||||
|
+ self.cancelRequest()
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for provider in attachments {
|
||||||
|
+ if provider.isText {
|
||||||
|
+ self.storeText(withProvider: provider, semaphore)
|
||||||
|
+ } else if provider.isURL {
|
||||||
|
+ self.storeUrl(withProvider: provider, semaphore)
|
||||||
|
+ } else {
|
||||||
|
+ self.storeFile(withProvider: provider, semaphore)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ userDefaults.set(self.sharedItems,
|
||||||
|
+ forKey: USER_DEFAULTS_KEY)
|
||||||
|
+ userDefaults.synchronize()
|
||||||
|
+
|
||||||
|
+ self.openHostApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -99,7 +128,7 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
userDefaults.synchronize()
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeText(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeText(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -109,24 +138,13 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: COULD_NOT_FIND_STRING_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let hostAppId = self.hostAppId else {
|
||||||
|
- self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
|
||||||
|
- userDefaults.set([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
-
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeUrl(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeUrl(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -136,24 +154,13 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: COULD_NOT_FIND_URL_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let hostAppId = self.hostAppId else {
|
||||||
|
- self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- userDefaults.set([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeFile(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeFile(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -167,10 +174,6 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
guard let groupFileManagerContainer = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppId)")
|
||||||
|
else {
|
||||||
|
@@ -189,11 +192,8 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- userDefaults.set([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
-
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
src/@types/mastodon.d.ts
vendored
14
src/@types/mastodon.d.ts
vendored
@ -320,8 +320,6 @@ declare namespace Mastodon {
|
|||||||
max_expiration: number
|
max_expiration: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Custom - to be deprecated in v4
|
|
||||||
max_toot_chars?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mention = {
|
type Mention = {
|
||||||
@ -428,6 +426,7 @@ declare namespace Mastodon {
|
|||||||
reblogs_count: number
|
reblogs_count: number
|
||||||
favourites_count: number
|
favourites_count: number
|
||||||
replies_count: number
|
replies_count: number
|
||||||
|
edited_at?: string // FEATURE edit_post
|
||||||
favourited: boolean
|
favourited: boolean
|
||||||
reblogged: boolean
|
reblogged: boolean
|
||||||
muted: boolean
|
muted: boolean
|
||||||
@ -445,6 +444,17 @@ declare namespace Mastodon {
|
|||||||
text?: string
|
text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StatusHistory = {
|
||||||
|
content: Status['content']
|
||||||
|
spoiler_text: Status['spoiler_text']
|
||||||
|
sensitive: Status['sensitive']
|
||||||
|
created_at: Status['created_at']
|
||||||
|
poll: Status['poll']
|
||||||
|
account: Status['account']
|
||||||
|
media_attachments: Status['media_attachments']
|
||||||
|
emojis: Status['emojis']
|
||||||
|
}
|
||||||
|
|
||||||
type Source = {
|
type Source = {
|
||||||
// Base
|
// Base
|
||||||
note: string
|
note: string
|
||||||
|
5
src/@types/untyped.d.ts
vendored
5
src/@types/untyped.d.ts
vendored
@ -4,3 +4,8 @@ declare module 'li'
|
|||||||
declare module 'react-native-feather'
|
declare module 'react-native-feather'
|
||||||
declare module 'react-native-htmlview'
|
declare module 'react-native-htmlview'
|
||||||
declare module 'react-native-toast-message'
|
declare module 'react-native-toast-message'
|
||||||
|
|
||||||
|
declare module '@helpers/features' {
|
||||||
|
const features: { feature: string; version: number; reference?: string }[]
|
||||||
|
export default features
|
||||||
|
}
|
||||||
|
@ -27,8 +27,10 @@ import { addScreenshotListener } from 'expo-screen-capture'
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert, Platform, StatusBar } from 'react-native'
|
import { Alert, Platform, StatusBar } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import ShareMenu from 'react-native-share-menu'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import * as Sentry from 'sentry-expo'
|
import * as Sentry from 'sentry-expo'
|
||||||
|
import { useAppDispatch } from './store'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<RootStackParamList>()
|
const Stack = createNativeStackNavigator<RootStackParamList>()
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ export interface Props {
|
|||||||
|
|
||||||
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||||
const { t } = useTranslation('screens')
|
const { t } = useTranslation('screens')
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const instanceActive = useSelector(getInstanceActive)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
|
|
||||||
@ -157,6 +159,77 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
}
|
}
|
||||||
}, [instanceActive, instances, deeplinked])
|
}, [instanceActive, instances, deeplinked])
|
||||||
|
|
||||||
|
// Share Extension
|
||||||
|
const handleShare = useCallback(
|
||||||
|
(item?: {
|
||||||
|
extraData?: { share: { mimeType: string; data: string }[] }
|
||||||
|
}) => {
|
||||||
|
if (instanceActive < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!item ||
|
||||||
|
!item.extraData ||
|
||||||
|
!Array.isArray(item.extraData.share) ||
|
||||||
|
!item.extraData.share.length
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: string | undefined = undefined
|
||||||
|
let images: { type: string; uri: string }[] = []
|
||||||
|
let video: { type: string; uri: string } | undefined = undefined
|
||||||
|
item.extraData.share.forEach((d, i) => {
|
||||||
|
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
const typesVideo = ['mp4', 'm4v', 'mov', 'webm']
|
||||||
|
const { mimeType, data } = d
|
||||||
|
console.log('mimeType', mimeType)
|
||||||
|
console.log('data', data)
|
||||||
|
if (mimeType.startsWith('image/')) {
|
||||||
|
if (!typesImage.includes(mimeType.split('/')[1])) {
|
||||||
|
console.warn('Image type not supported:', mimeType.split('/')[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
images.push({ type: mimeType.split('/')[1], uri: data })
|
||||||
|
} else if (mimeType.startsWith('video/')) {
|
||||||
|
if (!typesVideo.includes(mimeType.split('/')[1])) {
|
||||||
|
console.warn('Video type not supported:', mimeType.split('/')[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
video = { type: mimeType.split('/')[1], uri: data }
|
||||||
|
} else {
|
||||||
|
if (typesImage.includes(data.split('.').pop() || '')) {
|
||||||
|
images.push({ type: data.split('.').pop()!, uri: data })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typesVideo.includes(data.split('.').pop() || '')) {
|
||||||
|
video = { type: data.split('.').pop()!, uri: data }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = !text ? data : text.concat(text, `\n${data}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
navigationRef.navigate('Screen-Compose', {
|
||||||
|
type: 'share',
|
||||||
|
text,
|
||||||
|
images,
|
||||||
|
video
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[instanceActive]
|
||||||
|
)
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('getting intial share')
|
||||||
|
ShareMenu.getInitialShare(handleShare)
|
||||||
|
}, [])
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('getting just share')
|
||||||
|
const listener = ShareMenu.addNewShareListener(handleShare)
|
||||||
|
return () => {
|
||||||
|
listener.remove()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StatusBar backgroundColor={colors.backgroundDefault} />
|
<StatusBar backgroundColor={colors.backgroundDefault} />
|
||||||
|
105
src/ShareExtension.tsx
Normal file
105
src/ShareExtension.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { Appearance, Platform, Pressable, Text } from 'react-native'
|
||||||
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
|
import RNFS from 'react-native-fs'
|
||||||
|
import { ShareMenuReactView } from 'react-native-share-menu'
|
||||||
|
import uuid from 'react-native-uuid'
|
||||||
|
|
||||||
|
// mimeType
|
||||||
|
// text/plain - text only, website URL, video?!
|
||||||
|
// image/jpeg - image
|
||||||
|
// video/mp4 - video
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
primary: {
|
||||||
|
light: 'rgb(18, 18, 18)',
|
||||||
|
dark: 'rgb(180, 180, 180)'
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
light: 'rgb(250, 250, 250)',
|
||||||
|
dark: 'rgb(18, 18, 18)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearDir = async (dir: string) => {
|
||||||
|
try {
|
||||||
|
const files = await RNFS.readDir(dir)
|
||||||
|
for (const file of files) {
|
||||||
|
await RNFS.unlink(file.path)
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.warn(err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShareExtension = () => {
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ShareMenuReactView.data().then(async ({ data }) => {
|
||||||
|
console.log('length', data.length)
|
||||||
|
const newData = []
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'ios':
|
||||||
|
for (const d of data) {
|
||||||
|
if (d.data.startsWith('file:///')) {
|
||||||
|
const extension = d.data.split('.').pop()?.toLowerCase()
|
||||||
|
const filename = `${uuid.v4()}.${extension}`
|
||||||
|
const groupDirectory = await RNFS.pathForGroup(
|
||||||
|
'group.com.xmflsct.app.tooot'
|
||||||
|
)
|
||||||
|
await clearDir(groupDirectory)
|
||||||
|
const newFilepath = `file://${groupDirectory}/${filename}`
|
||||||
|
console.log('newFilepath', newFilepath)
|
||||||
|
try {
|
||||||
|
await RNFS.copyFile(d.data, newFilepath)
|
||||||
|
newData.push({ ...d, data: newFilepath })
|
||||||
|
} catch (err: any) {
|
||||||
|
setErrorMessage(err.message)
|
||||||
|
console.warn(err.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newData.push(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'android':
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('new data', newData)
|
||||||
|
if (!errorMessage) {
|
||||||
|
ShareMenuReactView.continueInApp({ share: newData })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const theme = Appearance.getColorScheme() || 'light'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.background[theme]
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
if (errorMessage) {
|
||||||
|
ShareMenuReactView.dismissExtension(errorMessage)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!errorMessage ? (
|
||||||
|
<Text style={{ fontSize: 16, color: colors.primary[theme] }}>
|
||||||
|
{errorMessage}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Circle size={18} color={colors.primary[theme]} />
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShareExtension
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
@ -15,13 +16,12 @@ import {
|
|||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import validUrl from 'valid-url'
|
import validUrl from 'valid-url'
|
||||||
import EmojisContext from './helpers/EmojisContext'
|
import EmojisContext from './helpers/EmojisContext'
|
||||||
|
|
||||||
const EmojisList = React.memo(
|
const EmojisList = React.memo(
|
||||||
() => {
|
() => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
AccessibilityProps,
|
AccessibilityProps,
|
||||||
Image,
|
Image,
|
||||||
@ -39,125 +39,103 @@ export interface Props {
|
|||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
const GracefullyImage = React.memo(
|
const GracefullyImage = ({
|
||||||
({
|
accessibilityLabel,
|
||||||
accessibilityLabel,
|
accessibilityHint,
|
||||||
accessibilityHint,
|
hidden = false,
|
||||||
hidden = false,
|
uri,
|
||||||
uri,
|
blurhash,
|
||||||
blurhash,
|
dimension,
|
||||||
dimension,
|
onPress,
|
||||||
onPress,
|
style,
|
||||||
style,
|
imageStyle,
|
||||||
imageStyle,
|
setImageDimensions
|
||||||
setImageDimensions
|
}: Props) => {
|
||||||
}: Props) => {
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
const [originalFailed, setOriginalFailed] = useState(false)
|
||||||
const [originalFailed, setOriginalFailed] = useState(false)
|
const [imageLoaded, setImageLoaded] = useState(false)
|
||||||
const [imageLoaded, setImageLoaded] = useState(false)
|
|
||||||
|
|
||||||
const source = useMemo(() => {
|
const source = originalFailed
|
||||||
if (originalFailed) {
|
? { uri: uri.remote || undefined }
|
||||||
return { uri: uri.remote || undefined }
|
: {
|
||||||
} else {
|
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
||||||
return {
|
|
||||||
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [originalFailed])
|
|
||||||
|
|
||||||
const onLoad = useCallback(() => {
|
const onLoad = () => {
|
||||||
setImageLoaded(true)
|
setImageLoaded(true)
|
||||||
if (setImageDimensions && source.uri) {
|
if (setImageDimensions && source.uri) {
|
||||||
Image.getSize(source.uri, (width, height) =>
|
Image.getSize(source.uri, (width, height) =>
|
||||||
setImageDimensions({ width, height })
|
setImageDimensions({ width, height })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onError = () => {
|
||||||
|
if (!originalFailed) {
|
||||||
|
setOriginalFailed(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blurhashView = useMemo(() => {
|
||||||
|
if (hidden || !imageLoaded) {
|
||||||
|
if (blurhash) {
|
||||||
|
return (
|
||||||
|
<Blurhash
|
||||||
|
decodeAsync
|
||||||
|
blurhash={blurhash}
|
||||||
|
style={styles.placeholder}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
}, [source.uri])
|
return (
|
||||||
const onError = useCallback(() => {
|
<View
|
||||||
if (!originalFailed) {
|
|
||||||
setOriginalFailed(true)
|
|
||||||
}
|
|
||||||
}, [originalFailed])
|
|
||||||
|
|
||||||
const previewView = useMemo(
|
|
||||||
() =>
|
|
||||||
uri.preview && !imageLoaded ? (
|
|
||||||
<Image
|
|
||||||
fadeDuration={0}
|
|
||||||
source={{ uri: uri.preview }}
|
|
||||||
style={[
|
style={[
|
||||||
styles.placeholder,
|
styles.placeholder,
|
||||||
{ backgroundColor: colors.shimmerDefault }
|
{ backgroundColor: colors.shimmerDefault }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
) : null,
|
)
|
||||||
[]
|
}
|
||||||
)
|
} else {
|
||||||
const originalView = useMemo(
|
return null
|
||||||
() => (
|
}
|
||||||
|
}, [hidden, imageLoaded])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
{...(onPress
|
||||||
|
? { accessibilityRole: 'imagebutton' }
|
||||||
|
: { accessibilityRole: 'image' })}
|
||||||
|
accessibilityLabel={accessibilityLabel}
|
||||||
|
accessibilityHint={accessibilityHint}
|
||||||
|
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
|
||||||
|
{...(onPress
|
||||||
|
? hidden
|
||||||
|
? { disabled: true }
|
||||||
|
: { onPress }
|
||||||
|
: { disabled: true })}
|
||||||
|
>
|
||||||
|
{uri.preview && !imageLoaded ? (
|
||||||
<Image
|
<Image
|
||||||
fadeDuration={0}
|
fadeDuration={0}
|
||||||
source={source}
|
source={{ uri: uri.preview }}
|
||||||
style={[{ flex: 1 }, imageStyle]}
|
style={[
|
||||||
onLoad={onLoad}
|
styles.placeholder,
|
||||||
onError={onError}
|
{ backgroundColor: colors.shimmerDefault }
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
),
|
) : null}
|
||||||
[source]
|
<Image
|
||||||
)
|
fadeDuration={0}
|
||||||
const blurhashView = useMemo(() => {
|
source={source}
|
||||||
if (hidden || !imageLoaded) {
|
style={[{ flex: 1 }, imageStyle]}
|
||||||
if (blurhash) {
|
onLoad={onLoad}
|
||||||
return (
|
onError={onError}
|
||||||
<Blurhash
|
/>
|
||||||
decodeAsync
|
{blurhashView}
|
||||||
blurhash={blurhash}
|
</Pressable>
|
||||||
style={styles.placeholder}
|
)
|
||||||
/>
|
}
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.placeholder,
|
|
||||||
{ backgroundColor: colors.shimmerDefault }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}, [hidden, imageLoaded])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
{...(onPress
|
|
||||||
? { accessibilityRole: 'imagebutton' }
|
|
||||||
: { accessibilityRole: 'image' })}
|
|
||||||
accessibilityLabel={accessibilityLabel}
|
|
||||||
accessibilityHint={accessibilityHint}
|
|
||||||
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
|
|
||||||
{...(onPress
|
|
||||||
? hidden
|
|
||||||
? { disabled: true }
|
|
||||||
: { onPress }
|
|
||||||
: { disabled: true })}
|
|
||||||
>
|
|
||||||
{previewView}
|
|
||||||
{originalView}
|
|
||||||
{blurhashView}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prev, next) =>
|
|
||||||
prev.hidden === next.hidden &&
|
|
||||||
prev.uri.preview === next.uri.preview &&
|
|
||||||
prev.uri.original === next.uri.original &&
|
|
||||||
prev.uri.remote === next.uri.remote
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
placeholder: {
|
placeholder: {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
@ -19,7 +20,7 @@ const ComponentHashtag: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('search_account_press', { page: origin })
|
analytics('search_account_press', { page: origin })
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
||||||
import addInstance from '@utils/slices/instances/add'
|
import addInstance from '@utils/slices/instances/add'
|
||||||
import { Instance } from '@utils/slices/instancesSlice'
|
import { checkInstanceFeature, Instance } from '@utils/slices/instancesSlice'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
instanceDomain: string
|
instanceDomain: string
|
||||||
@ -25,13 +26,18 @@ const InstanceAuth = React.memo(
|
|||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const deprecateAuthFollow = useSelector(
|
||||||
|
checkInstanceFeature('deprecate_auth_follow')
|
||||||
|
)
|
||||||
const [request, response, promptAsync] = AuthSession.useAuthRequest(
|
const [request, response, promptAsync] = AuthSession.useAuthRequest(
|
||||||
{
|
{
|
||||||
clientId: appData.clientId,
|
clientId: appData.clientId,
|
||||||
clientSecret: appData.clientSecret,
|
clientSecret: appData.clientSecret,
|
||||||
scopes: ['read', 'write', 'follow', 'push'],
|
scopes: deprecateAuthFollow
|
||||||
|
? ['read', 'write', 'push']
|
||||||
|
: ['read', 'write', 'follow', 'push'],
|
||||||
redirectUri
|
redirectUri
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@ import openLink from '@components/openLink'
|
|||||||
import ParseEmojis from '@components/Parse/Emojis'
|
import ParseEmojis from '@components/Parse/Emojis'
|
||||||
import { useNavigation, useRoute } from '@react-navigation/native'
|
import { useNavigation, useRoute } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
@ -35,7 +36,7 @@ const renderNode = ({
|
|||||||
index: number
|
index: number
|
||||||
adaptedFontsize: number
|
adaptedFontsize: number
|
||||||
adaptedLineheight: number
|
adaptedLineheight: number
|
||||||
navigation: StackNavigationProp<Nav.TabLocalStackParamList>
|
navigation: StackNavigationProp<TabLocalStackParamList>
|
||||||
mentions?: Mastodon.Mention[]
|
mentions?: Mastodon.Mention[]
|
||||||
tags?: Mastodon.Tag[]
|
tags?: Mastodon.Tag[]
|
||||||
showFullLink: boolean
|
showFullLink: boolean
|
||||||
@ -194,7 +195,7 @@ const ParseHTML = React.memo(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('componentParse')
|
const { t, i18n } = useTranslation('componentParse')
|
||||||
@ -301,7 +302,7 @@ const ParseHTML = React.memo(
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => prev.content === next.content
|
||||||
)
|
)
|
||||||
|
|
||||||
export default ParseHTML
|
export default ParseHTML
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import { useScrollToTop } from '@react-navigation/native'
|
import { useScrollToTop } from '@react-navigation/native'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import {
|
import {
|
||||||
getInstanceActive,
|
getInstanceActive,
|
||||||
@ -20,7 +21,7 @@ import Animated, {
|
|||||||
useAnimatedScrollHandler,
|
useAnimatedScrollHandler,
|
||||||
useSharedValue
|
useSharedValue
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineEmpty from './Timeline/Empty'
|
import TimelineEmpty from './Timeline/Empty'
|
||||||
import TimelineFooter from './Timeline/Footer'
|
import TimelineFooter from './Timeline/Footer'
|
||||||
import TimelineRefresh, {
|
import TimelineRefresh, {
|
||||||
@ -127,7 +128,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const viewabilityPairs = useRef<ViewabilityConfigCallbackPairs>([
|
const viewabilityPairs = useRef<ViewabilityConfigCallbackPairs>([
|
||||||
{
|
{
|
||||||
viewabilityConfig: {
|
viewabilityConfig: {
|
||||||
|
@ -3,12 +3,14 @@ import analytics from '@components/analytics'
|
|||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import { isEqual } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useMutation, useQueryClient } from 'react-query'
|
import { useMutation, useQueryClient } from 'react-query'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineActions from './Shared/Actions'
|
import TimelineActions from './Shared/Actions'
|
||||||
@ -53,117 +55,107 @@ export interface Props {
|
|||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineConversation: React.FC<Props> = ({
|
const TimelineConversation = React.memo(
|
||||||
conversation,
|
({ conversation, queryKey, highlighted = false }: Props) => {
|
||||||
queryKey,
|
const instanceAccount = useSelector(
|
||||||
highlighted = false
|
getInstanceAccount,
|
||||||
}) => {
|
(prev, next) => prev?.id === next?.id
|
||||||
const instanceAccount = useSelector(
|
)
|
||||||
getInstanceAccount,
|
const { colors } = useTheme()
|
||||||
(prev, next) => prev?.id === next?.id
|
|
||||||
)
|
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const fireMutation = useCallback(() => {
|
const fireMutation = useCallback(() => {
|
||||||
return apiInstance<Mastodon.Conversation>({
|
return apiInstance<Mastodon.Conversation>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `conversations/${conversation.id}/read`
|
url: `conversations/${conversation.id}/read`
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
const { mutate } = useMutation(fireMutation, {
|
|
||||||
onSettled: () => {
|
|
||||||
queryClient.invalidateQueries(queryKey)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const navigation =
|
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
|
||||||
const onPress = useCallback(() => {
|
|
||||||
analytics('timeline_conversation_press')
|
|
||||||
if (conversation.last_status) {
|
|
||||||
conversation.unread && mutate()
|
|
||||||
navigation.push('Tab-Shared-Toot', {
|
|
||||||
toot: conversation.last_status,
|
|
||||||
rootQueryKey: queryKey
|
|
||||||
})
|
})
|
||||||
}
|
}, [])
|
||||||
}, [])
|
const { mutate } = useMutation(fireMutation, {
|
||||||
|
onSettled: () => {
|
||||||
|
queryClient.invalidateQueries(queryKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
const navigation =
|
||||||
<Pressable
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
style={[
|
const onPress = useCallback(() => {
|
||||||
styles.base,
|
analytics('timeline_conversation_press')
|
||||||
{ backgroundColor: colors.backgroundDefault },
|
if (conversation.last_status) {
|
||||||
conversation.unread && {
|
conversation.unread && mutate()
|
||||||
borderLeftWidth: StyleConstants.Spacing.XS,
|
navigation.push('Tab-Shared-Toot', {
|
||||||
borderLeftColor: colors.blue,
|
toot: conversation.last_status,
|
||||||
paddingLeft:
|
rootQueryKey: queryKey
|
||||||
StyleConstants.Spacing.Global.PagePadding -
|
})
|
||||||
StyleConstants.Spacing.XS
|
}
|
||||||
}
|
}, [])
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
<View style={styles.header}>
|
|
||||||
<Avatars accounts={conversation.accounts} />
|
|
||||||
<TimelineHeaderConversation
|
|
||||||
queryKey={queryKey}
|
|
||||||
conversation={conversation}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{conversation.last_status ? (
|
return (
|
||||||
<>
|
<Pressable
|
||||||
<View
|
style={[
|
||||||
style={{
|
{
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
flex: 1,
|
||||||
paddingLeft: highlighted
|
flexDirection: 'column',
|
||||||
? 0
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
paddingBottom: 0,
|
||||||
}}
|
backgroundColor: colors.backgroundDefault
|
||||||
>
|
},
|
||||||
<TimelineContent
|
conversation.unread && {
|
||||||
|
borderLeftWidth: StyleConstants.Spacing.XS,
|
||||||
|
borderLeftColor: colors.blue,
|
||||||
|
paddingLeft:
|
||||||
|
StyleConstants.Spacing.Global.PagePadding -
|
||||||
|
StyleConstants.Spacing.XS
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<Avatars accounts={conversation.accounts} />
|
||||||
|
<TimelineHeaderConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
conversation={conversation}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{conversation.last_status ? (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TimelineContent
|
||||||
|
status={conversation.last_status}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
{conversation.last_status.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
statusId={conversation.last_status.id}
|
||||||
|
poll={conversation.last_status.poll}
|
||||||
|
reblog={false}
|
||||||
|
sameAccount={
|
||||||
|
conversation.last_status.id === instanceAccount?.id
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<TimelineActions
|
||||||
|
queryKey={queryKey}
|
||||||
status={conversation.last_status}
|
status={conversation.last_status}
|
||||||
highlighted={highlighted}
|
highlighted={highlighted}
|
||||||
|
accts={conversation.accounts.map(account => account.acct)}
|
||||||
|
reblog={false}
|
||||||
/>
|
/>
|
||||||
{conversation.last_status.poll ? (
|
</>
|
||||||
<TimelinePoll
|
) : null}
|
||||||
queryKey={queryKey}
|
</Pressable>
|
||||||
statusId={conversation.last_status.id}
|
)
|
||||||
poll={conversation.last_status.poll}
|
|
||||||
reblog={false}
|
|
||||||
sameAccount={
|
|
||||||
conversation.last_status.id === instanceAccount?.id
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={conversation.last_status}
|
|
||||||
highlighted={highlighted}
|
|
||||||
accts={conversation.accounts.map(account => account.acct)}
|
|
||||||
reblog={false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'column',
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingBottom: 0
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.conversation, next.conversation)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineConversation
|
export default TimelineConversation
|
||||||
|
@ -9,15 +9,16 @@ import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
|||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { uniqBy } from 'lodash'
|
import { isEqual, uniqBy } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineActionsUsers from './Shared/ActionsUsers'
|
import TimelineFeedback from './Shared/Feedback'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
import TimelineTranslate from './Shared/Translate'
|
import TimelineTranslate from './Shared/Translate'
|
||||||
@ -33,148 +34,143 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When the poll is long
|
// When the poll is long
|
||||||
const TimelineDefault: React.FC<Props> = ({
|
const TimelineDefault = React.memo(
|
||||||
item,
|
({
|
||||||
queryKey,
|
item,
|
||||||
rootQueryKey,
|
queryKey,
|
||||||
origin,
|
rootQueryKey,
|
||||||
highlighted = false,
|
origin,
|
||||||
disableDetails = false,
|
highlighted = false,
|
||||||
disableOnPress = false
|
disableDetails = false,
|
||||||
}) => {
|
disableOnPress = false
|
||||||
const { colors } = useTheme()
|
}: Props) => {
|
||||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
const navigation =
|
||||||
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const actualStatus = item.reblog ? item.reblog : item
|
const actualStatus = item.reblog ? item.reblog : item
|
||||||
|
|
||||||
const ownAccount = actualStatus.account?.id === instanceAccount?.id
|
const ownAccount = actualStatus.account?.id === instanceAccount?.id
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!highlighted &&
|
|
||||||
queryKey &&
|
|
||||||
shouldFilter({ status: actualStatus, queryKey })
|
|
||||||
) {
|
|
||||||
return <TimelineFiltered />
|
|
||||||
}
|
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
|
||||||
analytics('timeline_default_press', {
|
|
||||||
page: queryKey ? queryKey[1].page : origin
|
|
||||||
})
|
|
||||||
!disableOnPress &&
|
|
||||||
!highlighted &&
|
!highlighted &&
|
||||||
navigation.push('Tab-Shared-Toot', {
|
queryKey &&
|
||||||
toot: actualStatus,
|
shouldFilter({ status: actualStatus, queryKey })
|
||||||
rootQueryKey: queryKey
|
) {
|
||||||
})
|
return <TimelineFiltered />
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
return (
|
const onPress = useCallback(() => {
|
||||||
<Pressable
|
analytics('timeline_default_press', {
|
||||||
accessible={highlighted ? false : true}
|
page: queryKey ? queryKey[1].page : origin
|
||||||
style={[
|
})
|
||||||
styles.statusView,
|
!disableOnPress &&
|
||||||
{
|
!highlighted &&
|
||||||
|
navigation.push('Tab-Shared-Toot', {
|
||||||
|
toot: actualStatus,
|
||||||
|
rootQueryKey: queryKey
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
accessible={highlighted ? false : true}
|
||||||
|
style={{
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
backgroundColor: colors.backgroundDefault,
|
backgroundColor: colors.backgroundDefault,
|
||||||
paddingBottom:
|
paddingBottom:
|
||||||
disableDetails && disableOnPress
|
disableDetails && disableOnPress
|
||||||
? StyleConstants.Spacing.Global.PagePadding
|
? StyleConstants.Spacing.Global.PagePadding
|
||||||
: 0
|
: 0
|
||||||
}
|
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
{item.reblog ? (
|
|
||||||
<TimelineActioned action='reblog' account={item.account} />
|
|
||||||
) : item._pinned ? (
|
|
||||||
<TimelineActioned action='pinned' account={item.account} />
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<View style={styles.header}>
|
|
||||||
<TimelineAvatar
|
|
||||||
queryKey={disableOnPress ? undefined : queryKey}
|
|
||||||
account={actualStatus.account}
|
|
||||||
highlighted={highlighted}
|
|
||||||
/>
|
|
||||||
<TimelineHeaderDefault
|
|
||||||
queryKey={disableOnPress ? undefined : queryKey}
|
|
||||||
rootQueryKey={disableOnPress ? undefined : rootQueryKey}
|
|
||||||
status={actualStatus}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
|
||||||
paddingLeft: highlighted
|
|
||||||
? 0
|
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
|
||||||
}}
|
}}
|
||||||
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
{typeof actualStatus.content === 'string' &&
|
{item.reblog ? (
|
||||||
actualStatus.content.length > 0 ? (
|
<TimelineActioned action='reblog' account={item.account} />
|
||||||
<TimelineContent
|
) : item._pinned ? (
|
||||||
status={actualStatus}
|
<TimelineActioned action='pinned' account={item.account} />
|
||||||
highlighted={highlighted}
|
|
||||||
disableDetails={disableDetails}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
{queryKey && actualStatus.poll ? (
|
|
||||||
<TimelinePoll
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<TimelineAvatar
|
||||||
|
queryKey={disableOnPress ? undefined : queryKey}
|
||||||
|
account={actualStatus.account}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
<TimelineHeaderDefault
|
||||||
|
queryKey={disableOnPress ? undefined : queryKey}
|
||||||
|
rootQueryKey={disableOnPress ? undefined : rootQueryKey}
|
||||||
|
status={actualStatus}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{typeof actualStatus.content === 'string' &&
|
||||||
|
actualStatus.content.length > 0 ? (
|
||||||
|
<TimelineContent
|
||||||
|
status={actualStatus}
|
||||||
|
highlighted={highlighted}
|
||||||
|
disableDetails={disableDetails}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{queryKey && actualStatus.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
rootQueryKey={rootQueryKey}
|
||||||
|
statusId={actualStatus.id}
|
||||||
|
poll={actualStatus.poll}
|
||||||
|
reblog={item.reblog ? true : false}
|
||||||
|
sameAccount={ownAccount}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{!disableDetails &&
|
||||||
|
Array.isArray(actualStatus.media_attachments) &&
|
||||||
|
actualStatus.media_attachments.length ? (
|
||||||
|
<TimelineAttachment status={actualStatus} />
|
||||||
|
) : null}
|
||||||
|
{!disableDetails && actualStatus.card ? (
|
||||||
|
<TimelineCard card={actualStatus.card} />
|
||||||
|
) : null}
|
||||||
|
{!disableDetails ? (
|
||||||
|
<TimelineFullConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
status={actualStatus}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<TimelineTranslate status={actualStatus} highlighted={highlighted} />
|
||||||
|
<TimelineFeedback status={actualStatus} highlighted={highlighted} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{queryKey && !disableDetails ? (
|
||||||
|
<TimelineActions
|
||||||
queryKey={queryKey}
|
queryKey={queryKey}
|
||||||
rootQueryKey={rootQueryKey}
|
rootQueryKey={rootQueryKey}
|
||||||
statusId={actualStatus.id}
|
highlighted={highlighted}
|
||||||
poll={actualStatus.poll}
|
status={actualStatus}
|
||||||
|
accts={uniqBy(
|
||||||
|
(
|
||||||
|
[actualStatus.account] as Mastodon.Account[] &
|
||||||
|
Mastodon.Mention[]
|
||||||
|
)
|
||||||
|
.concat(actualStatus.mentions)
|
||||||
|
.filter(d => d?.id !== instanceAccount?.id),
|
||||||
|
d => d?.id
|
||||||
|
).map(d => d?.acct)}
|
||||||
reblog={item.reblog ? true : false}
|
reblog={item.reblog ? true : false}
|
||||||
sameAccount={ownAccount}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{!disableDetails &&
|
</Pressable>
|
||||||
Array.isArray(actualStatus.media_attachments) &&
|
)
|
||||||
actualStatus.media_attachments.length ? (
|
|
||||||
<TimelineAttachment status={actualStatus} />
|
|
||||||
) : null}
|
|
||||||
{!disableDetails && actualStatus.card ? (
|
|
||||||
<TimelineCard card={actualStatus.card} />
|
|
||||||
) : null}
|
|
||||||
{!disableDetails ? (
|
|
||||||
<TimelineFullConversation queryKey={queryKey} status={actualStatus} />
|
|
||||||
) : null}
|
|
||||||
<TimelineTranslate status={actualStatus} highlighted={highlighted} />
|
|
||||||
<TimelineActionsUsers status={actualStatus} highlighted={highlighted} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{queryKey && !disableDetails ? (
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
rootQueryKey={rootQueryKey}
|
|
||||||
highlighted={highlighted}
|
|
||||||
status={actualStatus}
|
|
||||||
accts={uniqBy(
|
|
||||||
([actualStatus.account] as Mastodon.Account[] & Mastodon.Mention[])
|
|
||||||
.concat(actualStatus.mentions)
|
|
||||||
.filter(d => d?.id !== instanceAccount?.id),
|
|
||||||
d => d?.id
|
|
||||||
).map(d => d?.acct)}
|
|
||||||
reblog={item.reblog ? true : false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
statusView: {
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingBottom: 0
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.item, next.item)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineDefault
|
export default TimelineDefault
|
||||||
|
@ -4,7 +4,7 @@ import Icon from '@components/Icon'
|
|||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
@ -20,10 +20,10 @@ const TimelineEmpty = React.memo(
|
|||||||
options: { notifyOnChangeProps: ['status'] }
|
options: { notifyOnChangeProps: ['status'] }
|
||||||
})
|
})
|
||||||
|
|
||||||
const { colors, theme } = useTheme()
|
const { colors } = useTheme()
|
||||||
const { t, i18n } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = () => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'loading':
|
case 'loading':
|
||||||
return (
|
return (
|
||||||
@ -67,24 +67,25 @@ const TimelineEmpty = React.memo(
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [theme, i18n.language, status])
|
}
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
|
style={{
|
||||||
children={children}
|
flex: 1,
|
||||||
/>
|
minHeight: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.backgroundDefault
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children()}
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
minHeight: '100%',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
error: {
|
error: {
|
||||||
...StyleConstants.FontStyle.M,
|
...StyleConstants.FontStyle.M,
|
||||||
marginTop: StyleConstants.Spacing.S,
|
marginTop: StyleConstants.Spacing.S,
|
||||||
|
@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { Text, View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -27,11 +27,20 @@ const TimelineFooter = React.memo(
|
|||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: StyleConstants.Spacing.M
|
||||||
|
}}
|
||||||
|
>
|
||||||
{!disableInfinity && hasNextPage ? (
|
{!disableInfinity && hasNextPage ? (
|
||||||
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||||
) : (
|
) : (
|
||||||
<Text style={[styles.text, { color: colors.secondary }]}>
|
<Text
|
||||||
|
style={{ ...StyleConstants.FontStyle.S, color: colors.secondary }}
|
||||||
|
>
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey='componentTimeline:end.message'
|
i18nKey='componentTimeline:end.message'
|
||||||
components={[
|
components={[
|
||||||
@ -50,16 +59,4 @@ const TimelineFooter = React.memo(
|
|||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineFooter
|
export default TimelineFooter
|
||||||
|
@ -2,7 +2,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { Text, View } from 'react-native'
|
||||||
|
|
||||||
const TimelineLookback = React.memo(
|
const TimelineLookback = React.memo(
|
||||||
() => {
|
() => {
|
||||||
@ -11,10 +11,19 @@ const TimelineLookback = React.memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: StyleConstants.Spacing.S,
|
||||||
|
backgroundColor: colors.backgroundDefault
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={[StyleConstants.FontStyle.S, { color: colors.primaryDefault }]}
|
style={{
|
||||||
|
...StyleConstants.FontStyle.S,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{t('lookback.message')}
|
{t('lookback.message')}
|
||||||
</Text>
|
</Text>
|
||||||
@ -24,16 +33,4 @@ const TimelineLookback = React.memo(
|
|||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineLookback
|
export default TimelineLookback
|
||||||
|
@ -9,13 +9,14 @@ import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotifi
|
|||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { uniqBy } from 'lodash'
|
import { isEqual, uniqBy } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
@ -26,151 +27,137 @@ export interface Props {
|
|||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineNotifications: React.FC<Props> = ({
|
const TimelineNotifications = React.memo(
|
||||||
notification,
|
({ notification, queryKey, highlighted = false }: Props) => {
|
||||||
queryKey,
|
if (
|
||||||
highlighted = false
|
notification.status &&
|
||||||
}) => {
|
shouldFilter({ status: notification.status, queryKey })
|
||||||
if (
|
) {
|
||||||
notification.status &&
|
return <TimelineFiltered />
|
||||||
shouldFilter({ status: notification.status, queryKey })
|
}
|
||||||
) {
|
|
||||||
return <TimelineFiltered />
|
|
||||||
}
|
|
||||||
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const instanceAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getInstanceAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const actualAccount = notification.status
|
const actualAccount = notification.status
|
||||||
? notification.status.account
|
? notification.status.account
|
||||||
: notification.account
|
: notification.account
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_notification_press')
|
analytics('timeline_notification_press')
|
||||||
notification.status &&
|
notification.status &&
|
||||||
navigation.push('Tab-Shared-Toot', {
|
navigation.push('Tab-Shared-Toot', {
|
||||||
toot: notification.status,
|
toot: notification.status,
|
||||||
rootQueryKey: queryKey
|
rootQueryKey: queryKey
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={[
|
style={{
|
||||||
styles.notificationView,
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
{
|
|
||||||
backgroundColor: colors.backgroundDefault,
|
backgroundColor: colors.backgroundDefault,
|
||||||
paddingBottom: notification.status
|
paddingBottom: notification.status
|
||||||
? 0
|
? 0
|
||||||
: StyleConstants.Spacing.Global.PagePadding
|
: StyleConstants.Spacing.Global.PagePadding
|
||||||
}
|
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
{notification.type !== 'mention' ? (
|
|
||||||
<TimelineActioned
|
|
||||||
action={notification.type}
|
|
||||||
account={notification.account}
|
|
||||||
notification
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
opacity:
|
|
||||||
notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request' ||
|
|
||||||
notification.type === 'mention' ||
|
|
||||||
notification.type === 'status'
|
|
||||||
? 1
|
|
||||||
: 0.5
|
|
||||||
}}
|
}}
|
||||||
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
<View style={styles.header}>
|
{notification.type !== 'mention' ? (
|
||||||
<TimelineAvatar
|
<TimelineActioned
|
||||||
queryKey={queryKey}
|
action={notification.type}
|
||||||
account={actualAccount}
|
account={notification.account}
|
||||||
highlighted={highlighted}
|
notification
|
||||||
/>
|
|
||||||
<TimelineHeaderNotification
|
|
||||||
queryKey={queryKey}
|
|
||||||
notification={notification}
|
|
||||||
/>
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
opacity:
|
||||||
|
notification.type === 'follow' ||
|
||||||
|
notification.type === 'follow_request' ||
|
||||||
|
notification.type === 'mention' ||
|
||||||
|
notification.type === 'status'
|
||||||
|
? 1
|
||||||
|
: 0.5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<TimelineAvatar
|
||||||
|
queryKey={queryKey}
|
||||||
|
account={actualAccount}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
<TimelineHeaderNotification
|
||||||
|
queryKey={queryKey}
|
||||||
|
notification={notification}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{notification.status ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{notification.status.content.length > 0 ? (
|
||||||
|
<TimelineContent
|
||||||
|
status={notification.status}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{notification.status.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
statusId={notification.status.id}
|
||||||
|
poll={notification.status.poll}
|
||||||
|
reblog={false}
|
||||||
|
sameAccount={notification.account.id === instanceAccount?.id}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{notification.status.media_attachments.length > 0 ? (
|
||||||
|
<TimelineAttachment status={notification.status} />
|
||||||
|
) : null}
|
||||||
|
{notification.status.card ? (
|
||||||
|
<TimelineCard card={notification.status.card} />
|
||||||
|
) : null}
|
||||||
|
<TimelineFullConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
status={notification.status}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{notification.status ? (
|
{notification.status ? (
|
||||||
<View
|
<TimelineActions
|
||||||
style={{
|
queryKey={queryKey}
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
status={notification.status}
|
||||||
paddingLeft: highlighted
|
highlighted={highlighted}
|
||||||
? 0
|
accts={uniqBy(
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
(
|
||||||
}}
|
[notification.status.account] as Mastodon.Account[] &
|
||||||
>
|
Mastodon.Mention[]
|
||||||
{notification.status.content.length > 0 ? (
|
)
|
||||||
<TimelineContent
|
.concat(notification.status.mentions)
|
||||||
status={notification.status}
|
.filter(d => d?.id !== instanceAccount?.id),
|
||||||
highlighted={highlighted}
|
d => d?.id
|
||||||
/>
|
).map(d => d?.acct)}
|
||||||
) : null}
|
reblog={false}
|
||||||
{notification.status.poll ? (
|
/>
|
||||||
<TimelinePoll
|
|
||||||
queryKey={queryKey}
|
|
||||||
statusId={notification.status.id}
|
|
||||||
poll={notification.status.poll}
|
|
||||||
reblog={false}
|
|
||||||
sameAccount={notification.account.id === instanceAccount?.id}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{notification.status.media_attachments.length > 0 ? (
|
|
||||||
<TimelineAttachment status={notification.status} />
|
|
||||||
) : null}
|
|
||||||
{notification.status.card ? (
|
|
||||||
<TimelineCard card={notification.status.card} />
|
|
||||||
) : null}
|
|
||||||
<TimelineFullConversation
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={notification.status}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</Pressable>
|
||||||
|
)
|
||||||
{notification.status ? (
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={notification.status}
|
|
||||||
highlighted={highlighted}
|
|
||||||
accts={uniqBy(
|
|
||||||
(
|
|
||||||
[notification.status.account] as Mastodon.Account[] &
|
|
||||||
Mastodon.Mention[]
|
|
||||||
)
|
|
||||||
.concat(notification.status.mentions)
|
|
||||||
.filter(d => d?.id !== instanceAccount?.id),
|
|
||||||
d => d?.id
|
|
||||||
).map(d => d?.acct)}
|
|
||||||
reblog={false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
notificationView: {
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.notification, next.notification)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineNotifications
|
export default TimelineNotifications
|
||||||
|
@ -3,6 +3,7 @@ import Icon from '@components/Icon'
|
|||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
@ -20,7 +21,7 @@ const TimelineActioned = React.memo(
|
|||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const name = account.display_name || account.username
|
const name = account.display_name || account.username
|
||||||
const iconColor = colors.primaryDefault
|
const iconColor = colors.primaryDefault
|
||||||
|
|
||||||
@ -143,19 +144,23 @@ const TimelineActioned = React.memo(
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <View style={styles.actioned} children={children} />
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: StyleConstants.Spacing.S,
|
||||||
|
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
||||||
|
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
|
children={children}
|
||||||
|
/>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
actioned: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: StyleConstants.Spacing.S,
|
|
||||||
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
|
||||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
icon: {
|
icon: {
|
||||||
marginRight: StyleConstants.Spacing.S
|
marginRight: StyleConstants.Spacing.S
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import analytics from '@components/analytics'
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import {
|
import {
|
||||||
MutationVarsTimelineUpdateStatusProperty,
|
MutationVarsTimelineUpdateStatusProperty,
|
||||||
QueryKeyTimeline,
|
QueryKeyTimeline,
|
||||||
@ -31,7 +33,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
accts,
|
accts,
|
||||||
reblog
|
reblog
|
||||||
}) => {
|
}) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const iconColor = colors.secondary
|
const iconColor = colors.secondary
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
import analytics from '@components/analytics'
|
|
||||||
import { useNavigation } from '@react-navigation/native'
|
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
status: Mastodon.Status
|
|
||||||
highlighted: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimelineActionsUsers = React.memo(
|
|
||||||
({ status, highlighted }: Props) => {
|
|
||||||
if (!highlighted) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
const { colors } = useTheme()
|
|
||||||
const navigation =
|
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.base}>
|
|
||||||
{status.reblogs_count > 0 ? (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={t(
|
|
||||||
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
|
||||||
{
|
|
||||||
count: status.reblogs_count
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
accessibilityHint={t(
|
|
||||||
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
|
||||||
)}
|
|
||||||
accessibilityRole='button'
|
|
||||||
style={[styles.text, { color: colors.blue }]}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
|
||||||
count: status.reblogs_count
|
|
||||||
})
|
|
||||||
navigation.push('Tab-Shared-Users', {
|
|
||||||
reference: 'statuses',
|
|
||||||
id: status.id,
|
|
||||||
type: 'reblogged_by',
|
|
||||||
count: status.reblogs_count
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('shared.actionsUsers.reblogged_by.text', {
|
|
||||||
count: status.reblogs_count
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
{status.favourites_count > 0 ? (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={t(
|
|
||||||
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
|
||||||
{
|
|
||||||
count: status.reblogs_count
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
accessibilityHint={t(
|
|
||||||
'shared.actionsUsers.favourited_by.accessibilityHint'
|
|
||||||
)}
|
|
||||||
accessibilityRole='button'
|
|
||||||
style={[styles.text, { color: colors.blue }]}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
|
||||||
count: status.favourites_count
|
|
||||||
})
|
|
||||||
navigation.push('Tab-Shared-Users', {
|
|
||||||
reference: 'statuses',
|
|
||||||
id: status.id,
|
|
||||||
type: 'favourited_by',
|
|
||||||
count: status.favourites_count
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('shared.actionsUsers.favourited_by.text', {
|
|
||||||
count: status.favourites_count
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prev, next) =>
|
|
||||||
prev.status.reblogs_count === next.status.reblogs_count &&
|
|
||||||
prev.status.favourites_count === next.status.favourites_count
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.M,
|
|
||||||
padding: StyleConstants.Spacing.S,
|
|
||||||
paddingLeft: 0,
|
|
||||||
marginRight: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineActionsUsers
|
|
@ -6,13 +6,13 @@ import AttachmentImage from '@components/Timeline/Shared/Attachment/Image'
|
|||||||
import AttachmentUnsupported from '@components/Timeline/Shared/Attachment/Unsupported'
|
import AttachmentUnsupported from '@components/Timeline/Shared/Attachment/Unsupported'
|
||||||
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
status: Pick<Mastodon.Status, 'media_attachments' | 'sensitive'>
|
status: Pick<Mastodon.Status, 'media_attachments' | 'sensitive'>
|
||||||
@ -23,24 +23,13 @@ const TimelineAttachment = React.memo(
|
|||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
const [sensitiveShown, setSensitiveShown] = useState(status.sensitive)
|
const [sensitiveShown, setSensitiveShown] = useState(status.sensitive)
|
||||||
const onPressBlurView = useCallback(() => {
|
|
||||||
analytics('timeline_shared_attachment_blurview_press_show')
|
|
||||||
layoutAnimation()
|
|
||||||
setSensitiveShown(false)
|
|
||||||
haptics('Light')
|
|
||||||
}, [])
|
|
||||||
const onPressShow = useCallback(() => {
|
|
||||||
analytics('timeline_shared_attachment_blurview_press_hide')
|
|
||||||
setSensitiveShown(true)
|
|
||||||
haptics('Light')
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const imageUrls = useRef<
|
const imageUrls = useRef<
|
||||||
RootStackParamList['Screen-ImagesViewer']['imageUrls']
|
RootStackParamList['Screen-ImagesViewer']['imageUrls']
|
||||||
>([])
|
>([])
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
useEffect(() => {
|
const navigateToImagesViewer = (id: string) => {
|
||||||
status.media_attachments.forEach((attachment, index) => {
|
status.media_attachments.forEach(attachment => {
|
||||||
switch (attachment.type) {
|
switch (attachment.type) {
|
||||||
case 'image':
|
case 'image':
|
||||||
imageUrls.current.push({
|
imageUrls.current.push({
|
||||||
@ -54,117 +43,136 @@ const TimelineAttachment = React.memo(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [])
|
|
||||||
const navigateToImagesViewer = (id: string) =>
|
|
||||||
navigation.navigate('Screen-ImagesViewer', {
|
navigation.navigate('Screen-ImagesViewer', {
|
||||||
imageUrls: imageUrls.current,
|
imageUrls: imageUrls.current,
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
const attachments = useMemo(
|
}
|
||||||
() =>
|
|
||||||
status.media_attachments.map((attachment, index) => {
|
return (
|
||||||
switch (attachment.type) {
|
<View>
|
||||||
case 'image':
|
<View
|
||||||
return (
|
style={{
|
||||||
<AttachmentImage
|
marginTop: StyleConstants.Spacing.S,
|
||||||
key={index}
|
flex: 1,
|
||||||
total={status.media_attachments.length}
|
flexDirection: 'row',
|
||||||
index={index}
|
flexWrap: 'wrap',
|
||||||
sensitiveShown={sensitiveShown}
|
justifyContent: 'center',
|
||||||
image={attachment}
|
alignContent: 'stretch'
|
||||||
navigateToImagesViewer={navigateToImagesViewer}
|
}}
|
||||||
/>
|
>
|
||||||
)
|
{status.media_attachments.map((attachment, index) => {
|
||||||
case 'video':
|
switch (attachment.type) {
|
||||||
return (
|
case 'image':
|
||||||
<AttachmentVideo
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
video={attachment}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'gifv':
|
|
||||||
return (
|
|
||||||
<AttachmentVideo
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
video={attachment}
|
|
||||||
gifv
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'audio':
|
|
||||||
return (
|
|
||||||
<AttachmentAudio
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
audio={attachment}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
if (
|
|
||||||
attachment.preview_url?.endsWith('.jpg') ||
|
|
||||||
attachment.preview_url?.endsWith('.jpeg') ||
|
|
||||||
attachment.preview_url?.endsWith('.png') ||
|
|
||||||
attachment.preview_url?.endsWith('.gif') ||
|
|
||||||
attachment.remote_url?.endsWith('.jpg') ||
|
|
||||||
attachment.remote_url?.endsWith('.jpeg') ||
|
|
||||||
attachment.remote_url?.endsWith('.png') ||
|
|
||||||
attachment.remote_url?.endsWith('.gif')
|
|
||||||
) {
|
|
||||||
imageUrls.current.push({
|
|
||||||
id: attachment.id,
|
|
||||||
preview_url: attachment.preview_url,
|
|
||||||
url: attachment.url,
|
|
||||||
remote_url: attachment.remote_url,
|
|
||||||
blurhash: attachment.blurhash,
|
|
||||||
width: attachment.meta?.original?.width,
|
|
||||||
height: attachment.meta?.original?.height
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<AttachmentImage
|
<AttachmentImage
|
||||||
key={index}
|
key={index}
|
||||||
total={status.media_attachments.length}
|
total={status.media_attachments.length}
|
||||||
index={index}
|
index={index}
|
||||||
sensitiveShown={sensitiveShown}
|
sensitiveShown={sensitiveShown}
|
||||||
// @ts-ignore
|
|
||||||
image={attachment}
|
image={attachment}
|
||||||
navigateToImagesViewer={navigateToImagesViewer}
|
navigateToImagesViewer={navigateToImagesViewer}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
case 'video':
|
||||||
return (
|
return (
|
||||||
<AttachmentUnsupported
|
<AttachmentVideo
|
||||||
key={index}
|
key={index}
|
||||||
total={status.media_attachments.length}
|
total={status.media_attachments.length}
|
||||||
index={index}
|
index={index}
|
||||||
sensitiveShown={sensitiveShown}
|
sensitiveShown={sensitiveShown}
|
||||||
attachment={attachment}
|
video={attachment}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
case 'gifv':
|
||||||
}
|
return (
|
||||||
}),
|
<AttachmentVideo
|
||||||
[sensitiveShown]
|
key={index}
|
||||||
)
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
return (
|
sensitiveShown={sensitiveShown}
|
||||||
<View>
|
video={attachment}
|
||||||
<View style={styles.container} children={attachments} />
|
gifv
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'audio':
|
||||||
|
return (
|
||||||
|
<AttachmentAudio
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
audio={attachment}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
if (
|
||||||
|
attachment.preview_url?.endsWith('.jpg') ||
|
||||||
|
attachment.preview_url?.endsWith('.jpeg') ||
|
||||||
|
attachment.preview_url?.endsWith('.png') ||
|
||||||
|
attachment.preview_url?.endsWith('.gif') ||
|
||||||
|
attachment.remote_url?.endsWith('.jpg') ||
|
||||||
|
attachment.remote_url?.endsWith('.jpeg') ||
|
||||||
|
attachment.remote_url?.endsWith('.png') ||
|
||||||
|
attachment.remote_url?.endsWith('.gif')
|
||||||
|
) {
|
||||||
|
imageUrls.current.push({
|
||||||
|
id: attachment.id,
|
||||||
|
preview_url: attachment.preview_url,
|
||||||
|
url: attachment.url,
|
||||||
|
remote_url: attachment.remote_url,
|
||||||
|
blurhash: attachment.blurhash,
|
||||||
|
width: attachment.meta?.original?.width,
|
||||||
|
height: attachment.meta?.original?.height
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<AttachmentImage
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
// @ts-ignore
|
||||||
|
image={attachment}
|
||||||
|
navigateToImagesViewer={navigateToImagesViewer}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<AttachmentUnsupported
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
attachment={attachment}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
|
||||||
{status.sensitive &&
|
{status.sensitive &&
|
||||||
(sensitiveShown ? (
|
(sensitiveShown ? (
|
||||||
<Pressable style={styles.sensitiveBlur}>
|
<Pressable
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('shared.attachment.sensitive.button')}
|
content={t('shared.attachment.sensitive.button')}
|
||||||
overlay
|
overlay
|
||||||
onPress={onPressBlurView}
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_attachment_blurview_press_show')
|
||||||
|
layoutAnimation()
|
||||||
|
setSensitiveShown(false)
|
||||||
|
haptics('Light')
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
) : (
|
) : (
|
||||||
@ -173,7 +181,11 @@ const TimelineAttachment = React.memo(
|
|||||||
content='EyeOff'
|
content='EyeOff'
|
||||||
round
|
round
|
||||||
overlay
|
overlay
|
||||||
onPress={onPressShow}
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_attachment_blurview_press_hide')
|
||||||
|
setSensitiveShown(true)
|
||||||
|
haptics('Light')
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: StyleConstants.Spacing.S * 2,
|
top: StyleConstants.Spacing.S * 2,
|
||||||
@ -184,33 +196,28 @@ const TimelineAttachment = React.memo(
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => {
|
||||||
|
let isEqual = true
|
||||||
|
|
||||||
|
if (
|
||||||
|
prev.status.media_attachments.length !==
|
||||||
|
next.status.media_attachments.length
|
||||||
|
) {
|
||||||
|
isEqual = false
|
||||||
|
return isEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
prev.status.media_attachments.forEach((attachment, index) => {
|
||||||
|
if (
|
||||||
|
attachment.preview_url !==
|
||||||
|
next.status.media_attachments[index].preview_url
|
||||||
|
) {
|
||||||
|
isEqual = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return isEqual
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
marginTop: StyleConstants.Spacing.S,
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignContent: 'stretch'
|
|
||||||
},
|
|
||||||
sensitiveBlur: {
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
sensitiveBlurButton: {
|
|
||||||
padding: StyleConstants.Spacing.S,
|
|
||||||
borderRadius: 6
|
|
||||||
},
|
|
||||||
sensitiveText: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineAttachment
|
export default TimelineAttachment
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useCallback } from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -13,44 +13,43 @@ export interface Props {
|
|||||||
navigateToImagesViewer: (imageIndex: string) => void
|
navigateToImagesViewer: (imageIndex: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AttachmentImage = React.memo(
|
const AttachmentImage = ({
|
||||||
({ total, index, sensitiveShown, image, navigateToImagesViewer }: Props) => {
|
total,
|
||||||
const onPress = useCallback(() => {
|
index,
|
||||||
analytics('timeline_shared_attachment_image_press', { id: image.id })
|
sensitiveShown,
|
||||||
navigateToImagesViewer(image.id)
|
image,
|
||||||
}, [])
|
navigateToImagesViewer
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View
|
||||||
<GracefullyImage
|
style={{
|
||||||
accessibilityLabel={image.description}
|
flex: 1,
|
||||||
hidden={sensitiveShown}
|
flexBasis: '50%',
|
||||||
uri={{ original: image.preview_url, remote: image.remote_url }}
|
padding: StyleConstants.Spacing.XS / 2
|
||||||
blurhash={image.blurhash}
|
}}
|
||||||
onPress={onPress}
|
>
|
||||||
style={{
|
<GracefullyImage
|
||||||
aspectRatio:
|
accessibilityLabel={image.description}
|
||||||
total > 1 ||
|
hidden={sensitiveShown}
|
||||||
!image.meta?.original?.width ||
|
uri={{ original: image.preview_url, remote: image.remote_url }}
|
||||||
!image.meta?.original?.height
|
blurhash={image.blurhash}
|
||||||
? attachmentAspectRatio({ total, index })
|
onPress={() => {
|
||||||
: image.meta.original.height / image.meta.original.width > 1
|
analytics('timeline_shared_attachment_image_press', { id: image.id })
|
||||||
? 1
|
navigateToImagesViewer(image.id)
|
||||||
: image.meta.original.width / image.meta.original.height
|
}}
|
||||||
}}
|
style={{
|
||||||
/>
|
aspectRatio:
|
||||||
</View>
|
total > 1 ||
|
||||||
)
|
!image.meta?.original?.width ||
|
||||||
},
|
!image.meta?.original?.height
|
||||||
(prev, next) => prev.sensitiveShown === next.sensitiveShown
|
? attachmentAspectRatio({ total, index })
|
||||||
)
|
: image.meta.original.height / image.meta.original.width > 1
|
||||||
|
? 1
|
||||||
const styles = StyleSheet.create({
|
: image.meta.original.width / image.meta.original.height
|
||||||
base: {
|
}}
|
||||||
flex: 1,
|
/>
|
||||||
flexBasis: '50%',
|
</View>
|
||||||
padding: StyleConstants.Spacing.XS / 2
|
)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export default AttachmentImage
|
export default AttachmentImage
|
||||||
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
|||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
@ -17,7 +18,7 @@ const TimelineAvatar = React.memo(
|
|||||||
({ queryKey, account, highlighted }: Props) => {
|
({ queryKey, account, highlighted }: Props) => {
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
// Need to fix go back root
|
// Need to fix go back root
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_shared_avatar_press', {
|
analytics('timeline_shared_avatar_press', {
|
||||||
@ -49,8 +50,7 @@ const TimelineAvatar = React.memo(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
() => true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export default TimelineAvatar
|
export default TimelineAvatar
|
||||||
|
@ -8,65 +8,62 @@ import React from 'react'
|
|||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
card: Mastodon.Card
|
card: Pick<
|
||||||
|
Mastodon.Card,
|
||||||
|
'url' | 'image' | 'blurhash' | 'title' | 'description'
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineCard = React.memo(
|
const TimelineCard = React.memo(({ card }: Props) => {
|
||||||
({ card }: Props) => {
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
const navigation = useNavigation()
|
||||||
const navigation = useNavigation()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
accessible
|
accessible
|
||||||
accessibilityRole='link'
|
accessibilityRole='link'
|
||||||
style={[styles.card, { borderColor: colors.border }]}
|
style={[styles.card, { borderColor: colors.border }]}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
analytics('timeline_shared_card_press')
|
analytics('timeline_shared_card_press')
|
||||||
await openLink(card.url, navigation)
|
await openLink(card.url, navigation)
|
||||||
}}
|
}}
|
||||||
testID='base'
|
testID='base'
|
||||||
>
|
>
|
||||||
{card.image ? (
|
{card.image ? (
|
||||||
<GracefullyImage
|
<GracefullyImage
|
||||||
uri={{ original: card.image }}
|
uri={{ original: card.image }}
|
||||||
blurhash={card.blurhash}
|
blurhash={card.blurhash}
|
||||||
style={styles.left}
|
style={styles.left}
|
||||||
imageStyle={styles.image}
|
imageStyle={styles.image}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.right}>
|
<View style={styles.right}>
|
||||||
<Text
|
<Text
|
||||||
numberOfLines={2}
|
numberOfLines={2}
|
||||||
style={[styles.rightTitle, { color: colors.primaryDefault }]}
|
style={[styles.rightTitle, { color: colors.primaryDefault }]}
|
||||||
testID='title'
|
testID='title'
|
||||||
>
|
>
|
||||||
{card.title}
|
{card.title}
|
||||||
</Text>
|
</Text>
|
||||||
{card.description ? (
|
{card.description ? (
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={[
|
|
||||||
styles.rightDescription,
|
|
||||||
{ color: colors.primaryDefault }
|
|
||||||
]}
|
|
||||||
testID='description'
|
|
||||||
>
|
|
||||||
{card.description}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
<Text
|
<Text
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[styles.rightLink, { color: colors.secondary }]}
|
style={[styles.rightDescription, { color: colors.primaryDefault }]}
|
||||||
|
testID='description'
|
||||||
>
|
>
|
||||||
{card.url}
|
{card.description}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
) : null}
|
||||||
</Pressable>
|
<Text
|
||||||
)
|
numberOfLines={1}
|
||||||
},
|
style={[styles.rightLink, { color: colors.secondary }]}
|
||||||
() => true
|
>
|
||||||
)
|
{card.url}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
|
@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
status: Mastodon.Status
|
status: Pick<
|
||||||
|
Mastodon.Status,
|
||||||
|
'content' | 'spoiler_text' | 'emojis' | 'mentions' | 'tags'
|
||||||
|
>
|
||||||
numberOfLines?: number
|
numberOfLines?: number
|
||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
disableDetails?: boolean
|
disableDetails?: boolean
|
||||||
@ -69,7 +72,9 @@ const TimelineContent = React.memo(
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) =>
|
||||||
|
prev.status.content === next.status.content &&
|
||||||
|
prev.status.spoiler_text === next.status.spoiler_text
|
||||||
)
|
)
|
||||||
|
|
||||||
export default TimelineContent
|
export default TimelineContent
|
||||||
|
145
src/components/Timeline/Shared/Feedback.tsx
Normal file
145
src/components/Timeline/Shared/Feedback.tsx
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import analytics from '@components/analytics'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { useStatusHistory } from '@utils/queryHooks/statusesHistory'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
status: Pick<
|
||||||
|
Mastodon.Status,
|
||||||
|
'id' | 'edited_at' | 'reblogs_count' | 'favourites_count'
|
||||||
|
>
|
||||||
|
highlighted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimelineFeedback = React.memo(
|
||||||
|
({ status, highlighted }: Props) => {
|
||||||
|
if (!highlighted) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
const { colors } = useTheme()
|
||||||
|
const navigation =
|
||||||
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
|
const { data } = useStatusHistory({
|
||||||
|
id: status.id,
|
||||||
|
options: { enabled: status.edited_at !== undefined }
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
|
<View style={{ flexDirection: 'row' }}>
|
||||||
|
{status.reblogs_count > 0 ? (
|
||||||
|
<Text
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: status.reblogs_count
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_reblog', {
|
||||||
|
count: status.reblogs_count
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-Users', {
|
||||||
|
reference: 'statuses',
|
||||||
|
id: status.id,
|
||||||
|
type: 'reblogged_by',
|
||||||
|
count: status.reblogs_count
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.reblogged_by.text', {
|
||||||
|
count: status.reblogs_count
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
{status.favourites_count > 0 ? (
|
||||||
|
<Text
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: status.reblogs_count
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.favourited_by.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_favourite', {
|
||||||
|
count: status.favourites_count
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-Users', {
|
||||||
|
reference: 'statuses',
|
||||||
|
id: status.id,
|
||||||
|
type: 'favourited_by',
|
||||||
|
count: status.favourites_count
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.favourited_by.text', {
|
||||||
|
count: status.favourites_count
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
{data && data.length > 1 ? (
|
||||||
|
<Text
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.history.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: data.length - 1
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.history.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { marginRight: 0, color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_history', {
|
||||||
|
count: data.length - 1
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-History', { id: status.id })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.history.text', {
|
||||||
|
count: data.length - 1
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(prev, next) =>
|
||||||
|
prev.status.edited_at === next.status.edited_at &&
|
||||||
|
prev.status.reblogs_count === next.status.reblogs_count &&
|
||||||
|
prev.status.favourites_count === next.status.favourites_count
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
...StyleConstants.FontStyle.M,
|
||||||
|
padding: StyleConstants.Spacing.S,
|
||||||
|
paddingLeft: 0,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TimelineFeedback
|
@ -10,141 +10,116 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, Text, View } from 'react-native'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||||
|
|
||||||
const Names = React.memo(
|
const Names = ({ accounts }: { accounts: Mastodon.Account[] }) => {
|
||||||
({ accounts }: { accounts: Mastodon.Account[] }) => {
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[styles.namesLeading, { color: colors.secondary }]}
|
style={{ ...StyleConstants.FontStyle.M, color: colors.secondary }}
|
||||||
>
|
>
|
||||||
<Text>{t('shared.header.conversation.withAccounts')}</Text>
|
<Text>{t('shared.header.conversation.withAccounts')}</Text>
|
||||||
{accounts.map((account, index) => (
|
{accounts.map((account, index) => (
|
||||||
<Text key={account.id} numberOfLines={1}>
|
<Text key={account.id} numberOfLines={1}>
|
||||||
{index !== 0 ? t('common:separator') : undefined}
|
{index !== 0 ? t('common:separator') : undefined}
|
||||||
<ParseEmojis
|
<ParseEmojis
|
||||||
content={account.display_name || account.username}
|
content={account.display_name || account.username}
|
||||||
emojis={account.emojis}
|
emojis={account.emojis}
|
||||||
fontBold
|
fontBold
|
||||||
/>
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey: QueryKeyTimeline
|
queryKey: QueryKeyTimeline
|
||||||
conversation: Mastodon.Conversation
|
conversation: Mastodon.Conversation
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderConversation = React.memo(
|
const HeaderConversation = ({ queryKey, conversation }: Props) => {
|
||||||
({ queryKey, conversation }: Props) => {
|
const { colors, theme } = useTheme()
|
||||||
const { colors, theme } = useTheme()
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const mutation = useTimelineMutation({
|
const mutation = useTimelineMutation({
|
||||||
onMutate: true,
|
onMutate: true,
|
||||||
onError: (err: any, _, oldData) => {
|
onError: (err: any, _, oldData) => {
|
||||||
displayMessage({
|
displayMessage({
|
||||||
theme,
|
theme,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t('common:message.error.message', {
|
message: t('common:message.error.message', {
|
||||||
function: t(`shared.header.conversation.delete.function`)
|
function: t(`shared.header.conversation.delete.function`)
|
||||||
}),
|
}),
|
||||||
...(err.status &&
|
...(err.status &&
|
||||||
typeof err.status === 'number' &&
|
typeof err.status === 'number' &&
|
||||||
err.data &&
|
err.data &&
|
||||||
err.data.error &&
|
err.data.error &&
|
||||||
typeof err.data.error === 'string' && {
|
typeof err.data.error === 'string' && {
|
||||||
description: err.data.error
|
description: err.data.error
|
||||||
})
|
})
|
||||||
})
|
|
||||||
queryClient.setQueryData(queryKey, oldData)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actionOnPress = useCallback(() => {
|
|
||||||
analytics('timeline_conversation_delete_press')
|
|
||||||
mutation.mutate({
|
|
||||||
type: 'deleteItem',
|
|
||||||
source: 'conversations',
|
|
||||||
queryKey,
|
|
||||||
id: conversation.id
|
|
||||||
})
|
})
|
||||||
}, [])
|
queryClient.setQueryData(queryKey, oldData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const actionChildren = useMemo(
|
const actionOnPress = useCallback(() => {
|
||||||
() => (
|
analytics('timeline_conversation_delete_press')
|
||||||
<Icon
|
mutation.mutate({
|
||||||
name='Trash'
|
type: 'deleteItem',
|
||||||
color={colors.secondary}
|
source: 'conversations',
|
||||||
size={StyleConstants.Font.Size.L}
|
queryKey,
|
||||||
/>
|
id: conversation.id
|
||||||
),
|
})
|
||||||
[]
|
}, [])
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
const actionChildren = useMemo(
|
||||||
<View style={styles.base}>
|
() => (
|
||||||
<View style={styles.nameAndMeta}>
|
<Icon
|
||||||
<Names accounts={conversation.accounts} />
|
name='Trash'
|
||||||
<View style={styles.meta}>
|
color={colors.secondary}
|
||||||
{conversation.last_status?.created_at ? (
|
size={StyleConstants.Font.Size.L}
|
||||||
<HeaderSharedCreated
|
/>
|
||||||
created_at={conversation.last_status?.created_at}
|
),
|
||||||
/>
|
[]
|
||||||
) : null}
|
)
|
||||||
<HeaderSharedMuted muted={conversation.last_status?.muted} />
|
|
||||||
</View>
|
return (
|
||||||
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
|
<View style={{ flex: 3 }}>
|
||||||
|
<Names accounts={conversation.accounts} />
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
marginBottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{conversation.last_status?.created_at ? (
|
||||||
|
<HeaderSharedCreated
|
||||||
|
created_at={conversation.last_status?.created_at}
|
||||||
|
edited_at={conversation.last_status?.edited_at}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<HeaderSharedMuted muted={conversation.last_status?.muted} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Pressable
|
|
||||||
style={styles.action}
|
|
||||||
onPress={actionOnPress}
|
|
||||||
children={actionChildren}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
<Pressable
|
||||||
base: {
|
style={{ flex: 1, flexDirection: 'row', justifyContent: 'center' }}
|
||||||
flex: 1,
|
onPress={actionOnPress}
|
||||||
flexDirection: 'row'
|
children={actionChildren}
|
||||||
},
|
/>
|
||||||
nameAndMeta: {
|
</View>
|
||||||
flex: 3
|
)
|
||||||
},
|
}
|
||||||
meta: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
|
||||||
marginBottom: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
namesLeading: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderConversation
|
export default HeaderConversation
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import HeaderSharedAccount from './HeaderShared/Account'
|
import HeaderSharedAccount from './HeaderShared/Account'
|
||||||
import HeaderSharedApplication from './HeaderShared/Application'
|
import HeaderSharedApplication from './HeaderShared/Application'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
@ -18,75 +20,61 @@ export interface Props {
|
|||||||
status: Mastodon.Status
|
status: Mastodon.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderDefault = React.memo(
|
const TimelineHeaderDefault = ({ queryKey, rootQueryKey, status }: Props) => {
|
||||||
({ queryKey, rootQueryKey, status }: Props) => {
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { t } = useTranslation('componentTimeline')
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const navigation = useNavigation()
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
<View style={styles.accountAndMeta}>
|
<View style={{ flex: 5 }}>
|
||||||
<HeaderSharedAccount account={status.account} />
|
<HeaderSharedAccount account={status.account} />
|
||||||
<View style={styles.meta}>
|
<View
|
||||||
<HeaderSharedCreated created_at={status.created_at} />
|
style={{
|
||||||
<HeaderSharedVisibility visibility={status.visibility} />
|
flexDirection: 'row',
|
||||||
<HeaderSharedMuted muted={status.muted} />
|
alignItems: 'center',
|
||||||
<HeaderSharedApplication application={status.application} />
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
</View>
|
marginBottom: StyleConstants.Spacing.S
|
||||||
</View>
|
}}
|
||||||
|
>
|
||||||
{queryKey ? (
|
<HeaderSharedCreated
|
||||||
<Pressable
|
created_at={status.created_at}
|
||||||
accessibilityHint={t('shared.header.actions.accessibilityHint')}
|
edited_at={status.edited_at}
|
||||||
style={styles.action}
|
|
||||||
onPress={() =>
|
|
||||||
navigation.navigate('Screen-Actions', {
|
|
||||||
queryKey,
|
|
||||||
rootQueryKey,
|
|
||||||
status,
|
|
||||||
url: status.url || status.uri,
|
|
||||||
type: 'status'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
children={
|
|
||||||
<Icon
|
|
||||||
name='MoreHorizontal'
|
|
||||||
color={colors.secondary}
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
<HeaderSharedVisibility visibility={status.visibility} />
|
||||||
|
<HeaderSharedMuted muted={status.muted} />
|
||||||
|
<HeaderSharedApplication application={status.application} />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
{queryKey ? (
|
||||||
base: {
|
<Pressable
|
||||||
flex: 1,
|
accessibilityHint={t('shared.header.actions.accessibilityHint')}
|
||||||
flexDirection: 'row'
|
style={{
|
||||||
},
|
flex: 1,
|
||||||
accountAndMeta: {
|
flexDirection: 'row',
|
||||||
flex: 5
|
justifyContent: 'center',
|
||||||
},
|
paddingBottom: StyleConstants.Spacing.S
|
||||||
meta: {
|
}}
|
||||||
flexDirection: 'row',
|
onPress={() =>
|
||||||
alignItems: 'center',
|
navigation.navigate('Screen-Actions', {
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
queryKey,
|
||||||
marginBottom: StyleConstants.Spacing.S
|
rootQueryKey,
|
||||||
},
|
status,
|
||||||
created_at: {
|
type: 'status'
|
||||||
...StyleConstants.FontStyle.S
|
})
|
||||||
},
|
}
|
||||||
action: {
|
children={
|
||||||
flex: 1,
|
<Icon
|
||||||
flexDirection: 'row',
|
name='MoreHorizontal'
|
||||||
justifyContent: 'center',
|
color={colors.secondary}
|
||||||
paddingBottom: StyleConstants.Spacing.S
|
size={StyleConstants.Font.Size.L}
|
||||||
}
|
/>
|
||||||
})
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default TimelineHeaderDefault
|
export default TimelineHeaderDefault
|
||||||
|
@ -4,11 +4,13 @@ import {
|
|||||||
RelationshipOutgoing
|
RelationshipOutgoing
|
||||||
} from '@components/Relationship'
|
} from '@components/Relationship'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import HeaderSharedAccount from './HeaderShared/Account'
|
import HeaderSharedAccount from './HeaderShared/Account'
|
||||||
import HeaderSharedApplication from './HeaderShared/Application'
|
import HeaderSharedApplication from './HeaderShared/Application'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
@ -20,113 +22,103 @@ export interface Props {
|
|||||||
notification: Mastodon.Notification
|
notification: Mastodon.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderNotification = React.memo(
|
const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
||||||
({ queryKey, notification }: Props) => {
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const navigation = useNavigation()
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const actions = useMemo(() => {
|
const actions = useMemo(() => {
|
||||||
switch (notification.type) {
|
switch (notification.type) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
return <RelationshipOutgoing id={notification.account.id} />
|
return <RelationshipOutgoing id={notification.account.id} />
|
||||||
case 'follow_request':
|
case 'follow_request':
|
||||||
return <RelationshipIncoming id={notification.account.id} />
|
return <RelationshipIncoming id={notification.account.id} />
|
||||||
default:
|
default:
|
||||||
if (notification.status) {
|
if (notification.status) {
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingBottom: StyleConstants.Spacing.S
|
paddingBottom: StyleConstants.Spacing.S
|
||||||
}}
|
}}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
navigation.navigate('Screen-Actions', {
|
navigation.navigate('Screen-Actions', {
|
||||||
queryKey,
|
queryKey,
|
||||||
status: notification.status,
|
status: notification.status!,
|
||||||
url: notification.status?.url || notification.status?.uri,
|
type: 'status'
|
||||||
type: 'status'
|
})
|
||||||
})
|
}
|
||||||
}
|
children={
|
||||||
children={
|
<Icon
|
||||||
<Icon
|
name='MoreHorizontal'
|
||||||
name='MoreHorizontal'
|
color={colors.secondary}
|
||||||
color={colors.secondary}
|
size={StyleConstants.Font.Size.L}
|
||||||
size={StyleConstants.Font.Size.L}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [notification.type])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.base}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex:
|
|
||||||
notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request'
|
|
||||||
? 1
|
|
||||||
: 4
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HeaderSharedAccount
|
|
||||||
account={
|
|
||||||
notification.status
|
|
||||||
? notification.status.account
|
|
||||||
: notification.account
|
|
||||||
}
|
|
||||||
{...((notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request') && { withoutName: true })}
|
|
||||||
/>
|
|
||||||
<View style={styles.meta}>
|
|
||||||
<HeaderSharedCreated created_at={notification.created_at} />
|
|
||||||
{notification.status?.visibility ? (
|
|
||||||
<HeaderSharedVisibility
|
|
||||||
visibility={notification.status.visibility}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<HeaderSharedMuted muted={notification.status?.muted} />
|
|
||||||
<HeaderSharedApplication
|
|
||||||
application={notification.status?.application}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
)
|
||||||
</View>
|
}
|
||||||
|
}
|
||||||
|
}, [notification.type])
|
||||||
|
|
||||||
<View
|
return (
|
||||||
style={[
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
styles.relationship,
|
<View
|
||||||
|
style={{
|
||||||
|
flex:
|
||||||
notification.type === 'follow' ||
|
notification.type === 'follow' ||
|
||||||
notification.type === 'follow_request'
|
notification.type === 'follow_request'
|
||||||
? { flexShrink: 1 }
|
? 1
|
||||||
: { flex: 1 }
|
: 4
|
||||||
]}
|
}}
|
||||||
|
>
|
||||||
|
<HeaderSharedAccount
|
||||||
|
account={
|
||||||
|
notification.status
|
||||||
|
? notification.status.account
|
||||||
|
: notification.account
|
||||||
|
}
|
||||||
|
{...((notification.type === 'follow' ||
|
||||||
|
notification.type === 'follow_request') && { withoutName: true })}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
marginBottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{actions}
|
<HeaderSharedCreated
|
||||||
|
created_at={notification.created_at}
|
||||||
|
edited_at={notification.status?.edited_at}
|
||||||
|
/>
|
||||||
|
{notification.status?.visibility ? (
|
||||||
|
<HeaderSharedVisibility
|
||||||
|
visibility={notification.status.visibility}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<HeaderSharedMuted muted={notification.status?.muted} />
|
||||||
|
<HeaderSharedApplication
|
||||||
|
application={notification.status?.application}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
<View
|
||||||
base: {
|
style={[
|
||||||
flex: 1,
|
{ marginLeft: StyleConstants.Spacing.M },
|
||||||
flexDirection: 'row'
|
notification.type === 'follow' ||
|
||||||
},
|
notification.type === 'follow_request'
|
||||||
meta: {
|
? { flexShrink: 1 }
|
||||||
flexDirection: 'row',
|
: { flex: 1 }
|
||||||
alignItems: 'center',
|
]}
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
>
|
||||||
marginBottom: StyleConstants.Spacing.S
|
{actions}
|
||||||
},
|
</View>
|
||||||
relationship: {
|
</View>
|
||||||
marginLeft: StyleConstants.Spacing.M
|
)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineHeaderNotification
|
export default TimelineHeaderNotification
|
||||||
|
@ -1,30 +1,43 @@
|
|||||||
|
import Icon from '@components/Icon'
|
||||||
import RelativeTime from '@components/RelativeTime'
|
import RelativeTime from '@components/RelativeTime'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Text } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
created_at: Mastodon.Status['created_at'] | number
|
created_at: Mastodon.Status['created_at']
|
||||||
|
edited_at?: Mastodon.Status['edited_at']
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderSharedCreated = React.memo(
|
const HeaderSharedCreated = React.memo(
|
||||||
({ created_at }: Props) => {
|
({ created_at, edited_at }: Props) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.created_at, { color: colors.secondary }]}>
|
<>
|
||||||
<RelativeTime date={created_at} />
|
<Text
|
||||||
</Text>
|
style={{ ...StyleConstants.FontStyle.S, color: colors.secondary }}
|
||||||
|
>
|
||||||
|
<RelativeTime date={edited_at || created_at} />
|
||||||
|
</Text>
|
||||||
|
{edited_at ? (
|
||||||
|
<Icon
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.header.shared.edited.accessibilityLabel'
|
||||||
|
)}
|
||||||
|
name='Edit'
|
||||||
|
size={StyleConstants.Font.Size.S}
|
||||||
|
color={colors.secondary}
|
||||||
|
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => prev.edited_at === next.edited_at
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
created_at: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderSharedCreated
|
export default HeaderSharedCreated
|
||||||
|
@ -7,13 +7,16 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||||||
import * as Localization from 'expo-localization'
|
import * as Localization from 'expo-localization'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, Text } from 'react-native'
|
import { Pressable, Text } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
highlighted: boolean
|
highlighted: boolean
|
||||||
status: Mastodon.Status
|
status: Pick<
|
||||||
|
Mastodon.Status,
|
||||||
|
'language' | 'spoiler_text' | 'content' | 'emojis'
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineTranslate = React.memo(
|
const TimelineTranslate = React.memo(
|
||||||
@ -60,7 +63,12 @@ const TimelineTranslate = React.memo(
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={[styles.button, { paddingBottom: isSuccess ? 0 : undefined }]}
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: StyleConstants.Spacing.S,
|
||||||
|
paddingBottom: isSuccess ? 0 : undefined
|
||||||
|
}}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
@ -128,15 +136,10 @@ const TimelineTranslate = React.memo(
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) =>
|
||||||
|
prev.status.language === next.status.language &&
|
||||||
|
prev.status.content === next.status.content &&
|
||||||
|
prev.status.spoiler_text === next.status.spoiler_text
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingVertical: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineTranslate
|
export default TimelineTranslate
|
||||||
|
12
src/helpers/features.json
Normal file
12
src/helpers/features.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"feature": "edit_post",
|
||||||
|
"version": 3.5,
|
||||||
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"feature": "deprecate_auth_follow",
|
||||||
|
"version": 3.5,
|
||||||
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
|
}
|
||||||
|
]
|
21
src/i18n/de/common.json
Normal file
21
src/i18n/de/common.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"buttons": {
|
||||||
|
"apply": "Übernehmen",
|
||||||
|
"cancel": "Abbrechen"
|
||||||
|
},
|
||||||
|
"customEmoji": {
|
||||||
|
"accessibilityLabel": "Eigenes Emoji {{emoji}}"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"success": {
|
||||||
|
"message": "{{function}} erfolgreich"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"message": ""
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"message": "{{function}} fehlgeschlagen, bitte wiederholen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"separator": ", "
|
||||||
|
}
|
28
src/i18n/de/components/instance.json
Normal file
28
src/i18n/de/components/instance.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"textInput": {
|
||||||
|
"placeholder": "Instanz"
|
||||||
|
},
|
||||||
|
"button": "Login",
|
||||||
|
"information": {
|
||||||
|
"name": "Name",
|
||||||
|
"accounts": "Konten",
|
||||||
|
"statuses": "Tröts",
|
||||||
|
"domains": "Domains"
|
||||||
|
},
|
||||||
|
"disclaimer": {
|
||||||
|
"base": "Der Login erfolgt über den Browser, so dass Ihre Kontoinformationen für die Toot-App nicht sichtbar sind. Weitere Informationen",
|
||||||
|
"privacy": "Datenschutzbestimmungen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"alert": {
|
||||||
|
"title": "Bei der Instanz angemeldet",
|
||||||
|
"message": "Du kannst dich mit einem weiteren Konto anmelden, während du weiterhin mit deinem bestehen Account eingeloggt bleibst",
|
||||||
|
"buttons": {
|
||||||
|
"cancel": "$t(common:buttons.cancel)",
|
||||||
|
"continue": "Weiter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/i18n/de/components/parse.json
Normal file
9
src/i18n/de/components/parse.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"HTML": {
|
||||||
|
"expanded": {
|
||||||
|
"true": "Zusammenklappen {{hint}}",
|
||||||
|
"false": "Ausklappen {{hint}}"
|
||||||
|
},
|
||||||
|
"defaultHint": "Artikel"
|
||||||
|
}
|
||||||
|
}
|
234
src/i18n/de/components/timeline.json
Normal file
234
src/i18n/de/components/timeline.json
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
{
|
||||||
|
"empty": {
|
||||||
|
"error": {
|
||||||
|
"message": "Fehler beim Laden",
|
||||||
|
"button": "Wiederholen"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"message": "Timeline ist leer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"message": "Ende der Geschichte. Wie wäre es mit einer Tasse <0 />?"
|
||||||
|
},
|
||||||
|
"lookback": {
|
||||||
|
"message": "Letzte Lesung am"
|
||||||
|
},
|
||||||
|
"refresh": {
|
||||||
|
"fetchPreviousPage": "Neuere Einträge",
|
||||||
|
"refetch": "Zum letzten"
|
||||||
|
},
|
||||||
|
"shared": {
|
||||||
|
"actioned": {
|
||||||
|
"pinned": "Angepinnt",
|
||||||
|
"favourite": "{{name}} mag deinen Tröt",
|
||||||
|
"status": "{{name}} hat gerade getrötet",
|
||||||
|
"follow": "{{name}} folgt dir",
|
||||||
|
"follow_request": "{{name}} möchte dir gerne folgen",
|
||||||
|
"poll": "Eine Umfrage, an der du teilgenommen hast, ist beendet",
|
||||||
|
"reblog": {
|
||||||
|
"default": "{{name}} hat geboostet",
|
||||||
|
"notification": "{{name}} hat deinen Tröt geboostet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"reply": {
|
||||||
|
"accessibilityLabel": "Antworten"
|
||||||
|
},
|
||||||
|
"reblogged": {
|
||||||
|
"accessibilityLabel": "Tröt boosten",
|
||||||
|
"function": "Boost"
|
||||||
|
},
|
||||||
|
"favourited": {
|
||||||
|
"accessibilityLabel": "Favorit",
|
||||||
|
"function": "Sternen"
|
||||||
|
},
|
||||||
|
"bookmarked": {
|
||||||
|
"accessibilityLabel": "Lesezeichen hinzufügen",
|
||||||
|
"function": "Lesezeichen setzen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actionsUsers": {
|
||||||
|
"reblogged_by": {
|
||||||
|
"accessibilityLabel": "{{count}} Leute haben den Tröt geboostet",
|
||||||
|
"accessibilityHint": "Booster anzeigen",
|
||||||
|
"text": "$t(screenTabs:shared.users.statuses.reblogged_by)"
|
||||||
|
},
|
||||||
|
"favourited_by": {
|
||||||
|
"accessibilityLabel": "{{count}} Leute mögen diesen Tröt",
|
||||||
|
"accessibilityHint": "Anzeigen, wer diesen Tröt mag",
|
||||||
|
"text": "$t(screenTabs:shared.users.statuses.favourited_by)"
|
||||||
|
},
|
||||||
|
"history": {
|
||||||
|
"accessibilityLabel": "Dieser Tröt wurde {{count}} mal bearbeitet",
|
||||||
|
"accessibilityHint": "Für den vollständigen Verlauf auswählen",
|
||||||
|
"text": "{{count}} bearbeitet",
|
||||||
|
"text_plural": "{{count}} mal bearbeitet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attachment": {
|
||||||
|
"sensitive": {
|
||||||
|
"button": "Sensiblen Inhalt anzeigen"
|
||||||
|
},
|
||||||
|
"unsupported": {
|
||||||
|
"text": "Ladefehler",
|
||||||
|
"button": "Remote Link versuchen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"avatar": {
|
||||||
|
"accessibilityLabel": "Profilbild von {{name}}",
|
||||||
|
"accessibilityHint": "Zum Profil von {{name}}"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"expandHint": "Ausgeblendeter Inhalt"
|
||||||
|
},
|
||||||
|
"filtered": "Ausgeblendet",
|
||||||
|
"fullConversation": "Unterhaltung anzeigen",
|
||||||
|
"translate": {
|
||||||
|
"default": "Übersetzen",
|
||||||
|
"succeed": "Übersetzt durch {{provider}} von {{source}}",
|
||||||
|
"failed": "Übersetzung fehlgeschlagen",
|
||||||
|
"source_not_supported": "Sprache des Tröts wird nicht unterstützt",
|
||||||
|
"target_not_supported": "Zielsprache wird nicht unterstützt"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"shared": {
|
||||||
|
"account": {
|
||||||
|
"name": {
|
||||||
|
"accessibilityHint": "Benutzername anzeigen"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"accessibilityHint": "Profil"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application": "Getrötet mit {{application}}",
|
||||||
|
"edited": {
|
||||||
|
"accessibilityLabel": "Tröt bearbeitet"
|
||||||
|
},
|
||||||
|
"muted": {
|
||||||
|
"accessibilityLabel": "Tröt stummgeschaltet"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"direct": {
|
||||||
|
"accessibilityLabel": "Tröt ist eine Direktnachricht"
|
||||||
|
},
|
||||||
|
"private": {
|
||||||
|
"accessibilityLabel": "Tröt ist nur für Follower sichtbar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"withAccounts": "mit",
|
||||||
|
"delete": {
|
||||||
|
"function": "Nachricht löschen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"accessibilityHint": "Funktionen für diesen Tröt - wie z. B. Autor und Originaltröt",
|
||||||
|
"account": {
|
||||||
|
"heading": "Über die Nutzerin/den Nutzer",
|
||||||
|
"mute": {
|
||||||
|
"function": "Profil stummschalten",
|
||||||
|
"button": "@{{acct}} stummschalten"
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"function": "Nutzer blockieren",
|
||||||
|
"button": "@{{acct}} blockieren"
|
||||||
|
},
|
||||||
|
"reports": {
|
||||||
|
"function": "User melden",
|
||||||
|
"button": "@{{acct}} melden"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"heading": "Über diese Instanz",
|
||||||
|
"block": {
|
||||||
|
"function": "Instanz blockieren",
|
||||||
|
"button": "Instanz {{domain}} blockieren"
|
||||||
|
},
|
||||||
|
"alert": {
|
||||||
|
"title": "{{domain}} wirklich blockieren?",
|
||||||
|
"message": "Üblicherweise kannst du einen User stummschalten oder blockieren.\nBlockierst du hingegegen eine Instanz, wird deren gesamter Inhalt samt Usern, die dir von dieser Instanz folgen, entfernt!",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Blockierung bestätigen",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"share": {
|
||||||
|
"status": {
|
||||||
|
"heading": "Tröt teilen",
|
||||||
|
"button": "Link zu diesem Tröt teilen"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"heading": "User verlinken",
|
||||||
|
"button": "Link zu diesem Benutzer teilen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"heading": "Über Toot",
|
||||||
|
"edit": {
|
||||||
|
"function": "Tröt bearbeiten",
|
||||||
|
"button": "Diesen Tröt bearbeiten"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"function": "Tröt löschen",
|
||||||
|
"button": "Diesen Tröt löschen",
|
||||||
|
"alert": {
|
||||||
|
"title": "Tröt sicher löschen?",
|
||||||
|
"message": "Bist du wirklich sicher, diesen Tröt löschen zu wollen? Sämtliche Boosts und Sterne werden samt der Antworten entfernt.",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Löschen bestätigen",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deleteEdit": {
|
||||||
|
"function": "Tröt löschen",
|
||||||
|
"button": "Diesen Tröt neu entwerfen",
|
||||||
|
"alert": {
|
||||||
|
"title": "Tröt sicher löschen?",
|
||||||
|
"message": "Bist du wirklich sicher, diesen Tröt neu zu entwerfen? Alle Boosts und Sterne werden entfernt - samt der Antworten.",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Löschen bestätigen",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mute": {
|
||||||
|
"function": "Tröt stummschalten",
|
||||||
|
"button": {
|
||||||
|
"positive": "Diesen Tröt sowie die Antworten stummschalten",
|
||||||
|
"negative": "Diesen Tröt sowie die Antworten nicht mehr stummschalten"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pin": {
|
||||||
|
"function": "Anheften",
|
||||||
|
"button": {
|
||||||
|
"positive": "Diesen Tröt anheften",
|
||||||
|
"negative": "Diesen Tröt nicht mehr anheften"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"poll": {
|
||||||
|
"meta": {
|
||||||
|
"button": {
|
||||||
|
"vote": "Abstimmen",
|
||||||
|
"refresh": "Aktualisieren"
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"voters": "{{count}} Benutzer haben abgestimmt",
|
||||||
|
"voters_plural": "{{count}} Benutzer haben abgestimmt",
|
||||||
|
"votes": "{{count}} Stimmen",
|
||||||
|
"votes_plural": "{{count}} Stimmen"
|
||||||
|
},
|
||||||
|
"expiration": {
|
||||||
|
"expired": "Abstimmung abgelaufen",
|
||||||
|
"until": "Läuft in <0 /> ab"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/i18n/de/screens.json
Normal file
14
src/i18n/de/screens.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"screenshot": {
|
||||||
|
"title": "Datenschutz",
|
||||||
|
"message": "Bitte geben Sie nicht die Identität anderer Nutzer preis, wie z. B. Benutzername, Avatar, etc. Vielen Dank!",
|
||||||
|
"button": "Bestätigen"
|
||||||
|
},
|
||||||
|
"localCorrupt": {
|
||||||
|
"message": "Login abgelaufen, bitte erneut anmelden"
|
||||||
|
},
|
||||||
|
"pushError": {
|
||||||
|
"message": "Fehler des Benachrichtigungsdienstes",
|
||||||
|
"description": "Bitte Benachrichtigungsdienst in den Einstellungen erneut aktivieren"
|
||||||
|
}
|
||||||
|
}
|
19
src/i18n/de/screens/actions.json
Normal file
19
src/i18n/de/screens/actions.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"button": {
|
||||||
|
"apply": "$t(common:buttons.apply)",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
},
|
||||||
|
"notificationsFilter": {
|
||||||
|
"heading": "Benachrichtigungsart anzeigen",
|
||||||
|
"content": {
|
||||||
|
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||||
|
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
||||||
|
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
||||||
|
"mention": "$t(screenTabs:me.push.mention.heading)",
|
||||||
|
"poll": "$t(screenTabs:me.push.poll.heading)",
|
||||||
|
"follow_request": "Followeranfrage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,12 @@
|
|||||||
"accessibilityLabel": "{{count}} users have favourited this toot",
|
"accessibilityLabel": "{{count}} users have favourited this toot",
|
||||||
"accessibilityHint": "Tap to know the users",
|
"accessibilityHint": "Tap to know the users",
|
||||||
"text": "$t(screenTabs:shared.users.statuses.favourited_by)"
|
"text": "$t(screenTabs:shared.users.statuses.favourited_by)"
|
||||||
|
},
|
||||||
|
"history": {
|
||||||
|
"accessibilityLabel": "This toot has been edited {{count}} times",
|
||||||
|
"accessibilityHint": "Tap to know view the full history",
|
||||||
|
"text": "{{count}} edit",
|
||||||
|
"text_plural": "{{count}} edits"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"attachment": {
|
"attachment": {
|
||||||
@ -96,6 +102,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"application": "Tooted with {{application}}",
|
"application": "Tooted with {{application}}",
|
||||||
|
"edited": {
|
||||||
|
"accessibilityLabel": "Toot edited"
|
||||||
|
},
|
||||||
"muted": {
|
"muted": {
|
||||||
"accessibilityLabel": "Toot muted"
|
"accessibilityLabel": "Toot muted"
|
||||||
},
|
},
|
||||||
@ -158,11 +167,23 @@
|
|||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"heading": "About toot",
|
"heading": "About toot",
|
||||||
|
"edit": {
|
||||||
|
"function": "Edit toot",
|
||||||
|
"button": "Edit this toot"
|
||||||
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"function": "Delete toot",
|
"function": "Delete toot",
|
||||||
"button": "Delete this toot"
|
"button": "Delete this toot",
|
||||||
|
"alert": {
|
||||||
|
"title": "Confirm deleting toot?",
|
||||||
|
"message": "Are you sure to delete this toot? All boosts and favourites will be cleared, including all replies.",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Confirm deleting",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"edit": {
|
"deleteEdit": {
|
||||||
"function": "Delete toot",
|
"function": "Delete toot",
|
||||||
"button": "Delete and re-draft",
|
"button": "Delete and re-draft",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
"default": "Toot",
|
"default": "Toot",
|
||||||
"conversation": "Toot DM",
|
"conversation": "Toot DM",
|
||||||
"reply": "Toot reply",
|
"reply": "Toot reply",
|
||||||
"edit": "Toot"
|
"deleteEdit": "Toot",
|
||||||
|
"edit": "Toot",
|
||||||
|
"share": "Toot"
|
||||||
},
|
},
|
||||||
"alert": {
|
"alert": {
|
||||||
"default": {
|
"default": {
|
||||||
|
@ -267,7 +267,8 @@
|
|||||||
"heading": "Help us improve",
|
"heading": "Help us improve",
|
||||||
"description": "Collecting only non-user relative usage"
|
"description": "Collecting only non-user relative usage"
|
||||||
},
|
},
|
||||||
"version": "Version v{{version}}"
|
"version": "Version v{{version}}",
|
||||||
|
"instanceVersion": "Mastodon version v{{version}}"
|
||||||
},
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
"existing": "Choose from logged in",
|
"existing": "Choose from logged in",
|
||||||
@ -331,6 +332,9 @@
|
|||||||
"reblogged_by": "{{count}} boosted",
|
"reblogged_by": "{{count}} boosted",
|
||||||
"favourited_by": "{{count}} favourited"
|
"favourited_by": "{{count}} favourited"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"history": {
|
||||||
|
"name": "Edit History"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -160,7 +160,7 @@
|
|||||||
"function": "툿 삭제",
|
"function": "툿 삭제",
|
||||||
"button": "이 툿 삭제"
|
"button": "이 툿 삭제"
|
||||||
},
|
},
|
||||||
"edit": {
|
"deleteEdit": {
|
||||||
"function": "툿 삭제",
|
"function": "툿 삭제",
|
||||||
"button": "삭제하고 다시 쓰기",
|
"button": "삭제하고 다시 쓰기",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
},
|
},
|
||||||
"button": "Đăng nhập",
|
"button": "Đăng nhập",
|
||||||
"information": {
|
"information": {
|
||||||
"name": "Tên",
|
"name": "Tên máy chủ",
|
||||||
"accounts": "Người dùng",
|
"accounts": "Người dùng",
|
||||||
"statuses": "Tút",
|
"statuses": "Tút",
|
||||||
"domains": "Liên hợp"
|
"domains": "Liên hợp"
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
},
|
},
|
||||||
"avatar": {
|
"avatar": {
|
||||||
"accessibilityLabel": "Ảnh đại diện của {{name}}",
|
"accessibilityLabel": "Ảnh đại diện của {{name}}",
|
||||||
"accessibilityHint": "Đến trang cá nhân {{name}}"
|
"accessibilityHint": "Đến trang của {{name}}"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"expandHint": "nội dung ẩn"
|
"expandHint": "nội dung ẩn"
|
||||||
@ -162,7 +162,7 @@
|
|||||||
"function": "Xóa tút",
|
"function": "Xóa tút",
|
||||||
"button": "Xóa tút này"
|
"button": "Xóa tút này"
|
||||||
},
|
},
|
||||||
"edit": {
|
"deleteEdit": {
|
||||||
"function": "Xóa tút",
|
"function": "Xóa tút",
|
||||||
"button": "Xóa và viết lại",
|
"button": "Xóa và viết lại",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
@ -38,10 +38,10 @@
|
|||||||
"name": "Nhắn riêng"
|
"name": "Nhắn riêng"
|
||||||
},
|
},
|
||||||
"favourites": {
|
"favourites": {
|
||||||
"name": "Lượt thích"
|
"name": "Đã thích"
|
||||||
},
|
},
|
||||||
"fontSize": {
|
"fontSize": {
|
||||||
"name": "Kích cỡ phông chữ"
|
"name": "Cỡ chữ"
|
||||||
},
|
},
|
||||||
"lists": {
|
"lists": {
|
||||||
"name": "Danh sách"
|
"name": "Danh sách"
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"name": "Thông báo đẩy"
|
"name": "Thông báo đẩy"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"name": "Cài đặt cá nhân"
|
"name": "Sửa hồ sơ"
|
||||||
},
|
},
|
||||||
"profileName": {
|
"profileName": {
|
||||||
"name": "Tên hiển thị mới"
|
"name": "Tên hiển thị mới"
|
||||||
@ -75,8 +75,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fontSize": {
|
"fontSize": {
|
||||||
"showcase": "Kết quả",
|
"showcase": "Xem trước",
|
||||||
"demo": "<p>Đây là một tút mẫu 😊 Bạn có thể chọn một trong nhiều lựa chọn bên dưới.<br /><br />Tùy chọn này chỉ áp dụng cho nội dung tút chứ không thay đổi những phần khác của app.</p>",
|
"demo": "<p>Đây là một tút mẫu 😊 Bạn có thể chọn một trong nhiều lựa chọn bên dưới.<br /><br />Tùy chọn này chỉ áp dụng cho nội dung tút chứ không ảnh hưởng những phần tử khác của app.</p>",
|
||||||
"availableSizes": "Kích cỡ",
|
"availableSizes": "Kích cỡ",
|
||||||
"sizes": {
|
"sizes": {
|
||||||
"S": "S",
|
"S": "S",
|
||||||
@ -270,7 +270,7 @@
|
|||||||
"version": "Phiên bản {{version}}"
|
"version": "Phiên bản {{version}}"
|
||||||
},
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
"existing": "Đã đăng nhập trước đó",
|
"existing": "Đã đăng nhập trước đây",
|
||||||
"new": "Đăng nhập máy chủ"
|
"new": "Đăng nhập máy chủ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -320,7 +320,7 @@
|
|||||||
"notFound": "Không tìm thấy {{type}} <bold>{{searchTerm}}</bold>"
|
"notFound": "Không tìm thấy {{type}} <bold>{{searchTerm}}</bold>"
|
||||||
},
|
},
|
||||||
"toot": {
|
"toot": {
|
||||||
"name": "Thảo luận"
|
"name": "Nội dung tút"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"accounts": {
|
"accounts": {
|
||||||
|
@ -162,7 +162,7 @@
|
|||||||
"function": "删除",
|
"function": "删除",
|
||||||
"button": "删除此条嘟文"
|
"button": "删除此条嘟文"
|
||||||
},
|
},
|
||||||
"edit": {
|
"deleteEdit": {
|
||||||
"function": "删除",
|
"function": "删除",
|
||||||
"button": "删除并重新编辑此条嘟文",
|
"button": "删除并重新编辑此条嘟文",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
21
src/i18n/zh-Hant/common.json
Normal file
21
src/i18n/zh-Hant/common.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"buttons": {
|
||||||
|
"apply": "套用",
|
||||||
|
"cancel": "取消"
|
||||||
|
},
|
||||||
|
"customEmoji": {
|
||||||
|
"accessibilityLabel": "自定的表情符號 {{emoji}}"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"success": {
|
||||||
|
"message": "{{function}} 成功完成"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"message": ""
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"message": "{{function}} 失敗,請再試一次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"separator": ","
|
||||||
|
}
|
3
src/i18n/zh-Hant/components/emojis.json
Normal file
3
src/i18n/zh-Hant/components/emojis.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"frequentUsed": "常用"
|
||||||
|
}
|
28
src/i18n/zh-Hant/components/instance.json
Normal file
28
src/i18n/zh-Hant/components/instance.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"textInput": {
|
||||||
|
"placeholder": "站點的網域"
|
||||||
|
},
|
||||||
|
"button": "登入",
|
||||||
|
"information": {
|
||||||
|
"name": "名稱",
|
||||||
|
"accounts": "使用者",
|
||||||
|
"statuses": "嘟文",
|
||||||
|
"domains": "串聯宇宙"
|
||||||
|
},
|
||||||
|
"disclaimer": {
|
||||||
|
"base": "將使用系統內建的瀏覽器來登入,tooot app 無法讀取您的帳號資訊;詳見 ",
|
||||||
|
"privacy": "隱私權政策"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"alert": {
|
||||||
|
"title": "已登入站點",
|
||||||
|
"message": "您可以多登入其他的帳號,已登入的帳號會被保留",
|
||||||
|
"buttons": {
|
||||||
|
"cancel": "$t(common:buttons.cancel)",
|
||||||
|
"continue": "繼續"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/i18n/zh-Hant/components/mediaSelector.json
Normal file
28
src/i18n/zh-Hant/components/mediaSelector.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"title": "選擇媒體來源",
|
||||||
|
"options": {
|
||||||
|
"library": "上傳",
|
||||||
|
"photo": "拍照",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"alert": {
|
||||||
|
"title": "權限不足",
|
||||||
|
"message": "上傳照片需要讀取的權限",
|
||||||
|
"buttons": {
|
||||||
|
"settings": "更新設定",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"photo": {
|
||||||
|
"alert": {
|
||||||
|
"title": "權限不足",
|
||||||
|
"message": "需要使用相機的權限來上傳",
|
||||||
|
"buttons": {
|
||||||
|
"settings": "更新設定",
|
||||||
|
"cancel": "$t(common:buttons.cancel)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/i18n/zh-Hant/components/parse.json
Normal file
9
src/i18n/zh-Hant/components/parse.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"HTML": {
|
||||||
|
"expanded": {
|
||||||
|
"true": "收起{{hint}}",
|
||||||
|
"false": "展開{{hint}}"
|
||||||
|
},
|
||||||
|
"defaultHint": "全文"
|
||||||
|
}
|
||||||
|
}
|
16
src/i18n/zh-Hant/components/relationship.json
Normal file
16
src/i18n/zh-Hant/components/relationship.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"follow": {
|
||||||
|
"function": "追蹤使用者"
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"function": "封鎖使用者"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"error": "載入錯誤",
|
||||||
|
"blocked_by": "已被使用者封鎖",
|
||||||
|
"blocking": "解除封鎖",
|
||||||
|
"following": "取消追蹤",
|
||||||
|
"requested": "收回要求",
|
||||||
|
"default": "追蹤"
|
||||||
|
}
|
||||||
|
}
|
20
src/i18n/zh-Hant/components/relativeTime.json
Normal file
20
src/i18n/zh-Hant/components/relativeTime.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"strings": {
|
||||||
|
"prefixAgo": "",
|
||||||
|
"prefixFromNow": "",
|
||||||
|
"suffixAgo": "前",
|
||||||
|
"suffixFromNow": "",
|
||||||
|
"seconds": "%d 秒",
|
||||||
|
"minute": "約 1 分",
|
||||||
|
"minutes": "%d 分",
|
||||||
|
"hour": "約 1 小時",
|
||||||
|
"hours": "約 %d 小時",
|
||||||
|
"day": "1 天",
|
||||||
|
"days": "%d 天",
|
||||||
|
"month": "約 1 個月",
|
||||||
|
"months": "%d 個月",
|
||||||
|
"year": "約 1 年",
|
||||||
|
"years": "%d 年",
|
||||||
|
"wordSeparator": ""
|
||||||
|
}
|
||||||
|
}
|
14
src/i18n/zh-Hant/screens.json
Normal file
14
src/i18n/zh-Hant/screens.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"screenshot": {
|
||||||
|
"title": "保護隱私",
|
||||||
|
"message": "請不要散佈使用者的身份,像是使用者名稱、大頭照等,謝謝你!",
|
||||||
|
"button": "確認"
|
||||||
|
},
|
||||||
|
"localCorrupt": {
|
||||||
|
"message": "登入狀態已過期,請再登入一次"
|
||||||
|
},
|
||||||
|
"pushError": {
|
||||||
|
"message": "推播服務錯誤",
|
||||||
|
"description": "請在設定裡重新啟用推播"
|
||||||
|
}
|
||||||
|
}
|
@ -35,191 +35,189 @@ import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
|||||||
import ActionsShare from './Actions/Share'
|
import ActionsShare from './Actions/Share'
|
||||||
import ActionsStatus from './Actions/Status'
|
import ActionsStatus from './Actions/Status'
|
||||||
|
|
||||||
const ScreenActions = React.memo(
|
const ScreenActions = ({
|
||||||
({
|
route: { params },
|
||||||
route: { params },
|
navigation
|
||||||
navigation
|
}: RootStackScreenProps<'Screen-Actions'>) => {
|
||||||
}: RootStackScreenProps<'Screen-Actions'>) => {
|
const { t } = useTranslation()
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const instanceAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getInstanceAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
let sameAccount = false
|
let sameAccount = false
|
||||||
|
switch (params.type) {
|
||||||
|
case 'status':
|
||||||
|
console.log('media length', params.status.media_attachments.length)
|
||||||
|
sameAccount = instanceAccount?.id === params.status.account.id
|
||||||
|
break
|
||||||
|
case 'account':
|
||||||
|
sameAccount = instanceAccount?.id === params.account.id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const instanceDomain = useSelector(getInstanceUrl)
|
||||||
|
let sameDomain = true
|
||||||
|
let statusDomain: string
|
||||||
|
switch (params.type) {
|
||||||
|
case 'status':
|
||||||
|
statusDomain = params.status.uri
|
||||||
|
? params.status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||||
|
: ''
|
||||||
|
sameDomain = instanceDomain === statusDomain
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const { colors } = useTheme()
|
||||||
|
const insets = useSafeAreaInsets()
|
||||||
|
|
||||||
|
const DEFAULT_VALUE = 350
|
||||||
|
const screenHeight = Dimensions.get('screen').height
|
||||||
|
const panY = useSharedValue(DEFAULT_VALUE)
|
||||||
|
useEffect(() => {
|
||||||
|
panY.value = withTiming(0)
|
||||||
|
}, [])
|
||||||
|
const styleTop = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
bottom: interpolate(
|
||||||
|
panY.value,
|
||||||
|
[0, screenHeight],
|
||||||
|
[0, -screenHeight],
|
||||||
|
Extrapolate.CLAMP
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const dismiss = useCallback(() => {
|
||||||
|
navigation.goBack()
|
||||||
|
}, [])
|
||||||
|
const onGestureEvent = useAnimatedGestureHandler({
|
||||||
|
onActive: ({ translationY }) => {
|
||||||
|
panY.value = translationY
|
||||||
|
},
|
||||||
|
onEnd: ({ velocityY }) => {
|
||||||
|
if (velocityY > 500) {
|
||||||
|
runOnJS(dismiss)()
|
||||||
|
} else {
|
||||||
|
panY.value = withTiming(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = () => {
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
case 'status':
|
case 'status':
|
||||||
sameAccount = instanceAccount?.id === params.status.account.id
|
return (
|
||||||
break
|
<>
|
||||||
case 'account':
|
{!sameAccount ? (
|
||||||
sameAccount = instanceAccount?.id === params.account.id
|
<ActionsAccount
|
||||||
break
|
queryKey={params.queryKey}
|
||||||
}
|
rootQueryKey={params.rootQueryKey}
|
||||||
|
account={params.status.account}
|
||||||
const instanceDomain = useSelector(getInstanceUrl)
|
dismiss={dismiss}
|
||||||
let sameDomain = true
|
|
||||||
let statusDomain: string
|
|
||||||
switch (params.type) {
|
|
||||||
case 'status':
|
|
||||||
statusDomain = params.status.uri
|
|
||||||
? params.status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
|
||||||
: ''
|
|
||||||
sameDomain = instanceDomain === statusDomain
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const { colors } = useTheme()
|
|
||||||
const insets = useSafeAreaInsets()
|
|
||||||
|
|
||||||
const DEFAULT_VALUE = 350
|
|
||||||
const screenHeight = Dimensions.get('screen').height
|
|
||||||
const panY = useSharedValue(DEFAULT_VALUE)
|
|
||||||
useEffect(() => {
|
|
||||||
panY.value = withTiming(0)
|
|
||||||
}, [])
|
|
||||||
const styleTop = useAnimatedStyle(() => {
|
|
||||||
return {
|
|
||||||
bottom: interpolate(
|
|
||||||
panY.value,
|
|
||||||
[0, screenHeight],
|
|
||||||
[0, -screenHeight],
|
|
||||||
Extrapolate.CLAMP
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const dismiss = useCallback(() => {
|
|
||||||
navigation.goBack()
|
|
||||||
}, [])
|
|
||||||
const onGestureEvent = useAnimatedGestureHandler({
|
|
||||||
onActive: ({ translationY }) => {
|
|
||||||
panY.value = translationY
|
|
||||||
},
|
|
||||||
onEnd: ({ velocityY }) => {
|
|
||||||
if (velocityY > 500) {
|
|
||||||
runOnJS(dismiss)()
|
|
||||||
} else {
|
|
||||||
panY.value = withTiming(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = useMemo(() => {
|
|
||||||
switch (params.type) {
|
|
||||||
case 'status':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!sameAccount ? (
|
|
||||||
<ActionsAccount
|
|
||||||
queryKey={params.queryKey}
|
|
||||||
rootQueryKey={params.rootQueryKey}
|
|
||||||
account={params.status.account}
|
|
||||||
dismiss={dismiss}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{sameAccount && params.status ? (
|
|
||||||
<ActionsStatus
|
|
||||||
navigation={navigation}
|
|
||||||
queryKey={params.queryKey}
|
|
||||||
rootQueryKey={params.rootQueryKey}
|
|
||||||
status={params.status}
|
|
||||||
dismiss={dismiss}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{!sameDomain && statusDomain ? (
|
|
||||||
<ActionsDomain
|
|
||||||
queryKey={params.queryKey}
|
|
||||||
rootQueryKey={params.rootQueryKey}
|
|
||||||
domain={statusDomain}
|
|
||||||
dismiss={dismiss}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{params.status.visibility !== 'direct' ? (
|
|
||||||
<ActionsShare
|
|
||||||
url={params.status.url || params.status.uri}
|
|
||||||
type={params.type}
|
|
||||||
dismiss={dismiss}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
content={t('common:buttons.cancel')}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('bottomsheet_acknowledge')
|
|
||||||
}}
|
|
||||||
style={styles.button}
|
|
||||||
/>
|
/>
|
||||||
</>
|
) : null}
|
||||||
)
|
{sameAccount && params.status ? (
|
||||||
case 'account':
|
<ActionsStatus
|
||||||
return (
|
navigation={navigation}
|
||||||
<>
|
queryKey={params.queryKey}
|
||||||
{!sameAccount ? (
|
rootQueryKey={params.rootQueryKey}
|
||||||
<ActionsAccount account={params.account} dismiss={dismiss} />
|
status={params.status}
|
||||||
) : null}
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{!sameDomain && statusDomain ? (
|
||||||
|
<ActionsDomain
|
||||||
|
queryKey={params.queryKey}
|
||||||
|
rootQueryKey={params.rootQueryKey}
|
||||||
|
domain={statusDomain}
|
||||||
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{params.status.visibility !== 'direct' ? (
|
||||||
<ActionsShare
|
<ActionsShare
|
||||||
url={params.account.url}
|
url={params.status.url || params.status.uri}
|
||||||
type={params.type}
|
type={params.type}
|
||||||
dismiss={dismiss}
|
dismiss={dismiss}
|
||||||
/>
|
/>
|
||||||
<Button
|
) : null}
|
||||||
type='text'
|
<Button
|
||||||
content={t('common:buttons.cancel')}
|
type='text'
|
||||||
onPress={() => {
|
content={t('common:buttons.cancel')}
|
||||||
analytics('bottomsheet_acknowledge')
|
onPress={() => {
|
||||||
}}
|
analytics('bottomsheet_acknowledge')
|
||||||
style={styles.button}
|
}}
|
||||||
/>
|
style={styles.button}
|
||||||
</>
|
/>
|
||||||
)
|
</>
|
||||||
case 'notifications_filter':
|
)
|
||||||
return <ActionsNotificationsFilter />
|
case 'account':
|
||||||
}
|
return (
|
||||||
}, [])
|
<>
|
||||||
|
{!sameAccount ? (
|
||||||
|
<ActionsAccount account={params.account} dismiss={dismiss} />
|
||||||
|
) : null}
|
||||||
|
<ActionsShare
|
||||||
|
url={params.account.url}
|
||||||
|
type={params.type}
|
||||||
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
content={t('common:buttons.cancel')}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('bottomsheet_acknowledge')
|
||||||
|
}}
|
||||||
|
style={styles.button}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case 'notifications_filter':
|
||||||
|
return <ActionsNotificationsFilter />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<Animated.View style={{ flex: 1 }}>
|
<Animated.View style={{ flex: 1 }}>
|
||||||
<TapGestureHandler
|
<TapGestureHandler
|
||||||
onHandlerStateChange={({ nativeEvent }) => {
|
onHandlerStateChange={({ nativeEvent }) => {
|
||||||
if (nativeEvent.state === State.ACTIVE) {
|
if (nativeEvent.state === State.ACTIVE) {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.overlay,
|
||||||
|
{ backgroundColor: colors.backgroundOverlayInvert }
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Animated.View
|
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
||||||
style={[
|
<Animated.View
|
||||||
styles.overlay,
|
style={[
|
||||||
{ backgroundColor: colors.backgroundOverlayInvert }
|
styles.container,
|
||||||
]}
|
styleTop,
|
||||||
>
|
{
|
||||||
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
backgroundColor: colors.backgroundDefault,
|
||||||
<Animated.View
|
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.handle,
|
||||||
styleTop,
|
{ backgroundColor: colors.primaryOverlay }
|
||||||
{
|
|
||||||
backgroundColor: colors.backgroundDefault,
|
|
||||||
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
>
|
/>
|
||||||
<View
|
{actions()}
|
||||||
style={[
|
</Animated.View>
|
||||||
styles.handle,
|
</PanGestureHandler>
|
||||||
{ backgroundColor: colors.primaryOverlay }
|
</Animated.View>
|
||||||
]}
|
</TapGestureHandler>
|
||||||
/>
|
</Animated.View>
|
||||||
{actions}
|
</SafeAreaProvider>
|
||||||
</Animated.View>
|
)
|
||||||
</PanGestureHandler>
|
}
|
||||||
</Animated.View>
|
|
||||||
</TapGestureHandler>
|
|
||||||
</Animated.View>
|
|
||||||
</SafeAreaProvider>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
overlay: {
|
overlay: {
|
||||||
|
@ -11,9 +11,10 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
|
|
||||||
const ActionsNotificationsFilter: React.FC = () => {
|
const ActionsNotificationsFilter: React.FC = () => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
@ -22,7 +23,7 @@ const ActionsNotificationsFilter: React.FC = () => {
|
|||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const instanceNotificationsFilter = useSelector(
|
const instanceNotificationsFilter = useSelector(
|
||||||
getInstanceNotificationsFilter
|
getInstanceNotificationsFilter
|
||||||
)
|
)
|
||||||
@ -35,21 +36,23 @@ const ActionsNotificationsFilter: React.FC = () => {
|
|||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
instanceNotificationsFilter &&
|
instanceNotificationsFilter &&
|
||||||
([
|
(
|
||||||
'follow',
|
[
|
||||||
'favourite',
|
'follow',
|
||||||
'reblog',
|
'favourite',
|
||||||
'mention',
|
'reblog',
|
||||||
'poll',
|
'mention',
|
||||||
'follow_request'
|
'poll',
|
||||||
] as [
|
'follow_request'
|
||||||
'follow',
|
] as [
|
||||||
'favourite',
|
'follow',
|
||||||
'reblog',
|
'favourite',
|
||||||
'mention',
|
'reblog',
|
||||||
'poll',
|
'mention',
|
||||||
'follow_request'
|
'poll',
|
||||||
]).map(type => (
|
'follow_request'
|
||||||
|
]
|
||||||
|
).map(type => (
|
||||||
<MenuRow
|
<MenuRow
|
||||||
key={type}
|
key={type}
|
||||||
title={t(`content.notificationsFilter.content.${type}`)}
|
title={t(`content.notificationsFilter.content.${type}`)}
|
||||||
|
@ -14,6 +14,8 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||||||
import apiInstance from '@api/instance'
|
import apiInstance from '@api/instance'
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
navigation: NativeStackNavigationProp<RootStackParamList, 'Screen-Actions'>
|
navigation: NativeStackNavigationProp<RootStackParamList, 'Screen-Actions'>
|
||||||
@ -59,22 +61,89 @@ const ActionsStatus: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const canEditPost = useSelector(checkInstanceFeature('edit_post'))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<MenuContainer>
|
||||||
<MenuHeader heading={t('shared.header.actions.status.heading')} />
|
<MenuHeader heading={t('shared.header.actions.status.heading')} />
|
||||||
|
{canEditPost ? (
|
||||||
|
<MenuRow
|
||||||
|
onPress={async () => {
|
||||||
|
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<Mastodon.Status>({
|
||||||
|
method: 'get',
|
||||||
|
url: `statuses/${status.in_reply_to_id}`
|
||||||
|
}).then(res => res.body)
|
||||||
|
}
|
||||||
|
apiInstance<{
|
||||||
|
id: Mastodon.Status['id']
|
||||||
|
text: NonNullable<Mastodon.Status['text']>
|
||||||
|
spoiler_text: Mastodon.Status['spoiler_text']
|
||||||
|
}>({
|
||||||
|
method: 'get',
|
||||||
|
url: `statuses/${status.id}/source`
|
||||||
|
}).then(res => {
|
||||||
|
dismiss()
|
||||||
|
navigation.navigate('Screen-Compose', {
|
||||||
|
type: 'edit',
|
||||||
|
incomingStatus: {
|
||||||
|
...status,
|
||||||
|
text: res.body.text,
|
||||||
|
spoiler_text: res.body.spoiler_text
|
||||||
|
},
|
||||||
|
...(replyToStatus && { replyToStatus }),
|
||||||
|
queryKey,
|
||||||
|
rootQueryKey
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='Edit3'
|
||||||
|
title={t('shared.header.actions.status.edit.button')}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<MenuRow
|
<MenuRow
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
analytics('timeline_shared_headeractions_status_delete_press', {
|
analytics('timeline_shared_headeractions_status_delete_press', {
|
||||||
page: queryKey && queryKey[1].page
|
page: queryKey && queryKey[1].page
|
||||||
})
|
})
|
||||||
dismiss()
|
Alert.alert(
|
||||||
mutation.mutate({
|
t('shared.header.actions.status.delete.alert.title'),
|
||||||
type: 'deleteItem',
|
t('shared.header.actions.status.delete.alert.message'),
|
||||||
source: 'statuses',
|
[
|
||||||
queryKey,
|
{
|
||||||
rootQueryKey,
|
text: t(
|
||||||
id: status.id
|
'shared.header.actions.status.delete.alert.buttons.cancel'
|
||||||
})
|
),
|
||||||
|
style: 'cancel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t(
|
||||||
|
'shared.header.actions.status.delete.alert.buttons.confirm'
|
||||||
|
),
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: async () => {
|
||||||
|
analytics(
|
||||||
|
'timeline_shared_headeractions_status_delete_confirm',
|
||||||
|
{
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
}
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'deleteItem',
|
||||||
|
source: 'statuses',
|
||||||
|
queryKey,
|
||||||
|
rootQueryKey,
|
||||||
|
id: status.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
iconFront='Trash'
|
iconFront='Trash'
|
||||||
title={t('shared.header.actions.status.delete.button')}
|
title={t('shared.header.actions.status.delete.button')}
|
||||||
@ -85,18 +154,18 @@ const ActionsStatus: React.FC<Props> = ({
|
|||||||
page: queryKey && queryKey[1].page
|
page: queryKey && queryKey[1].page
|
||||||
})
|
})
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t('shared.header.actions.status.edit.alert.title'),
|
t('shared.header.actions.status.deleteEdit.alert.title'),
|
||||||
t('shared.header.actions.status.edit.alert.message'),
|
t('shared.header.actions.status.deleteEdit.alert.message'),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: t(
|
text: t(
|
||||||
'shared.header.actions.status.edit.alert.buttons.cancel'
|
'shared.header.actions.status.deleteEdit.alert.buttons.cancel'
|
||||||
),
|
),
|
||||||
style: 'cancel'
|
style: 'cancel'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t(
|
text: t(
|
||||||
'shared.header.actions.status.edit.alert.buttons.confirm'
|
'shared.header.actions.status.deleteEdit.alert.buttons.confirm'
|
||||||
),
|
),
|
||||||
style: 'destructive',
|
style: 'destructive',
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
@ -106,7 +175,7 @@ const ActionsStatus: React.FC<Props> = ({
|
|||||||
page: queryKey && queryKey[1].page
|
page: queryKey && queryKey[1].page
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let replyToStatus: Mastodon.Status
|
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||||
if (status.in_reply_to_id) {
|
if (status.in_reply_to_id) {
|
||||||
replyToStatus = await apiInstance<Mastodon.Status>({
|
replyToStatus = await apiInstance<Mastodon.Status>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -122,10 +191,9 @@ const ActionsStatus: React.FC<Props> = ({
|
|||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
dismiss()
|
dismiss()
|
||||||
// @ts-ignore
|
|
||||||
navigation.navigate('Screen-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'edit',
|
type: 'deleteEdit',
|
||||||
incomingStatus: res.body,
|
incomingStatus: res.body as Mastodon.Status,
|
||||||
...(replyToStatus && { replyToStatus }),
|
...(replyToStatus && { replyToStatus }),
|
||||||
queryKey
|
queryKey
|
||||||
})
|
})
|
||||||
@ -136,7 +204,7 @@ const ActionsStatus: React.FC<Props> = ({
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
iconFront='Edit'
|
iconFront='Edit'
|
||||||
title={t('shared.header.actions.status.edit.button')}
|
title={t('shared.header.actions.status.deleteEdit.button')}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
|
import apiInstance from '@api/instance'
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||||
import haptics from '@root/components/haptics'
|
import haptics from '@root/components/haptics'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import formatText from '@screens/Compose/formatText'
|
import formatText from '@screens/Compose/formatText'
|
||||||
import ComposeRoot from '@screens/Compose/Root'
|
import ComposeRoot from '@screens/Compose/Root'
|
||||||
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import {
|
||||||
|
QueryKeyTimeline,
|
||||||
|
useTimelineMutation
|
||||||
|
} from '@utils/queryHooks/timeline'
|
||||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||||
import {
|
import {
|
||||||
getInstanceAccount,
|
getInstanceAccount,
|
||||||
@ -33,10 +38,11 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import * as Sentry from 'sentry-expo'
|
import * as Sentry from 'sentry-expo'
|
||||||
import ComposeDraftsList from './Compose/DraftsList'
|
import ComposeDraftsList from './Compose/DraftsList'
|
||||||
import ComposeEditAttachment from './Compose/EditAttachment'
|
import ComposeEditAttachment from './Compose/EditAttachment'
|
||||||
|
import { uploadAttachment } from './Compose/Root/Footer/addAttachment'
|
||||||
import ComposeContext from './Compose/utils/createContext'
|
import ComposeContext from './Compose/utils/createContext'
|
||||||
import composeInitialState from './Compose/utils/initialState'
|
import composeInitialState from './Compose/utils/initialState'
|
||||||
import composeParseState from './Compose/utils/parseState'
|
import composeParseState from './Compose/utils/parseState'
|
||||||
@ -131,8 +137,38 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const uploadImage = async ({
|
||||||
|
type,
|
||||||
|
uri
|
||||||
|
}: {
|
||||||
|
type: 'image' | 'video'
|
||||||
|
uri: string
|
||||||
|
}) => {
|
||||||
|
await uploadAttachment({
|
||||||
|
composeDispatch,
|
||||||
|
imageInfo: { type, uri, width: 100, height: 100 }
|
||||||
|
})
|
||||||
|
}
|
||||||
switch (params?.type) {
|
switch (params?.type) {
|
||||||
|
case 'share':
|
||||||
|
if (params.text) {
|
||||||
|
formatText({
|
||||||
|
textInput: 'text',
|
||||||
|
composeDispatch,
|
||||||
|
content: params.text,
|
||||||
|
disableDebounce: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (params.images?.length) {
|
||||||
|
params.images.forEach(image => {
|
||||||
|
uploadImage({ type: 'image', uri: image.uri })
|
||||||
|
})
|
||||||
|
} else if (params.video) {
|
||||||
|
uploadImage({ type: 'video', uri: params.video.uri })
|
||||||
|
}
|
||||||
|
break
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
case 'deleteEdit':
|
||||||
if (params.incomingStatus.spoiler_text) {
|
if (params.incomingStatus.spoiler_text) {
|
||||||
formatText({
|
formatText({
|
||||||
textInput: 'spoiler',
|
textInput: 'spoiler',
|
||||||
@ -249,7 +285,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
),
|
),
|
||||||
[composeState]
|
[composeState]
|
||||||
)
|
)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const headerRightDisabled = useMemo(() => {
|
const headerRightDisabled = useMemo(() => {
|
||||||
if (totalTextCount > maxTootChars) {
|
if (totalTextCount > maxTootChars) {
|
||||||
return true
|
return true
|
||||||
@ -268,6 +304,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, [totalTextCount, composeState.attachments.uploads, composeState.text.raw])
|
}, [totalTextCount, composeState.attachments.uploads, composeState.text.raw])
|
||||||
|
const mutateTimeline = useTimelineMutation({ onMutate: true })
|
||||||
const headerRight = useCallback(
|
const headerRight = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<HeaderRight
|
<HeaderRight
|
||||||
@ -282,7 +319,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
composeDispatch({ type: 'posting', payload: true })
|
composeDispatch({ type: 'posting', payload: true })
|
||||||
|
|
||||||
composePost(params, composeState)
|
composePost(params, composeState)
|
||||||
.then(() => {
|
.then(res => {
|
||||||
haptics('Success')
|
haptics('Success')
|
||||||
if (
|
if (
|
||||||
Platform.OS === 'ios' &&
|
Platform.OS === 'ios' &&
|
||||||
@ -300,6 +337,14 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
|
|
||||||
switch (params?.type) {
|
switch (params?.type) {
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
mutateTimeline.mutate({
|
||||||
|
type: 'editItem',
|
||||||
|
queryKey: params.queryKey,
|
||||||
|
rootQueryKey: params.rootQueryKey,
|
||||||
|
status: res.body
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'deleteEdit':
|
||||||
case 'reply':
|
case 'reply':
|
||||||
if (params?.queryKey && params.queryKey[1].page === 'Toot') {
|
if (params?.queryKey && params.queryKey[1].page === 'Toot') {
|
||||||
queryClient.invalidateQueries(params.queryKey)
|
queryClient.invalidateQueries(params.queryKey)
|
||||||
|
@ -3,6 +3,7 @@ import Icon from '@components/Icon'
|
|||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import {
|
import {
|
||||||
getInstanceDrafts,
|
getInstanceDrafts,
|
||||||
removeInstanceDraft
|
removeInstanceDraft
|
||||||
@ -23,7 +24,7 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { PanGestureHandler } from 'react-native-gesture-handler'
|
import { PanGestureHandler } from 'react-native-gesture-handler'
|
||||||
import { SwipeListView } from 'react-native-swipe-list-view'
|
import { SwipeListView } from 'react-native-swipe-list-view'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import formatText from '../formatText'
|
import formatText from '../formatText'
|
||||||
import ComposeContext from '../utils/createContext'
|
import ComposeContext from '../utils/createContext'
|
||||||
import { ComposeStateDraft, ExtendedAttachment } from '../utils/types'
|
import { ComposeStateDraft, ExtendedAttachment } from '../utils/types'
|
||||||
@ -36,7 +37,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
const { composeDispatch } = useContext(ComposeContext)
|
const { composeDispatch } = useContext(ComposeContext)
|
||||||
const { t } = useTranslation('screenCompose')
|
const { t } = useTranslation('screenCompose')
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const instanceDrafts = useSelector(getInstanceDrafts)?.filter(
|
const instanceDrafts = useSelector(getInstanceDrafts)?.filter(
|
||||||
draft => draft.timestamp !== timestamp
|
draft => draft.timestamp !== timestamp
|
||||||
|
@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, StyleSheet, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import ComposeContext from '../utils/createContext'
|
import ComposeContext from '../utils/createContext'
|
||||||
import addAttachment from './Footer/addAttachment'
|
import chooseAndUploadAttachment from './Footer/addAttachment'
|
||||||
|
|
||||||
const ComposeActions: React.FC = () => {
|
const ComposeActions: React.FC = () => {
|
||||||
const { showActionSheetWithOptions } = useActionSheet()
|
const { showActionSheetWithOptions } = useActionSheet()
|
||||||
@ -41,7 +41,7 @@ const ComposeActions: React.FC = () => {
|
|||||||
analytics('compose_actions_attachment_press', {
|
analytics('compose_actions_attachment_press', {
|
||||||
count: composeState.attachments.uploads.length
|
count: composeState.attachments.uploads.length
|
||||||
})
|
})
|
||||||
return await addAttachment({
|
return await chooseAndUploadAttachment({
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
showActionSheetWithOptions
|
showActionSheetWithOptions
|
||||||
})
|
})
|
||||||
|
@ -27,7 +27,7 @@ import {
|
|||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
import ComposeContext from '../../utils/createContext'
|
import ComposeContext from '../../utils/createContext'
|
||||||
import { ExtendedAttachment } from '../../utils/types'
|
import { ExtendedAttachment } from '../../utils/types'
|
||||||
import addAttachment from './addAttachment'
|
import chooseAndUploadAttachment from './addAttachment'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
accessibleRefAttachments: RefObject<View>
|
accessibleRefAttachments: RefObject<View>
|
||||||
@ -218,7 +218,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
|
|||||||
]}
|
]}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
analytics('compose_attachment_add_container_press')
|
analytics('compose_attachment_add_container_press')
|
||||||
await addAttachment({ composeDispatch, showActionSheetWithOptions })
|
await chooseAndUploadAttachment({ composeDispatch, showActionSheetWithOptions })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@ -229,7 +229,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
|
|||||||
overlay
|
overlay
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
analytics('compose_attachment_add_button_press')
|
analytics('compose_attachment_add_button_press')
|
||||||
await addAttachment({ composeDispatch, showActionSheetWithOptions })
|
await chooseAndUploadAttachment({ composeDispatch, showActionSheetWithOptions })
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
@ -15,7 +16,6 @@ import {
|
|||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import validUrl from 'valid-url'
|
import validUrl from 'valid-url'
|
||||||
import updateText from '../../updateText'
|
import updateText from '../../updateText'
|
||||||
import ComposeContext from '../../utils/createContext'
|
import ComposeContext from '../../utils/createContext'
|
||||||
@ -29,7 +29,7 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
|||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
|
const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
|
||||||
|
@ -121,7 +121,7 @@ const ComposePoll: React.FC = () => {
|
|||||||
round
|
round
|
||||||
disabled={!(total > 2)}
|
disabled={!(total > 2)}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.controlCount}>
|
<Text style={[styles.controlCount, { color: colors.secondary }]}>
|
||||||
{total} / {MAX_OPTIONS}
|
{total} / {MAX_OPTIONS}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
@ -216,7 +216,7 @@ const ComposePoll: React.FC = () => {
|
|||||||
userInterfaceStyle: mode
|
userInterfaceStyle: mode
|
||||||
},
|
},
|
||||||
index => {
|
index => {
|
||||||
if (index && index < expirations.length) {
|
if (index !== undefined && index < expirations.length) {
|
||||||
analytics('compose_poll_expiration_press', {
|
analytics('compose_poll_expiration_press', {
|
||||||
current: expire,
|
current: expire,
|
||||||
new: expirations[index]
|
new: expirations[index]
|
||||||
|
@ -17,114 +17,127 @@ export interface Props {
|
|||||||
) => void
|
) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const addAttachment = async ({
|
export const uploadAttachment = async ({
|
||||||
|
composeDispatch,
|
||||||
|
imageInfo
|
||||||
|
}: {
|
||||||
|
composeDispatch: Dispatch<ComposeAction>
|
||||||
|
imageInfo: Pick<ImageInfo, 'type' | 'uri' | 'width' | 'height'>
|
||||||
|
}) => {
|
||||||
|
const hash = await Crypto.digestStringAsync(
|
||||||
|
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||||
|
imageInfo.uri + Math.random()
|
||||||
|
)
|
||||||
|
|
||||||
|
let attachmentType: string
|
||||||
|
|
||||||
|
switch (imageInfo.type) {
|
||||||
|
case 'image':
|
||||||
|
console.log('uri', imageInfo.uri)
|
||||||
|
attachmentType = `image/${imageInfo.uri.split('.')[1]}`
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/start',
|
||||||
|
payload: {
|
||||||
|
local: { ...imageInfo, local_thumbnail: imageInfo.uri, hash },
|
||||||
|
uploading: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'video':
|
||||||
|
attachmentType = `video/${imageInfo.uri.split('.')[1]}`
|
||||||
|
VideoThumbnails.getThumbnailAsync(imageInfo.uri)
|
||||||
|
.then(({ uri, width, height }) =>
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/start',
|
||||||
|
payload: {
|
||||||
|
local: {
|
||||||
|
...imageInfo,
|
||||||
|
local_thumbnail: uri,
|
||||||
|
hash,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
},
|
||||||
|
uploading: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(() =>
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/start',
|
||||||
|
payload: {
|
||||||
|
local: { ...imageInfo, hash },
|
||||||
|
uploading: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
attachmentType = 'unknown'
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/start',
|
||||||
|
payload: {
|
||||||
|
local: { ...imageInfo, hash },
|
||||||
|
uploading: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadFailed = () => {
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/fail',
|
||||||
|
payload: hash
|
||||||
|
})
|
||||||
|
Alert.alert(
|
||||||
|
i18next.t(
|
||||||
|
'screenCompose:content.root.actions.attachment.failed.alert.title'
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: i18next.t(
|
||||||
|
'screenCompose:content.root.actions.attachment.failed.alert.button'
|
||||||
|
),
|
||||||
|
onPress: () => {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', {
|
||||||
|
// @ts-ignore
|
||||||
|
uri: imageInfo.uri,
|
||||||
|
name: attachmentType,
|
||||||
|
type: attachmentType
|
||||||
|
})
|
||||||
|
|
||||||
|
return apiInstance<Mastodon.Attachment>({
|
||||||
|
method: 'post',
|
||||||
|
url: 'media',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.body.id) {
|
||||||
|
composeDispatch({
|
||||||
|
type: 'attachment/upload/end',
|
||||||
|
payload: { remote: res.body, local: imageInfo }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uploadFailed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
uploadFailed()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseAndUploadAttachment = async ({
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
showActionSheetWithOptions
|
showActionSheetWithOptions
|
||||||
}: Props): Promise<any> => {
|
}: Props): Promise<any> => {
|
||||||
const uploader = async (imageInfo: ImageInfo) => {
|
|
||||||
const hash = await Crypto.digestStringAsync(
|
|
||||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
|
||||||
imageInfo.uri + Math.random()
|
|
||||||
)
|
|
||||||
|
|
||||||
let attachmentType: string
|
|
||||||
|
|
||||||
switch (imageInfo.type) {
|
|
||||||
case 'image':
|
|
||||||
attachmentType = `image/${imageInfo.uri.split('.')[1]}`
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/start',
|
|
||||||
payload: {
|
|
||||||
local: { ...imageInfo, local_thumbnail: imageInfo.uri, hash },
|
|
||||||
uploading: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'video':
|
|
||||||
attachmentType = `video/${imageInfo.uri.split('.')[1]}`
|
|
||||||
VideoThumbnails.getThumbnailAsync(imageInfo.uri)
|
|
||||||
.then(({ uri }) =>
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/start',
|
|
||||||
payload: {
|
|
||||||
local: { ...imageInfo, local_thumbnail: uri, hash },
|
|
||||||
uploading: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.catch(() =>
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/start',
|
|
||||||
payload: {
|
|
||||||
local: { ...imageInfo, hash },
|
|
||||||
uploading: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
attachmentType = 'unknown'
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/start',
|
|
||||||
payload: {
|
|
||||||
local: { ...imageInfo, hash },
|
|
||||||
uploading: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadFailed = () => {
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/fail',
|
|
||||||
payload: hash
|
|
||||||
})
|
|
||||||
Alert.alert(
|
|
||||||
i18next.t(
|
|
||||||
'screenCompose:content.root.actions.attachment.failed.alert.title'
|
|
||||||
),
|
|
||||||
undefined,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: i18next.t(
|
|
||||||
'screenCompose:content.root.actions.attachment.failed.alert.button'
|
|
||||||
),
|
|
||||||
onPress: () => {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', {
|
|
||||||
// @ts-ignore
|
|
||||||
uri: imageInfo.uri,
|
|
||||||
name: attachmentType,
|
|
||||||
type: attachmentType
|
|
||||||
})
|
|
||||||
|
|
||||||
return apiInstance<Mastodon.Attachment>({
|
|
||||||
method: 'post',
|
|
||||||
url: 'media',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (res.body.id) {
|
|
||||||
composeDispatch({
|
|
||||||
type: 'attachment/upload/end',
|
|
||||||
payload: { remote: res.body, local: imageInfo }
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
uploadFailed()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
uploadFailed()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await mediaSelector({ showActionSheetWithOptions })
|
const result = await mediaSelector({ showActionSheetWithOptions })
|
||||||
await uploader(result)
|
await uploadAttachment({ composeDispatch, imageInfo: result })
|
||||||
}
|
}
|
||||||
|
|
||||||
export default addAttachment
|
export default chooseAndUploadAttachment
|
||||||
|
@ -38,7 +38,10 @@ const composeParseState = (
|
|||||||
params: NonNullable<RootStackParamList['Screen-Compose']>
|
params: NonNullable<RootStackParamList['Screen-Compose']>
|
||||||
): ComposeState => {
|
): ComposeState => {
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
|
case 'share':
|
||||||
|
return { ...composeInitialState, dirty: true, timestamp: Date.now() }
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
case 'deleteEdit':
|
||||||
return {
|
return {
|
||||||
...composeInitialState,
|
...composeInitialState,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
@ -69,7 +72,8 @@ const composeParseState = (
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
...assignVisibility(params.incomingStatus.visibility),
|
...assignVisibility(params.incomingStatus.visibility),
|
||||||
...(params.replyToStatus && { replyToStatus: params.replyToStatus })
|
...(params.replyToStatus && { replyToStatus: params.replyToStatus }),
|
||||||
|
visibilityLock: params.type === 'edit'
|
||||||
}
|
}
|
||||||
case 'reply':
|
case 'reply':
|
||||||
const actualStatus = params.incomingStatus.reblog || params.incomingStatus
|
const actualStatus = params.incomingStatus.reblog || params.incomingStatus
|
||||||
|
@ -51,8 +51,11 @@ const composePost = async (
|
|||||||
formData.append('visibility', composeState.visibility)
|
formData.append('visibility', composeState.visibility)
|
||||||
|
|
||||||
return apiInstance<Mastodon.Status>({
|
return apiInstance<Mastodon.Status>({
|
||||||
method: 'post',
|
method: params?.type === 'edit' ? 'put' : 'post',
|
||||||
url: 'statuses',
|
url:
|
||||||
|
params?.type === 'edit'
|
||||||
|
? `statuses/${params.incomingStatus.id}`
|
||||||
|
: 'statuses',
|
||||||
headers: {
|
headers: {
|
||||||
'Idempotency-Key': await Crypto.digestStringAsync(
|
'Idempotency-Key': await Crypto.digestStringAsync(
|
||||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||||
@ -67,7 +70,9 @@ const composePost = async (
|
|||||||
composeState.attachments.sensitive +
|
composeState.attachments.sensitive +
|
||||||
composeState.attachments.uploads.map(upload => upload.remote?.id) +
|
composeState.attachments.uploads.map(upload => upload.remote?.id) +
|
||||||
composeState.visibility +
|
composeState.visibility +
|
||||||
(params?.type === 'edit' ? Math.random() : '')
|
(params?.type === 'edit' || params?.type === 'deleteEdit'
|
||||||
|
? Math.random()
|
||||||
|
: '')
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
body: formData
|
body: formData
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
createBottomTabNavigator
|
createBottomTabNavigator
|
||||||
} from '@react-navigation/bottom-tabs'
|
} from '@react-navigation/bottom-tabs'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import {
|
import {
|
||||||
RootStackScreenProps,
|
RootStackScreenProps,
|
||||||
ScreenTabsStackParamList
|
ScreenTabsStackParamList
|
||||||
@ -21,7 +22,7 @@ import {
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useEffect, useMemo } from 'react'
|
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TabLocal from './Tabs/Local'
|
import TabLocal from './Tabs/Local'
|
||||||
import TabMe from './Tabs/Me'
|
import TabMe from './Tabs/Me'
|
||||||
import TabNotifications from './Tabs/Notifications'
|
import TabNotifications from './Tabs/Notifications'
|
||||||
@ -118,7 +119,7 @@ const ScreenTabs = React.memo(
|
|||||||
const previousTab = useSelector(getPreviousTab, () => true)
|
const previousTab = useSelector(getPreviousTab, () => true)
|
||||||
|
|
||||||
const versionUpdate = useSelector(getVersionUpdate)
|
const versionUpdate = useSelector(getVersionUpdate)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(retriveVersionLatest())
|
dispatch(retriveVersionLatest())
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { TabMeProfileStackScreenProps } from '@utils/navigation/navigators'
|
import { TabMeProfileStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile'
|
import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile'
|
||||||
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||||
@ -9,7 +10,6 @@ import React, { RefObject, useCallback } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import FlashMessage from 'react-native-flash-message'
|
import FlashMessage from 'react-native-flash-message'
|
||||||
import { ScrollView } from 'react-native-gesture-handler'
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import ProfileAvatarHeader from './Root/AvatarHeader'
|
import ProfileAvatarHeader from './Root/AvatarHeader'
|
||||||
|
|
||||||
const TabMeProfileRoot: React.FC<
|
const TabMeProfileRoot: React.FC<
|
||||||
@ -24,7 +24,7 @@ const TabMeProfileRoot: React.FC<
|
|||||||
|
|
||||||
const { data, isLoading } = useProfileQuery({})
|
const { data, isLoading } = useProfileQuery({})
|
||||||
const { mutateAsync } = useProfileMutation()
|
const { mutateAsync } = useProfileMutation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const onPressVisibility = useCallback(() => {
|
const onPressVisibility = useCallback(() => {
|
||||||
showActionSheetWithOptions(
|
showActionSheetWithOptions(
|
||||||
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
|||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
import { updateInstancePush } from '@utils/slices/instances/updatePush'
|
import { updateInstancePush } from '@utils/slices/instances/updatePush'
|
||||||
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
|
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
|
||||||
@ -20,7 +21,7 @@ import * as WebBrowser from 'expo-web-browser'
|
|||||||
import React, { useState, useEffect, useMemo } from 'react'
|
import React, { useState, useEffect, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AppState, Linking, ScrollView, Text, View } from 'react-native'
|
import { AppState, Linking, ScrollView, Text, View } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const TabMePush: React.FC = () => {
|
const TabMePush: React.FC = () => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
@ -31,7 +32,7 @@ const TabMePush: React.FC = () => {
|
|||||||
)
|
)
|
||||||
const instanceUri = useSelector(getInstanceUri)
|
const instanceUri = useSelector(getInstanceUri)
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const instancePush = useSelector(getInstancePush)
|
const instancePush = useSelector(getInstancePush)
|
||||||
|
|
||||||
const [pushAvailable, setPushAvailable] = useState<boolean | undefined>(
|
const [pushAvailable, setPushAvailable] = useState<boolean | undefined>(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
||||||
import { useListsQuery } from '@utils/queryHooks/lists'
|
import { useListsQuery } from '@utils/queryHooks/lists'
|
||||||
import {
|
import {
|
||||||
@ -9,13 +10,13 @@ import {
|
|||||||
import { getInstancePush } from '@utils/slices/instancesSlice'
|
import { getInstancePush } from '@utils/slices/instancesSlice'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const Collections: React.FC = () => {
|
const Collections: React.FC = () => {
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const navigation = useNavigation<any>()
|
const navigation = useNavigation<any>()
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const mePage = useSelector(getInstanceMePage)
|
const mePage = useSelector(getInstanceMePage)
|
||||||
|
|
||||||
const listsQuery = useListsQuery({
|
const listsQuery = useListsQuery({
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import haptics from '@root/components/haptics'
|
import haptics from '@root/components/haptics'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import removeInstance from '@utils/slices/instances/remove'
|
import removeInstance from '@utils/slices/instances/remove'
|
||||||
import { getInstance } from '@utils/slices/instancesSlice'
|
import { getInstance } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
@ -7,11 +8,11 @@ import React from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const Logout: React.FC = () => {
|
const Logout: React.FC = () => {
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const instance = useSelector(getInstance)
|
const instance = useSelector(getInstance)
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
|
import { getInstanceVersion } from '@utils/slices/instancesSlice'
|
||||||
import {
|
import {
|
||||||
changeAnalytics,
|
changeAnalytics,
|
||||||
getSettingsAnalytics
|
getSettingsAnalytics
|
||||||
@ -8,15 +10,16 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||||||
import Constants from 'expo-constants'
|
import Constants from 'expo-constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
import { Text } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const SettingsAnalytics: React.FC = () => {
|
const SettingsAnalytics: React.FC = () => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
const settingsAnalytics = useSelector(getSettingsAnalytics)
|
const settingsAnalytics = useSelector(getSettingsAnalytics)
|
||||||
|
const instanceVersion = useSelector(getInstanceVersion, () => true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<MenuContainer>
|
||||||
@ -28,19 +31,27 @@ const SettingsAnalytics: React.FC = () => {
|
|||||||
dispatch(changeAnalytics(!settingsAnalytics))
|
dispatch(changeAnalytics(!settingsAnalytics))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.version, { color: colors.secondary }]}>
|
<Text
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
...StyleConstants.FontStyle.S,
|
||||||
|
marginTop: StyleConstants.Spacing.S,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('me.settings.version', { version: Constants.manifest?.version })}
|
{t('me.settings.version', { version: Constants.manifest?.version })}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
...StyleConstants.FontStyle.S,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('me.settings.instanceVersion', { version: instanceVersion })}
|
||||||
|
</Text>
|
||||||
</MenuContainer>
|
</MenuContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
version: {
|
|
||||||
textAlign: 'center',
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
marginTop: StyleConstants.Spacing.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default SettingsAnalytics
|
export default SettingsAnalytics
|
||||||
|
@ -4,6 +4,7 @@ import { MenuContainer, MenuRow } from '@components/Menu'
|
|||||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { LOCALES } from '@root/i18n/locales'
|
import { LOCALES } from '@root/i18n/locales'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import androidDefaults from '@utils/slices/instances/push/androidDefaults'
|
import androidDefaults from '@utils/slices/instances/push/androidDefaults'
|
||||||
import { getInstances } from '@utils/slices/instancesSlice'
|
import { getInstances } from '@utils/slices/instancesSlice'
|
||||||
import {
|
import {
|
||||||
@ -21,12 +22,12 @@ import * as Notifications from 'expo-notifications'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { mapFontsizeToName } from '../SettingsFontsize'
|
import { mapFontsizeToName } from '../SettingsFontsize'
|
||||||
|
|
||||||
const SettingsApp: React.FC = () => {
|
const SettingsApp: React.FC = () => {
|
||||||
const navigation = useNavigation<any>()
|
const navigation = useNavigation<any>()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { showActionSheetWithOptions } = useActionSheet()
|
const { showActionSheetWithOptions } = useActionSheet()
|
||||||
const { mode } = useTheme()
|
const { mode } = useTheme()
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t, i18n } = useTranslation('screenTabs')
|
||||||
|
@ -2,6 +2,7 @@ import Button from '@components/Button'
|
|||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
|
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import {
|
import {
|
||||||
changeFontsize,
|
changeFontsize,
|
||||||
@ -15,7 +16,7 @@ import React, { useMemo } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
import { ScrollView } from 'react-native-gesture-handler'
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export const mapFontsizeToName = (size: SettingsState['fontsize']) => {
|
export const mapFontsizeToName = (size: SettingsState['fontsize']) => {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
@ -38,7 +39,7 @@ const TabMeSettingsFontsize: React.FC<
|
|||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const initialSize = useSelector(getSettingsFontsize)
|
const initialSize = useSelector(getSettingsFontsize)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
id: 'demo',
|
id: 'demo',
|
||||||
|
@ -9,13 +9,11 @@ import {
|
|||||||
TabPublicStackParamList
|
TabPublicStackParamList
|
||||||
} from '@utils/navigation/navigators'
|
} from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceTimelinesLookback } from '@utils/slices/instancesSlice'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dimensions, StyleSheet } from 'react-native'
|
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||||
import { TabView } from 'react-native-tab-view'
|
import { TabView } from 'react-native-tab-view'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import TabSharedRoot from './Shared/Root'
|
import TabSharedRoot from './Shared/Root'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabPublicStackParamList>()
|
const Stack = createNativeStackNavigator<TabPublicStackParamList>()
|
||||||
@ -72,10 +70,6 @@ const TabPublic = React.memo(
|
|||||||
|
|
||||||
const routes = pages.map(p => ({ key: p.key }))
|
const routes = pages.map(p => ({ key: p.key }))
|
||||||
|
|
||||||
const timelinesLookback = useSelector(
|
|
||||||
getInstanceTimelinesLookback,
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
const renderScene = useCallback(
|
const renderScene = useCallback(
|
||||||
({
|
({
|
||||||
route: { key: page }
|
route: { key: page }
|
||||||
@ -101,7 +95,6 @@ const TabPublic = React.memo(
|
|||||||
)
|
)
|
||||||
const children = useCallback(
|
const children = useCallback(
|
||||||
() => (
|
() => (
|
||||||
// @ts-ignore
|
|
||||||
<TabView
|
<TabView
|
||||||
lazy
|
lazy
|
||||||
swipeEnabled
|
swipeEnabled
|
||||||
|
105
src/screens/Tabs/Shared/History.tsx
Normal file
105
src/screens/Tabs/Shared/History.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import Icon from '@components/Icon'
|
||||||
|
import { ParseEmojis } from '@components/Parse'
|
||||||
|
import ComponentSeparator from '@components/Separator'
|
||||||
|
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||||
|
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||||
|
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
||||||
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
|
import { useStatusHistory } from '@utils/queryHooks/statusesHistory'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React from 'react'
|
||||||
|
import { Text, View } from 'react-native'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
|
|
||||||
|
const ContentView = ({
|
||||||
|
history,
|
||||||
|
first,
|
||||||
|
last
|
||||||
|
}: {
|
||||||
|
history: Mastodon.StatusHistory
|
||||||
|
first: boolean
|
||||||
|
last: boolean
|
||||||
|
}) => {
|
||||||
|
const { colors } = useTheme()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
paddingTop: first ? 0 : undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeaderSharedCreated created_at={history.created_at} />
|
||||||
|
{typeof history.content === 'string' && history.content.length > 0 ? (
|
||||||
|
<TimelineContent status={history} />
|
||||||
|
) : null}
|
||||||
|
{history.poll
|
||||||
|
? history.poll.options.map((option, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
|
||||||
|
>
|
||||||
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
|
<Icon
|
||||||
|
style={{
|
||||||
|
paddingTop:
|
||||||
|
StyleConstants.Font.LineHeight.M -
|
||||||
|
StyleConstants.Font.Size.M,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
name='Circle'
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
color={colors.disabled}
|
||||||
|
/>
|
||||||
|
<Text style={{ flex: 1 }}>
|
||||||
|
<ParseEmojis
|
||||||
|
content={option.title}
|
||||||
|
emojis={history.poll?.emojis}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
{Array.isArray(history.media_attachments) &&
|
||||||
|
history.media_attachments.length ? (
|
||||||
|
<TimelineAttachment status={history} />
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
{!last ? <ComponentSeparator extraMarginLeft={0} /> : null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const TabSharedHistory: React.FC<
|
||||||
|
TabSharedStackScreenProps<'Tab-Shared-History'>
|
||||||
|
> = ({
|
||||||
|
route: {
|
||||||
|
params: { id }
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
const { data } = useStatusHistory({ id })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
{data && data.length > 0
|
||||||
|
? data
|
||||||
|
.slice(0)
|
||||||
|
.reverse()
|
||||||
|
.map((d, i) =>
|
||||||
|
i !== 0 ? (
|
||||||
|
<ContentView
|
||||||
|
key={i}
|
||||||
|
history={d}
|
||||||
|
first={i === 1}
|
||||||
|
last={i === data.length - 1}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabSharedHistory
|
@ -4,6 +4,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|||||||
import TabSharedAccount from '@screens/Tabs/Shared/Account'
|
import TabSharedAccount from '@screens/Tabs/Shared/Account'
|
||||||
import TabSharedAttachments from '@screens/Tabs/Shared/Attachments'
|
import TabSharedAttachments from '@screens/Tabs/Shared/Attachments'
|
||||||
import TabSharedHashtag from '@screens/Tabs/Shared/Hashtag'
|
import TabSharedHashtag from '@screens/Tabs/Shared/Hashtag'
|
||||||
|
import TabSharedHistory from '@screens/Tabs/Shared/History'
|
||||||
import TabSharedSearch from '@screens/Tabs/Shared/Search'
|
import TabSharedSearch from '@screens/Tabs/Shared/Search'
|
||||||
import TabSharedToot from '@screens/Tabs/Shared/Toot'
|
import TabSharedToot from '@screens/Tabs/Shared/Toot'
|
||||||
import TabSharedUsers from '@screens/Tabs/Shared/Users'
|
import TabSharedUsers from '@screens/Tabs/Shared/Users'
|
||||||
@ -95,6 +96,13 @@ const TabSharedRoot = ({
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Stack.Screen
|
||||||
|
key='Tab-Shared-History'
|
||||||
|
name='Tab-Shared-History'
|
||||||
|
component={TabSharedHistory}
|
||||||
|
options={{ title: t('screenTabs:shared.history.name') }}
|
||||||
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
key='Tab-Shared-Search'
|
key='Tab-Shared-Search'
|
||||||
name='Tab-Shared-Search'
|
name='Tab-Shared-Search'
|
||||||
|
@ -8,6 +8,7 @@ import contextsSlice, { ContextsState } from '@utils/slices/contextsSlice'
|
|||||||
import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice'
|
import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice'
|
||||||
import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice'
|
import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice'
|
||||||
import versionSlice from '@utils/slices/versionSlice'
|
import versionSlice from '@utils/slices/versionSlice'
|
||||||
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||||
import { createMigrate, persistReducer, persistStore } from 'redux-persist'
|
import { createMigrate, persistReducer, persistStore } from 'redux-persist'
|
||||||
|
|
||||||
const secureStorage = createSecureStore()
|
const secureStorage = createSecureStore()
|
||||||
@ -27,7 +28,7 @@ const instancesPersistConfig = {
|
|||||||
key: 'instances',
|
key: 'instances',
|
||||||
prefix,
|
prefix,
|
||||||
storage: secureStorage,
|
storage: secureStorage,
|
||||||
version: 8,
|
version: 9,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
migrate: createMigrate(instancesMigration)
|
migrate: createMigrate(instancesMigration)
|
||||||
}
|
}
|
||||||
@ -69,3 +70,7 @@ let persistor = persistStore(store)
|
|||||||
|
|
||||||
export { store, persistor }
|
export { store, persistor }
|
||||||
export type RootState = ReturnType<typeof store.getState>
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
|
|
||||||
|
type AppDispatch = typeof store.dispatch
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
|
@ -4,6 +4,7 @@ import { InstanceV5 } from './v5'
|
|||||||
import { InstanceV6 } from './v6'
|
import { InstanceV6 } from './v6'
|
||||||
import { InstanceV7 } from './v7'
|
import { InstanceV7 } from './v7'
|
||||||
import { InstanceV8 } from './v8'
|
import { InstanceV8 } from './v8'
|
||||||
|
import { InstanceV9 } from './v9'
|
||||||
|
|
||||||
const instancesMigration = {
|
const instancesMigration = {
|
||||||
4: (state: InstanceV3): InstanceV4 => {
|
4: (state: InstanceV3): InstanceV4 => {
|
||||||
@ -87,6 +88,17 @@ const instancesMigration = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
9: (state: InstanceV8): { instances: InstanceV9[] } => {
|
||||||
|
return {
|
||||||
|
// @ts-ignore
|
||||||
|
instances: state.instances.map(instance => {
|
||||||
|
return {
|
||||||
|
...instance,
|
||||||
|
version: '0'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
79
src/utils/migrations/instances/v9.ts
Normal file
79
src/utils/migrations/instances/v9.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
|
||||||
|
export type InstanceV9 = {
|
||||||
|
active: boolean
|
||||||
|
appData: {
|
||||||
|
clientId: string
|
||||||
|
clientSecret: string
|
||||||
|
}
|
||||||
|
url: string
|
||||||
|
token: string
|
||||||
|
uri: Mastodon.Instance['uri']
|
||||||
|
urls: Mastodon.Instance['urls']
|
||||||
|
account: {
|
||||||
|
id: Mastodon.Account['id']
|
||||||
|
acct: Mastodon.Account['acct']
|
||||||
|
avatarStatic: Mastodon.Account['avatar_static']
|
||||||
|
preferences: Mastodon.Preferences
|
||||||
|
}
|
||||||
|
version: string
|
||||||
|
configuration?: Mastodon.Instance['configuration']
|
||||||
|
filters: Mastodon.Filter[]
|
||||||
|
notifications_filter: {
|
||||||
|
follow: boolean
|
||||||
|
favourite: boolean
|
||||||
|
reblog: boolean
|
||||||
|
mention: boolean
|
||||||
|
poll: boolean
|
||||||
|
follow_request: boolean
|
||||||
|
}
|
||||||
|
push: {
|
||||||
|
global: { loading: boolean; value: boolean }
|
||||||
|
decode: { loading: boolean; value: boolean }
|
||||||
|
alerts: {
|
||||||
|
follow: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['follow']
|
||||||
|
}
|
||||||
|
favourite: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['favourite']
|
||||||
|
}
|
||||||
|
reblog: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['reblog']
|
||||||
|
}
|
||||||
|
mention: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['mention']
|
||||||
|
}
|
||||||
|
poll: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['poll']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys: {
|
||||||
|
auth?: string
|
||||||
|
public?: string // legacy
|
||||||
|
private?: string // legacy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timelinesLookback?: {
|
||||||
|
[key: string]: {
|
||||||
|
queryKey: QueryKeyTimeline
|
||||||
|
ids: Mastodon.Status['id'][]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mePage: {
|
||||||
|
lists: { shown: boolean }
|
||||||
|
announcements: { shown: boolean; unread: number }
|
||||||
|
}
|
||||||
|
drafts: ComposeStateDraft[]
|
||||||
|
frequentEmojis: {
|
||||||
|
emoji: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>
|
||||||
|
score: number
|
||||||
|
count: number
|
||||||
|
lastUsed: number
|
||||||
|
}[]
|
||||||
|
}
|
@ -26,36 +26,31 @@ export type RootStackParamList = {
|
|||||||
type: 'edit'
|
type: 'edit'
|
||||||
incomingStatus: Mastodon.Status
|
incomingStatus: Mastodon.Status
|
||||||
replyToStatus?: Mastodon.Status
|
replyToStatus?: Mastodon.Status
|
||||||
queryKey?: [
|
queryKey?: QueryKeyTimeline
|
||||||
'Timeline',
|
rootQueryKey?: QueryKeyTimeline
|
||||||
{
|
}
|
||||||
page: App.Pages
|
| {
|
||||||
hashtag?: Mastodon.Tag['name']
|
type: 'deleteEdit'
|
||||||
list?: Mastodon.List['id']
|
incomingStatus: Mastodon.Status
|
||||||
toot?: Mastodon.Status['id']
|
replyToStatus?: Mastodon.Status
|
||||||
account?: Mastodon.Account['id']
|
queryKey?: QueryKeyTimeline
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'reply'
|
type: 'reply'
|
||||||
incomingStatus: Mastodon.Status
|
incomingStatus: Mastodon.Status
|
||||||
accts: Mastodon.Account['acct'][]
|
accts: Mastodon.Account['acct'][]
|
||||||
queryKey?: [
|
queryKey?: QueryKeyTimeline
|
||||||
'Timeline',
|
|
||||||
{
|
|
||||||
page: App.Pages
|
|
||||||
hashtag?: Mastodon.Tag['name']
|
|
||||||
list?: Mastodon.List['id']
|
|
||||||
toot?: Mastodon.Status['id']
|
|
||||||
account?: Mastodon.Account['id']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'conversation'
|
type: 'conversation'
|
||||||
accts: Mastodon.Account['acct'][]
|
accts: Mastodon.Account['acct'][]
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'share'
|
||||||
|
text?: string
|
||||||
|
images?: { type: string; uri: string }[]
|
||||||
|
video?: { type: string; uri: string }
|
||||||
|
}
|
||||||
| undefined
|
| undefined
|
||||||
'Screen-ImagesViewer': {
|
'Screen-ImagesViewer': {
|
||||||
imageUrls: {
|
imageUrls: {
|
||||||
@ -70,9 +65,8 @@ export type RootStackParamList = {
|
|||||||
id: Mastodon.Attachment['id']
|
id: Mastodon.Attachment['id']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type RootStackScreenProps<
|
export type RootStackScreenProps<T extends keyof RootStackParamList> =
|
||||||
T extends keyof RootStackParamList
|
NativeStackScreenProps<RootStackParamList, T>
|
||||||
> = NativeStackScreenProps<RootStackParamList, T>
|
|
||||||
|
|
||||||
export type ScreenComposeStackParamList = {
|
export type ScreenComposeStackParamList = {
|
||||||
'Screen-Compose-Root': undefined
|
'Screen-Compose-Root': undefined
|
||||||
@ -90,9 +84,8 @@ export type ScreenTabsStackParamList = {
|
|||||||
'Tab-Notifications': NavigatorScreenParams<TabNotificationsStackParamList>
|
'Tab-Notifications': NavigatorScreenParams<TabNotificationsStackParamList>
|
||||||
'Tab-Me': NavigatorScreenParams<TabMeStackParamList>
|
'Tab-Me': NavigatorScreenParams<TabMeStackParamList>
|
||||||
}
|
}
|
||||||
export type ScreenTabsScreenProps<
|
export type ScreenTabsScreenProps<T extends keyof ScreenTabsStackParamList> =
|
||||||
T extends keyof ScreenTabsStackParamList
|
BottomTabScreenProps<ScreenTabsStackParamList, T>
|
||||||
> = BottomTabScreenProps<ScreenTabsStackParamList, T>
|
|
||||||
|
|
||||||
export type TabSharedStackParamList = {
|
export type TabSharedStackParamList = {
|
||||||
'Tab-Shared-Account': {
|
'Tab-Shared-Account': {
|
||||||
@ -102,6 +95,9 @@ export type TabSharedStackParamList = {
|
|||||||
'Tab-Shared-Hashtag': {
|
'Tab-Shared-Hashtag': {
|
||||||
hashtag: Mastodon.Tag['name']
|
hashtag: Mastodon.Tag['name']
|
||||||
}
|
}
|
||||||
|
'Tab-Shared-History': {
|
||||||
|
id: Mastodon.Status['id']
|
||||||
|
}
|
||||||
'Tab-Shared-Search': { text: string | undefined }
|
'Tab-Shared-Search': { text: string | undefined }
|
||||||
'Tab-Shared-Toot': {
|
'Tab-Shared-Toot': {
|
||||||
toot: Mastodon.Status
|
toot: Mastodon.Status
|
||||||
@ -121,9 +117,8 @@ export type TabSharedStackParamList = {
|
|||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type TabSharedStackScreenProps<
|
export type TabSharedStackScreenProps<T extends keyof TabSharedStackParamList> =
|
||||||
T extends keyof TabSharedStackParamList
|
NativeStackScreenProps<TabSharedStackParamList, T>
|
||||||
> = NativeStackScreenProps<TabSharedStackParamList, T>
|
|
||||||
|
|
||||||
export type TabLocalStackParamList = {
|
export type TabLocalStackParamList = {
|
||||||
'Tab-Local-Root': undefined
|
'Tab-Local-Root': undefined
|
||||||
@ -153,9 +148,8 @@ export type TabMeStackParamList = {
|
|||||||
'Tab-Me-Settings-Fontsize': undefined
|
'Tab-Me-Settings-Fontsize': undefined
|
||||||
'Tab-Me-Switch': undefined
|
'Tab-Me-Switch': undefined
|
||||||
} & TabSharedStackParamList
|
} & TabSharedStackParamList
|
||||||
export type TabMeStackScreenProps<
|
export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> =
|
||||||
T extends keyof TabMeStackParamList
|
NativeStackScreenProps<TabMeStackParamList, T>
|
||||||
> = NativeStackScreenProps<TabMeStackParamList, T>
|
|
||||||
export type TabMeStackNavigationProp<
|
export type TabMeStackNavigationProp<
|
||||||
RouteName extends keyof TabMeStackParamList
|
RouteName extends keyof TabMeStackParamList
|
||||||
> = StackNavigationProp<TabMeStackParamList, RouteName>
|
> = StackNavigationProp<TabMeStackParamList, RouteName>
|
||||||
|
@ -2,13 +2,13 @@ import apiGeneral from '@api/general'
|
|||||||
import apiTooot from '@api/tooot'
|
import apiTooot from '@api/tooot'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import navigationRef from '@helpers/navigationRef'
|
import navigationRef from '@helpers/navigationRef'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
import { disableAllPushes, Instance } from '@utils/slices/instancesSlice'
|
import { disableAllPushes, Instance } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { TFunction } from 'react-i18next'
|
import { TFunction } from 'react-i18next'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
t: TFunction<'screens'>
|
t: TFunction<'screens'>
|
||||||
@ -16,7 +16,7 @@ export interface Params {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pushUseConnect = ({ t, instances }: Params) => {
|
const pushUseConnect = ({ t, instances }: Params) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
|
@ -2,10 +2,9 @@ import { displayMessage } from '@components/Message'
|
|||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import initQuery from '@utils/initQuery'
|
import initQuery from '@utils/initQuery'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { Instance, updateInstanceActive } from '@utils/slices/instancesSlice'
|
import { Instance } from '@utils/slices/instancesSlice'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import pushUseNavigate from './useNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
@ -13,8 +12,6 @@ export interface Params {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pushUseReceive = ({ instances }: Params) => {
|
const pushUseReceive = ({ instances }: Params) => {
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
const subscription = Notifications.addNotificationReceivedListener(
|
const subscription = Notifications.addNotificationReceivedListener(
|
||||||
notification => {
|
notification => {
|
||||||
|
@ -4,7 +4,6 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
|||||||
import { Instance } from '@utils/slices/instancesSlice'
|
import { Instance } from '@utils/slices/instancesSlice'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import pushUseNavigate from './useNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
@ -12,8 +11,6 @@ export interface Params {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pushUseRespond = ({ instances }: Params) => {
|
const pushUseRespond = ({ instances }: Params) => {
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
const subscription = Notifications.addNotificationResponseReceivedListener(
|
const subscription = Notifications.addNotificationResponseReceivedListener(
|
||||||
({ notification }) => {
|
({ notification }) => {
|
||||||
|
34
src/utils/queryHooks/statusesHistory.ts
Normal file
34
src/utils/queryHooks/statusesHistory.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import { AxiosError } from 'axios'
|
||||||
|
import { QueryFunctionContext, useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
|
export type QueryKeyStatusesHistory = [
|
||||||
|
'StatusesHistory',
|
||||||
|
{ id: Mastodon.Status['id'] }
|
||||||
|
]
|
||||||
|
|
||||||
|
const queryFunction = async ({
|
||||||
|
queryKey
|
||||||
|
}: QueryFunctionContext<QueryKeyStatusesHistory>) => {
|
||||||
|
const { id } = queryKey[1]
|
||||||
|
const res = await apiInstance<Mastodon.StatusHistory[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: `statuses/${id}/history`
|
||||||
|
})
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStatusHistory = ({
|
||||||
|
options,
|
||||||
|
...queryKeyParams
|
||||||
|
}: QueryKeyStatusesHistory[1] & {
|
||||||
|
options?: UseQueryOptions<Mastodon.StatusHistory[], AxiosError>
|
||||||
|
}) => {
|
||||||
|
const queryKey: QueryKeyStatusesHistory = [
|
||||||
|
'StatusesHistory',
|
||||||
|
{ ...queryKeyParams }
|
||||||
|
]
|
||||||
|
return useQuery(queryKey, queryFunction, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useStatusHistory }
|
@ -13,6 +13,7 @@ import {
|
|||||||
useMutation
|
useMutation
|
||||||
} from 'react-query'
|
} from 'react-query'
|
||||||
import deleteItem from './timeline/deleteItem'
|
import deleteItem from './timeline/deleteItem'
|
||||||
|
import editItem from './timeline/editItem'
|
||||||
import updateStatusProperty from './timeline/updateStatusProperty'
|
import updateStatusProperty from './timeline/updateStatusProperty'
|
||||||
|
|
||||||
export type QueryKeyTimeline = [
|
export type QueryKeyTimeline = [
|
||||||
@ -303,13 +304,21 @@ export type MutationVarsTimelineUpdateAccountProperty = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MutationVarsTimelineEditItem = {
|
||||||
|
// This is for editing status
|
||||||
|
type: 'editItem'
|
||||||
|
queryKey?: QueryKeyTimeline
|
||||||
|
rootQueryKey?: QueryKeyTimeline
|
||||||
|
status: Mastodon.Status
|
||||||
|
}
|
||||||
|
|
||||||
export type MutationVarsTimelineDeleteItem = {
|
export type MutationVarsTimelineDeleteItem = {
|
||||||
// This is for deleting status and conversation
|
// This is for deleting status and conversation
|
||||||
type: 'deleteItem'
|
type: 'deleteItem'
|
||||||
source: 'statuses' | 'conversations'
|
source: 'statuses' | 'conversations'
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
rootQueryKey?: QueryKeyTimeline
|
rootQueryKey?: QueryKeyTimeline
|
||||||
id: Mastodon.Conversation['id']
|
id: Mastodon.Status['id']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationVarsTimelineDomainBlock = {
|
export type MutationVarsTimelineDomainBlock = {
|
||||||
@ -322,6 +331,7 @@ export type MutationVarsTimelineDomainBlock = {
|
|||||||
export type MutationVarsTimeline =
|
export type MutationVarsTimeline =
|
||||||
| MutationVarsTimelineUpdateStatusProperty
|
| MutationVarsTimelineUpdateStatusProperty
|
||||||
| MutationVarsTimelineUpdateAccountProperty
|
| MutationVarsTimelineUpdateAccountProperty
|
||||||
|
| MutationVarsTimelineEditItem
|
||||||
| MutationVarsTimelineDeleteItem
|
| MutationVarsTimelineDeleteItem
|
||||||
| MutationVarsTimelineDomainBlock
|
| MutationVarsTimelineDomainBlock
|
||||||
|
|
||||||
@ -371,6 +381,8 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
case 'editItem':
|
||||||
|
return { body: params.status }
|
||||||
case 'deleteItem':
|
case 'deleteItem':
|
||||||
return apiInstance<Mastodon.Conversation>({
|
return apiInstance<Mastodon.Conversation>({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@ -423,6 +435,9 @@ const useTimelineMutation = ({
|
|||||||
case 'updateStatusProperty':
|
case 'updateStatusProperty':
|
||||||
updateStatusProperty(params)
|
updateStatusProperty(params)
|
||||||
break
|
break
|
||||||
|
case 'editItem':
|
||||||
|
editItem(params)
|
||||||
|
break
|
||||||
case 'deleteItem':
|
case 'deleteItem':
|
||||||
deleteItem(params)
|
deleteItem(params)
|
||||||
break
|
break
|
||||||
|
@ -6,11 +6,7 @@ const deleteItem = ({
|
|||||||
queryKey,
|
queryKey,
|
||||||
rootQueryKey,
|
rootQueryKey,
|
||||||
id
|
id
|
||||||
}: {
|
}: MutationVarsTimelineDeleteItem) => {
|
||||||
queryKey?: MutationVarsTimelineDeleteItem['queryKey']
|
|
||||||
rootQueryKey?: MutationVarsTimelineDeleteItem['rootQueryKey']
|
|
||||||
id: MutationVarsTimelineDeleteItem['id']
|
|
||||||
}) => {
|
|
||||||
queryKey &&
|
queryKey &&
|
||||||
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
|
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
|
||||||
if (old) {
|
if (old) {
|
||||||
|
46
src/utils/queryHooks/timeline/editItem.ts
Normal file
46
src/utils/queryHooks/timeline/editItem.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import queryClient from '@helpers/queryClient'
|
||||||
|
import { InfiniteData } from 'react-query'
|
||||||
|
import { MutationVarsTimelineEditItem } from '../timeline'
|
||||||
|
|
||||||
|
const editItem = ({
|
||||||
|
queryKey,
|
||||||
|
rootQueryKey,
|
||||||
|
status
|
||||||
|
}: MutationVarsTimelineEditItem) => {
|
||||||
|
queryKey &&
|
||||||
|
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
|
||||||
|
if (old) {
|
||||||
|
old.pages = old.pages.map(page => {
|
||||||
|
page.body = page.body.map((item: Mastodon.Status) => {
|
||||||
|
if (item.id === status.id) {
|
||||||
|
item = status
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
return page
|
||||||
|
})
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rootQueryKey &&
|
||||||
|
queryClient.setQueryData<InfiniteData<any> | undefined>(
|
||||||
|
rootQueryKey,
|
||||||
|
old => {
|
||||||
|
if (old) {
|
||||||
|
old.pages = old.pages.map(page => {
|
||||||
|
page.body = page.body.map((item: Mastodon.Status) => {
|
||||||
|
if (item.id === status.id) {
|
||||||
|
item = status
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
return page
|
||||||
|
})
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default editItem
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user