From 4e4bfc12ed9485be8b269da367d6ebd8742c4452 Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Sun, 16 Jan 2022 15:27:52 +0100 Subject: [PATCH 01/10] Limit downloading dsym to current version --- fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 919c0f52..aa8e270a 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -108,7 +108,7 @@ private_lane :build_ios do silent: true ) upload_to_app_store( ipa: IPA_FILE, app_version: VERSION ) - download_dsyms + download_dsyms( version: VERSION, build_number: BUILD_NUMBER, wait_for_dsym_processing: true ) sentry_upload_dsym( org_slug: ENV["SENTRY_ORGANIZATION"], project_slug: ENV["SENTRY_PROJECT"], From 0b4a8ead84abe9fcc2bb10d5dd61d76a79c8859f Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Sun, 16 Jan 2022 15:28:02 +0100 Subject: [PATCH 02/10] Fix crrashing --- src/components/Timeline/Default.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 47e18c98..9a208205 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -50,7 +50,7 @@ const TimelineDefault: React.FC = ({ const actualStatus = item.reblog ? item.reblog : item - const ownAccount = actualStatus.account.id === instanceAccount?.id + const ownAccount = actualStatus.account?.id === instanceAccount?.id if ( !highlighted && From 9a41dd21914d14d1a2fa97c0e784e967bf1ac765 Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Sun, 16 Jan 2022 23:26:05 +0100 Subject: [PATCH 03/10] MVP last read position --- src/Screens.tsx | 4 +- src/components/Timeline.tsx | 36 +++++- src/components/Timeline/Lookback.tsx | 37 ++++++ .../Timeline/Shared/Attachment/Video.tsx | 6 +- src/i18n/en/components/timeline.json | 3 + src/i18n/zh-Hans/components/timeline.json | 3 + src/screens/Tabs/Local.tsx | 37 +++++- src/screens/Tabs/Me/Root/Collections.tsx | 14 +-- src/screens/Tabs/Me/Settings/Dev.tsx | 6 +- src/screens/Tabs/Public.tsx | 38 +++++- src/startup/netInfo.ts | 118 +++++++++++++----- src/store.ts | 2 +- src/utils/migrations/contexts/migration.ts | 6 + src/utils/migrations/contexts/v1.ts | 17 +++ src/utils/migrations/instances/migration.ts | 15 +++ src/utils/migrations/instances/v6.ts | 66 ++++++++++ src/utils/queryHooks/timeline.ts | 58 ++++++++- src/utils/slices/contextsSlice.ts | 20 +-- src/utils/slices/instances/add.ts | 5 + src/utils/slices/instancesSlice.ts | 42 ++++++- 20 files changed, 446 insertions(+), 87 deletions(-) create mode 100644 src/components/Timeline/Lookback.tsx create mode 100644 src/utils/migrations/contexts/v1.ts create mode 100644 src/utils/migrations/instances/v6.ts diff --git a/src/Screens.tsx b/src/Screens.tsx index 229eff2e..9b6bf5a8 100644 --- a/src/Screens.tsx +++ b/src/Screens.tsx @@ -25,7 +25,7 @@ import { addScreenshotListener } from 'expo-screen-capture' import React, { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Platform, StatusBar } from 'react-native' -import { onlineManager, useQueryClient } from 'react-query' +import { useQueryClient } from 'react-query' import { useDispatch, useSelector } from 'react-redux' import * as Sentry from 'sentry-expo' @@ -51,11 +51,9 @@ const Screens: React.FC = ({ localCorrupt }) => { useEffect(() => { switch (isConnected) { case true: - onlineManager.setOnline(isConnected) removeMessage() break case false: - onlineManager.setOnline(isConnected) displayMessage({ mode, type: 'error', diff --git a/src/components/Timeline.tsx b/src/components/Timeline.tsx index f554c58e..3b168aa6 100644 --- a/src/components/Timeline.tsx +++ b/src/components/Timeline.tsx @@ -1,7 +1,10 @@ import ComponentSeparator from '@components/Separator' import { useScrollToTop } from '@react-navigation/native' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' -import { getInstanceActive } from '@utils/slices/instancesSlice' +import { + getInstanceActive, + updateInstanceTimelineLookback +} from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { RefObject, useCallback, useRef } from 'react' @@ -10,13 +13,14 @@ import { FlatListProps, Platform, RefreshControl, - StyleSheet + StyleSheet, + ViewabilityConfigCallbackPairs } from 'react-native' import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import TimelineEmpty from './Timeline/Empty' import TimelineFooter from './Timeline/Footer' import TimelineRefresh, { @@ -31,6 +35,7 @@ export interface Props { queryKey: QueryKeyTimeline disableRefresh?: boolean disableInfinity?: boolean + lookback?: Extract customProps: Partial> & Pick, 'renderItem'> } @@ -40,6 +45,7 @@ const Timeline: React.FC = ({ queryKey, disableRefresh = false, disableInfinity = false, + lookback, customProps }) => { // Switching account update timeline @@ -124,6 +130,27 @@ const Timeline: React.FC = ({ } }) + const dispatch = useDispatch() + const viewabilityPairs = useRef([ + { + viewabilityConfig: { + minimumViewTime: 10, + viewAreaCoveragePercentThreshold: 10 + }, + onViewableItemsChanged: ({ viewableItems }) => { + lookback && + dispatch( + updateInstanceTimelineLookback({ + [lookback]: { + queryKey, + ids: viewableItems.map(item => item.key).slice(0, 3) + } + }) + ) + } + } + ]) + useScrollToTop(flRef) return ( <> @@ -157,6 +184,9 @@ const Timeline: React.FC = ({ maintainVisibleContentPosition={{ minIndexForVisible: 0 }} + {...(lookback && { + viewabilityConfigCallbackPairs: viewabilityPairs.current + })} {...androidRefreshControl} {...customProps} /> diff --git a/src/components/Timeline/Lookback.tsx b/src/components/Timeline/Lookback.tsx new file mode 100644 index 00000000..d263c4ee --- /dev/null +++ b/src/components/Timeline/Lookback.tsx @@ -0,0 +1,37 @@ +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet, Text, View } from 'react-native' + +const TimelineLookback = React.memo( + () => { + const { t } = useTranslation('componentTimeline') + const { theme } = useTheme() + + return ( + + + {t('lookback.message')} + + + ) + }, + () => true +) + +const styles = StyleSheet.create({ + base: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + padding: StyleConstants.Spacing.S + }, + text: { + ...StyleConstants.FontStyle.S + } +}) + +export default TimelineLookback diff --git a/src/components/Timeline/Shared/Attachment/Video.tsx b/src/components/Timeline/Shared/Attachment/Video.tsx index 4c204716..a51c4c67 100644 --- a/src/components/Timeline/Shared/Attachment/Video.tsx +++ b/src/components/Timeline/Shared/Attachment/Video.tsx @@ -60,11 +60,9 @@ const AttachmentVideo: React.FC = ({ const appState = useRef(AppState.currentState) useEffect(() => { - AppState.addEventListener('change', _handleAppStateChange) + const appState = AppState.addEventListener('change', _handleAppStateChange) - return () => { - AppState.removeEventListener('change', _handleAppStateChange) - } + return () => appState.remove() }, []) const _handleAppStateChange = async (nextAppState: AppStateStatus) => { if (appState.current.match(/active/) && nextAppState.match(/inactive/)) { diff --git a/src/i18n/en/components/timeline.json b/src/i18n/en/components/timeline.json index d301f76a..30c63de1 100644 --- a/src/i18n/en/components/timeline.json +++ b/src/i18n/en/components/timeline.json @@ -11,6 +11,9 @@ "end": { "message": "The end, what about a cup of <0 />" }, + "lookback": { + "message": "Last read at" + }, "refresh": { "fetchPreviousPage": "Newer from here", "refetch": "To latest" diff --git a/src/i18n/zh-Hans/components/timeline.json b/src/i18n/zh-Hans/components/timeline.json index 7910bf49..52592cd7 100644 --- a/src/i18n/zh-Hans/components/timeline.json +++ b/src/i18n/zh-Hans/components/timeline.json @@ -11,6 +11,9 @@ "end": { "message": "居然刷到底了,喝杯 <0 /> 吧" }, + "lookback": { + "message": "上次阅读至" + }, "refresh": { "fetchPreviousPage": "较新于此的嘟嘟", "refetch": "最新的嘟嘟" diff --git a/src/screens/Tabs/Local.tsx b/src/screens/Tabs/Local.tsx index 21afed29..29498c3e 100644 --- a/src/screens/Tabs/Local.tsx +++ b/src/screens/Tabs/Local.tsx @@ -1,16 +1,21 @@ import analytics from '@components/analytics' import { HeaderCenter, HeaderRight } from '@components/Header' +import ComponentSeparator from '@components/Separator' import Timeline from '@components/Timeline' import TimelineDefault from '@components/Timeline/Default' +import TimelineLookback from '@components/Timeline/Lookback' import { createNativeStackNavigator } from '@react-navigation/native-stack' import { ScreenTabsScreenProps, TabLocalStackParamList } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' +import { getInstanceTimelinesLookback } from '@utils/slices/instancesSlice' +import { StyleConstants } from '@utils/styles/constants' import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Platform } from 'react-native' +import { useSelector } from 'react-redux' import TabSharedRoot from './Shared/Root' const Stack = createNativeStackNavigator() @@ -43,13 +48,35 @@ const TabLocal = React.memo( [i18n.language] ) - const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }] - const renderItem = useCallback( - ({ item }) => , - [] + const timelinesLookback = useSelector( + getInstanceTimelinesLookback, + () => true ) + const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }] + const renderItem = useCallback(({ item }) => { + if (timelinesLookback?.['Following']?.ids?.[0] === item.id) { + return ( + <> + + + + + ) + } + return + }, []) const children = useCallback( - () => , + () => ( + + ), [] ) diff --git a/src/screens/Tabs/Me/Root/Collections.tsx b/src/screens/Tabs/Me/Root/Collections.tsx index bd75030f..3f0aecc6 100644 --- a/src/screens/Tabs/Me/Root/Collections.tsx +++ b/src/screens/Tabs/Me/Root/Collections.tsx @@ -2,7 +2,10 @@ import { MenuContainer, MenuRow } from '@components/Menu' import { useNavigation } from '@react-navigation/native' import { useAnnouncementQuery } from '@utils/queryHooks/announcement' import { useListsQuery } from '@utils/queryHooks/lists' -import { getMePage, updateContextMePage } from '@utils/slices/contextsSlice' +import { + getInstanceMePage, + updateInstanceMePage +} from '@utils/slices/instancesSlice' import { getInstancePush } from '@utils/slices/instancesSlice' import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' @@ -13,10 +16,7 @@ const Collections: React.FC = () => { const navigation = useNavigation() const dispatch = useDispatch() - const mePage = useSelector( - getMePage, - (a, b) => a.announcements.unread === b.announcements.unread - ) + const mePage = useSelector(getInstanceMePage) const listsQuery = useListsQuery({ options: { @@ -26,7 +26,7 @@ const Collections: React.FC = () => { useEffect(() => { if (listsQuery.isSuccess) { dispatch( - updateContextMePage({ + updateInstanceMePage({ lists: { shown: listsQuery.data?.length ? true : false } }) ) @@ -42,7 +42,7 @@ const Collections: React.FC = () => { useEffect(() => { if (announcementsQuery.isSuccess) { dispatch( - updateContextMePage({ + updateInstanceMePage({ announcements: { shown: announcementsQuery.data?.length ? true : false, unread: announcementsQuery.data.filter( diff --git a/src/screens/Tabs/Me/Settings/Dev.tsx b/src/screens/Tabs/Me/Settings/Dev.tsx index fc3860f9..c7675a79 100644 --- a/src/screens/Tabs/Me/Settings/Dev.tsx +++ b/src/screens/Tabs/Me/Settings/Dev.tsx @@ -7,7 +7,7 @@ import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' -import { Text } from 'react-native' +import { DevSettings, Text } from 'react-native' import { useSelector } from 'react-redux' const SettingsDev: React.FC = () => { @@ -68,7 +68,9 @@ const SettingsDev: React.FC = () => { marginBottom: StyleConstants.Spacing.Global.PagePadding }} destructive - onPress={() => persistor.purge()} + onPress={() => { + persistor.purge().then(() => DevSettings.reload()) + }} />