mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Merge branch 'main' into candidate
This commit is contained in:
@ -231,6 +231,7 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
namespace 'com.xmflsct.app.tooot'
|
||||||
|
|
||||||
// applicationVariants are e.g. debug, release
|
// applicationVariants are e.g. debug, release
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools" package="com.xmflsct.app.tooot">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.xmflsct.app.tooot">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||||
|
@ -22,7 +22,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:7.2.1")
|
classpath('com.android.tools.build:gradle:7.3.1')
|
||||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
classpath("de.undercouch:gradle-download-task:5.0.1")
|
classpath("de.undercouch:gradle-download-task:5.0.1")
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|
||||||
# Version of flipper SDK to use with React Native
|
# Version of flipper SDK to use with React Native
|
||||||
FLIPPER_VERSION=0.125.0
|
FLIPPER_VERSION=0.176.1
|
||||||
|
|
||||||
# Use this property to specify which architecture you want to build.
|
# Use this property to specify which architecture you want to build.
|
||||||
# You can also override it from the CLI using
|
# You can also override it from the CLI using
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
Enjoy toooting! This version includes following improvements and fixes:
|
Enjoy toooting! This version includes following improvements and fixes:
|
||||||
- Auto fetch remote content in conversations!
|
- Auto fetch remote content in conversations!
|
||||||
|
- Remember last read position in timeline!
|
||||||
- Allowing adding more context of reports
|
- Allowing adding more context of reports
|
||||||
- Option to disable autoplay gif
|
- Option to disable autoplay gif
|
||||||
- Hide boosts from users
|
- Hide boosts from users
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
toooting愉快!此版本包括以下改进和修复:
|
toooting愉快!此版本包括以下改进和修复:
|
||||||
- 主动获取对话的远程内容
|
- 主动获取对话的远程内容
|
||||||
|
- 自动加载上次我的关注的阅读位置
|
||||||
- 可添加举报细节
|
- 可添加举报细节
|
||||||
- 新增暂停自动播放gif动画选项
|
- 新增暂停自动播放gif动画选项
|
||||||
- 隐藏用户的转嘟
|
- 隐藏用户的转嘟
|
||||||
|
@ -16,7 +16,7 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- EXNotifications (0.17.0):
|
- EXNotifications (0.17.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- Expo (47.0.11):
|
- Expo (47.0.12):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoCrypto (12.1.0):
|
- ExpoCrypto (12.1.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
@ -26,7 +26,7 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoLocalization (14.0.0):
|
- ExpoLocalization (14.0.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoModulesCore (1.1.0):
|
- ExpoModulesCore (1.1.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- ReactCommon/turbomodule/core
|
- ReactCommon/turbomodule/core
|
||||||
- ExpoRandom (13.0.0):
|
- ExpoRandom (13.0.0):
|
||||||
@ -39,6 +39,9 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- EXScreenCapture (5.0.0):
|
- EXScreenCapture (5.0.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
|
- EXScreenOrientation (5.0.1):
|
||||||
|
- ExpoModulesCore
|
||||||
|
- React-Core
|
||||||
- EXSecureStore (12.0.0):
|
- EXSecureStore (12.0.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- EXSplashScreen (0.17.5):
|
- EXSplashScreen (0.17.5):
|
||||||
@ -298,7 +301,7 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- react-native-blurhash (1.1.10):
|
- react-native-blurhash (1.1.10):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-cameraroll (5.2.0):
|
- react-native-cameraroll (5.2.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-image-picker (4.10.3):
|
- react-native-image-picker (4.10.3):
|
||||||
- React-Core
|
- React-Core
|
||||||
@ -470,6 +473,7 @@ DEPENDENCIES:
|
|||||||
- ExpoVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`)
|
- ExpoVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`)
|
||||||
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
||||||
- EXScreenCapture (from `../node_modules/expo-screen-capture/ios`)
|
- EXScreenCapture (from `../node_modules/expo-screen-capture/ios`)
|
||||||
|
- EXScreenOrientation (from `../node_modules/expo-screen-orientation/ios`)
|
||||||
- EXSecureStore (from `../node_modules/expo-secure-store/ios`)
|
- EXSecureStore (from `../node_modules/expo-secure-store/ios`)
|
||||||
- EXSplashScreen (from `../node_modules/expo-splash-screen/ios`)
|
- EXSplashScreen (from `../node_modules/expo-splash-screen/ios`)
|
||||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||||
@ -582,6 +586,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-web-browser/ios"
|
:path: "../node_modules/expo-web-browser/ios"
|
||||||
EXScreenCapture:
|
EXScreenCapture:
|
||||||
:path: "../node_modules/expo-screen-capture/ios"
|
:path: "../node_modules/expo-screen-capture/ios"
|
||||||
|
EXScreenOrientation:
|
||||||
|
:path: "../node_modules/expo-screen-orientation/ios"
|
||||||
EXSecureStore:
|
EXSecureStore:
|
||||||
:path: "../node_modules/expo-secure-store/ios"
|
:path: "../node_modules/expo-secure-store/ios"
|
||||||
EXSplashScreen:
|
EXSplashScreen:
|
||||||
@ -705,17 +711,18 @@ SPEC CHECKSUMS:
|
|||||||
EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6
|
EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6
|
||||||
EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80
|
EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80
|
||||||
EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a
|
EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a
|
||||||
Expo: dedd83acfd4d70cbec6ac2f1b4433462d95c70bc
|
Expo: f48d305fda3e4e501d686e6bad7d8c8373828279
|
||||||
ExpoCrypto: 6eb2a5ede7d95b7359a5f0391ee0c5d2ecd144b3
|
ExpoCrypto: 6eb2a5ede7d95b7359a5f0391ee0c5d2ecd144b3
|
||||||
ExpoHaptics: 129d3f8d44c2205adcdf8db760602818463d5437
|
ExpoHaptics: 129d3f8d44c2205adcdf8db760602818463d5437
|
||||||
ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318
|
ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318
|
||||||
ExpoLocalization: e202d1e2a4950df17ac8d0889d65a1ffd7532d7e
|
ExpoLocalization: e202d1e2a4950df17ac8d0889d65a1ffd7532d7e
|
||||||
ExpoModulesCore: 089e1ac0f0edee4dd0af0eb4e3f7b44d72cc418d
|
ExpoModulesCore: 485dff3a59b036a33b6050c0a5aea3cf1037fdd1
|
||||||
ExpoRandom: 58b7e0a5fe1adf1cb6dc1cbe503a6fe9524f36ce
|
ExpoRandom: 58b7e0a5fe1adf1cb6dc1cbe503a6fe9524f36ce
|
||||||
ExpoStoreReview: 713336ff504db3a6983475bf7c67519cc5efc86f
|
ExpoStoreReview: 713336ff504db3a6983475bf7c67519cc5efc86f
|
||||||
ExpoVideoThumbnails: 424db02cedfbbe2d498bcb2712ea4ba8a9dcb453
|
ExpoVideoThumbnails: 424db02cedfbbe2d498bcb2712ea4ba8a9dcb453
|
||||||
ExpoWebBrowser: 073e50f16669d498fb49063b9b7fe780b24f7fda
|
ExpoWebBrowser: 073e50f16669d498fb49063b9b7fe780b24f7fda
|
||||||
EXScreenCapture: d9f1ec31042dfef109290d06c2b4789b7444d16d
|
EXScreenCapture: d9f1ec31042dfef109290d06c2b4789b7444d16d
|
||||||
|
EXScreenOrientation: 07e5aeff07bce09a2b214981e612d87fd7719997
|
||||||
EXSecureStore: daec0117c922a67c658cb229152a9e252e5c1750
|
EXSecureStore: daec0117c922a67c658cb229152a9e252e5c1750
|
||||||
EXSplashScreen: 3e989924f61a8dd07ee4ea584c6ba14be9b51949
|
EXSplashScreen: 3e989924f61a8dd07ee4ea584c6ba14be9b51949
|
||||||
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
|
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
|
||||||
@ -744,7 +751,7 @@ SPEC CHECKSUMS:
|
|||||||
React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0
|
React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0
|
||||||
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
||||||
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
||||||
react-native-cameraroll: 0ff04cc4e0ff5f19a94ff4313e5c8bc4503cd86d
|
react-native-cameraroll: f94bf9f46c998963ecd2bb6e9a3f9cca59b6d9f1
|
||||||
react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695
|
react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695
|
||||||
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
|
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
|
||||||
react-native-language-detection: f414937fa715108ab50a6269a3de0bcb95e4ceb0
|
react-native-language-detection: f414937fa715108ab50a6269a3de0bcb95e4ceb0
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"@mattermost/react-native-paste-input": "^0.5.2",
|
"@mattermost/react-native-paste-input": "^0.5.2",
|
||||||
"@neverdull-agency/expo-unlimited-secure-store": "^1.0.10",
|
"@neverdull-agency/expo-unlimited-secure-store": "^1.0.10",
|
||||||
"@react-native-async-storage/async-storage": "~1.17.11",
|
"@react-native-async-storage/async-storage": "~1.17.11",
|
||||||
"@react-native-camera-roll/camera-roll": "^5.2.0",
|
"@react-native-camera-roll/camera-roll": "^5.2.1",
|
||||||
"@react-native-clipboard/clipboard": "^1.11.1",
|
"@react-native-clipboard/clipboard": "^1.11.1",
|
||||||
"@react-native-community/blur": "^4.3.0",
|
"@react-native-community/blur": "^4.3.0",
|
||||||
"@react-native-community/netinfo": "9.3.7",
|
"@react-native-community/netinfo": "9.3.7",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"@tanstack/react-query": "^4.20.9",
|
"@tanstack/react-query": "^4.20.9",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.1.0",
|
||||||
"expo": "^47.0.11",
|
"expo": "^47.0.12",
|
||||||
"expo-auth-session": "^3.8.0",
|
"expo-auth-session": "^3.8.0",
|
||||||
"expo-av": "^13.1.0",
|
"expo-av": "^13.1.0",
|
||||||
"expo-constants": "^14.1.0",
|
"expo-constants": "^14.1.0",
|
||||||
@ -54,6 +54,7 @@
|
|||||||
"expo-notifications": "^0.17.0",
|
"expo-notifications": "^0.17.0",
|
||||||
"expo-random": "^13.0.0",
|
"expo-random": "^13.0.0",
|
||||||
"expo-screen-capture": "^5.0.0",
|
"expo-screen-capture": "^5.0.0",
|
||||||
|
"expo-screen-orientation": "^5.0.1",
|
||||||
"expo-secure-store": "^12.0.0",
|
"expo-secure-store": "^12.0.0",
|
||||||
"expo-splash-screen": "^0.17.5",
|
"expo-splash-screen": "^0.17.5",
|
||||||
"expo-store-review": "^6.1.0",
|
"expo-store-review": "^6.1.0",
|
||||||
@ -65,7 +66,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-i18next": "^12.1.1",
|
"react-i18next": "^12.1.4",
|
||||||
"react-intl": "^6.2.5",
|
"react-intl": "^6.2.5",
|
||||||
"react-native": "^0.70.6",
|
"react-native": "^0.70.6",
|
||||||
"react-native-animated-spinkit": "^1.5.2",
|
"react-native-animated-spinkit": "^1.5.2",
|
||||||
|
@ -49,10 +49,10 @@ const ParseHTML: React.FC<Props> = ({
|
|||||||
StyleConstants.Font.Size[size],
|
StyleConstants.Font.Size[size],
|
||||||
adaptiveSize ? adaptiveFontsize : 0
|
adaptiveSize ? adaptiveFontsize : 0
|
||||||
)
|
)
|
||||||
const adaptedLineheight = adaptiveScale(
|
const adaptedLineheight =
|
||||||
StyleConstants.Font.LineHeight[size],
|
Platform.OS === 'ios'
|
||||||
adaptiveSize ? adaptiveFontsize : 0
|
? adaptiveScale(StyleConstants.Font.LineHeight[size], adaptiveSize ? adaptiveFontsize : 0)
|
||||||
)
|
: undefined
|
||||||
|
|
||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const { params } = useRoute()
|
const { params } = useRoute()
|
||||||
@ -119,7 +119,11 @@ const ParseHTML: React.FC<Props> = ({
|
|||||||
const href = node.attribs.href
|
const href = node.attribs.href
|
||||||
if (classes) {
|
if (classes) {
|
||||||
if (classes.includes('hashtag')) {
|
if (classes.includes('hashtag')) {
|
||||||
const tag = href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1].toLowerCase()
|
const children = node.children.map(unwrapNode).join('')
|
||||||
|
const tag =
|
||||||
|
href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1]?.toLowerCase() ||
|
||||||
|
children.match(new RegExp(/#(\S+)/))?.[1]?.toLowerCase()
|
||||||
|
|
||||||
const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined)
|
const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined)
|
||||||
?.hashtag
|
?.hashtag
|
||||||
const sameHashtag = paramsHashtag === tag
|
const sameHashtag = paramsHashtag === tag
|
||||||
@ -143,7 +147,7 @@ const ParseHTML: React.FC<Props> = ({
|
|||||||
!sameHashtag &&
|
!sameHashtag &&
|
||||||
navigation.push('Tab-Shared-Hashtag', { hashtag: tag })
|
navigation.push('Tab-Shared-Hashtag', { hashtag: tag })
|
||||||
}
|
}
|
||||||
children={node.children.map(unwrapNode).join('')}
|
children={children}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -199,7 +203,10 @@ const ParseHTML: React.FC<Props> = ({
|
|||||||
break
|
break
|
||||||
case 'br':
|
case 'br':
|
||||||
return (
|
return (
|
||||||
<Text key={index} style={{ lineHeight: adaptedLineheight / 2 }}>
|
<Text
|
||||||
|
key={index}
|
||||||
|
style={{ lineHeight: adaptedLineheight ? adaptedLineheight / 2 : undefined }}
|
||||||
|
>
|
||||||
{'\n'}
|
{'\n'}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
@ -208,7 +215,11 @@ const ParseHTML: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<Text key={index}>
|
<Text key={index}>
|
||||||
{node.children.map((c, i) => renderNode(c, i))}
|
{node.children.map((c, i) => renderNode(c, i))}
|
||||||
<Text style={{ lineHeight: adaptedLineheight / 2 }}>{'\n\n'}</Text>
|
<Text
|
||||||
|
style={{ lineHeight: adaptedLineheight ? adaptedLineheight / 2 : undefined }}
|
||||||
|
>
|
||||||
|
{'\n\n'}
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,16 +16,7 @@ export interface Props {
|
|||||||
const TimelineFooter: React.FC<Props> = ({ queryKey, disableInfinity }) => {
|
const TimelineFooter: React.FC<Props> = ({ queryKey, disableInfinity }) => {
|
||||||
const { hasNextPage } = useTimelineQuery({
|
const { hasNextPage } = useTimelineQuery({
|
||||||
...queryKey[1],
|
...queryKey[1],
|
||||||
options: {
|
options: { enabled: !disableInfinity, notifyOnChangeProps: ['hasNextPage'] }
|
||||||
enabled: !disableInfinity,
|
|
||||||
notifyOnChangeProps: ['hasNextPage'],
|
|
||||||
getNextPageParam: lastPage =>
|
|
||||||
lastPage?.links?.next && {
|
|
||||||
...(lastPage.links.next.isOffset
|
|
||||||
? { offset: lastPage.links.next.id }
|
|
||||||
: { max_id: lastPage.links.next.id })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
@ -7,18 +7,19 @@ import {
|
|||||||
QueryKeyTimeline,
|
QueryKeyTimeline,
|
||||||
useTimelineQuery
|
useTimelineQuery
|
||||||
} from '@utils/queryHooks/timeline'
|
} from '@utils/queryHooks/timeline'
|
||||||
|
import { setAccountStorage } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { RefObject, useEffect, useRef, useState } from 'react'
|
import React, { RefObject, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FlatList, Platform, Text, View } from 'react-native'
|
import { FlatList, Platform, Text, View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
Extrapolate,
|
Extrapolate,
|
||||||
interpolate,
|
interpolate,
|
||||||
runOnJS,
|
runOnJS,
|
||||||
useAnimatedReaction,
|
useAnimatedReaction,
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
|
useDerivedValue,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming
|
withTiming
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
@ -26,21 +27,25 @@ import Animated, {
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
flRef: RefObject<FlatList<any>>
|
flRef: RefObject<FlatList<any>>
|
||||||
queryKey: QueryKeyTimeline
|
queryKey: QueryKeyTimeline
|
||||||
|
fetchingActive: React.MutableRefObject<boolean>
|
||||||
scrollY: Animated.SharedValue<number>
|
scrollY: Animated.SharedValue<number>
|
||||||
fetchingType: Animated.SharedValue<0 | 1 | 2>
|
fetchingType: Animated.SharedValue<0 | 1 | 2>
|
||||||
disableRefresh?: boolean
|
disableRefresh?: boolean
|
||||||
|
readMarker?: 'read_marker_following'
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONTAINER_HEIGHT = StyleConstants.Spacing.M * 2
|
const CONTAINER_HEIGHT = StyleConstants.Spacing.M * 2.5
|
||||||
export const SEPARATION_Y_1 = -(CONTAINER_HEIGHT / 2 + StyleConstants.Font.Size.S / 2)
|
export const SEPARATION_Y_1 = -(CONTAINER_HEIGHT / 2 + StyleConstants.Font.Size.S / 2)
|
||||||
export const SEPARATION_Y_2 = -(CONTAINER_HEIGHT * 1.5 + StyleConstants.Font.Size.S / 2)
|
export const SEPARATION_Y_2 = -(CONTAINER_HEIGHT * 1.5 + StyleConstants.Font.Size.S / 2)
|
||||||
|
|
||||||
const TimelineRefresh: React.FC<Props> = ({
|
const TimelineRefresh: React.FC<Props> = ({
|
||||||
flRef,
|
flRef,
|
||||||
queryKey,
|
queryKey,
|
||||||
|
fetchingActive,
|
||||||
scrollY,
|
scrollY,
|
||||||
fetchingType,
|
fetchingType,
|
||||||
disableRefresh = false
|
disableRefresh = false,
|
||||||
|
readMarker
|
||||||
}) => {
|
}) => {
|
||||||
if (Platform.OS !== 'ios') {
|
if (Platform.OS !== 'ios') {
|
||||||
return null
|
return null
|
||||||
@ -55,7 +60,15 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
const prevStatusId = useRef<Mastodon.Status['id']>()
|
const prevStatusId = useRef<Mastodon.Status['id']>()
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const { refetch, isFetching } = useTimelineQuery({ ...queryKey[1] })
|
const { refetch, isRefetching } = useTimelineQuery({ ...queryKey[1] })
|
||||||
|
|
||||||
|
useDerivedValue(() => {
|
||||||
|
if (prevActive.current || isRefetching) {
|
||||||
|
fetchingActive.current = true
|
||||||
|
} else {
|
||||||
|
fetchingActive.current = false
|
||||||
|
}
|
||||||
|
}, [prevActive.current, isRefetching])
|
||||||
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
@ -83,7 +96,7 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
const arrowStage = useSharedValue(0)
|
const arrowStage = useSharedValue(0)
|
||||||
useAnimatedReaction(
|
useAnimatedReaction(
|
||||||
() => {
|
() => {
|
||||||
if (isFetching) {
|
if (fetchingActive.current) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch (arrowStage.value) {
|
switch (arrowStage.value) {
|
||||||
@ -116,9 +129,10 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
runOnJS(haptics)('Light')
|
runOnJS(haptics)('Light')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isFetching]
|
[fetchingActive.current]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fetchAndScrolled = useSharedValue(false)
|
||||||
const runFetchPrevious = async () => {
|
const runFetchPrevious = async () => {
|
||||||
if (prevActive.current) return
|
if (prevActive.current) return
|
||||||
|
|
||||||
@ -134,29 +148,12 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
|
|
||||||
await queryFunctionTimeline({
|
await queryFunctionTimeline({
|
||||||
queryKey,
|
queryKey,
|
||||||
pageParam: firstPage?.links?.prev && {
|
pageParam: firstPage?.links?.prev,
|
||||||
...(firstPage.links.prev.isOffset
|
|
||||||
? { offset: firstPage.links.prev.id }
|
|
||||||
: { min_id: firstPage.links.prev.id })
|
|
||||||
},
|
|
||||||
meta: {}
|
meta: {}
|
||||||
}).then(res => {
|
|
||||||
queryClient.setQueryData<
|
|
||||||
InfiniteData<
|
|
||||||
PagedResponse<(Mastodon.Status | Mastodon.Notification | Mastodon.Conversation)[]>
|
|
||||||
>
|
|
||||||
>(queryKey, old => {
|
|
||||||
if (!old) return old
|
|
||||||
|
|
||||||
prevCache.current = res.body.slice(0, -PREV_PER_BATCH)
|
|
||||||
return { ...old, pages: [{ ...res, body: res.body.slice(-PREV_PER_BATCH) }, ...old.pages] }
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
.then(res => {
|
||||||
useEffect(() => {
|
if (!res.body.length) return
|
||||||
const loop = async () => {
|
|
||||||
for await (const _ of Array(Math.ceil((prevCache.current?.length || 0) / PREV_PER_BATCH))) {
|
|
||||||
await new Promise(promise => setTimeout(promise, 32))
|
|
||||||
queryClient.setQueryData<
|
queryClient.setQueryData<
|
||||||
InfiniteData<
|
InfiniteData<
|
||||||
PagedResponse<(Mastodon.Status | Mastodon.Notification | Mastodon.Conversation)[]>
|
PagedResponse<(Mastodon.Status | Mastodon.Notification | Mastodon.Conversation)[]>
|
||||||
@ -164,33 +161,62 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
>(queryKey, old => {
|
>(queryKey, old => {
|
||||||
if (!old) return old
|
if (!old) return old
|
||||||
|
|
||||||
|
prevCache.current = res.body.slice(0, -PREV_PER_BATCH)
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
pages: old.pages.map((page, index) => {
|
pages: [{ ...res, body: res.body.slice(-PREV_PER_BATCH) }, ...old.pages]
|
||||||
if (index === 0) {
|
|
||||||
const insert = prevCache.current?.slice(-PREV_PER_BATCH)
|
|
||||||
prevCache.current = prevCache.current?.slice(0, -PREV_PER_BATCH)
|
|
||||||
if (insert) {
|
|
||||||
return { ...page, body: [...insert, ...page.body] }
|
|
||||||
} else {
|
|
||||||
return page
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return page
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
return res.body.length - PREV_PER_BATCH
|
||||||
}
|
})
|
||||||
prevActive.current = false
|
.then(async nextLength => {
|
||||||
}
|
if (!nextLength) {
|
||||||
loop()
|
prevActive.current = false
|
||||||
}, [prevCache.current])
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let [index] of Array(Math.ceil(nextLength / PREV_PER_BATCH)).entries()) {
|
||||||
|
if (!fetchAndScrolled.value && index < 3 && scrollY.value > 15) {
|
||||||
|
fetchAndScrolled.value = true
|
||||||
|
flRef.current?.scrollToOffset({ offset: scrollY.value - 15, animated: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(promise => setTimeout(promise, 32))
|
||||||
|
queryClient.setQueryData<
|
||||||
|
InfiniteData<
|
||||||
|
PagedResponse<(Mastodon.Status | Mastodon.Notification | Mastodon.Conversation)[]>
|
||||||
|
>
|
||||||
|
>(queryKey, old => {
|
||||||
|
if (!old) return old
|
||||||
|
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
pages: old.pages.map((page, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
const insert = prevCache.current?.slice(-PREV_PER_BATCH)
|
||||||
|
prevCache.current = prevCache.current?.slice(0, -PREV_PER_BATCH)
|
||||||
|
if (insert) {
|
||||||
|
return { ...page, body: [...insert, ...page.body] }
|
||||||
|
} else {
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
prevActive.current = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const runFetchLatest = async () => {
|
const runFetchLatest = async () => {
|
||||||
queryClient.invalidateQueries(queryKey)
|
queryClient.invalidateQueries(queryKey)
|
||||||
|
if (readMarker) {
|
||||||
|
setAccountStorage([{ key: readMarker, value: undefined }])
|
||||||
|
}
|
||||||
await refetch()
|
await refetch()
|
||||||
setTimeout(() => flRef.current?.scrollToOffset({ offset: 0 }), 50)
|
setTimeout(() => flRef.current?.scrollToOffset({ offset: 0 }), 50)
|
||||||
}
|
}
|
||||||
@ -224,61 +250,53 @@ const TimelineRefresh: React.FC<Props> = ({
|
|||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{prevActive.current || isFetching ? (
|
<View style={{ flex: 1, flexDirection: 'row', height: CONTAINER_HEIGHT }}>
|
||||||
<View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
|
<Text
|
||||||
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
style={{
|
||||||
</View>
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
) : (
|
lineHeight: CONTAINER_HEIGHT,
|
||||||
<>
|
color: colors.primaryDefault
|
||||||
<View style={{ flex: 1, flexDirection: 'row', height: CONTAINER_HEIGHT }}>
|
}}
|
||||||
<Text
|
onLayout={({ nativeEvent }) => {
|
||||||
style={{
|
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
|
||||||
fontSize: StyleConstants.Font.Size.S,
|
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
|
||||||
lineHeight: CONTAINER_HEIGHT,
|
}
|
||||||
color: colors.primaryDefault
|
}}
|
||||||
}}
|
children={t('refresh.fetchPreviousPage')}
|
||||||
onLayout={({ nativeEvent }) => {
|
/>
|
||||||
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
|
<Animated.View
|
||||||
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
|
style={[
|
||||||
}
|
{
|
||||||
}}
|
position: 'absolute',
|
||||||
children={t('refresh.fetchPreviousPage')}
|
left: textRight + StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
arrowY,
|
||||||
|
arrowTop
|
||||||
|
]}
|
||||||
|
children={
|
||||||
|
<Icon
|
||||||
|
name='ArrowLeft'
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
color={colors.primaryDefault}
|
||||||
/>
|
/>
|
||||||
<Animated.View
|
}
|
||||||
style={[
|
/>
|
||||||
{
|
</View>
|
||||||
position: 'absolute',
|
<View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
|
||||||
left: textRight + StyleConstants.Spacing.S
|
<Text
|
||||||
},
|
style={{
|
||||||
arrowY,
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
arrowTop
|
lineHeight: CONTAINER_HEIGHT,
|
||||||
]}
|
color: colors.primaryDefault
|
||||||
children={
|
}}
|
||||||
<Icon
|
onLayout={({ nativeEvent }) => {
|
||||||
name='ArrowLeft'
|
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
|
||||||
size={StyleConstants.Font.Size.M}
|
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
|
||||||
color={colors.primaryDefault}
|
}
|
||||||
/>
|
}}
|
||||||
}
|
children={t('refresh.refetch')}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontSize: StyleConstants.Font.Size.S,
|
|
||||||
lineHeight: CONTAINER_HEIGHT,
|
|
||||||
color: colors.primaryDefault
|
|
||||||
}}
|
|
||||||
onLayout={({ nativeEvent }) => {
|
|
||||||
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
|
|
||||||
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
children={t('refresh.refetch')}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { useGlobalStorage } from '@utils/storage/actions'
|
|||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
|
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
|
||||||
import { Platform } from 'expo-modules-core'
|
import { Platform } from 'expo-modules-core'
|
||||||
|
import * as ScreenOrientation from 'expo-screen-orientation'
|
||||||
import React, { useRef, useState } from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { Pressable, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
@ -72,14 +73,21 @@ const AttachmentVideo: React.FC<Props> = ({
|
|||||||
posterStyle: { resizeMode: ResizeMode.COVER }
|
posterStyle: { resizeMode: ResizeMode.COVER }
|
||||||
})}
|
})}
|
||||||
useNativeControls={false}
|
useNativeControls={false}
|
||||||
onFullscreenUpdate={event => {
|
onFullscreenUpdate={async ({ fullscreenUpdate }) => {
|
||||||
if (event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS) {
|
switch (fullscreenUpdate) {
|
||||||
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER)
|
case VideoFullscreenUpdate.PLAYER_DID_PRESENT:
|
||||||
if (gifv && !reduceMotionEnabled && autoplayGifv) {
|
Platform.OS === 'android' && (await ScreenOrientation.unlockAsync())
|
||||||
videoPlayer.current?.playAsync()
|
break
|
||||||
} else {
|
case VideoFullscreenUpdate.PLAYER_WILL_DISMISS:
|
||||||
videoPlayer.current?.pauseAsync()
|
Platform.OS === 'android' &&
|
||||||
}
|
(await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT))
|
||||||
|
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER)
|
||||||
|
if (gifv && !reduceMotionEnabled && autoplayGifv) {
|
||||||
|
videoPlayer.current?.playAsync()
|
||||||
|
} else {
|
||||||
|
videoPlayer.current?.pauseAsync()
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPlaybackStatusUpdate={event => {
|
onPlaybackStatusUpdate={event => {
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
import { useScrollToTop } from '@react-navigation/native'
|
import { useScrollToTop } from '@react-navigation/native'
|
||||||
import { UseInfiniteQueryOptions } from '@tanstack/react-query'
|
import { UseInfiniteQueryOptions } from '@tanstack/react-query'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { flattenPages } from '@utils/queryHooks/utils'
|
import { flattenPages } from '@utils/queryHooks/utils'
|
||||||
import { useGlobalStorageListener } from '@utils/storage/actions'
|
import {
|
||||||
|
getAccountStorage,
|
||||||
|
setAccountStorage,
|
||||||
|
useGlobalStorageListener
|
||||||
|
} from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { RefObject, useRef } from 'react'
|
import React, { RefObject, useRef } from 'react'
|
||||||
@ -13,7 +18,7 @@ import TimelineEmpty from './Empty'
|
|||||||
import TimelineFooter from './Footer'
|
import TimelineFooter from './Footer'
|
||||||
import TimelineRefresh, { SEPARATION_Y_1, SEPARATION_Y_2 } from './Refresh'
|
import TimelineRefresh, { SEPARATION_Y_1, SEPARATION_Y_2 } from './Refresh'
|
||||||
|
|
||||||
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
|
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList<any>)
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
flRef?: RefObject<FlatList<any>>
|
flRef?: RefObject<FlatList<any>>
|
||||||
@ -24,7 +29,8 @@ export interface Props {
|
|||||||
>
|
>
|
||||||
disableRefresh?: boolean
|
disableRefresh?: boolean
|
||||||
disableInfinity?: boolean
|
disableInfinity?: boolean
|
||||||
customProps: Partial<FlatListProps<any>> & Pick<FlatListProps<any>, 'renderItem'>
|
readMarker?: 'read_marker_following'
|
||||||
|
customProps?: Partial<FlatListProps<any>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Timeline: React.FC<Props> = ({
|
const Timeline: React.FC<Props> = ({
|
||||||
@ -33,6 +39,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
queryOptions,
|
queryOptions,
|
||||||
disableRefresh = false,
|
disableRefresh = false,
|
||||||
disableInfinity = false,
|
disableInfinity = false,
|
||||||
|
readMarker = undefined,
|
||||||
customProps
|
customProps
|
||||||
}) => {
|
}) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
@ -45,17 +52,12 @@ const Timeline: React.FC<Props> = ({
|
|||||||
notifyOnChangeProps: Platform.select({
|
notifyOnChangeProps: Platform.select({
|
||||||
ios: ['dataUpdatedAt', 'isFetching'],
|
ios: ['dataUpdatedAt', 'isFetching'],
|
||||||
android: ['dataUpdatedAt', 'isFetching', 'isLoading']
|
android: ['dataUpdatedAt', 'isFetching', 'isLoading']
|
||||||
}),
|
})
|
||||||
getNextPageParam: lastPage =>
|
|
||||||
lastPage?.links?.next && {
|
|
||||||
...(lastPage.links.next.isOffset
|
|
||||||
? { offset: lastPage.links.next.id }
|
|
||||||
: { max_id: lastPage.links.next.id })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const flRef = useRef<FlatList>(null)
|
const flRef = useRef<FlatList>(null)
|
||||||
|
const fetchingActive = useRef<boolean>(false)
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
const scrollY = useSharedValue(0)
|
||||||
const fetchingType = useSharedValue<0 | 1 | 2>(0)
|
const fetchingType = useSharedValue<0 | 1 | 2>(0)
|
||||||
@ -78,6 +80,32 @@ const Timeline: React.FC<Props> = ({
|
|||||||
[isFetching]
|
[isFetching]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const viewabilityConfigCallbackPairs = useRef<
|
||||||
|
Pick<FlatListProps<any>, 'viewabilityConfigCallbackPairs'>['viewabilityConfigCallbackPairs']
|
||||||
|
>(
|
||||||
|
readMarker
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
viewabilityConfig: {
|
||||||
|
minimumViewTime: 300,
|
||||||
|
itemVisiblePercentThreshold: 80,
|
||||||
|
waitForInteraction: true
|
||||||
|
},
|
||||||
|
onViewableItemsChanged: ({ viewableItems }) => {
|
||||||
|
const marker = readMarker ? getAccountStorage.string(readMarker) : undefined
|
||||||
|
|
||||||
|
const firstItemId = viewableItems.filter(item => item.isViewable)[0]?.item.id
|
||||||
|
if (!fetchingActive.current && firstItemId && firstItemId > (marker || '0')) {
|
||||||
|
setAccountStorage([{ key: readMarker, value: firstItemId }])
|
||||||
|
} else {
|
||||||
|
// setAccountStorage([{ key: readMarker, value: '109519141378761752' }])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
|
||||||
const androidRefreshControl = Platform.select({
|
const androidRefreshControl = Platform.select({
|
||||||
android: {
|
android: {
|
||||||
refreshControl: (
|
refreshControl: (
|
||||||
@ -86,7 +114,12 @@ const Timeline: React.FC<Props> = ({
|
|||||||
colors={[colors.primaryDefault]}
|
colors={[colors.primaryDefault]}
|
||||||
progressBackgroundColor={colors.backgroundDefault}
|
progressBackgroundColor={colors.backgroundDefault}
|
||||||
refreshing={isFetching || isLoading}
|
refreshing={isFetching || isLoading}
|
||||||
onRefresh={() => refetch()}
|
onRefresh={() => {
|
||||||
|
if (readMarker) {
|
||||||
|
setAccountStorage([{ key: readMarker, value: undefined }])
|
||||||
|
}
|
||||||
|
refetch()
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -102,9 +135,11 @@ const Timeline: React.FC<Props> = ({
|
|||||||
<TimelineRefresh
|
<TimelineRefresh
|
||||||
flRef={flRef}
|
flRef={flRef}
|
||||||
queryKey={queryKey}
|
queryKey={queryKey}
|
||||||
|
fetchingActive={fetchingActive}
|
||||||
scrollY={scrollY}
|
scrollY={scrollY}
|
||||||
fetchingType={fetchingType}
|
fetchingType={fetchingType}
|
||||||
disableRefresh={disableRefresh}
|
disableRefresh={disableRefresh}
|
||||||
|
readMarker={readMarker}
|
||||||
/>
|
/>
|
||||||
<AnimatedFlatList
|
<AnimatedFlatList
|
||||||
ref={customFLRef || flRef}
|
ref={customFLRef || flRef}
|
||||||
@ -112,6 +147,9 @@ const Timeline: React.FC<Props> = ({
|
|||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
windowSize={7}
|
windowSize={7}
|
||||||
data={flattenPages(data)}
|
data={flattenPages(data)}
|
||||||
|
{...(customProps?.renderItem
|
||||||
|
? { renderItem: customProps.renderItem }
|
||||||
|
: { renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} /> })}
|
||||||
initialNumToRender={6}
|
initialNumToRender={6}
|
||||||
maxToRenderPerBatch={3}
|
maxToRenderPerBatch={3}
|
||||||
onEndReached={() => !disableInfinity && !isFetchingNextPage && fetchNextPage()}
|
onEndReached={() => !disableInfinity && !isFetchingNextPage && fetchNextPage()}
|
||||||
@ -129,6 +167,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
|
||||||
{...(!isLoading && {
|
{...(!isLoading && {
|
||||||
maintainVisibleContentPosition: {
|
maintainVisibleContentPosition: {
|
||||||
minIndexForVisible: 0
|
minIndexForVisible: 0
|
||||||
|
@ -2,7 +2,6 @@ import { HeaderRight } from '@components/Header'
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { useListsQuery } from '@utils/queryHooks/lists'
|
import { useListsQuery } from '@utils/queryHooks/lists'
|
||||||
@ -178,14 +177,7 @@ const Root: React.FC<NativeStackScreenProps<TabLocalStackParamList, 'Tab-Local-R
|
|||||||
navigation.setParams({ queryKey: queryKey })
|
navigation.setParams({ queryKey: queryKey })
|
||||||
}, [mode, queryKey[1], pageLocal, lists])
|
}, [mode, queryKey[1], pageLocal, lists])
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} readMarker='read_marker_following' />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Root
|
export default Root
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
@ -13,14 +12,7 @@ const TabMeBookmarks: React.FC<NativeStackScreenProps<TabMeStackParamList, 'Tab-
|
|||||||
navigation.setParams({ queryKey: queryKey })
|
navigation.setParams({ queryKey: queryKey })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabMeBookmarks
|
export default TabMeBookmarks
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
@ -13,14 +12,7 @@ const TabMeFavourites: React.FC<
|
|||||||
navigation.setParams({ queryKey: queryKey })
|
navigation.setParams({ queryKey: queryKey })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabMeFavourites
|
export default TabMeFavourites
|
||||||
|
@ -23,17 +23,7 @@ const TabMeListAccounts: React.FC<TabMeStackScreenProps<'Tab-Me-List-Accounts'>>
|
|||||||
const { t } = useTranslation(['common', 'screenTabs'])
|
const { t } = useTranslation(['common', 'screenTabs'])
|
||||||
|
|
||||||
const queryKey: QueryKeyListAccounts = ['ListAccounts', { id: params.id }]
|
const queryKey: QueryKeyListAccounts = ['ListAccounts', { id: params.id }]
|
||||||
const { data, refetch, fetchNextPage, hasNextPage } = useListAccountsQuery({
|
const { data, refetch, fetchNextPage, hasNextPage } = useListAccountsQuery({ ...queryKey[1] })
|
||||||
...queryKey[1],
|
|
||||||
options: {
|
|
||||||
getNextPageParam: lastPage =>
|
|
||||||
lastPage?.links?.next && {
|
|
||||||
...(lastPage.links.next.isOffset
|
|
||||||
? { offset: lastPage.links.next.id }
|
|
||||||
: { max_id: lastPage.links.next.id })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const mutation = useListAccountsMutation({
|
const mutation = useListAccountsMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
||||||
@ -74,14 +73,7 @@ const TabMeList: React.FC<NativeStackScreenProps<TabMeStackParamList, 'Tab-Me-Li
|
|||||||
navigation.setParams({ queryKey })
|
navigation.setParams({ queryKey })
|
||||||
}, [list])
|
}, [list])
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabMeList
|
export default TabMeList
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { HeaderRight } from '@components/Header'
|
import { HeaderRight } from '@components/Header'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
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 { NativeStackNavigationProp, NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackNavigationProp, NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
@ -21,15 +20,7 @@ const Route = ({ route: { key: page } }: { route: any }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setParams({ queryKey })
|
navigation.setParams({ queryKey })
|
||||||
}, [])
|
}, [])
|
||||||
return (
|
return <Timeline queryKey={queryKey} disableRefresh={page === 'Trending'} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
disableRefresh={page === 'Trending'}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }: any) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderScene = SceneMap({
|
const renderScene = SceneMap({
|
||||||
|
@ -2,7 +2,6 @@ import { HeaderLeft } from '@components/Header'
|
|||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
@ -49,14 +48,7 @@ const TabSharedAttachments: React.FC<TabSharedStackScreenProps<'Tab-Shared-Attac
|
|||||||
{ page: 'Account', id: account.id, exclude_reblogs: true, only_media: true }
|
{ page: 'Account', id: account.id, exclude_reblogs: true, only_media: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabSharedAttachments
|
export default TabSharedAttachments
|
||||||
|
@ -2,7 +2,6 @@ import haptics from '@components/haptics'
|
|||||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
import { featureCheck } from '@utils/helpers/featureCheck'
|
import { featureCheck } from '@utils/helpers/featureCheck'
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
@ -82,14 +81,7 @@ const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
|
|||||||
})
|
})
|
||||||
}, [canFollowTags, data?.following, isFetching])
|
}, [canFollowTags, data?.following, isFetching])
|
||||||
|
|
||||||
return (
|
return <Timeline queryKey={queryKey} />
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabSharedHashtag
|
export default TabSharedHashtag
|
||||||
|
@ -74,7 +74,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
|
|
||||||
const match = urlMatcher(toot.url || toot.uri)
|
const match = urlMatcher(toot.url || toot.uri)
|
||||||
const highlightIndex = useRef<number>(0)
|
const highlightIndex = useRef<number>(0)
|
||||||
const query = useQuery<{ pages: { body: (Mastodon.Status & { key?: 'cached' })[] }[] }>(
|
const query = useQuery<{ pages: { body: Mastodon.Status[] }[] }>(
|
||||||
queryKey.local,
|
queryKey.local,
|
||||||
async () => {
|
async () => {
|
||||||
const context = await apiInstance<{
|
const context = await apiInstance<{
|
||||||
@ -94,11 +94,13 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
{
|
{
|
||||||
body: statuses.map((status, index) => {
|
body: statuses.map((status, index) => {
|
||||||
if (index < highlightIndex.current || status.id === toot.id) {
|
if (index < highlightIndex.current || status.id === toot.id) {
|
||||||
return { ...status, _level: 0 }
|
status._level = 0
|
||||||
|
return status
|
||||||
} else {
|
} else {
|
||||||
const repliedLevel: number =
|
const repliedLevel: number =
|
||||||
statuses.find(s => s.id === status.in_reply_to_id)?._level || 0
|
statuses.find(s => s.id === status.in_reply_to_id)?._level || 0
|
||||||
return { ...status, _level: repliedLevel + 1 }
|
status._level = repliedLevel + 1
|
||||||
|
return status
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -106,7 +108,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
initialData: { pages: [{ body: [{ ...toot, _level: 0, key: 'cached' }] }] },
|
placeholderData: { pages: [{ body: [toot] }] },
|
||||||
enabled: !toot._remote,
|
enabled: !toot._remote,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
@ -169,11 +171,13 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
|
|
||||||
return statuses.map((status, index) => {
|
return statuses.map((status, index) => {
|
||||||
if (index < highlightIndex.current || status.id === toot.id) {
|
if (index < highlightIndex.current || status.id === toot.id) {
|
||||||
return { ...status, _level: 0 }
|
status._level = 0
|
||||||
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
const repliedLevel: number = statuses.find(s => s.id === status.in_reply_to_id)?._level || 0
|
const repliedLevel: number = statuses.find(s => s.id === status.in_reply_to_id)?._level || 0
|
||||||
return { ...status, _level: repliedLevel + 1 }
|
status._level = repliedLevel + 1
|
||||||
|
return status
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -184,12 +188,12 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
if (query.data.pages[0].body.length < 1 && data.length < 1) {
|
if ((query.data?.pages[0].body.length || 0) < 1 && data.length < 1) {
|
||||||
navigation.goBack()
|
navigation.goBack()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.data.pages[0].body.length < data.length) {
|
if ((query.data?.pages[0].body.length || 0) < data.length) {
|
||||||
queryClient.cancelQueries(queryKey.local)
|
queryClient.cancelQueries(queryKey.local)
|
||||||
queryClient.setQueryData<{
|
queryClient.setQueryData<{
|
||||||
pages: { body: Mastodon.Status[] }[]
|
pages: { body: Mastodon.Status[] }[]
|
||||||
@ -201,12 +205,11 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
pages: [
|
pages: [
|
||||||
{
|
{
|
||||||
body: data.map(remote => {
|
body: data.map(remote => {
|
||||||
const localMatch = query.data.pages[0].body.find(
|
const localMatch = query.data?.pages[0].body.find(
|
||||||
local => local.uri === remote.uri
|
local => local.uri === remote.uri
|
||||||
)
|
)
|
||||||
if (localMatch) {
|
if (localMatch) {
|
||||||
delete localMatch.key
|
return { ...localMatch, _level: remote._level }
|
||||||
return localMatch
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...remote,
|
...remote,
|
||||||
@ -265,9 +268,9 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
windowSize={7}
|
windowSize={7}
|
||||||
data={query.data?.pages?.[0].body}
|
data={query.data?.pages?.[0].body}
|
||||||
renderItem={({ item, index }) => {
|
renderItem={({ item, index }) => {
|
||||||
const prev = query.data.pages[0].body[index - 1]?._level || 0
|
const prev = query.data?.pages[0].body[index - 1]?._level || 0
|
||||||
const curr = item._level
|
const curr = item._level
|
||||||
const next = query.data.pages[0].body[index + 1]?._level || 0
|
const next = query.data?.pages[0].body[index + 1]?._level || 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@ -373,7 +376,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
{/* <CustomText
|
{/* <CustomText
|
||||||
children={finalData.current[index - 1]?._level}
|
children={query.data?.pages[0].body[index - 1]?._level}
|
||||||
style={{ position: 'absolute', top: 4, left: 4, color: colors.red }}
|
style={{ position: 'absolute', top: 4, left: 4, color: colors.red }}
|
||||||
/>
|
/>
|
||||||
<CustomText
|
<CustomText
|
||||||
@ -381,7 +384,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
style={{ position: 'absolute', top: 20, left: 4, color: colors.yellow }}
|
style={{ position: 'absolute', top: 20, left: 4, color: colors.yellow }}
|
||||||
/>
|
/>
|
||||||
<CustomText
|
<CustomText
|
||||||
children={finalData.current[index + 1]?._level}
|
children={query.data?.pages[0].body[index + 1]?._level}
|
||||||
style={{ position: 'absolute', top: 36, left: 4, color: colors.green }}
|
style={{ position: 'absolute', top: 36, left: 4, color: colors.green }}
|
||||||
/> */}
|
/> */}
|
||||||
</View>
|
</View>
|
||||||
@ -421,7 +424,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
|||||||
const offset = error.averageItemLength * error.index
|
const offset = error.averageItemLength * error.index
|
||||||
flRef.current?.scrollToOffset({ offset })
|
flRef.current?.scrollToOffset({ offset })
|
||||||
try {
|
try {
|
||||||
error.index < query.data.pages[0].body.length &&
|
error.index < (query.data?.pages[0].body.length || 0) &&
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() =>
|
() =>
|
||||||
flRef.current?.scrollToIndex({
|
flRef.current?.scrollToIndex({
|
||||||
|
@ -33,12 +33,7 @@ const TabSharedUsers: React.FC<TabSharedStackScreenProps<'Tab-Shared-Users'>> =
|
|||||||
|
|
||||||
const queryKey: QueryKeyUsers = ['Users', params]
|
const queryKey: QueryKeyUsers = ['Users', params]
|
||||||
const { data, isFetching, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({
|
const { data, isFetching, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({
|
||||||
...queryKey[1],
|
...queryKey[1]
|
||||||
options: {
|
|
||||||
getPreviousPageParam: firstPage =>
|
|
||||||
firstPage.links?.prev?.id && { min_id: firstPage.links.prev.id },
|
|
||||||
getNextPageParam: lastPage => lastPage.links?.next?.id && { max_id: lastPage.links.next.id }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const [isSearching, setIsSearching] = useState(false)
|
const [isSearching, setIsSearching] = useState(false)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ctx, handleError, PagedResponse, userAgent } from './helpers'
|
import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers'
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
method: 'get' | 'post' | 'put' | 'delete'
|
method: 'get' | 'post' | 'put' | 'delete'
|
||||||
@ -49,29 +49,7 @@ const apiGeneral = async <T = unknown>({
|
|||||||
? (body as (FormData & { _parts: [][] }) | undefined)?._parts?.length
|
? (body as (FormData & { _parts: [][] }) | undefined)?._parts?.length
|
||||||
: Object.keys(body).length) && { data: body })
|
: Object.keys(body).length) && { data: body })
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => ({ body: response.data, links: parseHeaderLinks(response.headers.link) }))
|
||||||
let links: {
|
|
||||||
prev?: { id: string; isOffset: boolean }
|
|
||||||
next?: { id: string; isOffset: boolean }
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (response.headers?.link) {
|
|
||||||
const linksParsed = response.headers.link.matchAll(
|
|
||||||
new RegExp('[?&](.*?_id|offset)=(.*?)>; *rel="(.*?)"', 'gi')
|
|
||||||
)
|
|
||||||
for (const link of linksParsed) {
|
|
||||||
switch (link[3]) {
|
|
||||||
case 'prev':
|
|
||||||
links.prev = { id: link[2], isOffset: link[1].includes('offset') }
|
|
||||||
break
|
|
||||||
case 'next':
|
|
||||||
links.next = { id: link[2], isOffset: link[1].includes('offset') }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve({ body: response.data, links })
|
|
||||||
})
|
|
||||||
.catch(handleError())
|
.catch(handleError())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import * as Sentry from '@sentry/react-native'
|
|||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import Constants from 'expo-constants'
|
import Constants from 'expo-constants'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
|
import parse from 'url-parse'
|
||||||
|
|
||||||
const userAgent = {
|
const userAgent = {
|
||||||
'User-Agent': `tooot/${Constants.expoConfig?.version} ${Platform.OS}/${Platform.Version}`
|
'User-Agent': `tooot/${Constants.expoConfig?.version} ${Platform.OS}/${Platform.Version}`
|
||||||
@ -64,10 +65,42 @@ const handleError =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const parseHeaderLinks = (headerLink?: string): PagedResponse['links'] => {
|
||||||
|
if (!headerLink) return undefined
|
||||||
|
|
||||||
|
const links: PagedResponse['links'] = {}
|
||||||
|
|
||||||
|
const linkParsed = [...headerLink.matchAll(/<(\S+?)>; *rel="(next|prev)"/gi)]
|
||||||
|
for (const link of linkParsed) {
|
||||||
|
const queries = parse(link[1], true).query
|
||||||
|
const isOffset = !!queries.offset?.length
|
||||||
|
|
||||||
|
switch (link[2]) {
|
||||||
|
case 'prev':
|
||||||
|
const prevId = isOffset ? queries.offset : queries.min_id
|
||||||
|
if (prevId) links.prev = isOffset ? { offset: prevId } : { min_id: prevId }
|
||||||
|
break
|
||||||
|
case 'next':
|
||||||
|
const nextId = isOffset ? queries.offset : queries.max_id
|
||||||
|
if (nextId) links.next = isOffset ? { offset: nextId } : { max_id: nextId }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (links.prev || links.next) {
|
||||||
|
return links
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type LinkFormat = { id: string; isOffset: boolean }
|
type LinkFormat = { id: string; isOffset: boolean }
|
||||||
export type PagedResponse<T = unknown> = {
|
export type PagedResponse<T = unknown> = {
|
||||||
body: T
|
body: T
|
||||||
links?: { prev?: LinkFormat; next?: LinkFormat }
|
links?: {
|
||||||
|
prev?: { min_id: string } | { offset: string }
|
||||||
|
next?: { max_id: string } | { offset: string }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ctx, handleError, userAgent }
|
export { ctx, handleError, userAgent }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getAccountDetails } from '@utils/storage/actions'
|
import { getAccountDetails } from '@utils/storage/actions'
|
||||||
import axios, { AxiosRequestConfig } from 'axios'
|
import axios, { AxiosRequestConfig } from 'axios'
|
||||||
import { ctx, handleError, PagedResponse, userAgent } from './helpers'
|
import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers'
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
||||||
@ -57,29 +57,7 @@ const apiInstance = async <T = unknown>({
|
|||||||
...((body as (FormData & { _parts: [][] }) | undefined)?._parts.length && { data: body }),
|
...((body as (FormData & { _parts: [][] }) | undefined)?._parts.length && { data: body }),
|
||||||
...extras
|
...extras
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => ({ body: response.data, links: parseHeaderLinks(response.headers.link) }))
|
||||||
let links: {
|
|
||||||
prev?: { id: string; isOffset: boolean }
|
|
||||||
next?: { id: string; isOffset: boolean }
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (response.headers?.link) {
|
|
||||||
const linksParsed = response.headers.link.matchAll(
|
|
||||||
new RegExp('[?&](.*?_id|offset)=(.*?)>; *rel="(.*?)"', 'gi')
|
|
||||||
)
|
|
||||||
for (const link of linksParsed) {
|
|
||||||
switch (link[3]) {
|
|
||||||
case 'prev':
|
|
||||||
links.prev = { id: link[2], isOffset: link[1].includes('offset') }
|
|
||||||
break
|
|
||||||
case 'next':
|
|
||||||
links.next = { id: link[2], isOffset: link[1].includes('offset') }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve({ body: response.data, links })
|
|
||||||
})
|
|
||||||
.catch(handleError())
|
.catch(handleError())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
QueryFunctionContext,
|
QueryFunctionContext,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
UseInfiniteQueryOptions,
|
UseInfiniteQueryOptions,
|
||||||
useMutation,
|
useMutation,
|
||||||
UseMutationOptions,
|
UseMutationOptions,
|
||||||
useQuery,
|
useQuery,
|
||||||
UseQueryOptions
|
UseQueryOptions
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
import { PagedResponse } from '@utils/api/helpers'
|
import { PagedResponse } from '@utils/api/helpers'
|
||||||
import apiInstance from '@utils/api/instance'
|
import apiInstance from '@utils/api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
|
import { infinitePageParams } from './utils'
|
||||||
|
|
||||||
export type QueryKeyLists = ['Lists']
|
export type QueryKeyLists = ['Lists']
|
||||||
|
|
||||||
@ -98,10 +99,16 @@ const useListAccountsQuery = ({
|
|||||||
options,
|
options,
|
||||||
...queryKeyParams
|
...queryKeyParams
|
||||||
}: QueryKeyListAccounts[1] & {
|
}: QueryKeyListAccounts[1] & {
|
||||||
options?: UseInfiniteQueryOptions<PagedResponse<Mastodon.Account[]>, AxiosError>
|
options?: Omit<
|
||||||
|
UseInfiniteQueryOptions<PagedResponse<Mastodon.Account[]>, AxiosError>,
|
||||||
|
'getPreviousPageParam' | 'getNextPageParam'
|
||||||
|
>
|
||||||
}) => {
|
}) => {
|
||||||
const queryKey: QueryKeyListAccounts = ['ListAccounts', queryKeyParams]
|
const queryKey: QueryKeyListAccounts = ['ListAccounts', queryKeyParams]
|
||||||
return useInfiniteQuery(queryKey, accountsQueryFunction, options)
|
return useInfiniteQuery(queryKey, accountsQueryFunction, {
|
||||||
|
...options,
|
||||||
|
...infinitePageParams
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountsMutationVarsLists = {
|
type AccountsMutationVarsLists = {
|
||||||
|
@ -18,6 +18,7 @@ import { searchLocalStatus } from './search'
|
|||||||
import deleteItem from './timeline/deleteItem'
|
import deleteItem from './timeline/deleteItem'
|
||||||
import editItem from './timeline/editItem'
|
import editItem from './timeline/editItem'
|
||||||
import updateStatusProperty from './timeline/updateStatusProperty'
|
import updateStatusProperty from './timeline/updateStatusProperty'
|
||||||
|
import { infinitePageParams } from './utils'
|
||||||
|
|
||||||
export type QueryKeyTimeline = [
|
export type QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
@ -57,7 +58,25 @@ export const queryFunctionTimeline = async ({
|
|||||||
pageParam
|
pageParam
|
||||||
}: QueryFunctionContext<QueryKeyTimeline>) => {
|
}: QueryFunctionContext<QueryKeyTimeline>) => {
|
||||||
const page = queryKey[1]
|
const page = queryKey[1]
|
||||||
let params: { [key: string]: string } = { limit: 40, ...pageParam }
|
|
||||||
|
let marker: string | undefined
|
||||||
|
if (page.page === 'Following' && !pageParam?.offset && !pageParam?.min_id && !pageParam?.max_id) {
|
||||||
|
const storedMarker = getAccountStorage.string('read_marker_following')
|
||||||
|
if (storedMarker) {
|
||||||
|
await apiInstance<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: 'timelines/home',
|
||||||
|
params: { limit: 1, min_id: storedMarker }
|
||||||
|
}).then(res => {
|
||||||
|
if (res.body.length) {
|
||||||
|
marker = storedMarker
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let params: { [key: string]: string } = marker
|
||||||
|
? { limit: 40, max_id: marker }
|
||||||
|
: { limit: 40, ...pageParam }
|
||||||
|
|
||||||
switch (page.page) {
|
switch (page.page) {
|
||||||
case 'Following':
|
case 'Following':
|
||||||
@ -137,7 +156,16 @@ export const queryFunctionTimeline = async ({
|
|||||||
case 'Account':
|
case 'Account':
|
||||||
if (!page.id) return Promise.reject('Timeline query account id not provided')
|
if (!page.id) return Promise.reject('Timeline query account id not provided')
|
||||||
|
|
||||||
if (page.exclude_reblogs) {
|
if (page.only_media) {
|
||||||
|
return apiInstance<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: `accounts/${page.id}/statuses`,
|
||||||
|
params: {
|
||||||
|
only_media: 'true',
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (page.exclude_reblogs) {
|
||||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
||||||
return apiInstance<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -177,8 +205,8 @@ export const queryFunctionTimeline = async ({
|
|||||||
url: `accounts/${page.id}/statuses`,
|
url: `accounts/${page.id}/statuses`,
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
exclude_replies: page.exclude_reblogs.toString(),
|
exclude_replies: false,
|
||||||
only_media: page.only_media.toString()
|
only_media: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -228,14 +256,18 @@ const useTimelineQuery = ({
|
|||||||
options,
|
options,
|
||||||
...queryKeyParams
|
...queryKeyParams
|
||||||
}: QueryKeyTimeline[1] & {
|
}: QueryKeyTimeline[1] & {
|
||||||
options?: UseInfiniteQueryOptions<PagedResponse<Mastodon.Status[]>, AxiosError>
|
options?: Omit<
|
||||||
|
UseInfiniteQueryOptions<PagedResponse<Mastodon.Status[]>, AxiosError>,
|
||||||
|
'getPreviousPageParam' | 'getNextPageParam'
|
||||||
|
>
|
||||||
}) => {
|
}) => {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { ...queryKeyParams }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { ...queryKeyParams }]
|
||||||
return useInfiniteQuery(queryKey, queryFunctionTimeline, {
|
return useInfiniteQuery(queryKey, queryFunctionTimeline, {
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
...options
|
...options,
|
||||||
|
...infinitePageParams
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +463,6 @@ const useTimelineMutation = ({
|
|||||||
updateStatusProperty(params, navigationState)
|
updateStatusProperty(params, navigationState)
|
||||||
break
|
break
|
||||||
case 'editItem':
|
case 'editItem':
|
||||||
console.log('YES!!!')
|
|
||||||
editItem(params)
|
editItem(params)
|
||||||
break
|
break
|
||||||
case 'deleteItem':
|
case 'deleteItem':
|
||||||
|
@ -9,6 +9,7 @@ import apiInstance from '@utils/api/instance'
|
|||||||
import { urlMatcher } from '@utils/helpers/urlMatcher'
|
import { urlMatcher } from '@utils/helpers/urlMatcher'
|
||||||
import { TabSharedStackParamList } from '@utils/navigation/navigators'
|
import { TabSharedStackParamList } from '@utils/navigation/navigators'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
|
import { infinitePageParams } from './utils'
|
||||||
|
|
||||||
export type QueryKeyUsers = ['Users', TabSharedStackParamList['Tab-Shared-Users']]
|
export type QueryKeyUsers = ['Users', TabSharedStackParamList['Tab-Shared-Users']]
|
||||||
|
|
||||||
@ -73,13 +74,19 @@ const useUsersQuery = ({
|
|||||||
options,
|
options,
|
||||||
...queryKeyParams
|
...queryKeyParams
|
||||||
}: QueryKeyUsers[1] & {
|
}: QueryKeyUsers[1] & {
|
||||||
options?: UseInfiniteQueryOptions<
|
options?: Omit<
|
||||||
PagedResponse<Mastodon.Account[]> & { warnIncomplete: boolean; remoteData: boolean },
|
UseInfiniteQueryOptions<
|
||||||
AxiosError
|
PagedResponse<Mastodon.Account[]> & { warnIncomplete: boolean; remoteData: boolean },
|
||||||
|
AxiosError
|
||||||
|
>,
|
||||||
|
'getPreviousPageParam' | 'getNextPageParam'
|
||||||
>
|
>
|
||||||
}) => {
|
}) => {
|
||||||
const queryKey: QueryKeyUsers = ['Users', { ...queryKeyParams }]
|
const queryKey: QueryKeyUsers = ['Users', { ...queryKeyParams }]
|
||||||
return useInfiniteQuery(queryKey, queryFunction, options)
|
return useInfiniteQuery(queryKey, queryFunction, {
|
||||||
|
...options,
|
||||||
|
...infinitePageParams
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useUsersQuery }
|
export { useUsersQuery }
|
||||||
|
@ -2,10 +2,8 @@ import { InfiniteData } from '@tanstack/react-query'
|
|||||||
import { PagedResponse } from '@utils/api/helpers'
|
import { PagedResponse } from '@utils/api/helpers'
|
||||||
|
|
||||||
export const infinitePageParams = {
|
export const infinitePageParams = {
|
||||||
getPreviousPageParam: (firstPage: PagedResponse<any>) =>
|
getPreviousPageParam: (firstPage: PagedResponse<any>) => firstPage.links?.prev,
|
||||||
firstPage.links?.prev && { min_id: firstPage.links.next },
|
getNextPageParam: (lastPage: PagedResponse<any>) => lastPage.links?.next
|
||||||
getNextPageParam: (lastPage: PagedResponse<any>) =>
|
|
||||||
lastPage.links?.next && { max_id: lastPage.links.next }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const flattenPages = <T>(data: InfiniteData<PagedResponse<T[]>> | undefined): T[] | [] =>
|
export const flattenPages = <T>(data: InfiniteData<PagedResponse<T[]>> | undefined): T[] | [] =>
|
||||||
|
@ -24,6 +24,7 @@ export type AccountV0 = {
|
|||||||
'auth.account.domain': string // used for username
|
'auth.account.domain': string // used for username
|
||||||
'auth.account.avatar_static': string
|
'auth.account.avatar_static': string
|
||||||
version: string
|
version: string
|
||||||
|
read_marker_following?: string
|
||||||
// number
|
// number
|
||||||
// boolean
|
// boolean
|
||||||
// object
|
// object
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { queryClient } from '@utils/queryHooks'
|
import { queryClient } from '@utils/queryHooks'
|
||||||
import { storage } from '@utils/storage'
|
import { storage } from '@utils/storage'
|
||||||
|
import { Platform } from 'react-native'
|
||||||
import {
|
import {
|
||||||
MMKV,
|
MMKV,
|
||||||
useMMKVBoolean,
|
useMMKVBoolean,
|
||||||
@ -40,10 +41,21 @@ export const useGlobalStorage = {
|
|||||||
useMMKVString(key, storage.global) as NonNullable<StorageGlobal[T]> extends string
|
useMMKVString(key, storage.global) as NonNullable<StorageGlobal[T]> extends string
|
||||||
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
||||||
: never,
|
: never,
|
||||||
number: <T extends keyof StorageGlobal>(key: T) =>
|
number: <T extends keyof StorageGlobal>(key: T) => {
|
||||||
useMMKVNumber(key, storage.global) as NonNullable<StorageGlobal[T]> extends number
|
if (Platform.OS === 'ios') {
|
||||||
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
return useMMKVString(key, storage.global) as NonNullable<StorageGlobal[T]> extends number
|
||||||
: never,
|
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
||||||
|
: never
|
||||||
|
} else {
|
||||||
|
const [num, setNum] = useMMKVString(key, storage.global)
|
||||||
|
// @ts-ignore
|
||||||
|
return [parseInt(num), v => setNum(v.toString())] as NonNullable<
|
||||||
|
StorageGlobal[T]
|
||||||
|
> extends number
|
||||||
|
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
||||||
|
: never
|
||||||
|
}
|
||||||
|
},
|
||||||
boolean: <T extends keyof StorageGlobal>(key: T) =>
|
boolean: <T extends keyof StorageGlobal>(key: T) =>
|
||||||
useMMKVBoolean(key, storage.global) as NonNullable<StorageGlobal[T]> extends boolean
|
useMMKVBoolean(key, storage.global) as NonNullable<StorageGlobal[T]> extends boolean
|
||||||
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
? [StorageGlobal[T], (valud: StorageGlobal[T]) => void]
|
||||||
|
54
yarn.lock
54
yarn.lock
@ -1476,7 +1476,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.8.4":
|
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.8.4":
|
||||||
version: 7.20.7
|
version: 7.20.7
|
||||||
resolution: "@babel/runtime@npm:7.20.7"
|
resolution: "@babel/runtime@npm:7.20.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2744,12 +2744,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@react-native-camera-roll/camera-roll@npm:^5.2.0":
|
"@react-native-camera-roll/camera-roll@npm:^5.2.1":
|
||||||
version: 5.2.0
|
version: 5.2.1
|
||||||
resolution: "@react-native-camera-roll/camera-roll@npm:5.2.0"
|
resolution: "@react-native-camera-roll/camera-roll@npm:5.2.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react-native: ">=0.59"
|
react-native: ">=0.59"
|
||||||
checksum: 27800224bdbd128800e6a64046d76865814a43215ad3a0df9a93eeac64b523e3b68382d7cabb79b574e354d03dddc625b93fa2b6010ed04f6a89296038e3eabf
|
checksum: db901554170cced81db4b01c0e89324905fe4c6915a41e4cf4b6d1fa35f5894d18d1d734304d516824bff434784e4b571e9fa379e850f8dd4d42a28e3eab9eee
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -5692,13 +5692,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"expo-modules-core@npm:1.1.0":
|
"expo-modules-core@npm:1.1.1":
|
||||||
version: 1.1.0
|
version: 1.1.1
|
||||||
resolution: "expo-modules-core@npm:1.1.0"
|
resolution: "expo-modules-core@npm:1.1.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
compare-versions: ^3.4.0
|
compare-versions: ^3.4.0
|
||||||
invariant: ^2.2.4
|
invariant: ^2.2.4
|
||||||
checksum: f5add659b2f43c2784dce86986ec709feac38a60881afd013049ef86b106eeba9217427d01b9da27e00b678e9f47518ffa5d7676f95408c6e3966093d48252e8
|
checksum: 6a2b6b5d1f56f197ddf8b3f3a638d8f15534657218bdf895bd96c07cb34fc8404761d6a9206527fa5b67baade60858b4530de147fe1f3b7db8205c5d9d059b31
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -5741,6 +5741,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"expo-screen-orientation@npm:^5.0.1":
|
||||||
|
version: 5.0.1
|
||||||
|
resolution: "expo-screen-orientation@npm:5.0.1"
|
||||||
|
peerDependencies:
|
||||||
|
expo: "*"
|
||||||
|
checksum: 7ede30533a8c492f82b58c3b8be110b6373ffcc2cbe273299d9f15d9aa943d678d8aaffb3d2565780b45d1d5a2a1ddea54d813fc84c06e30e3cfd59abbd8e30e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"expo-secure-store@npm:^12.0.0":
|
"expo-secure-store@npm:^12.0.0":
|
||||||
version: 12.0.0
|
version: 12.0.0
|
||||||
resolution: "expo-secure-store@npm:12.0.0"
|
resolution: "expo-secure-store@npm:12.0.0"
|
||||||
@ -5791,9 +5800,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"expo@npm:^47.0.11":
|
"expo@npm:^47.0.12":
|
||||||
version: 47.0.11
|
version: 47.0.12
|
||||||
resolution: "expo@npm:47.0.11"
|
resolution: "expo@npm:47.0.12"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": ^7.14.0
|
"@babel/runtime": ^7.14.0
|
||||||
"@expo/cli": 0.4.11
|
"@expo/cli": 0.4.11
|
||||||
@ -5810,7 +5819,7 @@ __metadata:
|
|||||||
expo-font: ~11.0.1
|
expo-font: ~11.0.1
|
||||||
expo-keep-awake: ~11.0.1
|
expo-keep-awake: ~11.0.1
|
||||||
expo-modules-autolinking: 1.0.1
|
expo-modules-autolinking: 1.0.1
|
||||||
expo-modules-core: 1.1.0
|
expo-modules-core: 1.1.1
|
||||||
fbemitter: ^3.0.0
|
fbemitter: ^3.0.0
|
||||||
getenv: ^1.0.0
|
getenv: ^1.0.0
|
||||||
invariant: ^2.2.4
|
invariant: ^2.2.4
|
||||||
@ -5823,7 +5832,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
expo: bin/cli.js
|
expo: bin/cli.js
|
||||||
checksum: 5e470d7b0c94ddade3f77e4e5038f025635f432f1e56a9e541455f5e71b5f4919c7c756639b5bf31ff6b877ad254a77290efe0305d48712e5406c76f3c8b7e77
|
checksum: 24f7073660fb4c4c76a398d722d1fcae1ea654463faf2a730e986f884618a93618d1ee9116fe6c8199338c2e8aa716c2d389344a967824d264e2440eb7f16340
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -9416,11 +9425,11 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-i18next@npm:^12.1.1":
|
"react-i18next@npm:^12.1.4":
|
||||||
version: 12.1.1
|
version: 12.1.4
|
||||||
resolution: "react-i18next@npm:12.1.1"
|
resolution: "react-i18next@npm:12.1.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": ^7.14.5
|
"@babel/runtime": ^7.20.6
|
||||||
html-parse-stringify: ^3.0.1
|
html-parse-stringify: ^3.0.1
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
i18next: ">= 19.0.0"
|
i18next: ">= 19.0.0"
|
||||||
@ -9430,7 +9439,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
react-native:
|
react-native:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: baeb1957d70281d1a95ef0b94801bc2e6bf17066a49a45f32cd1a706ead3ddce2ab3e7b321e07c43f082efa9987c2d55856a5bdf419d9ad628dbfa9ec87af8ea
|
checksum: 6f0f8a47f0bf7da2c9ac383c88b6ef02446d9d1aa2609cfcc98d8a28999da85361f065fbe7f7f4af910df4e8d53af7879db4b9cd681274d582fc7bdd1b07813b
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -11254,7 +11263,7 @@ __metadata:
|
|||||||
"@mattermost/react-native-paste-input": ^0.5.2
|
"@mattermost/react-native-paste-input": ^0.5.2
|
||||||
"@neverdull-agency/expo-unlimited-secure-store": ^1.0.10
|
"@neverdull-agency/expo-unlimited-secure-store": ^1.0.10
|
||||||
"@react-native-async-storage/async-storage": ~1.17.11
|
"@react-native-async-storage/async-storage": ~1.17.11
|
||||||
"@react-native-camera-roll/camera-roll": ^5.2.0
|
"@react-native-camera-roll/camera-roll": ^5.2.1
|
||||||
"@react-native-clipboard/clipboard": ^1.11.1
|
"@react-native-clipboard/clipboard": ^1.11.1
|
||||||
"@react-native-community/blur": ^4.3.0
|
"@react-native-community/blur": ^4.3.0
|
||||||
"@react-native-community/netinfo": 9.3.7
|
"@react-native-community/netinfo": 9.3.7
|
||||||
@ -11281,7 +11290,7 @@ __metadata:
|
|||||||
chalk: ^4.1.2
|
chalk: ^4.1.2
|
||||||
diff: ^5.1.0
|
diff: ^5.1.0
|
||||||
dotenv: ^16.0.3
|
dotenv: ^16.0.3
|
||||||
expo: ^47.0.11
|
expo: ^47.0.12
|
||||||
expo-auth-session: ^3.8.0
|
expo-auth-session: ^3.8.0
|
||||||
expo-av: ^13.1.0
|
expo-av: ^13.1.0
|
||||||
expo-constants: ^14.1.0
|
expo-constants: ^14.1.0
|
||||||
@ -11293,6 +11302,7 @@ __metadata:
|
|||||||
expo-notifications: ^0.17.0
|
expo-notifications: ^0.17.0
|
||||||
expo-random: ^13.0.0
|
expo-random: ^13.0.0
|
||||||
expo-screen-capture: ^5.0.0
|
expo-screen-capture: ^5.0.0
|
||||||
|
expo-screen-orientation: ^5.0.1
|
||||||
expo-secure-store: ^12.0.0
|
expo-secure-store: ^12.0.0
|
||||||
expo-splash-screen: ^0.17.5
|
expo-splash-screen: ^0.17.5
|
||||||
expo-store-review: ^6.1.0
|
expo-store-review: ^6.1.0
|
||||||
@ -11304,7 +11314,7 @@ __metadata:
|
|||||||
lodash: ^4.17.21
|
lodash: ^4.17.21
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
react-i18next: ^12.1.1
|
react-i18next: ^12.1.4
|
||||||
react-intl: ^6.2.5
|
react-intl: ^6.2.5
|
||||||
react-native: ^0.70.6
|
react-native: ^0.70.6
|
||||||
react-native-animated-spinkit: ^1.5.2
|
react-native-animated-spinkit: ^1.5.2
|
||||||
|
Reference in New Issue
Block a user