mirror of
https://github.com/tooot-app/app
synced 2025-02-24 23:57:44 +01:00
iOS almost working
This commit is contained in:
parent
9c36052c2a
commit
247f7a7982
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,9 +13,10 @@ web-build/
|
|||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
.env
|
.envrc
|
||||||
coverage/
|
coverage/
|
||||||
builds/
|
builds/
|
||||||
|
|
||||||
# @generated expo-cli sync-28e2ab0e9ece60556eaf932abe52d017ec33db50
|
# @generated expo-cli sync-28e2ab0e9ece60556eaf932abe52d017ec33db50
|
||||||
# The following patterns were generated by expo-cli
|
# The following patterns were generated by expo-cli
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ import com.android.build.OutputFile
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
project.ext.react = [
|
project.ext.react = [
|
||||||
enableHermes: false
|
enableHermes: true
|
||||||
]
|
]
|
||||||
|
|
||||||
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
|
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
|
||||||
|
@ -28,6 +28,9 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.JSIModulePackage; // <- react-native-reanimated-v2
|
||||||
|
import com.swmansion.reanimated.ReanimatedJSIModulePackage; // <- react-native-reanimated-v2
|
||||||
|
|
||||||
public class MainApplication extends Application implements ReactApplication {
|
public class MainApplication extends Application implements ReactApplication {
|
||||||
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
|
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
|
||||||
new BasePackageList().getPackageList()
|
new BasePackageList().getPackageList()
|
||||||
@ -51,6 +54,11 @@ public class MainApplication extends Application implements ReactApplication {
|
|||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override // <- react-native-reanimated-v2
|
||||||
|
protected JSIModulePackage getJSIModulePackage() {
|
||||||
|
return new ReanimatedJSIModulePackage();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable String getJSBundleFile() {
|
protected @Nullable String getJSBundleFile() {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
|
@ -3,7 +3,7 @@ import 'dotenv/config'
|
|||||||
|
|
||||||
export default (): ExpoConfig => ({
|
export default (): ExpoConfig => ({
|
||||||
name: 'tooot',
|
name: 'tooot',
|
||||||
description: 'tooot for mastodon',
|
description: 'tooot for Mastodon',
|
||||||
slug: 'tooot',
|
slug: 'tooot',
|
||||||
privacy: 'hidden',
|
privacy: 'hidden',
|
||||||
sdkVersion: '40.0.0',
|
sdkVersion: '40.0.0',
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ $# -ne 1 ]; then
|
|
||||||
echo "Arguments incorrect. Use 'xxx'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
JAVA_HOME=`/usr/libexec/java_home -v 1.8.0` \
|
|
||||||
EXPO_USERNAME='xmflsct' \
|
|
||||||
EXPO_PASSWORD=',8d_AJ1HmYJo8lbve&QoB40t3ImGdF)Dd' \
|
|
||||||
EXPO_ANDROID_KEYSTORE_PASSWORD="9c54265087704801ba5d3d88809110a1" \
|
|
||||||
EXPO_ANDROID_KEY_PASSWORD="748bb2e11529497dad7831c409175b94" \
|
|
||||||
turtle build:android \
|
|
||||||
--release-channel $1 \
|
|
||||||
--type app-bundle \
|
|
||||||
--keystore-path ./tooot.jks \
|
|
||||||
--keystore-alias "QHhtZmxzY3QvdG9vb3Q=" \
|
|
||||||
--build-dir ./builds
|
|
2
index.js
2
index.js
@ -1,6 +1,6 @@
|
|||||||
import { registerRootComponent } from 'expo';
|
import { registerRootComponent } from 'expo';
|
||||||
|
|
||||||
import App from './App';
|
import App from '@root/App';
|
||||||
|
|
||||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||||
// It also ensures that whether you load the app in the Expo client or in a native build,
|
// It also ensures that whether you load the app in the Expo client or in a native build,
|
||||||
|
@ -7,14 +7,12 @@ PODS:
|
|||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- UMFileSystemInterface
|
||||||
- UMPermissionsInterface
|
- UMPermissionsInterface
|
||||||
- EXBlur (8.2.2):
|
|
||||||
- UMCore
|
|
||||||
- EXConstants (10.0.1):
|
- EXConstants (10.0.1):
|
||||||
- UMConstantsInterface
|
- UMConstantsInterface
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXCrypto (8.4.0):
|
- EXCrypto (8.4.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXDevice (2.4.0):
|
- EXDevice (3.1.1):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXErrorRecovery (1.4.0):
|
- EXErrorRecovery (1.4.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
@ -25,7 +23,7 @@ PODS:
|
|||||||
- EXFirebaseCore
|
- EXFirebaseCore
|
||||||
- Firebase/Core (= 6.14.0)
|
- Firebase/Core (= 6.14.0)
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXFirebaseCore (1.3.0):
|
- EXFirebaseCore (1.2.0):
|
||||||
- Firebase/Core (= 6.14.0)
|
- Firebase/Core (= 6.14.0)
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXFont (8.4.0):
|
- EXFont (8.4.0):
|
||||||
@ -66,8 +64,8 @@ PODS:
|
|||||||
- UMFileSystemInterface
|
- UMFileSystemInterface
|
||||||
- EXStoreReview (2.3.0):
|
- EXStoreReview (2.3.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXUpdates (0.4.1):
|
- EXUpdates (0.3.5):
|
||||||
- React-Core
|
- React
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXVideoThumbnails (4.4.0):
|
- EXVideoThumbnails (4.4.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
@ -332,15 +330,17 @@ PODS:
|
|||||||
- React-cxxreact (= 0.63.4)
|
- React-cxxreact (= 0.63.4)
|
||||||
- React-jsi (= 0.63.4)
|
- React-jsi (= 0.63.4)
|
||||||
- React-jsinspector (0.63.4)
|
- React-jsinspector (0.63.4)
|
||||||
|
- react-native-blur (0.8.0):
|
||||||
|
- React
|
||||||
- react-native-blurhash (1.0.29):
|
- react-native-blurhash (1.0.29):
|
||||||
- React
|
- React
|
||||||
- react-native-netinfo (5.9.10):
|
- react-native-netinfo (5.9.10):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-safe-area-context (3.1.9):
|
- react-native-safe-area-context (3.1.9):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-segmented-control (2.2.1):
|
- react-native-segmented-control (2.2.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-viewpager (4.2.0):
|
- react-native-viewpager (4.2.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
- React-RCTActionSheet (0.63.4):
|
- React-RCTActionSheet (0.63.4):
|
||||||
- React-Core/RCTActionSheetHeaders (= 0.63.4)
|
- React-Core/RCTActionSheetHeaders (= 0.63.4)
|
||||||
@ -410,9 +410,9 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- SDWebImage (~> 5.8)
|
- SDWebImage (~> 5.8)
|
||||||
- SDWebImageWebPCoder (~> 0.6.1)
|
- SDWebImageWebPCoder (~> 0.6.1)
|
||||||
- RNGestureHandler (1.8.0):
|
- RNGestureHandler (1.9.0):
|
||||||
- React
|
- React-Core
|
||||||
- RNReanimated (2.0.0-rc.0):
|
- RNReanimated (2.0.0-rc.2):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- FBLazyVector
|
- FBLazyVector
|
||||||
- FBReactNativeSpec
|
- FBReactNativeSpec
|
||||||
@ -441,11 +441,13 @@ PODS:
|
|||||||
- React-RCTVibration
|
- React-RCTVibration
|
||||||
- ReactCommon/turbomodule/core
|
- ReactCommon/turbomodule/core
|
||||||
- Yoga
|
- Yoga
|
||||||
- RNScreens (2.15.2):
|
- RNScreens (2.17.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNSentry (2.1.1):
|
- RNSentry (2.1.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- Sentry (= 6.0.9)
|
- Sentry (= 6.0.9)
|
||||||
|
- RNSharedElement (0.7.0):
|
||||||
|
- React
|
||||||
- RNSVG (12.1.0):
|
- RNSVG (12.1.0):
|
||||||
- React
|
- React
|
||||||
- SDWebImage (5.10.3):
|
- SDWebImage (5.10.3):
|
||||||
@ -480,8 +482,7 @@ DEPENDENCIES:
|
|||||||
- 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`)
|
- EXApplication (from `../node_modules/expo-application/ios`)
|
||||||
- EXAV (from `../node_modules/expo-av/ios`)
|
- EXAV (from `../node_modules/expo-av/ios`)
|
||||||
- EXBlur (from `../node_modules/expo-blur/ios`)
|
- EXConstants (from `../node_modules/sentry-expo/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`)
|
||||||
- EXDevice (from `../node_modules/expo-device/ios`)
|
- EXDevice (from `../node_modules/expo-device/ios`)
|
||||||
- EXErrorRecovery (from `../node_modules/expo-error-recovery/ios`)
|
- EXErrorRecovery (from `../node_modules/expo-error-recovery/ios`)
|
||||||
@ -521,6 +522,7 @@ DEPENDENCIES:
|
|||||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||||
|
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
|
||||||
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
|
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
|
||||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||||
@ -543,6 +545,7 @@ DEPENDENCIES:
|
|||||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
- 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`)"
|
||||||
|
- RNSharedElement (from `../node_modules/react-native-shared-element`)
|
||||||
- RNSVG (from `../node_modules/react-native-svg`)
|
- RNSVG (from `../node_modules/react-native-svg`)
|
||||||
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
|
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
|
||||||
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
|
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
|
||||||
@ -586,10 +589,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-application/ios"
|
:path: "../node_modules/expo-application/ios"
|
||||||
EXAV:
|
EXAV:
|
||||||
:path: "../node_modules/expo-av/ios"
|
:path: "../node_modules/expo-av/ios"
|
||||||
EXBlur:
|
|
||||||
:path: "../node_modules/expo-blur/ios"
|
|
||||||
EXConstants:
|
EXConstants:
|
||||||
:path: "../node_modules/expo-constants/ios"
|
:path: "../node_modules/sentry-expo/node_modules/expo-constants/ios"
|
||||||
EXCrypto:
|
EXCrypto:
|
||||||
:path: "../node_modules/expo-crypto/ios"
|
:path: "../node_modules/expo-crypto/ios"
|
||||||
EXDevice:
|
EXDevice:
|
||||||
@ -664,6 +665,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||||
React-jsinspector:
|
React-jsinspector:
|
||||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||||
|
react-native-blur:
|
||||||
|
:path: "../node_modules/@react-native-community/blur"
|
||||||
react-native-blurhash:
|
react-native-blurhash:
|
||||||
:path: "../node_modules/react-native-blurhash"
|
:path: "../node_modules/react-native-blurhash"
|
||||||
react-native-netinfo:
|
react-native-netinfo:
|
||||||
@ -708,6 +711,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"
|
||||||
|
RNSharedElement:
|
||||||
|
:path: "../node_modules/react-native-shared-element"
|
||||||
RNSVG:
|
RNSVG:
|
||||||
:path: "../node_modules/react-native-svg"
|
:path: "../node_modules/react-native-svg"
|
||||||
UMAppLoader:
|
UMAppLoader:
|
||||||
@ -744,14 +749,13 @@ SPEC CHECKSUMS:
|
|||||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||||
EXApplication: e3c201e7b913d081bbd37bd3c5d0e2cdc21733b4
|
EXApplication: e3c201e7b913d081bbd37bd3c5d0e2cdc21733b4
|
||||||
EXAV: e32adf44c10a82f723172d5b01d5693890888dd6
|
EXAV: e32adf44c10a82f723172d5b01d5693890888dd6
|
||||||
EXBlur: 569d38c2b0b7b000866d928bc36acadfdc02ea8e
|
|
||||||
EXConstants: d9483a8fe0a963dd7ee3389f371303773e6ecd37
|
EXConstants: d9483a8fe0a963dd7ee3389f371303773e6ecd37
|
||||||
EXCrypto: 81948191c3d2caf18fb32f18ee88d87d42532d62
|
EXCrypto: 81948191c3d2caf18fb32f18ee88d87d42532d62
|
||||||
EXDevice: 01f54314f618aa4098893f66cd8f2a8a411f33ee
|
EXDevice: 101eddd9bc28faba8246ba0c499a65a4821a6b5e
|
||||||
EXErrorRecovery: b46af4b91e2b4ec598ab1fa51d2cf088aaf5511f
|
EXErrorRecovery: b46af4b91e2b4ec598ab1fa51d2cf088aaf5511f
|
||||||
EXFileSystem: 0e4974ab77bff04cda68d2886d070cbe64b93a6b
|
EXFileSystem: 0e4974ab77bff04cda68d2886d070cbe64b93a6b
|
||||||
EXFirebaseAnalytics: d72f553dc0c0a8ee451714499fac21883c3f7369
|
EXFirebaseAnalytics: d72f553dc0c0a8ee451714499fac21883c3f7369
|
||||||
EXFirebaseCore: f986c71bdd1cb5941c9cfa686b2395fe588a53d0
|
EXFirebaseCore: d5befdb22015f9b71168ed89dda0b50dbb07fed1
|
||||||
EXFont: 30c64ed8735a180e3f20046e4fdac4ea074d71d3
|
EXFont: 30c64ed8735a180e3f20046e4fdac4ea074d71d3
|
||||||
EXHaptics: 6d2e09387f0eb4d3c8dc97ae905cbea8afe33651
|
EXHaptics: 6d2e09387f0eb4d3c8dc97ae905cbea8afe33651
|
||||||
EXImageLoader: f96ec9992733a4224418bbd9382e5485c8948944
|
EXImageLoader: f96ec9992733a4224418bbd9382e5485c8948944
|
||||||
@ -766,7 +770,7 @@ SPEC CHECKSUMS:
|
|||||||
EXSplashScreen: 8c7c1112ce7611a853486af4737fe2298eda7657
|
EXSplashScreen: 8c7c1112ce7611a853486af4737fe2298eda7657
|
||||||
EXSQLite: bda6a286dded0637bb312ee781239dcca163ff4b
|
EXSQLite: bda6a286dded0637bb312ee781239dcca163ff4b
|
||||||
EXStoreReview: 9b161bd86c172a9755c787982bfc32c7193cf803
|
EXStoreReview: 9b161bd86c172a9755c787982bfc32c7193cf803
|
||||||
EXUpdates: 28368049118dbe4ceaf4422ec72e0ad5f770df1f
|
EXUpdates: 74b39409f68eca207075d87b0077bdf37865a8bf
|
||||||
EXVideoThumbnails: 8ff241784f1d89fdd451bcdb7f733d06361d44a8
|
EXVideoThumbnails: 8ff241784f1d89fdd451bcdb7f733d06361d44a8
|
||||||
EXWebBrowser: cb4811e6c883876d2e2ba1c10527de96963d410a
|
EXWebBrowser: cb4811e6c883876d2e2ba1c10527de96963d410a
|
||||||
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
||||||
@ -796,11 +800,12 @@ SPEC CHECKSUMS:
|
|||||||
React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31
|
React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31
|
||||||
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
|
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
|
||||||
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
|
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
|
||||||
|
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
||||||
react-native-blurhash: 90886ae897cafbbdf2773cb3654656bcb34e8f43
|
react-native-blurhash: 90886ae897cafbbdf2773cb3654656bcb34e8f43
|
||||||
react-native-netinfo: 30fb89fa913c342be82a887b56e96be6d71201dd
|
react-native-netinfo: 30fb89fa913c342be82a887b56e96be6d71201dd
|
||||||
react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94
|
react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94
|
||||||
react-native-segmented-control: eb9e25fbfbce226ecf66d643428fbe97601e691a
|
react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097
|
||||||
react-native-viewpager: c1a686e7b5e164a52387f1522358623c4f52070f
|
react-native-viewpager: ea945e2881ce9a4a8bcdc84de4ec65ff23c90f6e
|
||||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||||
React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0
|
React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0
|
||||||
@ -814,10 +819,11 @@ SPEC CHECKSUMS:
|
|||||||
RNCAsyncStorage: da95b83e241a7f5efe3da1a949b3ec3175380be0
|
RNCAsyncStorage: da95b83e241a7f5efe3da1a949b3ec3175380be0
|
||||||
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
|
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
|
||||||
RNFastImage: d4870d58f5936111c56218dbd7fcfc18e65b58ff
|
RNFastImage: d4870d58f5936111c56218dbd7fcfc18e65b58ff
|
||||||
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
|
RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b
|
||||||
RNReanimated: b9c929bfff7dedc9c89ab1875f1c6151023358d9
|
RNReanimated: e8a1520b15df106c96214161078c69e4a23b8b29
|
||||||
RNScreens: 3d682bcaba69a4f8e55543d90818704f34338db1
|
RNScreens: b6c9607e6fe47c1b6e2f1910d2acd46dd7ecea3a
|
||||||
RNSentry: 6b46b6fc1d715a378fbaa5d7d43bc9ce99b500e5
|
RNSentry: 6b46b6fc1d715a378fbaa5d7d43bc9ce99b500e5
|
||||||
|
RNSharedElement: 00b1a1420d213a34459bb9a5aacabb38107d7948
|
||||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||||
SDWebImage: e378178472b735e84b007bfb55514c97948a0598
|
SDWebImage: e378178472b735e84b007bfb55514c97948a0598
|
||||||
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
|
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
|
||||||
|
@ -177,6 +177,7 @@
|
|||||||
LastUpgradeCheck = 1130;
|
LastUpgradeCheck = 1130;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
|
DevelopmentTeam = 8EGBLQ2MA6;
|
||||||
LastSwiftMigration = 1120;
|
LastSwiftMigration = 1120;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -229,7 +230,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n../node_modules/expo-updates/scripts/create-manifest-ios.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n";
|
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n../node_modules/expo-updates/scripts/create-manifest-ios.sh\n";
|
||||||
};
|
};
|
||||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
@ -325,6 +326,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
@ -332,7 +334,7 @@
|
|||||||
"FB_SONARKIT_ENABLED=1",
|
"FB_SONARKIT_ENABLED=1",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = tooot/Info.plist;
|
INFOPLIST_FILE = tooot/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -356,9 +358,10 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||||
INFOPLIST_FILE = tooot/Info.plist;
|
INFOPLIST_FILE = tooot/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -421,7 +424,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
@ -475,7 +478,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<?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>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?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>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
62
package.json
62
package.json
@ -6,95 +6,91 @@
|
|||||||
"web": "expo start --web",
|
"web": "expo start --web",
|
||||||
"eject": "expo eject",
|
"eject": "expo eject",
|
||||||
"test": "jest --watchAll",
|
"test": "jest --watchAll",
|
||||||
"release": "./publish/publish.sh"
|
"release": "scripts/release.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/react-native-action-sheet": "^3.8.0",
|
"@expo/react-native-action-sheet": "^3.8.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.13.2",
|
"@react-native-async-storage/async-storage": "^1.13.3",
|
||||||
|
"@react-native-community/blur": "^3.6.0",
|
||||||
"@react-native-community/masked-view": "0.1.10",
|
"@react-native-community/masked-view": "0.1.10",
|
||||||
"@react-native-community/netinfo": "^5.9.7",
|
"@react-native-community/netinfo": "^5.9.10",
|
||||||
"@react-native-community/segmented-control": "2.2.1",
|
"@react-native-community/segmented-control": "2.2.2",
|
||||||
"@react-native-community/viewpager": "4.2.0",
|
"@react-native-community/viewpager": "4.2.2",
|
||||||
"@react-navigation/bottom-tabs": "^5.11.2",
|
"@react-navigation/bottom-tabs": "^5.11.7",
|
||||||
"@react-navigation/native": "^5.8.10",
|
"@react-navigation/native": "^5.9.2",
|
||||||
|
"@react-navigation/stack": "^5.14.2",
|
||||||
"@reduxjs/toolkit": "^1.5.0",
|
"@reduxjs/toolkit": "^1.5.0",
|
||||||
"@sentry/react-native": "^2.1.1",
|
"@sentry/react-native": "^2.1.1",
|
||||||
"@sharcoux/slider": "^5.0.1",
|
"@sharcoux/slider": "^5.0.4",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"expo": "^40.0.0",
|
"expo": "^40.0.1",
|
||||||
"expo-auth-session": "~3.0.0",
|
"expo-auth-session": "~3.0.0",
|
||||||
"expo-av": "~8.7.0",
|
"expo-av": "~8.7.0",
|
||||||
"expo-blur": "~8.2.2",
|
|
||||||
"expo-constants": "~10.0.1",
|
|
||||||
"expo-crypto": "~8.4.0",
|
"expo-crypto": "~8.4.0",
|
||||||
"expo-firebase-analytics": "~2.6.0",
|
"expo-firebase-analytics": "~2.6.0",
|
||||||
"expo-firebase-core": "~1.3.0",
|
|
||||||
"expo-haptics": "~8.4.0",
|
"expo-haptics": "~8.4.0",
|
||||||
"expo-image-picker": "~9.2.0",
|
"expo-image-picker": "~9.2.0",
|
||||||
"expo-linear-gradient": "~8.4.0",
|
"expo-linear-gradient": "~8.4.0",
|
||||||
"expo-linking": "~2.0.0",
|
"expo-linking": "~2.0.1",
|
||||||
"expo-localization": "~9.1.0",
|
"expo-localization": "~9.1.0",
|
||||||
"expo-random": "~10.0.0",
|
"expo-random": "~10.0.0",
|
||||||
"expo-secure-store": "~9.3.0",
|
"expo-secure-store": "~9.3.0",
|
||||||
"expo-splash-screen": "~0.8.1",
|
"expo-splash-screen": "~0.8.1",
|
||||||
"expo-status-bar": "~1.0.3",
|
"expo-status-bar": "~1.0.3",
|
||||||
"expo-store-review": "~2.3.0",
|
"expo-store-review": "~2.3.0",
|
||||||
"expo-updates": "~0.4.0",
|
|
||||||
"expo-video-thumbnails": "~4.4.0",
|
"expo-video-thumbnails": "~4.4.0",
|
||||||
"expo-web-browser": "~8.6.0",
|
"expo-web-browser": "~8.6.0",
|
||||||
"i18next": "^19.8.4",
|
"i18next": "^19.8.5",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
"react-i18next": "^11.8.5",
|
"react-i18next": "^11.8.5",
|
||||||
"react-native": "~0.63.4",
|
"react-native": "~0.63.4",
|
||||||
"react-native-animated-spinkit": "^1.4.2",
|
"react-native-animated-spinkit": "^1.5.1",
|
||||||
"react-native-blurhash": "^1.0.29",
|
"react-native-blurhash": "^1.0.29",
|
||||||
"react-native-fast-image": "^8.3.4",
|
"react-native-fast-image": "^8.3.4",
|
||||||
"react-native-feather": "^1.0.2",
|
"react-native-feather": "^1.0.2",
|
||||||
"react-native-gesture-handler": "~1.8.0",
|
"react-native-gesture-handler": "~1.9.0",
|
||||||
"react-native-htmlview": "^0.16.0",
|
"react-native-htmlview": "^0.16.0",
|
||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
"react-native-reanimated": "2.0.0-rc.0",
|
"react-native-reanimated": "^2.0.0-rc.2",
|
||||||
"react-native-safe-area-context": "3.1.9",
|
"react-native-safe-area-context": "3.1.9",
|
||||||
"react-native-screens": "~2.15.0",
|
"react-native-screens": "~2.17.1",
|
||||||
|
"react-native-shared-element": "^0.7.0",
|
||||||
"react-native-svg": "12.1.0",
|
"react-native-svg": "12.1.0",
|
||||||
"react-native-tab-view": "^2.15.2",
|
"react-native-tab-view": "^2.15.2",
|
||||||
"react-native-tab-view-viewpager-adapter": "^1.1.0",
|
"react-native-tab-view-viewpager-adapter": "^1.1.0",
|
||||||
"react-native-toast-message": "^1.4.2",
|
"react-native-toast-message": "^1.4.3",
|
||||||
"react-native-unimodules": "~0.12.0",
|
"react-native-unimodules": "~0.12.0",
|
||||||
"react-native-web": "~0.13.12",
|
"react-navigation-shared-element": "^3.0.0",
|
||||||
"react-navigation": "^4.4.3",
|
"react-query": "^3.6.0",
|
||||||
"react-query": "^3.5.6",
|
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-timeago": "^5.2.0",
|
"react-timeago": "^5.2.0",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"rn-placeholder": "^3.0.3",
|
"rn-placeholder": "^3.0.3",
|
||||||
"sentry-expo": "^3.0.4",
|
"sentry-expo": "^3.0.4",
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "~7.9.0",
|
"@babel/core": "~7.12.10",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
|
||||||
"@babel/preset-typescript": "^7.12.7",
|
"@babel/preset-typescript": "^7.12.7",
|
||||||
"@expo/config": "^3.3.15",
|
"@expo/config": "^3.3.26",
|
||||||
"@jest/types": "^26.6.2",
|
"@jest/types": "^26.6.2",
|
||||||
"@react-navigation/stack": "^5.12.8",
|
|
||||||
"@testing-library/jest-native": "^3.4.3",
|
"@testing-library/jest-native": "^3.4.3",
|
||||||
"@testing-library/react-hooks": "^3.7.0",
|
"@testing-library/react-hooks": "^3.7.0",
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/crypto-js": "^4.0.1",
|
|
||||||
"@types/jest": "^26.0.19",
|
"@types/jest": "^26.0.19",
|
||||||
"@types/lodash": "^4.14.164",
|
"@types/lodash": "^4.14.164",
|
||||||
"@types/react": "~16.9.35",
|
"@types/react": "~16.9.35",
|
||||||
"@types/react-dom": "~16.9.8",
|
"@types/react-dom": "~16.9.8",
|
||||||
"@types/react-native": "~0.63.2",
|
"@types/react-native": "~0.63.46",
|
||||||
"@types/react-navigation": "^3.4.0",
|
"@types/react-navigation": "^3.4.0",
|
||||||
"@types/react-redux": "^7.1.12",
|
"@types/react-redux": "^7.1.16",
|
||||||
"@types/react-test-renderer": "^17.0.0",
|
"@types/react-test-renderer": "^17.0.0",
|
||||||
"@types/react-timeago": "^4.1.2",
|
"@types/react-timeago": "^4.1.2",
|
||||||
"@welldone-software/why-did-you-render": "^6.0.4",
|
"@welldone-software/why-did-you-render": "^6.0.5",
|
||||||
"babel-jest": "~25.2.6",
|
"babel-jest": "~25.2.6",
|
||||||
"babel-plugin-module-resolver": "^4.1.0",
|
"babel-plugin-module-resolver": "^4.1.0",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
@ -102,6 +98,8 @@
|
|||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"jest-expo": "^40.0.1",
|
"jest-expo": "^40.0.1",
|
||||||
"nock": "^13.0.5",
|
"nock": "^13.0.5",
|
||||||
|
"react-navigation": "^4.4.3",
|
||||||
|
"react-navigation-stack": "^2.10.2",
|
||||||
"react-test-renderer": "^16.13.1",
|
"react-test-renderer": "^16.13.1",
|
||||||
"typescript": "~4.1.3"
|
"typescript": "~4.1.3"
|
||||||
},
|
},
|
||||||
|
@ -5,4 +5,4 @@ if [ $# -ne 1 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
expo publish --release-channel=$1
|
expo publish --target bare --release-channel=$1
|
116
src/@types/react-navigation.d.ts
vendored
116
src/@types/react-navigation.d.ts
vendored
@ -1,19 +1,22 @@
|
|||||||
|
interface IImageInfo {
|
||||||
|
url: string
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
originUrl?: string
|
||||||
|
props?: any
|
||||||
|
}
|
||||||
|
|
||||||
declare namespace Nav {
|
declare namespace Nav {
|
||||||
type RootStackParamList = {
|
type RootStackParamList = {
|
||||||
'Screen-Local': undefined
|
'Screen-Tabs': undefined
|
||||||
'Screen-Public': undefined
|
'Screen-Actions': {
|
||||||
'Screen-Post': undefined
|
queryKey: QueryKeyTimeline
|
||||||
'Screen-Notifications': undefined
|
status: Mastodon.Status
|
||||||
'Screen-Me': undefined
|
url?: string
|
||||||
|
type?: 'status' | 'account'
|
||||||
}
|
}
|
||||||
|
'Screen-Announcements': { showAll: boolean }
|
||||||
type SharedStackParamList = {
|
'Screen-Compose':
|
||||||
'Screen-Shared-Account': {
|
|
||||||
account: Mastodon.Account | Mastodon.Mention
|
|
||||||
}
|
|
||||||
'Screen-Shared-Announcements': { showAll?: boolean }
|
|
||||||
'Screen-Shared-Attachments': { account: Mastodon.Account }
|
|
||||||
'Screen-Shared-Compose':
|
|
||||||
| {
|
| {
|
||||||
type: 'edit'
|
type: 'edit'
|
||||||
incomingStatus: Mastodon.Status
|
incomingStatus: Mastodon.Status
|
||||||
@ -48,54 +51,71 @@ declare namespace Nav {
|
|||||||
accts: Mastodon.Account['acct'][]
|
accts: Mastodon.Account['acct'][]
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
'Screen-Shared-Hashtag': {
|
'Screen-ImagesViewer': {
|
||||||
hashtag: Mastodon.Tag['name']
|
imageUrls: (IImageInfo & {
|
||||||
}
|
|
||||||
'Screen-Shared-ImagesViewer': {
|
|
||||||
imageUrls: ({
|
|
||||||
url: string
|
|
||||||
width?: number
|
|
||||||
height?: number
|
|
||||||
originUrl?: string
|
|
||||||
props?: any
|
|
||||||
} & {
|
|
||||||
preview_url: Mastodon.AttachmentImage['preview_url']
|
preview_url: Mastodon.AttachmentImage['preview_url']
|
||||||
remote_url: Mastodon.AttachmentImage['remote_url']
|
remote_url?: Mastodon.AttachmentImage['remote_url']
|
||||||
imageIndex: number
|
imageIndex: number
|
||||||
})[]
|
})[]
|
||||||
imageIndex: number
|
imageIndex: number
|
||||||
}
|
}
|
||||||
'Screen-Shared-Relationships': {
|
}
|
||||||
|
|
||||||
|
type ScreenTabsStackParamList = {
|
||||||
|
'Tab-Local': undefined
|
||||||
|
'Tab-Public': undefined
|
||||||
|
'Tab-Compose': undefined
|
||||||
|
'Tab-Notifications': undefined
|
||||||
|
'Tab-Me': undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabSharedStackParamList = {
|
||||||
|
'Tab-Shared-Account': {
|
||||||
|
account: Mastodon.Account | Mastodon.Mention
|
||||||
|
}
|
||||||
|
'Tab-Shared-Attachments': { account: Mastodon.Account }
|
||||||
|
'Tab-Shared-Hashtag': {
|
||||||
|
hashtag: Mastodon.Tag['name']
|
||||||
|
}
|
||||||
|
'Tab-Shared-Relationships': {
|
||||||
account: Mastodon.Account
|
account: Mastodon.Account
|
||||||
initialType: 'following' | 'followers'
|
initialType: 'following' | 'followers'
|
||||||
}
|
}
|
||||||
'Screen-Shared-Search': undefined
|
'Tab-Shared-Search': undefined
|
||||||
'Screen-Shared-Toot': {
|
'Tab-Shared-Toot': {
|
||||||
toot: Mastodon.Status
|
toot: Mastodon.Status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalStackParamList = {
|
type TabLocalStackParamList = {
|
||||||
'Screen-Local-Root': undefined
|
'Tab-Local-Root': undefined
|
||||||
} & SharedStackParamList
|
} & TabSharedStackParamList
|
||||||
|
|
||||||
type RemoteStackParamList = {
|
type TabPublicStackParamList = {
|
||||||
'Screen-Remote-Root': undefined
|
'Tab-Public-Root': undefined
|
||||||
} & SharedStackParamList
|
} & TabSharedStackParamList
|
||||||
|
|
||||||
type NotificationsStackParamList = {
|
type TabComposeStackParamList = {
|
||||||
'Screen-Notifications-Root': undefined
|
'Tab-Compose-Root': undefined
|
||||||
} & SharedStackParamList
|
'Tab-Compose-EditAttachment': unknown
|
||||||
|
}
|
||||||
|
|
||||||
type MeStackParamList = {
|
type TabNotificationsStackParamList = {
|
||||||
'Screen-Me-Root': { navigateAway?: 'Screen-Me-Settings-UpdateRemote' }
|
'Tab-Notifications-Root': undefined
|
||||||
'Screen-Me-Bookmarks': undefined
|
} & TabSharedStackParamList
|
||||||
'Screen-Me-Conversations': undefined
|
|
||||||
'Screen-Me-Favourites': undefined
|
type TabMeStackParamList = {
|
||||||
'Screen-Me-Lists': undefined
|
'Tab-Me-Root': undefined
|
||||||
'Screen-Me-Lists-List': undefined
|
'Tab-Me-Bookmarks': undefined
|
||||||
'Screen-Me-Settings': undefined
|
'Tab-Me-Conversations': undefined
|
||||||
'Screen-Me-Settings-UpdateRemote': undefined
|
'Tab-Me-Favourites': undefined
|
||||||
'Screen-Me-Switch': undefined
|
'Tab-Me-Lists': undefined
|
||||||
} & SharedStackParamList
|
'Tab-Me-Lists-List': {
|
||||||
|
list: Mastodon.List['id']
|
||||||
|
title: Mastodon.List['title']
|
||||||
|
}
|
||||||
|
'Tab-Me-Settings': undefined
|
||||||
|
'Tab-Me-Settings-UpdateRemote': undefined
|
||||||
|
'Tab-Me-Switch': undefined
|
||||||
|
} & TabSharedStackParamList
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
|
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
import Index from '@root/Index'
|
import Index from '@root/Screens'
|
||||||
import audio from '@root/startup/audio'
|
import audio from '@root/startup/audio'
|
||||||
import dev from '@root/startup/dev'
|
import dev from '@root/startup/dev'
|
||||||
import log from '@root/startup/log'
|
import log from '@root/startup/log'
|
324
src/Index.tsx
324
src/Index.tsx
@ -1,324 +0,0 @@
|
|||||||
import client from '@api/client'
|
|
||||||
import haptics from '@components/haptics'
|
|
||||||
import Icon from '@components/Icon'
|
|
||||||
import { toast, toastConfig } from '@components/toast'
|
|
||||||
import {
|
|
||||||
BottomTabNavigationOptions,
|
|
||||||
createBottomTabNavigator
|
|
||||||
} from '@react-navigation/bottom-tabs'
|
|
||||||
import {
|
|
||||||
getFocusedRouteNameFromRoute,
|
|
||||||
NavigationContainer,
|
|
||||||
NavigationContainerRef
|
|
||||||
} from '@react-navigation/native'
|
|
||||||
import ScreenLocal from '@screens/Local'
|
|
||||||
import ScreenMe from '@screens/Me'
|
|
||||||
import ScreenNotifications from '@screens/Notifications'
|
|
||||||
import ScreenPublic from '@screens/Public'
|
|
||||||
import { useTimelineQuery } from '@utils/queryHooks/timeline'
|
|
||||||
import {
|
|
||||||
getLocalAccount,
|
|
||||||
getLocalActiveIndex,
|
|
||||||
getLocalNotification,
|
|
||||||
localUpdateAccountPreferences,
|
|
||||||
localUpdateNotification
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import { themes } from '@utils/styles/themes'
|
|
||||||
import * as Analytics from 'expo-firebase-analytics'
|
|
||||||
import React, {
|
|
||||||
createRef,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef
|
|
||||||
} from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { Image, Platform, StatusBar } from 'react-native'
|
|
||||||
import Toast from 'react-native-toast-message'
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
|
|
||||||
const Tab = createBottomTabNavigator<Nav.RootStackParamList>()
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
localCorrupt?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const navigationRef = createRef<NavigationContainerRef>()
|
|
||||||
|
|
||||||
const Index: React.FC<Props> = ({ localCorrupt }) => {
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
|
||||||
const { mode, theme } = useTheme()
|
|
||||||
enum barStyle {
|
|
||||||
light = 'dark-content',
|
|
||||||
dark = 'light-content'
|
|
||||||
}
|
|
||||||
|
|
||||||
const routeNameRef = useRef<string | undefined>()
|
|
||||||
|
|
||||||
// const isConnected = useNetInfo().isConnected
|
|
||||||
// const [firstRender, setFirstRender] = useState(false)
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (firstRender) {
|
|
||||||
// // bug in netInfo on first render as false
|
|
||||||
// if (isConnected !== false) {
|
|
||||||
// toast({ type: 'error', content: '手机🈚️网络', autoHide: false })
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// setFirstRender(true)
|
|
||||||
// }
|
|
||||||
// }, [isConnected, firstRender])
|
|
||||||
|
|
||||||
// On launch display login credentials corrupt information
|
|
||||||
const { t } = useTranslation('common')
|
|
||||||
useEffect(() => {
|
|
||||||
const showLocalCorrect = localCorrupt
|
|
||||||
? toast({
|
|
||||||
type: 'error',
|
|
||||||
message: t('index.localCorrupt'),
|
|
||||||
description: localCorrupt.length ? localCorrupt : undefined,
|
|
||||||
autoHide: false
|
|
||||||
})
|
|
||||||
: undefined
|
|
||||||
return showLocalCorrect
|
|
||||||
}, [localCorrupt])
|
|
||||||
|
|
||||||
// On launch check if there is any unread announcements
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('Checking announcements')
|
|
||||||
localActiveIndex !== null &&
|
|
||||||
client<Mastodon.Announcement[]>({
|
|
||||||
method: 'get',
|
|
||||||
instance: 'local',
|
|
||||||
url: `announcements`
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (res?.filter(announcement => !announcement.read).length) {
|
|
||||||
navigationRef.current?.navigate('Screen-Shared-Announcements', {
|
|
||||||
showAll: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// On launch check if there is any unread noficiations
|
|
||||||
const queryNotification = useTimelineQuery({
|
|
||||||
page: 'Notifications',
|
|
||||||
options: {
|
|
||||||
enabled: localActiveIndex !== null ? true : false,
|
|
||||||
refetchInterval: 1000 * 60,
|
|
||||||
refetchIntervalInBackground: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const prevNotification = useSelector(getLocalNotification)
|
|
||||||
useEffect(() => {
|
|
||||||
if (queryNotification.data?.pages) {
|
|
||||||
const flattenData = queryNotification.data.pages.flatMap(d => [...d])
|
|
||||||
const latestNotificationTime = flattenData.length
|
|
||||||
? (flattenData[0] as Mastodon.Notification).created_at
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
if (!prevNotification || !prevNotification.latestTime) {
|
|
||||||
dispatch(
|
|
||||||
localUpdateNotification({
|
|
||||||
unread: false
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else if (
|
|
||||||
latestNotificationTime &&
|
|
||||||
new Date(prevNotification.latestTime) < new Date(latestNotificationTime)
|
|
||||||
) {
|
|
||||||
dispatch(
|
|
||||||
localUpdateNotification({
|
|
||||||
unread: true,
|
|
||||||
latestTime: latestNotificationTime
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [queryNotification.data?.pages])
|
|
||||||
|
|
||||||
// Lazily update users's preferences, for e.g. composing default visibility
|
|
||||||
useEffect(() => {
|
|
||||||
if (localActiveIndex !== null) {
|
|
||||||
dispatch(localUpdateAccountPreferences())
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Callbacks
|
|
||||||
const navigationContainerOnReady = useCallback(
|
|
||||||
() =>
|
|
||||||
(routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const navigationContainerOnStateChange = useCallback(() => {
|
|
||||||
const previousRouteName = routeNameRef.current
|
|
||||||
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name
|
|
||||||
|
|
||||||
if (previousRouteName !== currentRouteName) {
|
|
||||||
Analytics.setCurrentScreen(currentRouteName)
|
|
||||||
}
|
|
||||||
|
|
||||||
routeNameRef.current = currentRouteName
|
|
||||||
}, [])
|
|
||||||
const localAccount = useSelector(getLocalAccount)
|
|
||||||
const tabNavigatorScreenOptions = useCallback(
|
|
||||||
({ route }): BottomTabNavigationOptions => ({
|
|
||||||
tabBarIcon: ({
|
|
||||||
focused,
|
|
||||||
color,
|
|
||||||
size
|
|
||||||
}: {
|
|
||||||
focused: boolean
|
|
||||||
color: string
|
|
||||||
size: number
|
|
||||||
}) => {
|
|
||||||
switch (route.name) {
|
|
||||||
case 'Screen-Local':
|
|
||||||
return <Icon name='Home' size={size} color={color} />
|
|
||||||
case 'Screen-Public':
|
|
||||||
return <Icon name='Globe' size={size} color={color} />
|
|
||||||
case 'Screen-Post':
|
|
||||||
return <Icon name='Plus' size={size} color={color} />
|
|
||||||
case 'Screen-Notifications':
|
|
||||||
return <Icon name='Bell' size={size} color={color} />
|
|
||||||
case 'Screen-Me':
|
|
||||||
return localActiveIndex !== null ? (
|
|
||||||
<Image
|
|
||||||
source={{ uri: localAccount?.avatarStatic }}
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
borderRadius: size,
|
|
||||||
borderWidth: focused ? 2 : 0,
|
|
||||||
borderColor: focused ? theme.secondary : color
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Icon
|
|
||||||
name={focused ? 'Meh' : 'Smile'}
|
|
||||||
size={size}
|
|
||||||
color={!focused ? theme.secondary : color}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return <Icon name='AlertOctagon' size={size} color={color} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
tabBarVisible:
|
|
||||||
getFocusedRouteNameFromRoute(route) !== 'Screen-Shared-Compose' &&
|
|
||||||
getFocusedRouteNameFromRoute(route) !==
|
|
||||||
'Screen-Shared-Announcements' &&
|
|
||||||
getFocusedRouteNameFromRoute(route) !==
|
|
||||||
'Screen-Shared-ImagesViewer' &&
|
|
||||||
getFocusedRouteNameFromRoute(route) !== 'Screen-Me-Switch'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
[localActiveIndex, localAccount]
|
|
||||||
)
|
|
||||||
const tabNavigatorTabBarOptions = useMemo(
|
|
||||||
() => ({
|
|
||||||
activeTintColor: theme.primary,
|
|
||||||
inactiveTintColor:
|
|
||||||
localActiveIndex !== null ? theme.secondary : theme.disabled,
|
|
||||||
showLabel: false,
|
|
||||||
...(Platform.OS === 'android' && { keyboardHidesTabBar: true })
|
|
||||||
}),
|
|
||||||
[theme, localActiveIndex]
|
|
||||||
)
|
|
||||||
const tabScreenLocalListeners = useCallback(
|
|
||||||
() => ({
|
|
||||||
tabPress: (e: any) => {
|
|
||||||
if (!(localActiveIndex !== null)) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[localActiveIndex]
|
|
||||||
)
|
|
||||||
const tabScreenComposeListeners = useMemo(
|
|
||||||
() => ({
|
|
||||||
tabPress: (e: any) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (localActiveIndex !== null) {
|
|
||||||
haptics('Medium')
|
|
||||||
navigationRef.current?.navigate('Screen-Shared-Compose')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[localActiveIndex]
|
|
||||||
)
|
|
||||||
const tabScreenComposeComponent = useCallback(() => null, [])
|
|
||||||
const tabScreenNotificationsListeners = useCallback(
|
|
||||||
() => ({
|
|
||||||
tabPress: (e: any) => {
|
|
||||||
if (!(localActiveIndex !== null)) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[localActiveIndex]
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<StatusBar barStyle={barStyle[mode]} backgroundColor={theme.background} />
|
|
||||||
<NavigationContainer
|
|
||||||
ref={navigationRef}
|
|
||||||
theme={themes[mode]}
|
|
||||||
onReady={navigationContainerOnReady}
|
|
||||||
onStateChange={navigationContainerOnStateChange}
|
|
||||||
>
|
|
||||||
<Tab.Navigator
|
|
||||||
initialRouteName={
|
|
||||||
localActiveIndex !== null ? 'Screen-Local' : 'Screen-Me'
|
|
||||||
}
|
|
||||||
screenOptions={tabNavigatorScreenOptions}
|
|
||||||
tabBarOptions={tabNavigatorTabBarOptions}
|
|
||||||
>
|
|
||||||
<Tab.Screen
|
|
||||||
name='Screen-Local'
|
|
||||||
component={ScreenLocal}
|
|
||||||
listeners={tabScreenLocalListeners}
|
|
||||||
/>
|
|
||||||
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
|
|
||||||
<Tab.Screen
|
|
||||||
name='Screen-Post'
|
|
||||||
component={tabScreenComposeComponent}
|
|
||||||
listeners={tabScreenComposeListeners}
|
|
||||||
/>
|
|
||||||
<Tab.Screen
|
|
||||||
name='Screen-Notifications'
|
|
||||||
component={ScreenNotifications}
|
|
||||||
listeners={tabScreenNotificationsListeners}
|
|
||||||
options={
|
|
||||||
prevNotification && prevNotification.unread
|
|
||||||
? {
|
|
||||||
tabBarBadge: '',
|
|
||||||
tabBarBadgeStyle: {
|
|
||||||
transform: [{ scale: 0.5 }],
|
|
||||||
backgroundColor: theme.red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
tabBarBadgeStyle: {
|
|
||||||
transform: [{ scale: 0.5 }],
|
|
||||||
backgroundColor: theme.red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Tab.Screen name='Screen-Me' component={ScreenMe} />
|
|
||||||
</Tab.Navigator>
|
|
||||||
|
|
||||||
{Platform.OS === 'ios' ? (
|
|
||||||
<Toast ref={Toast.setRef} config={toastConfig} />
|
|
||||||
) : null}
|
|
||||||
</NavigationContainer>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default React.memo(Index, () => true)
|
|
216
src/Screens.tsx
Normal file
216
src/Screens.tsx
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
import client from '@api/client'
|
||||||
|
import { HeaderLeft } from '@components/Header'
|
||||||
|
import { toast, toastConfig } from '@components/toast'
|
||||||
|
import {
|
||||||
|
NavigationContainer,
|
||||||
|
NavigationContainerRef
|
||||||
|
} from '@react-navigation/native'
|
||||||
|
import ScreenActions from '@screens/Actions'
|
||||||
|
import ScreenAnnouncements from '@screens/Announcements'
|
||||||
|
import ScreenCompose from '@screens/Compose'
|
||||||
|
import ScreenImagesViewer from '@screens/ImagesViewer'
|
||||||
|
import ScreenTabs from '@screens/Tabs'
|
||||||
|
import {
|
||||||
|
getLocalActiveIndex,
|
||||||
|
localUpdateAccountPreferences
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import { themes } from '@utils/styles/themes'
|
||||||
|
import * as Analytics from 'expo-firebase-analytics'
|
||||||
|
import React, { createRef, useCallback, useEffect, useRef } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Platform, StatusBar } from 'react-native'
|
||||||
|
import Toast from 'react-native-toast-message'
|
||||||
|
import { createSharedElementStackNavigator } from 'react-navigation-shared-element'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
const Stack = createSharedElementStackNavigator<Nav.RootStackParamList>()
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
localCorrupt?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const navigationRef = createRef<NavigationContainerRef>()
|
||||||
|
|
||||||
|
const Index: React.FC<Props> = ({ localCorrupt }) => {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||||
|
const { mode, theme } = useTheme()
|
||||||
|
enum barStyle {
|
||||||
|
light = 'dark-content',
|
||||||
|
dark = 'light-content'
|
||||||
|
}
|
||||||
|
|
||||||
|
const routeNameRef = useRef<string | undefined>()
|
||||||
|
|
||||||
|
// const isConnected = useNetInfo().isConnected
|
||||||
|
// const [firstRender, setFirstRender] = useState(false)
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (firstRender) {
|
||||||
|
// // bug in netInfo on first render as false
|
||||||
|
// if (isConnected !== false) {
|
||||||
|
// toast({ type: 'error', content: '手机🈚️网络', autoHide: false })
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// setFirstRender(true)
|
||||||
|
// }
|
||||||
|
// }, [isConnected, firstRender])
|
||||||
|
|
||||||
|
// On launch display login credentials corrupt information
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
useEffect(() => {
|
||||||
|
const showLocalCorrect = localCorrupt
|
||||||
|
? toast({
|
||||||
|
type: 'error',
|
||||||
|
message: t('index.localCorrupt'),
|
||||||
|
description: localCorrupt.length ? localCorrupt : undefined,
|
||||||
|
autoHide: false
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
return showLocalCorrect
|
||||||
|
}, [localCorrupt])
|
||||||
|
|
||||||
|
// On launch check if there is any unread announcements
|
||||||
|
useEffect(() => {
|
||||||
|
localActiveIndex !== null &&
|
||||||
|
client<Mastodon.Announcement[]>({
|
||||||
|
method: 'get',
|
||||||
|
instance: 'local',
|
||||||
|
url: `announcements`
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res?.filter(announcement => !announcement.read).length) {
|
||||||
|
navigationRef.current?.navigate('Screen-Announcements', {
|
||||||
|
showAll: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Lazily update users's preferences, for e.g. composing default visibility
|
||||||
|
useEffect(() => {
|
||||||
|
if (localActiveIndex !== null) {
|
||||||
|
dispatch(localUpdateAccountPreferences())
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
const navigationContainerOnReady = useCallback(
|
||||||
|
() =>
|
||||||
|
(routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const navigationContainerOnStateChange = useCallback(() => {
|
||||||
|
const previousRouteName = routeNameRef.current
|
||||||
|
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name
|
||||||
|
|
||||||
|
if (previousRouteName !== currentRouteName) {
|
||||||
|
Analytics.setCurrentScreen(currentRouteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
routeNameRef.current = currentRouteName
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar barStyle={barStyle[mode]} backgroundColor={theme.background} />
|
||||||
|
<NavigationContainer
|
||||||
|
ref={navigationRef}
|
||||||
|
theme={themes[mode]}
|
||||||
|
onReady={navigationContainerOnReady}
|
||||||
|
onStateChange={navigationContainerOnStateChange}
|
||||||
|
>
|
||||||
|
<Stack.Navigator mode='modal' initialRouteName='Screen-Tabs'>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-Tabs'
|
||||||
|
component={ScreenTabs}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-Actions'
|
||||||
|
component={ScreenActions}
|
||||||
|
options={{
|
||||||
|
headerShown: false,
|
||||||
|
cardStyle: { backgroundColor: 'transparent' },
|
||||||
|
cardStyleInterpolator: ({ current: { progress } }) => ({
|
||||||
|
cardStyle: {
|
||||||
|
opacity: progress.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [0, 1]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-Announcements'
|
||||||
|
component={ScreenAnnouncements}
|
||||||
|
options={{
|
||||||
|
gestureEnabled: false,
|
||||||
|
title: t('sharedAnnouncements:heading'),
|
||||||
|
headerTransparent: true,
|
||||||
|
headerLeft: () => (
|
||||||
|
<HeaderLeft
|
||||||
|
content='X'
|
||||||
|
native={false}
|
||||||
|
onPress={() => navigationRef.current?.goBack()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
animationTypeForReplace: 'pop',
|
||||||
|
cardStyle: { backgroundColor: 'transparent' },
|
||||||
|
cardStyleInterpolator: ({ current: { progress } }) => ({
|
||||||
|
cardStyle: {
|
||||||
|
opacity: progress.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [0, 1]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-Compose'
|
||||||
|
component={ScreenCompose}
|
||||||
|
options={{ gestureEnabled: false, headerShown: false }}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-ImagesViewer'
|
||||||
|
component={ScreenImagesViewer}
|
||||||
|
options={{
|
||||||
|
gestureEnabled: false,
|
||||||
|
headerTransparent: true,
|
||||||
|
headerLeft: () => (
|
||||||
|
<HeaderLeft
|
||||||
|
content='X'
|
||||||
|
native={false}
|
||||||
|
onPress={() => navigationRef.current?.goBack()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cardStyle: { backgroundColor: 'transparent' },
|
||||||
|
cardStyleInterpolator: ({ current: { progress } }) => ({
|
||||||
|
cardStyle: {
|
||||||
|
opacity: progress.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [0, 1]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
sharedElements={route => {
|
||||||
|
const { imageIndex, imageUrls } = route.params
|
||||||
|
return [{ id: `image.${imageUrls[imageIndex].url}`, debug: true }]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack.Navigator>
|
||||||
|
|
||||||
|
{Platform.OS === 'ios' ? (
|
||||||
|
<Toast ref={Toast.setRef} config={toastConfig} />
|
||||||
|
) : null}
|
||||||
|
</NavigationContainer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(Index, () => true)
|
@ -21,12 +21,12 @@ const ComponentAccount: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('search_account_press', { page: origin })
|
analytics('search_account_press', { page: origin })
|
||||||
navigation.push('Screen-Shared-Account', { account })
|
navigation.push('Tab-Shared-Account', { account })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Dimensions, Modal, StyleSheet, View } from 'react-native'
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import Button from '@components/Button'
|
|
||||||
import {
|
|
||||||
PanGestureHandler,
|
|
||||||
State,
|
|
||||||
TapGestureHandler
|
|
||||||
} from 'react-native-gesture-handler'
|
|
||||||
import Animated, {
|
|
||||||
Extrapolate,
|
|
||||||
interpolate,
|
|
||||||
runOnJS,
|
|
||||||
useAnimatedGestureHandler,
|
|
||||||
useAnimatedStyle,
|
|
||||||
useSharedValue,
|
|
||||||
withTiming
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import analytics from './analytics'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
children: React.ReactNode
|
|
||||||
visible: boolean
|
|
||||||
handleDismiss: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const BottomSheet: React.FC<Props> = ({ children, visible, handleDismiss }) => {
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const insets = useSafeAreaInsets()
|
|
||||||
|
|
||||||
const screenHeight = Dimensions.get('screen').height
|
|
||||||
const panY = useSharedValue(0)
|
|
||||||
const styleTop = useAnimatedStyle(() => {
|
|
||||||
return {
|
|
||||||
top: interpolate(
|
|
||||||
panY.value,
|
|
||||||
[0, screenHeight],
|
|
||||||
[0, screenHeight],
|
|
||||||
Extrapolate.CLAMP
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const callDismiss = () => {
|
|
||||||
analytics('bottomsheet_swipe_close')
|
|
||||||
handleDismiss()
|
|
||||||
}
|
|
||||||
const onGestureEvent = useAnimatedGestureHandler({
|
|
||||||
onActive: ({ translationY }) => {
|
|
||||||
panY.value = translationY
|
|
||||||
},
|
|
||||||
onEnd: ({ velocityY }) => {
|
|
||||||
if (velocityY > 500) {
|
|
||||||
runOnJS(callDismiss)()
|
|
||||||
} else {
|
|
||||||
panY.value = withTiming(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal animated animationType='fade' visible={visible} transparent>
|
|
||||||
<TapGestureHandler
|
|
||||||
onHandlerStateChange={({ nativeEvent }) => {
|
|
||||||
if (nativeEvent.state === State.ACTIVE) {
|
|
||||||
callDismiss()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Animated.View
|
|
||||||
style={[styles.overlay, { backgroundColor: theme.backgroundOverlay }]}
|
|
||||||
>
|
|
||||||
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.container,
|
|
||||||
styleTop,
|
|
||||||
{
|
|
||||||
backgroundColor: theme.background,
|
|
||||||
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.handle,
|
|
||||||
{ backgroundColor: theme.primaryOverlay }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
content='取消'
|
|
||||||
onPress={() => {
|
|
||||||
analytics('bottomsheet_cancel')
|
|
||||||
handleDismiss()
|
|
||||||
}}
|
|
||||||
style={styles.button}
|
|
||||||
/>
|
|
||||||
</Animated.View>
|
|
||||||
</PanGestureHandler>
|
|
||||||
</Animated.View>
|
|
||||||
</TapGestureHandler>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
overlay: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'flex-end'
|
|
||||||
},
|
|
||||||
container: {
|
|
||||||
paddingTop: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
handle: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
width: StyleConstants.Spacing.S * 8,
|
|
||||||
height: StyleConstants.Spacing.S / 2,
|
|
||||||
borderRadius: 100,
|
|
||||||
top: -StyleConstants.Spacing.M * 2
|
|
||||||
},
|
|
||||||
button: {
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default BottomSheet
|
|
@ -9,9 +9,11 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
|
import { SharedElement } from 'react-navigation-shared-element'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
sharedElement?: string
|
||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
uri: { preview?: string; original: string; remote?: string }
|
uri: { preview?: string; original: string; remote?: string }
|
||||||
blurhash?: string
|
blurhash?: string
|
||||||
@ -22,6 +24,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GracefullyImage: React.FC<Props> = ({
|
const GracefullyImage: React.FC<Props> = ({
|
||||||
|
sharedElement,
|
||||||
hidden = false,
|
hidden = false,
|
||||||
uri,
|
uri,
|
||||||
blurhash,
|
blurhash,
|
||||||
@ -36,11 +39,21 @@ const GracefullyImage: React.FC<Props> = ({
|
|||||||
const children = useCallback(() => {
|
const children = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{sharedElement ? (
|
||||||
|
<SharedElement id={`image.${sharedElement}`} style={[styles.image]}>
|
||||||
<FastImage
|
<FastImage
|
||||||
source={{ uri: uri.preview || uri.original || uri.remote }}
|
source={{ uri: uri.preview || uri.original || uri.remote }}
|
||||||
style={[styles.image, imageStyle]}
|
style={[styles.image, imageStyle]}
|
||||||
onLoad={() => setImageLoaded(true)}
|
onLoad={() => setImageLoaded(true)}
|
||||||
/>
|
/>
|
||||||
|
</SharedElement>
|
||||||
|
) : (
|
||||||
|
<FastImage
|
||||||
|
source={{ uri: uri.preview || uri.original || uri.remote }}
|
||||||
|
style={[styles.image, imageStyle]}
|
||||||
|
onLoad={() => setImageLoaded(true)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{blurhash && (hidden || !imageLoaded) ? (
|
{blurhash && (hidden || !imageLoaded) ? (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
decodeAsync
|
decodeAsync
|
||||||
|
@ -19,12 +19,12 @@ const ComponentHashtag: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('search_account_press', { page: origin })
|
analytics('search_account_press', { page: origin })
|
||||||
navigation.push('Screen-Shared-Hashtag', { hashtag: hashtag.name })
|
navigation.push('Tab-Shared-Hashtag', { hashtag: hashtag.name })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -7,11 +7,17 @@ import { Pressable, StyleSheet, Text } from 'react-native'
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
type?: 'icon' | 'text'
|
type?: 'icon' | 'text'
|
||||||
content?: string
|
content?: string
|
||||||
|
native?: boolean
|
||||||
|
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderLeft: React.FC<Props> = ({ type = 'icon', content, onPress }) => {
|
const HeaderLeft: React.FC<Props> = ({
|
||||||
|
type = 'icon',
|
||||||
|
content,
|
||||||
|
native = true,
|
||||||
|
onPress
|
||||||
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = useMemo(() => {
|
||||||
@ -42,7 +48,11 @@ const HeaderLeft: React.FC<Props> = ({ type = 'icon', content, onPress }) => {
|
|||||||
styles.base,
|
styles.base,
|
||||||
{
|
{
|
||||||
backgroundColor: theme.backgroundGradientStart,
|
backgroundColor: theme.backgroundGradientStart,
|
||||||
...(type === 'icon' && { height: 44, width: 44, marginLeft: -9 })
|
...(type === 'icon' && {
|
||||||
|
height: 44,
|
||||||
|
width: 44,
|
||||||
|
marginLeft: native ? -9 : 9
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -9,6 +9,7 @@ import { Chase } from 'react-native-animated-spinkit'
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
type?: 'icon' | 'text'
|
type?: 'icon' | 'text'
|
||||||
content: string
|
content: string
|
||||||
|
native?: boolean
|
||||||
|
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
@ -19,6 +20,7 @@ export interface Props {
|
|||||||
const HeaderRight: React.FC<Props> = ({
|
const HeaderRight: React.FC<Props> = ({
|
||||||
type = 'icon',
|
type = 'icon',
|
||||||
content,
|
content,
|
||||||
|
native = true,
|
||||||
loading,
|
loading,
|
||||||
disabled,
|
disabled,
|
||||||
onPress
|
onPress
|
||||||
@ -88,7 +90,11 @@ const HeaderRight: React.FC<Props> = ({
|
|||||||
styles.base,
|
styles.base,
|
||||||
{
|
{
|
||||||
backgroundColor: theme.backgroundGradientStart,
|
backgroundColor: theme.backgroundGradientStart,
|
||||||
...(type === 'icon' && { height: 44, width: 44, marginRight: -9 })
|
...(type === 'icon' && {
|
||||||
|
height: 44,
|
||||||
|
width: 44,
|
||||||
|
marginRight: native ? -9 : 9
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -230,12 +230,6 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
content={instanceQuery.data?.title || undefined}
|
content={instanceQuery.data?.title || undefined}
|
||||||
potentialWidth={2}
|
potentialWidth={2}
|
||||||
/>
|
/>
|
||||||
<InstanceInfo
|
|
||||||
visible={instanceQuery.data?.short_description !== undefined}
|
|
||||||
header={t('server.information.description.heading')}
|
|
||||||
content={instanceQuery.data?.short_description || undefined}
|
|
||||||
potentialLines={5}
|
|
||||||
/>
|
|
||||||
<View style={styles.instanceStats}>
|
<View style={styles.instanceStats}>
|
||||||
<InstanceInfo
|
<InstanceInfo
|
||||||
style={styles.stat1}
|
style={styles.stat1}
|
||||||
|
@ -58,7 +58,7 @@ const renderNode = ({
|
|||||||
analytics('status_hashtag_press')
|
analytics('status_hashtag_press')
|
||||||
!disableDetails &&
|
!disableDetails &&
|
||||||
differentTag &&
|
differentTag &&
|
||||||
navigation.push('Screen-Shared-Hashtag', {
|
navigation.push('Tab-Shared-Hashtag', {
|
||||||
hashtag: tag[1] || tag[2]
|
hashtag: tag[1] || tag[2]
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
@ -86,7 +86,7 @@ const renderNode = ({
|
|||||||
accountIndex !== -1 &&
|
accountIndex !== -1 &&
|
||||||
!disableDetails &&
|
!disableDetails &&
|
||||||
differentAccount &&
|
differentAccount &&
|
||||||
navigation.push('Screen-Shared-Account', {
|
navigation.push('Tab-Shared-Account', {
|
||||||
account: mentions[accountIndex]
|
account: mentions[accountIndex]
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
@ -115,7 +115,7 @@ const renderNode = ({
|
|||||||
analytics('status_link_press')
|
analytics('status_link_press')
|
||||||
!disableDetails && !shouldBeTag
|
!disableDetails && !shouldBeTag
|
||||||
? await openLink(href)
|
? await openLink(href)
|
||||||
: navigation.push('Screen-Shared-Hashtag', {
|
: navigation.push('Tab-Shared-Hashtag', {
|
||||||
hashtag: content.substring(1)
|
hashtag: content.substring(1)
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -2,7 +2,7 @@ import { HeaderRight } from '@components/Header'
|
|||||||
import Timeline from '@components/Timelines/Timeline'
|
import Timeline from '@components/Timelines/Timeline'
|
||||||
import SegmentedControl from '@react-native-community/segmented-control'
|
import SegmentedControl from '@react-native-community/segmented-control'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import sharedScreens from '@screens/Shared/sharedScreens'
|
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getLocalActiveIndex } 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'
|
||||||
@ -14,7 +14,7 @@ import ViewPagerAdapter from 'react-native-tab-view-viewpager-adapter'
|
|||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import analytics from './analytics'
|
import analytics from './analytics'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Nav.RemoteStackParamList>()
|
const Stack = createNativeStackNavigator<Nav.TabPublicStackParamList>()
|
||||||
|
|
||||||
const Timelines: React.FC = () => {
|
const Timelines: React.FC = () => {
|
||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
@ -28,7 +28,7 @@ const Timelines: React.FC = () => {
|
|||||||
|
|
||||||
const onPressSearch = useCallback(() => {
|
const onPressSearch = useCallback(() => {
|
||||||
analytics('search_tap', { page: pages[segment].page })
|
analytics('search_tap', { page: pages[segment].page })
|
||||||
navigation.navigate('Screen-Public', { screen: 'Screen-Shared-Search' })
|
navigation.navigate('Tab-Public', { screen: 'Tab-Shared-Search' })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const routes = pages.map(p => ({ key: p.page }))
|
const routes = pages.map(p => ({ key: p.page }))
|
||||||
|
@ -18,7 +18,6 @@ import TimelineConversation from './Timeline/Conversation'
|
|||||||
import TimelineDefault from './Timeline/Default'
|
import TimelineDefault from './Timeline/Default'
|
||||||
import TimelineEmpty from './Timeline/Empty'
|
import TimelineEmpty from './Timeline/Empty'
|
||||||
import TimelineEnd from './Timeline/End'
|
import TimelineEnd from './Timeline/End'
|
||||||
import TimelineHeader from './Timeline/Header'
|
|
||||||
import TimelineNotifications from './Timeline/Notifications'
|
import TimelineNotifications from './Timeline/Notifications'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -155,7 +154,6 @@ const Timeline: React.FC<Props> = ({
|
|||||||
() => !disableInfinity && !isFetchingNextPage && fetchNextPage(),
|
() => !disableInfinity && !isFetchingNextPage && fetchNextPage(),
|
||||||
[isFetchingNextPage]
|
[isFetchingNextPage]
|
||||||
)
|
)
|
||||||
const ListHeaderComponent = useCallback(() => <TimelineHeader />, [])
|
|
||||||
const ListFooterComponent = useCallback(
|
const ListFooterComponent = useCallback(
|
||||||
() => <TimelineEnd hasNextPage={!disableInfinity ? hasNextPage : false} />,
|
() => <TimelineEnd hasNextPage={!disableInfinity ? hasNextPage : false} />,
|
||||||
[hasNextPage]
|
[hasNextPage]
|
||||||
|
@ -76,13 +76,13 @@ const TimelineConversation: React.FC<Props> = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_conversation_press')
|
analytics('timeline_conversation_press')
|
||||||
if (conversation.last_status) {
|
if (conversation.last_status) {
|
||||||
conversation.unread && mutate()
|
conversation.unread && mutate()
|
||||||
navigation.push('Screen-Shared-Toot', {
|
navigation.push('Tab-Shared-Toot', {
|
||||||
toot: conversation.last_status
|
toot: conversation.last_status
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const localAccount = useSelector(getLocalAccount)
|
const localAccount = useSelector(getLocalAccount)
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
|
|
||||||
let actualStatus = item.reblog ? item.reblog : item
|
let actualStatus = item.reblog ? item.reblog : item
|
||||||
@ -47,7 +47,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
})
|
})
|
||||||
!disableOnPress &&
|
!disableOnPress &&
|
||||||
!highlighted &&
|
!highlighted &&
|
||||||
navigation.push('Screen-Shared-Toot', {
|
navigation.push('Tab-Shared-Toot', {
|
||||||
toot: actualStatus
|
toot: actualStatus
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
import analytics from '@components/analytics'
|
|
||||||
import { useNavigation } from '@react-navigation/native'
|
|
||||||
import Icon from '@root/components/Icon'
|
|
||||||
import { StyleConstants } from '@root/utils/styles/constants'
|
|
||||||
import { useTheme } from '@root/utils/styles/ThemeManager'
|
|
||||||
import { updatePublicRemoteNotice } from '@utils/slices/contextsSlice'
|
|
||||||
import React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
|
|
||||||
const TimelineHeader = React.memo(
|
|
||||||
() => {
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
const navigation = useNavigation()
|
|
||||||
const { theme } = useTheme()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={[styles.base, { borderColor: theme.border }]}>
|
|
||||||
<Text style={[styles.text, { color: theme.primary }]}>
|
|
||||||
{t('header.explanation')}
|
|
||||||
<Text
|
|
||||||
style={{ color: theme.blue }}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('timeline_remote_header_press')
|
|
||||||
dispatch(updatePublicRemoteNotice(1))
|
|
||||||
navigation.navigate('Screen-Me', {
|
|
||||||
screen: 'Screen-Me-Root',
|
|
||||||
params: { navigateAway: 'Screen-Me-Settings-UpdateRemote' }
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('header.button')}
|
|
||||||
<Icon
|
|
||||||
name='ArrowRight'
|
|
||||||
size={StyleConstants.Font.Size.S}
|
|
||||||
color={theme.blue}
|
|
||||||
/>
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
margin: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingHorizontal: StyleConstants.Spacing.M,
|
|
||||||
paddingVertical: StyleConstants.Spacing.S,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 6
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineHeader
|
|
@ -29,7 +29,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const localAccount = useSelector(getLocalAccount)
|
const localAccount = useSelector(getLocalAccount)
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
const actualAccount = notification.status
|
const actualAccount = notification.status
|
||||||
? notification.status.account
|
? notification.status.account
|
||||||
@ -38,7 +38,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_notification_press')
|
analytics('timeline_notification_press')
|
||||||
notification.status &&
|
notification.status &&
|
||||||
navigation.push('Screen-Shared-Toot', {
|
navigation.push('Tab-Shared-Toot', {
|
||||||
toot: notification.status
|
toot: notification.status
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -23,7 +23,7 @@ const TimelineActioned: React.FC<Props> = ({
|
|||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
const name = account.display_name || account.username
|
const name = account.display_name || account.username
|
||||||
const iconColor = theme.primary
|
const iconColor = theme.primary
|
||||||
@ -34,7 +34,7 @@ const TimelineActioned: React.FC<Props> = ({
|
|||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_shared_actioned_press', { action })
|
analytics('timeline_shared_actioned_press', { action })
|
||||||
navigation.push('Screen-Shared-Account', { account })
|
navigation.push('Tab-Shared-Account', { account })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = useMemo(() => {
|
||||||
|
@ -104,7 +104,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
page: queryKey[1].page,
|
page: queryKey[1].page,
|
||||||
count: status.replies_count
|
count: status.replies_count
|
||||||
})
|
})
|
||||||
navigation.navigate('Screen-Shared-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'reply',
|
type: 'reply',
|
||||||
incomingStatus: status,
|
incomingStatus: status,
|
||||||
accts,
|
accts,
|
||||||
|
@ -40,7 +40,7 @@ const TimelineAttachment: React.FC<Props> = ({ status }) => {
|
|||||||
})[] = []
|
})[] = []
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const navigateToImagesViewer = (imageIndex: number) =>
|
const navigateToImagesViewer = (imageIndex: number) =>
|
||||||
navigation.navigate('Screen-Shared-ImagesViewer', {
|
navigation.navigate('Screen-ImagesViewer', {
|
||||||
imageUrls,
|
imageUrls,
|
||||||
imageIndex
|
imageIndex
|
||||||
})
|
})
|
||||||
|
@ -33,6 +33,7 @@ const AttachmentImage: React.FC<Props> = ({
|
|||||||
original: image.url,
|
original: image.url,
|
||||||
remote: image.remote_url
|
remote: image.remote_url
|
||||||
}}
|
}}
|
||||||
|
sharedElement={image.url}
|
||||||
blurhash={image.blurhash}
|
blurhash={image.blurhash}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[
|
style={[
|
||||||
|
@ -13,12 +13,14 @@ export interface Props {
|
|||||||
|
|
||||||
const TimelineAvatar: React.FC<Props> = ({ queryKey, account }) => {
|
const TimelineAvatar: React.FC<Props> = ({ queryKey, account }) => {
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
StackNavigationProp<Nav.LocalStackParamList>
|
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||||
>()
|
>()
|
||||||
// Need to fix go back root
|
// Need to fix go back root
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_shared_avatar_press', { page: queryKey[1].page })
|
analytics('timeline_shared_avatar_press', {
|
||||||
queryKey && navigation.push('Screen-Shared-Account', { account })
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
queryKey && navigation.push('Tab-Shared-Account', { account })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import BottomSheet from '@components/BottomSheet'
|
import BottomSheet from '@screens/Tabs/Shared/node_modules/@screens/Actions/BottomSheet'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getLocalAccount, getLocalUrl } from '@utils/slices/instancesSlice'
|
import { getLocalAccount, getLocalUrl } from '@utils/slices/instancesSlice'
|
||||||
|
@ -106,7 +106,7 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||||||
id: status.id
|
id: status.id
|
||||||
})
|
})
|
||||||
if (res.id) {
|
if (res.id) {
|
||||||
navigation.navigate('Screen-Shared-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'edit',
|
type: 'edit',
|
||||||
incomingStatus: res,
|
incomingStatus: res,
|
||||||
queryKey
|
queryKey
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { Pressable, StyleSheet, 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'
|
||||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||||
import HeaderActions from './HeaderActions/Root'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import Icon from '@components/Icon'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
@ -15,6 +17,9 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
<View style={styles.accountAndMeta}>
|
<View style={styles.accountAndMeta}>
|
||||||
@ -28,11 +33,28 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{queryKey ? (
|
{queryKey ? (
|
||||||
<HeaderActions
|
<Pressable
|
||||||
queryKey={queryKey}
|
style={{
|
||||||
status={status}
|
flex: 1,
|
||||||
url={status.url || status.uri}
|
flexDirection: 'row',
|
||||||
type='status'
|
justifyContent: 'center',
|
||||||
|
paddingBottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
onPress={() =>
|
||||||
|
navigation.navigate('Screen-Actions', {
|
||||||
|
queryKey,
|
||||||
|
status,
|
||||||
|
url: status.url || status.uri,
|
||||||
|
type: 'status'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
children={
|
||||||
|
<Icon
|
||||||
|
name='MoreHorizontal'
|
||||||
|
color={theme.secondary}
|
||||||
|
size={StyleConstants.Font.Size.L}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
@ -8,8 +8,8 @@ import HeaderSharedCreated from './HeaderShared/Created'
|
|||||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||||
import RelationshipIncoming from '@root/components/Relationship/Incoming'
|
import RelationshipIncoming from '@root/components/Relationship/Incoming'
|
||||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||||
import HeaderActions from './HeaderActions/Root'
|
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
import ScreenActions from '@screens/Actions'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey: QueryKeyTimeline
|
queryKey: QueryKeyTimeline
|
||||||
@ -28,7 +28,7 @@ const TimelineHeaderNotification: React.FC<Props> = ({
|
|||||||
return <RelationshipIncoming id={notification.account.id} />
|
return <RelationshipIncoming id={notification.account.id} />
|
||||||
default:
|
default:
|
||||||
return notification.status ? (
|
return notification.status ? (
|
||||||
<HeaderActions queryKey={queryKey} status={notification.status} />
|
<ScreenActions queryKey={queryKey} status={notification.status} />
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
}, [notification.type])
|
}, [notification.type])
|
||||||
|
@ -18,6 +18,7 @@ export default {
|
|||||||
|
|
||||||
sharedAccount: require('./screens/sharedAccount').default,
|
sharedAccount: require('./screens/sharedAccount').default,
|
||||||
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
||||||
|
sharedAttachments: require('./screens/sharedAttachments').default,
|
||||||
sharedCompose: require('./screens/sharedCompose').default,
|
sharedCompose: require('./screens/sharedCompose').default,
|
||||||
sharedRelationships: require('./screens/sharedRelationships').default,
|
sharedRelationships: require('./screens/sharedRelationships').default,
|
||||||
sharedSearch: require('./screens/sharedSearch').default,
|
sharedSearch: require('./screens/sharedSearch').default,
|
||||||
|
3
src/i18n/en/screens/sharedAttachments.ts
Normal file
3
src/i18n/en/screens/sharedAttachments.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
heading: '<0 />\'s <1>media</1>'
|
||||||
|
}
|
@ -18,6 +18,7 @@ export default {
|
|||||||
|
|
||||||
sharedAccount: require('./screens/sharedAccount').default,
|
sharedAccount: require('./screens/sharedAccount').default,
|
||||||
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
||||||
|
sharedAttachments: require('./screens/sharedAttachments').default,
|
||||||
sharedCompose: require('./screens/sharedCompose').default,
|
sharedCompose: require('./screens/sharedCompose').default,
|
||||||
sharedRelationships: require('./screens/sharedRelationships').default,
|
sharedRelationships: require('./screens/sharedRelationships').default,
|
||||||
sharedSearch: require('./screens/sharedSearch').default,
|
sharedSearch: require('./screens/sharedSearch').default,
|
||||||
|
3
src/i18n/zh-Hans/screens/sharedAttachments.ts
Normal file
3
src/i18n/zh-Hans/screens/sharedAttachments.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
heading: '<0 /> <1>的媒体</1>'
|
||||||
|
}
|
175
src/screens/Actions.tsx
Normal file
175
src/screens/Actions.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
|
import { getLocalAccount, getLocalUrl } from '@utils/slices/instancesSlice'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React, { useCallback, useEffect } from 'react'
|
||||||
|
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||||
|
import {
|
||||||
|
PanGestureHandler,
|
||||||
|
State,
|
||||||
|
TapGestureHandler
|
||||||
|
} from 'react-native-gesture-handler'
|
||||||
|
import Animated, {
|
||||||
|
Extrapolate,
|
||||||
|
interpolate,
|
||||||
|
runOnJS,
|
||||||
|
useAnimatedGestureHandler,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming
|
||||||
|
} from 'react-native-reanimated'
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
import ActionsAccount from './Actions/Account'
|
||||||
|
import ActionsDomain from './Actions/Domain'
|
||||||
|
import ActionsShare from './Actions/Share'
|
||||||
|
import ActionsStatus from './Actions/Status'
|
||||||
|
|
||||||
|
export type ScreenAccountProp = StackScreenProps<
|
||||||
|
Nav.RootStackParamList,
|
||||||
|
'Screen-Actions'
|
||||||
|
>
|
||||||
|
|
||||||
|
const ScreenActions = React.memo(
|
||||||
|
({
|
||||||
|
route: {
|
||||||
|
params: { queryKey, status, url, type }
|
||||||
|
},
|
||||||
|
navigation
|
||||||
|
}: ScreenAccountProp) => {
|
||||||
|
const localAccount = useSelector(getLocalAccount)
|
||||||
|
const sameAccount = localAccount?.id === status.account.id
|
||||||
|
|
||||||
|
const localDomain = useSelector(getLocalUrl)
|
||||||
|
const statusDomain = status.uri
|
||||||
|
? status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||||
|
: ''
|
||||||
|
const sameDomain = localDomain === statusDomain
|
||||||
|
|
||||||
|
const { theme } = 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(() => {
|
||||||
|
panY.value = withTiming(DEFAULT_VALUE)
|
||||||
|
navigation.goBack()
|
||||||
|
}, [])
|
||||||
|
const onGestureEvent = useAnimatedGestureHandler({
|
||||||
|
onActive: ({ translationY }) => {
|
||||||
|
panY.value = translationY
|
||||||
|
},
|
||||||
|
onEnd: ({ velocityY }) => {
|
||||||
|
if (velocityY > 500) {
|
||||||
|
runOnJS(dismiss)()
|
||||||
|
} else {
|
||||||
|
panY.value = withTiming(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View style={{ flex: 1 }}>
|
||||||
|
<TapGestureHandler
|
||||||
|
onHandlerStateChange={({ nativeEvent }) => {
|
||||||
|
if (nativeEvent.state === State.ACTIVE) {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.overlay,
|
||||||
|
{ backgroundColor: theme.backgroundOverlay }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.container,
|
||||||
|
styleTop,
|
||||||
|
{
|
||||||
|
backgroundColor: theme.background,
|
||||||
|
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.handle,
|
||||||
|
{ backgroundColor: theme.primaryOverlay }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{!sameAccount && (
|
||||||
|
<ActionsAccount
|
||||||
|
queryKey={queryKey}
|
||||||
|
account={status.account}
|
||||||
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sameAccount && (
|
||||||
|
<ActionsStatus
|
||||||
|
navigation={navigation}
|
||||||
|
queryKey={queryKey}
|
||||||
|
status={status}
|
||||||
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!sameDomain && (
|
||||||
|
<ActionsDomain
|
||||||
|
queryKey={queryKey}
|
||||||
|
domain={statusDomain}
|
||||||
|
dismiss={dismiss}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{url && type ? (
|
||||||
|
<ActionsShare url={url} type={type} dismiss={dismiss} />
|
||||||
|
) : null}
|
||||||
|
</Animated.View>
|
||||||
|
</PanGestureHandler>
|
||||||
|
</Animated.View>
|
||||||
|
</TapGestureHandler>
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
() => true
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
paddingTop: StyleConstants.Spacing.M
|
||||||
|
},
|
||||||
|
handle: {
|
||||||
|
alignSelf: 'center',
|
||||||
|
width: StyleConstants.Spacing.S * 8,
|
||||||
|
height: StyleConstants.Spacing.S / 2,
|
||||||
|
borderRadius: 100,
|
||||||
|
top: -StyleConstants.Spacing.M * 2
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ScreenActions
|
126
src/screens/Actions/Account.tsx
Normal file
126
src/screens/Actions/Account.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import analytics from '@components/analytics'
|
||||||
|
import haptics from '@components/haptics'
|
||||||
|
import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu'
|
||||||
|
import { toast } from '@components/toast'
|
||||||
|
import {
|
||||||
|
MutationVarsTimelineUpdateAccountProperty,
|
||||||
|
QueryKeyTimeline,
|
||||||
|
useTimelineMutation
|
||||||
|
} from '@utils/queryHooks/timeline'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
queryKey?: QueryKeyTimeline
|
||||||
|
account: Mastodon.Account
|
||||||
|
dismiss: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsAccount: React.FC<Props> = ({ queryKey, account, dismiss }) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const mutateion = useTimelineMutation({
|
||||||
|
queryClient,
|
||||||
|
onSuccess: (_, params) => {
|
||||||
|
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||||
|
haptics('Success')
|
||||||
|
toast({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common:toastMessage.success.message', {
|
||||||
|
function: t(
|
||||||
|
`shared.header.actions.account.${theParams.payload.property}.function`,
|
||||||
|
{
|
||||||
|
acct: account.acct
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onError: (err: any, params) => {
|
||||||
|
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||||
|
haptics('Error')
|
||||||
|
toast({
|
||||||
|
type: 'error',
|
||||||
|
message: t('common:toastMessage.error.message', {
|
||||||
|
function: t(
|
||||||
|
`shared.header.actions.account.${theParams.payload.property}.function`
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
...(err.status &&
|
||||||
|
typeof err.status === 'number' &&
|
||||||
|
err.data &&
|
||||||
|
err.data.error &&
|
||||||
|
typeof err.data.error === 'string' && {
|
||||||
|
description: err.data.error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onSettled: () => {
|
||||||
|
queryKey && queryClient.invalidateQueries(queryKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuHeader heading={t('shared.header.actions.account.heading')} />
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_account_mute_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutateion.mutate({
|
||||||
|
type: 'updateAccountProperty',
|
||||||
|
queryKey,
|
||||||
|
id: account.id,
|
||||||
|
payload: { property: 'mute' }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='EyeOff'
|
||||||
|
title={t('shared.header.actions.account.mute.button', {
|
||||||
|
acct: account.acct
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_account_block_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutateion.mutate({
|
||||||
|
type: 'updateAccountProperty',
|
||||||
|
queryKey,
|
||||||
|
id: account.id,
|
||||||
|
payload: { property: 'block' }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='XCircle'
|
||||||
|
title={t('shared.header.actions.account.block.button', {
|
||||||
|
acct: account.acct
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_account_reports_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutateion.mutate({
|
||||||
|
type: 'updateAccountProperty',
|
||||||
|
queryKey,
|
||||||
|
id: account.id,
|
||||||
|
payload: { property: 'reports' }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='Flag'
|
||||||
|
title={t('shared.header.actions.account.reports.button', {
|
||||||
|
acct: account.acct
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</MenuContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsAccount
|
83
src/screens/Actions/Domain.tsx
Normal file
83
src/screens/Actions/Domain.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import analytics from '@components/analytics'
|
||||||
|
import MenuContainer from '@components/Menu/Container'
|
||||||
|
import MenuHeader from '@components/Menu/Header'
|
||||||
|
import MenuRow from '@components/Menu/Row'
|
||||||
|
import { toast } from '@components/toast'
|
||||||
|
import {
|
||||||
|
QueryKeyTimeline,
|
||||||
|
useTimelineMutation
|
||||||
|
} from '@utils/queryHooks/timeline'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Alert } from 'react-native'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
queryKey: QueryKeyTimeline
|
||||||
|
domain: string
|
||||||
|
dismiss: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsDomain: React.FC<Props> = ({ queryKey, domain, dismiss }) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const mutation = useTimelineMutation({
|
||||||
|
queryClient,
|
||||||
|
onSettled: () => {
|
||||||
|
toast({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common:toastMessage.success.message', {
|
||||||
|
function: t(`shared.header.actions.domain.block.function`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries(queryKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuHeader heading={t(`shared.header.actions.domain.heading`)} />
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_domain_block_press', {
|
||||||
|
page: queryKey[1].page
|
||||||
|
})
|
||||||
|
Alert.alert(
|
||||||
|
t('shared.header.actions.domain.alert.title', { domain }),
|
||||||
|
t('shared.header.actions.domain.alert.message'),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: t('shared.header.actions.domain.alert.buttons.cancel'),
|
||||||
|
style: 'cancel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t('shared.header.actions.domain.alert.buttons.confirm'),
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: () => {
|
||||||
|
analytics(
|
||||||
|
'timeline_shared_headeractions_domain_block_confirm',
|
||||||
|
{
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
}
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'domainBlock',
|
||||||
|
queryKey,
|
||||||
|
domain: domain
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
iconFront='CloudOff'
|
||||||
|
title={t(`shared.header.actions.domain.block.button`, {
|
||||||
|
domain
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</MenuContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsDomain
|
45
src/screens/Actions/Share.tsx
Normal file
45
src/screens/Actions/Share.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import analytics from '@components/analytics'
|
||||||
|
import MenuContainer from '@components/Menu/Container'
|
||||||
|
import MenuHeader from '@components/Menu/Header'
|
||||||
|
import MenuRow from '@components/Menu/Row'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Platform, Share } from 'react-native'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
type: 'status' | 'account'
|
||||||
|
url: string
|
||||||
|
dismiss: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsShare: React.FC<Props> = ({ type, url, dismiss }) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuHeader heading={t(`shared.header.actions.share.${type}.heading`)} />
|
||||||
|
<MenuRow
|
||||||
|
iconFront='Share2'
|
||||||
|
title={t(`shared.header.actions.share.${type}.button`)}
|
||||||
|
onPress={async () => {
|
||||||
|
analytics('timeline_shared_headeractions_share_press')
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'ios':
|
||||||
|
await Share.share({
|
||||||
|
url
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'android':
|
||||||
|
await Share.share({
|
||||||
|
message: url
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</MenuContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsShare
|
181
src/screens/Actions/Status.tsx
Normal file
181
src/screens/Actions/Status.tsx
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Alert } from 'react-native'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
|
import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu'
|
||||||
|
import { toast } from '@components/toast'
|
||||||
|
import {
|
||||||
|
MutationVarsTimelineUpdateStatusProperty,
|
||||||
|
QueryKeyTimeline,
|
||||||
|
useTimelineMutation
|
||||||
|
} from '@utils/queryHooks/timeline'
|
||||||
|
import analytics from '@components/analytics'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
navigation: StackNavigationProp<Nav.RootStackParamList, 'Screen-Actions'>
|
||||||
|
queryKey: QueryKeyTimeline
|
||||||
|
status: Mastodon.Status
|
||||||
|
dismiss: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsStatus: React.FC<Props> = ({
|
||||||
|
navigation,
|
||||||
|
queryKey,
|
||||||
|
status,
|
||||||
|
dismiss
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const mutation = useTimelineMutation({
|
||||||
|
queryClient,
|
||||||
|
onMutate: true,
|
||||||
|
onError: (err: any, params, oldData) => {
|
||||||
|
const theFunction = (params as MutationVarsTimelineUpdateStatusProperty)
|
||||||
|
.payload
|
||||||
|
? (params as MutationVarsTimelineUpdateStatusProperty).payload.property
|
||||||
|
: 'delete'
|
||||||
|
toast({
|
||||||
|
type: 'error',
|
||||||
|
message: t('common:toastMessage.error.message', {
|
||||||
|
function: t(`shared.header.actions.status.${theFunction}.function`)
|
||||||
|
}),
|
||||||
|
...(err.status &&
|
||||||
|
typeof err.status === 'number' &&
|
||||||
|
err.data &&
|
||||||
|
err.data.error &&
|
||||||
|
typeof err.data.error === 'string' && {
|
||||||
|
description: err.data.error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
queryClient.setQueryData(queryKey, oldData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuHeader heading={t('shared.header.actions.status.heading')} />
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_status_delete_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'deleteItem',
|
||||||
|
source: 'statuses',
|
||||||
|
queryKey,
|
||||||
|
id: status.id
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='Trash'
|
||||||
|
title={t('shared.header.actions.status.delete.button')}
|
||||||
|
/>
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_status_deleteedit_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
Alert.alert(
|
||||||
|
t('shared.header.actions.status.edit.alert.title'),
|
||||||
|
t('shared.header.actions.status.edit.alert.message'),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: t(
|
||||||
|
'shared.header.actions.status.edit.alert.buttons.cancel'
|
||||||
|
),
|
||||||
|
style: 'cancel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t(
|
||||||
|
'shared.header.actions.status.edit.alert.buttons.confirm'
|
||||||
|
),
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: async () => {
|
||||||
|
analytics(
|
||||||
|
'timeline_shared_headeractions_status_deleteedit_confirm',
|
||||||
|
{
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
}
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
const res = (await mutation.mutateAsync({
|
||||||
|
type: 'deleteItem',
|
||||||
|
source: 'statuses',
|
||||||
|
queryKey,
|
||||||
|
id: status.id
|
||||||
|
})) as Mastodon.Status
|
||||||
|
if (res.id) {
|
||||||
|
navigation.navigate('Screen-Compose', {
|
||||||
|
type: 'edit',
|
||||||
|
incomingStatus: res,
|
||||||
|
queryKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
iconFront='Edit'
|
||||||
|
title={t('shared.header.actions.status.edit.button')}
|
||||||
|
/>
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_status_mute_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'updateStatusProperty',
|
||||||
|
queryKey,
|
||||||
|
id: status.id,
|
||||||
|
payload: {
|
||||||
|
property: 'muted',
|
||||||
|
currentValue: status.muted,
|
||||||
|
propertyCount: undefined,
|
||||||
|
countValue: undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='VolumeX'
|
||||||
|
title={
|
||||||
|
status.muted
|
||||||
|
? t('shared.header.actions.status.mute.button.negative')
|
||||||
|
: t('shared.header.actions.status.mute.button.positive')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{/* Also note that reblogs cannot be pinned. */}
|
||||||
|
{(status.visibility === 'public' || status.visibility === 'unlisted') && (
|
||||||
|
<MenuRow
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_headeractions_status_pin_press', {
|
||||||
|
page: queryKey && queryKey[1].page
|
||||||
|
})
|
||||||
|
dismiss()
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'updateStatusProperty',
|
||||||
|
queryKey,
|
||||||
|
id: status.id,
|
||||||
|
payload: {
|
||||||
|
property: 'pinned',
|
||||||
|
currentValue: status.pinned,
|
||||||
|
propertyCount: undefined,
|
||||||
|
countValue: undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
iconFront='Anchor'
|
||||||
|
title={
|
||||||
|
status.pinned
|
||||||
|
? t('shared.header.actions.status.pin.button.negative')
|
||||||
|
: t('shared.header.actions.status.pin.button.positive')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MenuContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsStatus
|
@ -3,7 +3,8 @@ import Button from '@components/Button'
|
|||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import { ParseHTML } from '@components/Parse'
|
import { ParseHTML } from '@components/Parse'
|
||||||
import RelativeTime from '@components/RelativeTime'
|
import RelativeTime from '@components/RelativeTime'
|
||||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'
|
import { BlurView } from '@react-native-community/blur'
|
||||||
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
import {
|
import {
|
||||||
useAnnouncementMutation,
|
useAnnouncementMutation,
|
||||||
useAnnouncementQuery
|
useAnnouncementQuery
|
||||||
@ -23,16 +24,19 @@ import {
|
|||||||
import { Chase } from 'react-native-animated-spinkit'
|
import { Chase } from 'react-native-animated-spinkit'
|
||||||
import { FlatList, ScrollView } from 'react-native-gesture-handler'
|
import { FlatList, ScrollView } from 'react-native-gesture-handler'
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
import { SharedAnnouncementsProp } from './sharedScreens'
|
|
||||||
|
|
||||||
const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
export type ScreenAnnouncementsProp = StackScreenProps<
|
||||||
|
Nav.RootStackParamList,
|
||||||
|
'Screen-Announcements'
|
||||||
|
>
|
||||||
|
|
||||||
|
const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
||||||
route: {
|
route: {
|
||||||
params: { showAll = false }
|
params: { showAll = false }
|
||||||
},
|
},
|
||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
const bottomTabBarHeight = useBottomTabBarHeight()
|
|
||||||
const [index, setIndex] = useState(0)
|
const [index, setIndex] = useState(0)
|
||||||
const { t } = useTranslation('sharedAnnouncements')
|
const { t } = useTranslation('sharedAnnouncements')
|
||||||
|
|
||||||
@ -60,13 +64,7 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
({ item, index }: { item: Mastodon.Announcement; index: number }) => (
|
({ item, index }: { item: Mastodon.Announcement; index: number }) => (
|
||||||
<View
|
<View key={index} style={styles.announcementContainer}>
|
||||||
key={index}
|
|
||||||
style={[
|
|
||||||
styles.announcementContainer,
|
|
||||||
{ backgroundColor: theme.background }
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Pressable
|
<Pressable
|
||||||
style={styles.pressable}
|
style={styles.pressable}
|
||||||
onPress={() => navigation.goBack()}
|
onPress={() => navigation.goBack()}
|
||||||
@ -199,10 +197,13 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.base, { backgroundColor: theme.background }]}>
|
<BlurView
|
||||||
<View style={[styles.header, { height: bottomTabBarHeight }]}>
|
blurType={mode}
|
||||||
<Text style={[styles.headerText, { color: theme.primary }]}>公告</Text>
|
blurAmount={20}
|
||||||
</View>
|
style={styles.base}
|
||||||
|
reducedTransparencyFallbackColor={theme.background}
|
||||||
|
>
|
||||||
|
<SafeAreaView style={styles.base}>
|
||||||
<FlatList
|
<FlatList
|
||||||
horizontal
|
horizontal
|
||||||
data={query.data}
|
data={query.data}
|
||||||
@ -212,7 +213,7 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||||||
onMomentumScrollEnd={onMomentumScrollEnd}
|
onMomentumScrollEnd={onMomentumScrollEnd}
|
||||||
ListEmptyComponent={ListEmptyComponent}
|
ListEmptyComponent={ListEmptyComponent}
|
||||||
/>
|
/>
|
||||||
<View style={[styles.indicators, { height: bottomTabBarHeight }]}>
|
<View style={[styles.indicators]}>
|
||||||
{query.data && query.data.length > 1 ? (
|
{query.data && query.data.length > 1 ? (
|
||||||
<>
|
<>
|
||||||
{query.data.map((d, i) => (
|
{query.data.map((d, i) => (
|
||||||
@ -233,6 +234,7 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
</BlurView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,14 +243,6 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
invisibleTextInput: { ...StyleSheet.absoluteFillObject },
|
invisibleTextInput: { ...StyleSheet.absoluteFillObject },
|
||||||
header: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
headerText: {
|
|
||||||
...StyleConstants.FontStyle.L,
|
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold
|
|
||||||
},
|
|
||||||
announcementContainer: {
|
announcementContainer: {
|
||||||
width: Dimensions.get('screen').width,
|
width: Dimensions.get('screen').width,
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
@ -297,7 +291,8 @@ const styles = StyleSheet.create({
|
|||||||
indicators: {
|
indicators: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
|
minHeight: 49
|
||||||
},
|
},
|
||||||
indicator: {
|
indicator: {
|
||||||
width: StyleConstants.Spacing.S,
|
width: StyleConstants.Spacing.S,
|
||||||
@ -307,4 +302,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default ScreenSharedAnnouncements
|
export default ScreenAnnouncements
|
@ -1,9 +1,10 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
import haptics from '@root/components/haptics'
|
import haptics from '@root/components/haptics'
|
||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
import formatText from '@screens/Shared/Compose/formatText'
|
import formatText from '@screens/Compose/formatText'
|
||||||
import ComposeRoot from '@screens/Shared/Compose/Root'
|
import ComposeRoot from '@screens/Compose/Root'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
||||||
@ -30,11 +31,15 @@ import composeInitialState from './Compose/utils/initialState'
|
|||||||
import composeParseState from './Compose/utils/parseState'
|
import composeParseState from './Compose/utils/parseState'
|
||||||
import composePost from './Compose/utils/post'
|
import composePost from './Compose/utils/post'
|
||||||
import composeReducer from './Compose/utils/reducer'
|
import composeReducer from './Compose/utils/reducer'
|
||||||
import { SharedComposeProp } from './sharedScreens'
|
|
||||||
|
export type ScreenComposeProp = StackScreenProps<
|
||||||
|
Nav.RootStackParamList,
|
||||||
|
'Screen-Compose'
|
||||||
|
>
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
const Compose: React.FC<SharedComposeProp> = ({
|
const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
||||||
route: { params },
|
route: { params },
|
||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
@ -224,22 +229,22 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
|
style={styles.base}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
style={{ flex: 1 }}
|
|
||||||
>
|
>
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
style={{ flex: 1 }}
|
style={styles.base}
|
||||||
edges={hasKeyboard ? ['left', 'right'] : ['left', 'right', 'bottom']}
|
edges={hasKeyboard ? ['top'] : ['top', 'bottom']}
|
||||||
>
|
>
|
||||||
<ComposeContext.Provider value={{ composeState, composeDispatch }}>
|
<ComposeContext.Provider value={{ composeState, composeDispatch }}>
|
||||||
<Stack.Navigator screenOptions={{ headerTopInsetEnabled: false }}>
|
<Stack.Navigator screenOptions={{ headerTopInsetEnabled: false }}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Shared-Compose-Root'
|
name='Tab-Compose-Root'
|
||||||
component={ComposeRoot}
|
component={ComposeRoot}
|
||||||
options={{ headerLeft, headerCenter, headerRight }}
|
options={{ headerLeft, headerCenter, headerRight }}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Shared-Compose-EditAttachment'
|
name='Tab-Compose-EditAttachment'
|
||||||
component={ComposeEditAttachment}
|
component={ComposeEditAttachment}
|
||||||
options={{ stackPresentation: 'modal', headerShown: false }}
|
options={{ stackPresentation: 'modal', headerShown: false }}
|
||||||
/>
|
/>
|
||||||
@ -251,10 +256,11 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
base: { flex: 1 },
|
||||||
count: {
|
count: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
...StyleConstants.FontStyle.M
|
...StyleConstants.FontStyle.M
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default Compose
|
export default ScreenCompose
|
@ -168,7 +168,7 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
<SafeAreaView style={{ flex: 1 }} edges={['left', 'right', 'bottom']}>
|
<SafeAreaView style={{ flex: 1 }} edges={['left', 'right', 'bottom']}>
|
||||||
<Stack.Navigator screenOptions={{ headerTopInsetEnabled: false }}>
|
<Stack.Navigator screenOptions={{ headerTopInsetEnabled: false }}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Shared-Compose-EditAttachment-Root'
|
name='Screen-Compose-EditAttachment-Root'
|
||||||
children={children}
|
children={children}
|
||||||
options={{ headerLeft, headerRight, headerCenter: () => null }}
|
options={{ headerLeft, headerRight, headerCenter: () => null }}
|
||||||
/>
|
/>
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import ComposeAttachments from '@screens/Shared/Compose/Root/Footer/Attachments'
|
import ComposeAttachments from '@screens/Compose/Root/Footer/Attachments'
|
||||||
import ComposeEmojis from '@screens/Shared/Compose/Root/Footer/Emojis'
|
import ComposeEmojis from '@screens/Compose/Root/Footer/Emojis'
|
||||||
import ComposePoll from '@screens/Shared/Compose/Root/Footer/Poll'
|
import ComposePoll from '@screens/Compose/Root/Footer/Poll'
|
||||||
import ComposeReply from '@screens/Shared/Compose/Root/Footer/Reply'
|
import ComposeReply from '@screens/Compose/Root/Footer/Reply'
|
||||||
import ComposeContext from '@screens/Shared/Compose//utils/createContext'
|
import ComposeContext from '@screens/Compose/utils/createContext'
|
||||||
|
|
||||||
const ComposeRootFooter: React.FC = () => {
|
const ComposeRootFooter: React.FC = () => {
|
||||||
const { composeState } = useContext(ComposeContext)
|
const { composeState } = useContext(ComposeContext)
|
@ -177,7 +177,7 @@ const ComposeAttachments: React.FC = () => {
|
|||||||
overlay
|
overlay
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
analytics('compose_attachment_edit')
|
analytics('compose_attachment_edit')
|
||||||
navigation.navigate('Screen-Shared-Compose-EditAttachment', {
|
navigation.navigate('Screen-Compose-EditAttachment', {
|
||||||
index
|
index
|
||||||
})
|
})
|
||||||
}}
|
}}
|
@ -4,7 +4,7 @@ import composeInitialState from './initialState'
|
|||||||
import { ComposeState } from './types'
|
import { ComposeState } from './types'
|
||||||
|
|
||||||
const composeParseState = (
|
const composeParseState = (
|
||||||
params: NonNullable<Nav.SharedStackParamList['Screen-Shared-Compose']>
|
params: NonNullable<Nav.SharedStackParamList['Screen-Compose']>
|
||||||
): ComposeState => {
|
): ComposeState => {
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
case 'edit':
|
case 'edit':
|
@ -1,6 +1,6 @@
|
|||||||
import client from '@root/api/client'
|
import client from '@root/api/client'
|
||||||
import { ComposeState } from '@screens/Shared/Compose/utils/types'
|
import { ComposeState } from '@screens/Compose/utils/types'
|
||||||
import { SharedComposeProp } from '@screens/Shared/sharedScreens'
|
import { SharedComposeProp } from '@screens/Tabs/Shared/sharedScreens'
|
||||||
import * as Crypto from 'expo-crypto'
|
import * as Crypto from 'expo-crypto'
|
||||||
|
|
||||||
const composePost = async (
|
const composePost = async (
|
93
src/screens/ImagesViewer.tsx
Normal file
93
src/screens/ImagesViewer.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import analytics from '@components/analytics'
|
||||||
|
import { HeaderRight } from '@components/Header'
|
||||||
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import { findIndex } from 'lodash'
|
||||||
|
import React, { useCallback, useLayoutEffect, useState } from 'react'
|
||||||
|
import { Platform, Share, StyleSheet, Text } from 'react-native'
|
||||||
|
import FastImage from 'react-native-fast-image'
|
||||||
|
import ImageViewer from 'react-native-image-zoom-viewer'
|
||||||
|
import { SharedElement } from 'react-navigation-shared-element'
|
||||||
|
|
||||||
|
export type ScreenImagesViewerProp = StackScreenProps<
|
||||||
|
Nav.RootStackParamList,
|
||||||
|
'Screen-ImagesViewer'
|
||||||
|
>
|
||||||
|
|
||||||
|
const ScreenImagesViewer = React.memo(
|
||||||
|
({
|
||||||
|
route: {
|
||||||
|
params: { imageUrls, imageIndex }
|
||||||
|
},
|
||||||
|
navigation
|
||||||
|
}: ScreenImagesViewerProp) => {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(
|
||||||
|
findIndex(imageUrls, ['imageIndex', imageIndex])
|
||||||
|
)
|
||||||
|
|
||||||
|
const onPress = useCallback(() => {
|
||||||
|
analytics('imageviewer_share_press')
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'ios':
|
||||||
|
return Share.share({ url: imageUrls[currentIndex].url })
|
||||||
|
case 'android':
|
||||||
|
return Share.share({ message: imageUrls[currentIndex].url })
|
||||||
|
}
|
||||||
|
}, [currentIndex])
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() =>
|
||||||
|
navigation.setOptions({
|
||||||
|
headerTitle: () => (
|
||||||
|
<Text
|
||||||
|
style={[styles.headerCenter, { color: theme.primaryOverlay }]}
|
||||||
|
>
|
||||||
|
{currentIndex + 1} / {imageUrls.length}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
headerRight: () => (
|
||||||
|
<HeaderRight content='Share' native={false} onPress={onPress} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
[currentIndex]
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderImage = useCallback(
|
||||||
|
prop => (
|
||||||
|
<SharedElement id={`image.${imageUrls[imageIndex].url}`}>
|
||||||
|
<FastImage {...prop} resizeMode={'contain'} />
|
||||||
|
</SharedElement>
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageViewer
|
||||||
|
index={currentIndex}
|
||||||
|
imageUrls={imageUrls}
|
||||||
|
pageAnimateTime={250}
|
||||||
|
enableSwipeDown
|
||||||
|
useNativeDriver
|
||||||
|
swipeDownThreshold={100}
|
||||||
|
renderIndicator={() => <></>}
|
||||||
|
saveToLocalByLongPress={false}
|
||||||
|
onSwipeDown={() => navigation.goBack()}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
onChange={index => index !== undefined && setCurrentIndex(index)}
|
||||||
|
renderImage={renderImage}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
() => true
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
headerCenter: {
|
||||||
|
color: 'white',
|
||||||
|
...StyleConstants.FontStyle.M
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ScreenImagesViewer
|
@ -1,99 +0,0 @@
|
|||||||
import analytics from '@components/analytics'
|
|
||||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { findIndex } from 'lodash'
|
|
||||||
import React, { useCallback, useState } from 'react'
|
|
||||||
import {
|
|
||||||
Image,
|
|
||||||
Platform,
|
|
||||||
Share,
|
|
||||||
StatusBar,
|
|
||||||
StyleSheet,
|
|
||||||
Text
|
|
||||||
} from 'react-native'
|
|
||||||
import ImageViewer from 'react-native-image-zoom-viewer'
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
|
||||||
import { SharedImagesViewerProp } from './sharedScreens'
|
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
|
||||||
|
|
||||||
const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({
|
|
||||||
route: {
|
|
||||||
params: { imageUrls, imageIndex }
|
|
||||||
},
|
|
||||||
navigation
|
|
||||||
}) => {
|
|
||||||
const safeAreaInsets = useSafeAreaInsets()
|
|
||||||
|
|
||||||
const initialIndex = findIndex(imageUrls, ['imageIndex', imageIndex])
|
|
||||||
const [currentIndex, setCurrentIndex] = useState(initialIndex)
|
|
||||||
|
|
||||||
const component = useCallback(
|
|
||||||
() => (
|
|
||||||
<>
|
|
||||||
<StatusBar barStyle='light-content' />
|
|
||||||
<ImageViewer
|
|
||||||
index={initialIndex}
|
|
||||||
imageUrls={imageUrls}
|
|
||||||
pageAnimateTime={250}
|
|
||||||
enableSwipeDown
|
|
||||||
useNativeDriver
|
|
||||||
swipeDownThreshold={100}
|
|
||||||
renderIndicator={() => <></>}
|
|
||||||
saveToLocalByLongPress={false}
|
|
||||||
onSwipeDown={() => navigation.goBack()}
|
|
||||||
style={{ flex: 1, marginBottom: 44 + safeAreaInsets.bottom }}
|
|
||||||
onChange={index => index !== undefined && setCurrentIndex(index)}
|
|
||||||
renderImage={prop => {
|
|
||||||
return <Image {...prop} resizeMode={'contain'} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
|
||||||
analytics('imageviewer_share_press')
|
|
||||||
switch (Platform.OS) {
|
|
||||||
case 'ios':
|
|
||||||
return Share.share({ url: imageUrls[currentIndex].url })
|
|
||||||
case 'android':
|
|
||||||
return Share.share({ message: imageUrls[currentIndex].url })
|
|
||||||
}
|
|
||||||
}, [currentIndex])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack.Navigator
|
|
||||||
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
|
|
||||||
>
|
|
||||||
<Stack.Screen
|
|
||||||
name='Screen-Shared-ImagesViewer-Root'
|
|
||||||
component={component}
|
|
||||||
options={{
|
|
||||||
contentStyle: { backgroundColor: 'black' },
|
|
||||||
headerStyle: { backgroundColor: 'black' },
|
|
||||||
headerLeft: () => (
|
|
||||||
<HeaderLeft content='X' onPress={() => navigation.goBack()} />
|
|
||||||
),
|
|
||||||
headerCenter: () => (
|
|
||||||
<Text style={styles.headerCenter}>
|
|
||||||
{currentIndex + 1} / {imageUrls.length}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
headerRight: () => <HeaderRight content='Share' onPress={onPress} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack.Navigator>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerCenter: {
|
|
||||||
color: 'white',
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default React.memo(ScreenSharedImagesViewer, () => true)
|
|
@ -1,73 +0,0 @@
|
|||||||
import SegmentedControl from '@react-native-community/segmented-control'
|
|
||||||
import { useNavigation } from '@react-navigation/native'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
|
||||||
import { TabView } from 'react-native-tab-view'
|
|
||||||
import RelationshipsList from './Relationships/List'
|
|
||||||
import { SharedRelationshipsProp } from './sharedScreens'
|
|
||||||
|
|
||||||
const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
|
|
||||||
route: {
|
|
||||||
params: { account, initialType }
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation('sharedRelationships')
|
|
||||||
const { mode } = useTheme()
|
|
||||||
const navigation = useNavigation()
|
|
||||||
|
|
||||||
const [segment, setSegment] = useState(initialType === 'following' ? 0 : 1)
|
|
||||||
useEffect(() => {
|
|
||||||
const updateHeaderRight = () =>
|
|
||||||
navigation.setOptions({
|
|
||||||
headerCenter: () => (
|
|
||||||
<View style={styles.segmentsContainer}>
|
|
||||||
<SegmentedControl
|
|
||||||
appearance={mode}
|
|
||||||
values={[t('heading.segments.left'), t('heading.segments.right')]}
|
|
||||||
selectedIndex={segment}
|
|
||||||
onChange={({ nativeEvent }) =>
|
|
||||||
setSegment(nativeEvent.selectedSegmentIndex)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return updateHeaderRight()
|
|
||||||
}, [segment, mode])
|
|
||||||
|
|
||||||
const routes: {
|
|
||||||
key: SharedRelationshipsProp['route']['params']['initialType']
|
|
||||||
}[] = [{ key: 'following' }, { key: 'followers' }]
|
|
||||||
|
|
||||||
const renderScene = ({
|
|
||||||
route
|
|
||||||
}: {
|
|
||||||
route: {
|
|
||||||
key: SharedRelationshipsProp['route']['params']['initialType']
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
return <RelationshipsList id={account.id} type={route.key} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TabView
|
|
||||||
lazy
|
|
||||||
swipeEnabled
|
|
||||||
renderScene={renderScene}
|
|
||||||
renderTabBar={() => null}
|
|
||||||
onIndexChange={index => setSegment(index)}
|
|
||||||
navigationState={{ index: segment, routes }}
|
|
||||||
initialLayout={{ width: Dimensions.get('screen').width }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
segmentsContainer: {
|
|
||||||
flexBasis: '60%'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default React.memo(ScreenSharedRelationships, () => true)
|
|
213
src/screens/Tabs.tsx
Normal file
213
src/screens/Tabs.tsx
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import haptics from '@components/haptics'
|
||||||
|
import Icon from '@components/Icon'
|
||||||
|
import {
|
||||||
|
BottomTabNavigationOptions,
|
||||||
|
createBottomTabNavigator
|
||||||
|
} from '@react-navigation/bottom-tabs'
|
||||||
|
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||||
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
|
import { useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
|
import {
|
||||||
|
getLocalAccount,
|
||||||
|
getLocalActiveIndex,
|
||||||
|
getLocalNotification,
|
||||||
|
localUpdateNotification
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||||
|
import { Platform } from 'react-native'
|
||||||
|
import FastImage from 'react-native-fast-image'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
import TabLocal from './Tabs/Local'
|
||||||
|
import TabMe from './Tabs/Me'
|
||||||
|
import TabNotifications from './Tabs/Notifications'
|
||||||
|
import TabPublic from './Tabs/Public'
|
||||||
|
|
||||||
|
export type ScreenTabsParamList = {
|
||||||
|
'Tab-Local': NavigatorScreenParams<Nav.TabLocalStackParamList>
|
||||||
|
'Tab-Public': NavigatorScreenParams<Nav.TabPublicStackParamList>
|
||||||
|
'Tab-Compose': NavigatorScreenParams<Nav.TabComposeStackParamList>
|
||||||
|
'Tab-Notifications': NavigatorScreenParams<Nav.TabNotificationsStackParamList>
|
||||||
|
'Tab-Me': NavigatorScreenParams<Nav.TabMeStackParamList>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScreenTabsProp = StackScreenProps<
|
||||||
|
Nav.RootStackParamList,
|
||||||
|
'Screen-Tabs'
|
||||||
|
>
|
||||||
|
|
||||||
|
const Tab = createBottomTabNavigator<Nav.ScreenTabsStackParamList>()
|
||||||
|
|
||||||
|
const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||||
|
const localAccount = useSelector(getLocalAccount)
|
||||||
|
|
||||||
|
const screenOptions = useCallback(
|
||||||
|
({ route }): BottomTabNavigationOptions => ({
|
||||||
|
tabBarIcon: ({
|
||||||
|
focused,
|
||||||
|
color,
|
||||||
|
size
|
||||||
|
}: {
|
||||||
|
focused: boolean
|
||||||
|
color: string
|
||||||
|
size: number
|
||||||
|
}) => {
|
||||||
|
switch (route.name) {
|
||||||
|
case 'Tab-Local':
|
||||||
|
return <Icon name='Home' size={size} color={color} />
|
||||||
|
case 'Tab-Public':
|
||||||
|
return <Icon name='Globe' size={size} color={color} />
|
||||||
|
case 'Tab-Compose':
|
||||||
|
return <Icon name='Plus' size={size} color={color} />
|
||||||
|
case 'Tab-Notifications':
|
||||||
|
return <Icon name='Bell' size={size} color={color} />
|
||||||
|
case 'Tab-Me':
|
||||||
|
return localActiveIndex !== null ? (
|
||||||
|
<FastImage
|
||||||
|
source={{ uri: localAccount?.avatarStatic }}
|
||||||
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
borderRadius: size,
|
||||||
|
borderWidth: focused ? 2 : 0,
|
||||||
|
borderColor: focused ? theme.secondary : color
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
name={focused ? 'Meh' : 'Smile'}
|
||||||
|
size={size}
|
||||||
|
color={!focused ? theme.secondary : color}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return <Icon name='AlertOctagon' size={size} color={color} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[localActiveIndex, localAccount]
|
||||||
|
)
|
||||||
|
const tabNavigatorTabBarOptions = useMemo(
|
||||||
|
() => ({
|
||||||
|
activeTintColor: theme.primary,
|
||||||
|
inactiveTintColor:
|
||||||
|
localActiveIndex !== null ? theme.secondary : theme.disabled,
|
||||||
|
showLabel: false,
|
||||||
|
...(Platform.OS === 'android' && { keyboardHidesTabBar: true })
|
||||||
|
}),
|
||||||
|
[theme, localActiveIndex]
|
||||||
|
)
|
||||||
|
const tabScreenLocalListeners = useCallback(
|
||||||
|
() => ({
|
||||||
|
tabPress: (e: any) => {
|
||||||
|
if (!(localActiveIndex !== null)) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[localActiveIndex]
|
||||||
|
)
|
||||||
|
const tabScreenComposeListeners = useMemo(
|
||||||
|
() => ({
|
||||||
|
tabPress: (e: any) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (localActiveIndex !== null) {
|
||||||
|
haptics('Light')
|
||||||
|
navigation.navigate('Screen-Compose')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[localActiveIndex]
|
||||||
|
)
|
||||||
|
const tabScreenComposeComponent = useCallback(() => null, [])
|
||||||
|
const tabScreenNotificationsListeners = useCallback(
|
||||||
|
() => ({
|
||||||
|
tabPress: (e: any) => {
|
||||||
|
if (!(localActiveIndex !== null)) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[localActiveIndex]
|
||||||
|
)
|
||||||
|
|
||||||
|
// On launch check if there is any unread noficiations
|
||||||
|
const queryNotification = useTimelineQuery({
|
||||||
|
page: 'Notifications',
|
||||||
|
options: {
|
||||||
|
enabled: localActiveIndex !== null ? true : false,
|
||||||
|
refetchInterval: 1000 * 60,
|
||||||
|
refetchIntervalInBackground: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const prevNotification = useSelector(getLocalNotification)
|
||||||
|
useEffect(() => {
|
||||||
|
if (queryNotification.data?.pages) {
|
||||||
|
const flattenData = queryNotification.data.pages.flatMap(d => [...d])
|
||||||
|
const latestNotificationTime = flattenData.length
|
||||||
|
? (flattenData[0] as Mastodon.Notification).created_at
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
if (!prevNotification || !prevNotification.latestTime) {
|
||||||
|
dispatch(localUpdateNotification({ unread: false }))
|
||||||
|
} else if (
|
||||||
|
latestNotificationTime &&
|
||||||
|
new Date(prevNotification.latestTime) < new Date(latestNotificationTime)
|
||||||
|
) {
|
||||||
|
dispatch(
|
||||||
|
localUpdateNotification({
|
||||||
|
unread: true,
|
||||||
|
latestTime: latestNotificationTime
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [queryNotification.data?.pages])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tab.Navigator
|
||||||
|
initialRouteName={localActiveIndex !== null ? 'Tab-Local' : 'Tab-Me'}
|
||||||
|
screenOptions={screenOptions}
|
||||||
|
tabBarOptions={tabNavigatorTabBarOptions}
|
||||||
|
>
|
||||||
|
<Tab.Screen
|
||||||
|
name='Tab-Local'
|
||||||
|
component={TabLocal}
|
||||||
|
listeners={tabScreenLocalListeners}
|
||||||
|
/>
|
||||||
|
<Tab.Screen name='Tab-Public' component={TabPublic} />
|
||||||
|
<Tab.Screen
|
||||||
|
name='Tab-Compose'
|
||||||
|
component={tabScreenComposeComponent}
|
||||||
|
listeners={tabScreenComposeListeners}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name='Tab-Notifications'
|
||||||
|
component={TabNotifications}
|
||||||
|
listeners={tabScreenNotificationsListeners}
|
||||||
|
options={
|
||||||
|
prevNotification && prevNotification.unread
|
||||||
|
? {
|
||||||
|
tabBarBadge: '',
|
||||||
|
tabBarBadgeStyle: {
|
||||||
|
transform: [{ scale: 0.5 }],
|
||||||
|
backgroundColor: theme.red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
tabBarBadgeStyle: {
|
||||||
|
transform: [{ scale: 0.5 }],
|
||||||
|
backgroundColor: theme.red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Tab.Screen name='Tab-Me' component={TabMe} />
|
||||||
|
</Tab.Navigator>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScreenTabs
|
@ -1,7 +1,8 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { HeaderCenter, HeaderRight } from '@components/Header'
|
import { HeaderCenter, HeaderRight } from '@components/Header'
|
||||||
import Timeline from '@components/Timelines/Timeline'
|
import Timeline from '@components/Timelines/Timeline'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
|
||||||
|
import { ScreenTabsParamList } from '@screens/Tabs'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -10,26 +11,27 @@ import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
|||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import sharedScreens from './Shared/sharedScreens'
|
import sharedScreens from './Shared/sharedScreens'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Nav.LocalStackParamList>()
|
export type TabLocalProp = BottomTabScreenProps<
|
||||||
|
ScreenTabsParamList,
|
||||||
|
'Tab-Local'
|
||||||
|
>
|
||||||
|
|
||||||
const ScreenLocal = React.memo(
|
const Stack = createNativeStackNavigator<Nav.TabLocalStackParamList>()
|
||||||
() => {
|
|
||||||
|
const TabLocal = React.memo(
|
||||||
|
({ navigation }: TabLocalProp) => {
|
||||||
const { t } = useTranslation('local')
|
const { t } = useTranslation('local')
|
||||||
const navigation = useNavigation()
|
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||||
|
|
||||||
const onPressSearch = useCallback(() => {
|
const onPressSearch = useCallback(() => {
|
||||||
analytics('search_tap', { page: 'Local' })
|
analytics('search_tap', { page: 'Local' })
|
||||||
navigation.navigate('Screen-Local', { screen: 'Screen-Shared-Search' })
|
navigation.navigate('Tab-Local', { screen: 'Tab-Shared-Search' })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack.Navigator
|
<Stack.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
headerLeft: () => null,
|
headerLeft: () => null,
|
||||||
headerRight: () => (
|
|
||||||
<HeaderRight content='Search' onPress={onPressSearch} />
|
|
||||||
),
|
|
||||||
headerTitle: t('heading'),
|
headerTitle: t('heading'),
|
||||||
...(Platform.OS === 'android' && {
|
...(Platform.OS === 'android' && {
|
||||||
headerCenter: () => <HeaderCenter content={t('heading')} />
|
headerCenter: () => <HeaderCenter content={t('heading')} />
|
||||||
@ -38,7 +40,14 @@ const ScreenLocal = React.memo(
|
|||||||
headerTopInsetEnabled: false
|
headerTopInsetEnabled: false
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen name='Screen-Local-Root'>
|
<Stack.Screen
|
||||||
|
name='Tab-Local-Root'
|
||||||
|
options={{
|
||||||
|
headerRight: () => (
|
||||||
|
<HeaderRight content='Search' onPress={onPressSearch} />
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
{() =>
|
{() =>
|
||||||
localActiveIndex !== null ? <Timeline page='Following' /> : null
|
localActiveIndex !== null ? <Timeline page='Following' /> : null
|
||||||
}
|
}
|
||||||
@ -51,4 +60,4 @@ const ScreenLocal = React.memo(
|
|||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
export default ScreenLocal
|
export default TabLocal
|
@ -1,21 +1,21 @@
|
|||||||
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
||||||
import ScreenMeBookmarks from '@screens/Me/Bookmarks'
|
import ScreenMeBookmarks from '@screens/Tabs/Me/Bookmarks'
|
||||||
import ScreenMeConversations from '@screens/Me/Cconversations'
|
import ScreenMeConversations from '@screens/Tabs/Me/Cconversations'
|
||||||
import ScreenMeFavourites from '@screens/Me/Favourites'
|
import ScreenMeFavourites from '@screens/Tabs/Me/Favourites'
|
||||||
import ScreenMeLists from '@screens/Me/Lists'
|
import ScreenMeLists from '@screens/Tabs/Me/Lists'
|
||||||
import ScreenMeRoot from '@screens/Me/Root'
|
import ScreenMeRoot from '@screens/Tabs/Me/Root'
|
||||||
import ScreenMeListsList from '@screens/Me/Root/Lists/List'
|
import ScreenMeListsList from '@screens/Tabs/Me/Root/Lists/List'
|
||||||
import ScreenMeSettings from '@screens/Me/Settings'
|
import ScreenMeSettings from '@screens/Tabs/Me/Settings'
|
||||||
import ScreenMeSwitch from '@screens/Me/Switch'
|
import ScreenMeSwitch from '@screens/Tabs/Me/Switch'
|
||||||
import sharedScreens from '@screens/Shared/sharedScreens'
|
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
||||||
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 { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Nav.MeStackParamList>()
|
const Stack = createNativeStackNavigator<Nav.TabMeStackParamList>()
|
||||||
|
|
||||||
const ScreenMe: React.FC = () => {
|
const TabMe: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -23,7 +23,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
|
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
|
||||||
>
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Root'
|
name='Tab-Me-Root'
|
||||||
component={ScreenMeRoot}
|
component={ScreenMeRoot}
|
||||||
options={{
|
options={{
|
||||||
headerTranslucent: true,
|
headerTranslucent: true,
|
||||||
@ -32,7 +32,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Bookmarks'
|
name='Tab-Me-Bookmarks'
|
||||||
component={ScreenMeBookmarks}
|
component={ScreenMeBookmarks}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
headerTitle: t('meBookmarks:heading'),
|
headerTitle: t('meBookmarks:heading'),
|
||||||
@ -45,7 +45,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Conversations'
|
name='Tab-Me-Conversations'
|
||||||
component={ScreenMeConversations}
|
component={ScreenMeConversations}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
headerTitle: t('meConversations:heading'),
|
headerTitle: t('meConversations:heading'),
|
||||||
@ -58,7 +58,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Favourites'
|
name='Tab-Me-Favourites'
|
||||||
component={ScreenMeFavourites}
|
component={ScreenMeFavourites}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
headerTitle: t('meFavourites:heading'),
|
headerTitle: t('meFavourites:heading'),
|
||||||
@ -71,7 +71,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Lists'
|
name='Tab-Me-Lists'
|
||||||
component={ScreenMeLists}
|
component={ScreenMeLists}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
headerTitle: t('meLists:heading'),
|
headerTitle: t('meLists:heading'),
|
||||||
@ -82,7 +82,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Lists-List'
|
name='Tab-Me-Lists-List'
|
||||||
component={ScreenMeListsList}
|
component={ScreenMeListsList}
|
||||||
options={({ route, navigation }: any) => ({
|
options={({ route, navigation }: any) => ({
|
||||||
headerTitle: t('meListsList:heading', { list: route.params.title }),
|
headerTitle: t('meListsList:heading', { list: route.params.title }),
|
||||||
@ -97,7 +97,7 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Settings'
|
name='Tab-Me-Settings'
|
||||||
component={ScreenMeSettings}
|
component={ScreenMeSettings}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
headerTitle: t('meSettings:heading'),
|
headerTitle: t('meSettings:heading'),
|
||||||
@ -110,10 +110,10 @@ const ScreenMe: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Screen-Me-Switch'
|
name='Tab-Me-Switch'
|
||||||
component={ScreenMeSwitch}
|
component={ScreenMeSwitch}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
stackPresentation: 'fullScreenModal',
|
stackPresentation: 'modal',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
@ -124,4 +124,4 @@ const ScreenMe: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ScreenMe
|
export default TabMe
|
@ -1,11 +1,13 @@
|
|||||||
import { MenuRow } from '@components/Menu'
|
import { MenuRow } from '@components/Menu'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
import TimelineEmpty from '@root/components/Timelines/Timeline/Empty'
|
import TimelineEmpty from '@root/components/Timelines/Timeline/Empty'
|
||||||
import { useListsQuery } from '@utils/queryHooks/lists'
|
import { useListsQuery } from '@utils/queryHooks/lists'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
const ScreenMeLists: React.FC = () => {
|
const ScreenMeLists: React.FC<StackScreenProps<
|
||||||
const navigation = useNavigation()
|
Nav.TabMeStackParamList,
|
||||||
|
'Tab-Me-Switch'
|
||||||
|
>> = ({ navigation }) => {
|
||||||
const { status, data, refetch } = useListsQuery({})
|
const { status, data, refetch } = useListsQuery({})
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = useMemo(() => {
|
||||||
@ -16,7 +18,7 @@ const ScreenMeLists: React.FC = () => {
|
|||||||
iconFront='List'
|
iconFront='List'
|
||||||
title={d.title}
|
title={d.title}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
navigation.navigate('Screen-Me-Lists-List', {
|
navigation.navigate('Tab-Me-Lists-List', {
|
||||||
list: d.id,
|
list: d.id,
|
||||||
title: d.title
|
title: d.title
|
||||||
})
|
})
|
@ -1,31 +1,22 @@
|
|||||||
|
import ComponentInstance from '@components/Instance'
|
||||||
import { useScrollToTop } from '@react-navigation/native'
|
import { useScrollToTop } from '@react-navigation/native'
|
||||||
import Collections from '@screens/Me/Root/Collections'
|
import Collections from '@screens/Tabs/Me/Root/Collections'
|
||||||
import MyInfo from '@screens/Me/Root/MyInfo'
|
import Logout from '@screens/Tabs/Me/Root/Logout'
|
||||||
import Settings from '@screens/Me/Root/Settings'
|
import MyInfo from '@screens/Tabs/Me/Root/MyInfo'
|
||||||
import Logout from '@screens/Me/Root/Logout'
|
import Settings from '@screens/Tabs/Me/Root/Settings'
|
||||||
import AccountNav from '@screens/Shared/Account/Nav'
|
import AccountNav from '@screens/Tabs/Shared/Account/Nav'
|
||||||
import accountReducer from '@screens/Shared/Account/utils/reducer'
|
import AccountContext from '@screens/Tabs/Shared/Account/utils/createContext'
|
||||||
import accountInitialState from '@screens/Shared/Account/utils/initialState'
|
import accountInitialState from '@screens/Tabs/Shared/Account/utils/initialState'
|
||||||
import AccountContext from '@screens/Shared/Account/utils/createContext'
|
import accountReducer from '@screens/Tabs/Shared/Account/utils/reducer'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
||||||
import React, { useEffect, useReducer, useRef, useState } from 'react'
|
import React, { useReducer, useRef, useState } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useAnimatedScrollHandler,
|
useAnimatedScrollHandler,
|
||||||
useSharedValue
|
useSharedValue
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import ComponentInstance from '@components/Instance'
|
import { useSelector } from 'react-redux'
|
||||||
import { StackScreenProps } from '@react-navigation/stack'
|
|
||||||
|
|
||||||
const ScreenMeRoot: React.FC<StackScreenProps<
|
const ScreenMeRoot: React.FC = () => {
|
||||||
Nav.MeStackParamList,
|
|
||||||
'Screen-Me-Root'
|
|
||||||
>> = ({ route: { params }, navigation }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
if (params && params.navigateAway) {
|
|
||||||
navigation.navigate(params.navigateAway)
|
|
||||||
}
|
|
||||||
}, [params])
|
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||||
|
|
||||||
const scrollRef = useRef<Animated.ScrollView>(null)
|
const scrollRef = useRef<Animated.ScrollView>(null)
|
@ -27,25 +27,25 @@ const Collections: React.FC = () => {
|
|||||||
iconFront='Mail'
|
iconFront='Mail'
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
title={t('content.collections.conversations')}
|
title={t('content.collections.conversations')}
|
||||||
onPress={() => navigation.navigate('Screen-Me-Conversations')}
|
onPress={() => navigation.navigate('Tab-Me-Conversations')}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
iconFront='Bookmark'
|
iconFront='Bookmark'
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
title={t('content.collections.bookmarks')}
|
title={t('content.collections.bookmarks')}
|
||||||
onPress={() => navigation.navigate('Screen-Me-Bookmarks')}
|
onPress={() => navigation.navigate('Tab-Me-Bookmarks')}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
iconFront='Star'
|
iconFront='Star'
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
title={t('content.collections.favourites')}
|
title={t('content.collections.favourites')}
|
||||||
onPress={() => navigation.navigate('Screen-Me-Favourites')}
|
onPress={() => navigation.navigate('Tab-Me-Favourites')}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
iconFront='List'
|
iconFront='List'
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
title={t('content.collections.lists')}
|
title={t('content.collections.lists')}
|
||||||
onPress={() => navigation.navigate('Screen-Me-Lists')}
|
onPress={() => navigation.navigate('Tab-Me-Lists')}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
iconFront='Clipboard'
|
iconFront='Clipboard'
|
||||||
@ -56,7 +56,7 @@ const Collections: React.FC = () => {
|
|||||||
onPress={() =>
|
onPress={() =>
|
||||||
data &&
|
data &&
|
||||||
data.length &&
|
data.length &&
|
||||||
navigation.navigate('Screen-Shared-Announcements', { showAll: true })
|
navigation.navigate('Screen-Announcements', { showAll: true })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</MenuContainer>
|
</MenuContainer>
|
@ -1,5 +1,5 @@
|
|||||||
import AccountHeader from '@screens/Shared/Account/Header'
|
import AccountHeader from '@screens/Tabs/Shared/Account/Header'
|
||||||
import AccountInformation from '@screens/Shared/Account/Information'
|
import AccountInformation from '@screens/Tabs/Shared/Account/Information'
|
||||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
@ -21,7 +21,7 @@ const Settings: React.FC = () => {
|
|||||||
iconFront='Settings'
|
iconFront='Settings'
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
title={t('content.settings')}
|
title={t('content.settings')}
|
||||||
onPress={() => navigation.navigate('Screen-Me-Settings')}
|
onPress={() => navigation.navigate('Tab-Me-Settings')}
|
||||||
/>
|
/>
|
||||||
</MenuContainer>
|
</MenuContainer>
|
||||||
)
|
)
|
@ -68,7 +68,7 @@ const SettingsTooot: React.FC = () => {
|
|||||||
account => account.acct === 'tooot@xmflsct.com'
|
account => account.acct === 'tooot@xmflsct.com'
|
||||||
)
|
)
|
||||||
if (foundAccounts?.length === 1) {
|
if (foundAccounts?.length === 1) {
|
||||||
navigation.navigate('Screen-Shared-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'conversation',
|
type: 'conversation',
|
||||||
accts: [foundAccounts[0].acct]
|
accts: [foundAccounts[0].acct]
|
||||||
})
|
})
|
@ -1,6 +1,6 @@
|
|||||||
import { HeaderCenter } from '@components/Header'
|
import { HeaderCenter } from '@components/Header'
|
||||||
import Timeline from '@components/Timelines/Timeline'
|
import Timeline from '@components/Timelines/Timeline'
|
||||||
import sharedScreens from '@screens/Shared/sharedScreens'
|
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -8,9 +8,9 @@ import { Platform } from 'react-native'
|
|||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Nav.NotificationsStackParamList>()
|
const Stack = createNativeStackNavigator<Nav.TabNotificationsStackParamList>()
|
||||||
|
|
||||||
const ScreenNotifications: React.FC = () => {
|
const TabNotifications: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ const ScreenNotifications: React.FC = () => {
|
|||||||
headerTopInsetEnabled: false
|
headerTopInsetEnabled: false
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen name='Screen-Notifications-Root'>
|
<Stack.Screen name='Tab-Notifications-Root'>
|
||||||
{() =>
|
{() =>
|
||||||
localActiveIndex !== null ? <Timeline page='Notifications' /> : null
|
localActiveIndex !== null ? <Timeline page='Notifications' /> : null
|
||||||
}
|
}
|
||||||
@ -39,4 +39,4 @@ const ScreenNotifications: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ScreenNotifications
|
export default TabNotifications
|
@ -1,11 +1,11 @@
|
|||||||
import Timelines from '@components/Timelines'
|
import Timelines from '@components/Timelines'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
const ScreenPublic = React.memo(
|
const TabPublic = React.memo(
|
||||||
() => {
|
() => {
|
||||||
return <Timelines />
|
return <Timelines />
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
export default ScreenPublic
|
export default TabPublic
|
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