mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Fixed #538
This commit is contained in:
		| @@ -1,5 +1,6 @@ | |||||||
| Enjoy toooting! This version includes following improvements and fixes: | Enjoy toooting! This version includes following improvements and fixes: | ||||||
| - Automatic setting detected language when tooting | - Automatic setting detected language when tooting | ||||||
|  | - Remember public timeline type selection | ||||||
| - Added notification for admins | - Added notification for admins | ||||||
| - Fix whole word filter matching | - Fix whole word filter matching | ||||||
| - Fix tablet cannot delete toot drafts | - Fix tablet cannot delete toot drafts | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| toooting愉快!此版本包括以下改进和修复: | toooting愉快!此版本包括以下改进和修复: | ||||||
| - 自动识别发嘟语言 | - 自动识别发嘟语言 | ||||||
|  | - 记住上次公共时间轴选项 | ||||||
| - 新增管理员推送通知 | - 新增管理员推送通知 | ||||||
| - 修复过滤整词功能 | - 修复过滤整词功能 | ||||||
| - 修复平板不能删除草稿 | - 修复平板不能删除草稿 | ||||||
| @@ -3,14 +3,18 @@ import Timeline from '@components/Timeline' | |||||||
| import TimelineDefault from '@components/Timeline/Default' | import TimelineDefault from '@components/Timeline/Default' | ||||||
| import SegmentedControl from '@react-native-community/segmented-control' | import SegmentedControl from '@react-native-community/segmented-control' | ||||||
| import { NativeStackScreenProps } from '@react-navigation/native-stack' | import { NativeStackScreenProps } from '@react-navigation/native-stack' | ||||||
|  | import { useAppDispatch } from '@root/store' | ||||||
|  | import { ContextsLatest } from '@utils/migrations/contexts/migration' | ||||||
| import { TabPublicStackParamList } from '@utils/navigation/navigators' | import { TabPublicStackParamList } from '@utils/navigation/navigators' | ||||||
| import usePopToTop from '@utils/navigation/usePopToTop' | import usePopToTop from '@utils/navigation/usePopToTop' | ||||||
| import { QueryKeyTimeline } from '@utils/queryHooks/timeline' | import { QueryKeyTimeline } from '@utils/queryHooks/timeline' | ||||||
|  | import { getPreviousSegment, updatePreviousSegment } from '@utils/slices/contextsSlice' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' | import { useTheme } from '@utils/styles/ThemeManager' | ||||||
| import { useEffect, useState } from 'react' | import { useEffect, useState } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { Dimensions } from 'react-native' | import { Dimensions } from 'react-native' | ||||||
| import { SceneMap, TabView } from 'react-native-tab-view' | import { SceneMap, TabView } from 'react-native-tab-view' | ||||||
|  | import { useSelector } from 'react-redux' | ||||||
|  |  | ||||||
| const Route = ({ route: { key: page } }: { route: any }) => { | const Route = ({ route: { key: page } }: { route: any }) => { | ||||||
|   const queryKey: QueryKeyTimeline = ['Timeline', { page }] |   const queryKey: QueryKeyTimeline = ['Timeline', { page }] | ||||||
| @@ -37,7 +41,12 @@ const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public | |||||||
|   const { mode } = useTheme() |   const { mode } = useTheme() | ||||||
|   const { t } = useTranslation('screenTabs') |   const { t } = useTranslation('screenTabs') | ||||||
|  |  | ||||||
|   const [segment, setSegment] = useState(0) |   const dispatch = useAppDispatch() | ||||||
|  |   const previousSegment = useSelector(getPreviousSegment, () => true) | ||||||
|  |   const segments: ContextsLatest['previousSegment'][] = ['Local', 'LocalPublic', 'Trending'] | ||||||
|  |   const [segment, setSegment] = useState<number>( | ||||||
|  |     segments.findIndex(segment => segment === previousSegment) | ||||||
|  |   ) | ||||||
|   const [routes] = useState([ |   const [routes] = useState([ | ||||||
|     { key: 'Local', title: t('tabs.public.segments.local') }, |     { key: 'Local', title: t('tabs.public.segments.local') }, | ||||||
|     { key: 'LocalPublic', title: t('tabs.public.segments.federated') }, |     { key: 'LocalPublic', title: t('tabs.public.segments.federated') }, | ||||||
| @@ -51,7 +60,10 @@ const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public | |||||||
|           appearance={mode} |           appearance={mode} | ||||||
|           values={routes.map(({ title }) => title)} |           values={routes.map(({ title }) => title)} | ||||||
|           selectedIndex={segment} |           selectedIndex={segment} | ||||||
|           onChange={({ nativeEvent }) => setSegment(nativeEvent.selectedSegmentIndex)} |           onChange={({ nativeEvent }) => { | ||||||
|  |             setSegment(nativeEvent.selectedSegmentIndex) | ||||||
|  |             dispatch(updatePreviousSegment(segments[nativeEvent.selectedSegmentIndex])) | ||||||
|  |           }} | ||||||
|           style={{ flexBasis: '65%' }} |           style={{ flexBasis: '65%' }} | ||||||
|         /> |         /> | ||||||
|       ), |       ), | ||||||
|   | |||||||
| @@ -1,8 +1,3 @@ | |||||||
| /// <reference types="@welldone-software/why-did-you-render" /> |  | ||||||
|  |  | ||||||
| import React from 'react' |  | ||||||
| import log from './log' |  | ||||||
|  |  | ||||||
| const dev = () => { | const dev = () => { | ||||||
|   if (__DEV__) { |   if (__DEV__) { | ||||||
|     // log('log', 'devs', 'initializing wdyr') |     // log('log', 'devs', 'initializing wdyr') | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| import createSecureStore from '@neverdull-agency/expo-unlimited-secure-store' | import createSecureStore from '@neverdull-agency/expo-unlimited-secure-store' | ||||||
| import AsyncStorage from '@react-native-async-storage/async-storage' | import AsyncStorage from '@react-native-async-storage/async-storage' | ||||||
| import { AnyAction, configureStore, Reducer } from '@reduxjs/toolkit' | import { AnyAction, configureStore, Reducer } from '@reduxjs/toolkit' | ||||||
| import contextsMigration from '@utils/migrations/contexts/migration' | import contextsMigration, { ContextsLatest } from '@utils/migrations/contexts/migration' | ||||||
| import instancesMigration from '@utils/migrations/instances/migration' | import instancesMigration from '@utils/migrations/instances/migration' | ||||||
| import settingsMigration from '@utils/migrations/settings/migration' | import settingsMigration from '@utils/migrations/settings/migration' | ||||||
| import appSlice, { AppState } from '@utils/slices/appSlice' | import appSlice, { AppState } from '@utils/slices/appSlice' | ||||||
| import contextsSlice, { ContextsState } from '@utils/slices/contextsSlice' | import contextsSlice from '@utils/slices/contextsSlice' | ||||||
| import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice' | import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice' | ||||||
| import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice' | import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice' | ||||||
| import { Platform } from 'react-native' | import { Platform } from 'react-native' | ||||||
| @@ -37,7 +37,7 @@ const contextsPersistConfig = { | |||||||
|   key: 'contexts', |   key: 'contexts', | ||||||
|   prefix, |   prefix, | ||||||
|   storage: AsyncStorage, |   storage: AsyncStorage, | ||||||
|   version: 2, |   version: 3, | ||||||
|   // @ts-ignore |   // @ts-ignore | ||||||
|   migrate: createMigrate(contextsMigration) |   migrate: createMigrate(contextsMigration) | ||||||
| } | } | ||||||
| @@ -64,7 +64,7 @@ const store = configureStore({ | |||||||
|   reducer: { |   reducer: { | ||||||
|     app: persistReducer(appPersistConfig, appSlice) as Reducer<AppState, AnyAction>, |     app: persistReducer(appPersistConfig, appSlice) as Reducer<AppState, AnyAction>, | ||||||
|     contexts: persistReducer(contextsPersistConfig, contextsSlice) as Reducer< |     contexts: persistReducer(contextsPersistConfig, contextsSlice) as Reducer< | ||||||
|       ContextsState, |       ContextsLatest, | ||||||
|       AnyAction |       AnyAction | ||||||
|     >, |     >, | ||||||
|     instances: persistReducer(instancesPersistConfig, instancesSlice) as Reducer< |     instances: persistReducer(instancesPersistConfig, instancesSlice) as Reducer< | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { ContextsV0 } from './v0' | import { ContextsV0 } from './v0' | ||||||
| import { ContextsV1 } from './v1' | import { ContextsV1 } from './v1' | ||||||
| import { ContextsV2 } from './v2' | import { ContextsV2 } from './v2' | ||||||
|  | import { ContextsV3 } from './v3' | ||||||
|  |  | ||||||
| const contextsMigration = { | const contextsMigration = { | ||||||
|   1: (state: ContextsV0): ContextsV1 => { |   1: (state: ContextsV0): ContextsV1 => { | ||||||
| @@ -15,7 +16,12 @@ const contextsMigration = { | |||||||
|   2: (state: ContextsV1): ContextsV2 => { |   2: (state: ContextsV1): ContextsV2 => { | ||||||
|     const { mePage, ...rest } = state |     const { mePage, ...rest } = state | ||||||
|     return rest |     return rest | ||||||
|  |   }, | ||||||
|  |   3: (state: ContextsV2): ContextsV3 => { | ||||||
|  |     return { ...state, previousSegment: 'Local' } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export { ContextsV3 as ContextsLatest } | ||||||
|  |  | ||||||
| export default contextsMigration | export default contextsMigration | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								src/utils/migrations/contexts/v3.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/utils/migrations/contexts/v3.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | import { ScreenTabsStackParamList } from '@utils/navigation/navigators' | ||||||
|  |  | ||||||
|  | export type ContextsV3 = { | ||||||
|  |   storeReview: { | ||||||
|  |     context: Readonly<number> | ||||||
|  |     current: number | ||||||
|  |     shown: boolean | ||||||
|  |   } | ||||||
|  |   publicRemoteNotice: { | ||||||
|  |     context: Readonly<number> | ||||||
|  |     current: number | ||||||
|  |     hidden: boolean | ||||||
|  |   } | ||||||
|  |   previousTab: Extract< | ||||||
|  |     keyof ScreenTabsStackParamList, | ||||||
|  |     'Tab-Local' | 'Tab-Public' | 'Tab-Notifications' | 'Tab-Me' | ||||||
|  |   > | ||||||
|  |   previousSegment: Extract<App.Pages, 'Local' | 'LocalPublic' | 'Trending'> | ||||||
|  | } | ||||||
| @@ -1,23 +1,10 @@ | |||||||
| import { createSlice, PayloadAction } from '@reduxjs/toolkit' | import { createSlice, PayloadAction } from '@reduxjs/toolkit' | ||||||
| import { RootState } from '@root/store' | import { RootState } from '@root/store' | ||||||
|  | import { ContextsLatest } from '@utils/migrations/contexts/migration' | ||||||
| import Constants from 'expo-constants' | import Constants from 'expo-constants' | ||||||
| import * as StoreReview from 'expo-store-review' | import * as StoreReview from 'expo-store-review' | ||||||
|  |  | ||||||
| export type ContextsState = { | export const contextsInitialState: ContextsLatest = { | ||||||
|   storeReview: { |  | ||||||
|     context: Readonly<number> |  | ||||||
|     current: number |  | ||||||
|     shown: boolean |  | ||||||
|   } |  | ||||||
|   publicRemoteNotice: { |  | ||||||
|     context: Readonly<number> |  | ||||||
|     current: number |  | ||||||
|     hidden: boolean |  | ||||||
|   } |  | ||||||
|   previousTab: 'Tab-Local' | 'Tab-Public' | 'Tab-Notifications' | 'Tab-Me' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const contextsInitialState = { |  | ||||||
|   // After 10 successful postings |   // After 10 successful postings | ||||||
|   storeReview: { |   storeReview: { | ||||||
|     context: 10, |     context: 10, | ||||||
| @@ -30,49 +17,46 @@ export const contextsInitialState = { | |||||||
|     current: 0, |     current: 0, | ||||||
|     hidden: false |     hidden: false | ||||||
|   }, |   }, | ||||||
|   previousTab: 'Tab-Me' |   previousTab: 'Tab-Me', | ||||||
|  |   previousSegment: 'Local' | ||||||
| } | } | ||||||
|  |  | ||||||
| const contextsSlice = createSlice({ | const contextsSlice = createSlice({ | ||||||
|   name: 'contexts', |   name: 'contexts', | ||||||
|   initialState: contextsInitialState as ContextsState, |   initialState: contextsInitialState, | ||||||
|   reducers: { |   reducers: { | ||||||
|     updateStoreReview: (state, action: PayloadAction<1>) => { |     updateStoreReview: (state, action: PayloadAction<1>) => { | ||||||
|       if (Constants.expoConfig?.extra?.environment === 'release') { |       if (Constants.expoConfig?.extra?.environment === 'release') { | ||||||
|         state.storeReview.current = state.storeReview.current + action.payload |         state.storeReview.current = state.storeReview.current + action.payload | ||||||
|         if (state.storeReview.current === state.storeReview.context) { |         if (state.storeReview.current === state.storeReview.context) { | ||||||
|           StoreReview?.isAvailableAsync().then(() => |           StoreReview?.isAvailableAsync().then(() => StoreReview.requestReview()) | ||||||
|             StoreReview.requestReview() |  | ||||||
|           ) |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     updatePublicRemoteNotice: (state, action: PayloadAction<1>) => { |     updatePublicRemoteNotice: (state, action: PayloadAction<1>) => { | ||||||
|       state.publicRemoteNotice.current = |       state.publicRemoteNotice.current = state.publicRemoteNotice.current + action.payload | ||||||
|         state.publicRemoteNotice.current + action.payload |       if (state.publicRemoteNotice.current === state.publicRemoteNotice.context) { | ||||||
|       if ( |  | ||||||
|         state.publicRemoteNotice.current === state.publicRemoteNotice.context |  | ||||||
|       ) { |  | ||||||
|         state.publicRemoteNotice.hidden = true |         state.publicRemoteNotice.hidden = true | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     updatePreviousTab: ( |     updatePreviousTab: (state, action: PayloadAction<ContextsLatest['previousTab']>) => { | ||||||
|       state, |  | ||||||
|       action: PayloadAction<ContextsState['previousTab']> |  | ||||||
|     ) => { |  | ||||||
|       state.previousTab = action.payload |       state.previousTab = action.payload | ||||||
|  |     }, | ||||||
|  |     updatePreviousSegment: (state, action: PayloadAction<ContextsLatest['previousSegment']>) => { | ||||||
|  |       state.previousSegment = action.payload | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
| export const getPublicRemoteNotice = (state: RootState) => | export const getPublicRemoteNotice = (state: RootState) => state.contexts.publicRemoteNotice | ||||||
|   state.contexts.publicRemoteNotice |  | ||||||
| export const getPreviousTab = (state: RootState) => state.contexts.previousTab | export const getPreviousTab = (state: RootState) => state.contexts.previousTab | ||||||
|  | export const getPreviousSegment = (state: RootState) => state.contexts.previousSegment | ||||||
| export const getContexts = (state: RootState) => state.contexts | export const getContexts = (state: RootState) => state.contexts | ||||||
|  |  | ||||||
| export const { | export const { | ||||||
|   updateStoreReview, |   updateStoreReview, | ||||||
|   updatePublicRemoteNotice, |   updatePublicRemoteNotice, | ||||||
|   updatePreviousTab |   updatePreviousTab, | ||||||
|  |   updatePreviousSegment | ||||||
| } = contextsSlice.actions | } = contextsSlice.actions | ||||||
| export default contextsSlice.reducer | export default contextsSlice.reducer | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user