mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Refine notifications
https://github.com/tooot-app/app/issues/306 https://github.com/tooot-app/app/issues/305 This one uses the positive filtering that is added since v3.5, that a such a filter won't be shown as there is no way to check if a user is an admin or not and showing a useless option for majority users won't be a good experience.
This commit is contained in:
		
							
								
								
									
										1
									
								
								src/@types/mastodon.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/@types/mastodon.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -341,6 +341,7 @@ declare namespace Mastodon { | ||||
|       | 'favourite' | ||||
|       | 'poll' | ||||
|       | 'status' | ||||
|       | 'update' | ||||
|     created_at: string | ||||
|     account: Account | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import { useAppDispatch } from '@root/store' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { TabMeStackNavigationProp } from '@utils/navigation/navigators' | ||||
| import addInstance from '@utils/slices/instances/add' | ||||
| import { checkInstanceFeature, Instance } from '@utils/slices/instancesSlice' | ||||
| import { checkInstanceFeature } from '@utils/slices/instancesSlice' | ||||
| import * as AuthSession from 'expo-auth-session' | ||||
| import React, { useEffect } from 'react' | ||||
| import { useQueryClient } from 'react-query' | ||||
| @@ -12,7 +13,7 @@ export interface Props { | ||||
|   instanceDomain: string | ||||
|   // Domain can be different than uri | ||||
|   instance: Mastodon.Instance | ||||
|   appData: Instance['appData'] | ||||
|   appData: InstanceLatest['appData'] | ||||
|   goBack?: boolean | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ const TimelineActioned = React.memo( | ||||
|       navigation.push('Tab-Shared-Account', { account }) | ||||
|     }, []) | ||||
|  | ||||
|     const children = useMemo(() => { | ||||
|     const children = () => { | ||||
|       switch (action) { | ||||
|         case 'pinned': | ||||
|           return ( | ||||
| @@ -48,7 +48,6 @@ const TimelineActioned = React.memo( | ||||
|               {content(t('shared.actioned.pinned'))} | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'favourite': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -63,7 +62,6 @@ const TimelineActioned = React.memo( | ||||
|               </Pressable> | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'follow': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -78,7 +76,6 @@ const TimelineActioned = React.memo( | ||||
|               </Pressable> | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'follow_request': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -93,7 +90,6 @@ const TimelineActioned = React.memo( | ||||
|               </Pressable> | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'poll': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -106,7 +102,6 @@ const TimelineActioned = React.memo( | ||||
|               {content(t('shared.actioned.poll'))} | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'reblog': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -125,7 +120,6 @@ const TimelineActioned = React.memo( | ||||
|               </Pressable> | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'status': | ||||
|           return ( | ||||
|             <> | ||||
| @@ -140,9 +134,22 @@ const TimelineActioned = React.memo( | ||||
|               </Pressable> | ||||
|             </> | ||||
|           ) | ||||
|           break | ||||
|         case 'update': | ||||
|           return ( | ||||
|             <> | ||||
|               <Icon | ||||
|                 name='BarChart2' | ||||
|                 size={StyleConstants.Font.Size.S} | ||||
|                 color={iconColor} | ||||
|                 style={styles.icon} | ||||
|               /> | ||||
|               {content(t('shared.actioned.update'))} | ||||
|             </> | ||||
|           ) | ||||
|         default: | ||||
|           return <></> | ||||
|       } | ||||
|     }, []) | ||||
|     } | ||||
|  | ||||
|     return ( | ||||
|       <View | ||||
| @@ -153,8 +160,9 @@ const TimelineActioned = React.memo( | ||||
|           paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S, | ||||
|           paddingRight: StyleConstants.Spacing.Global.PagePadding | ||||
|         }} | ||||
|         children={children} | ||||
|       /> | ||||
|       > | ||||
|         {children()} | ||||
|       </View> | ||||
|     ) | ||||
|   }, | ||||
|   () => true | ||||
|   | ||||
| @@ -8,5 +8,20 @@ | ||||
|     "feature": "deprecate_auth_follow", | ||||
|     "version": 3.5, | ||||
|     "reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0" | ||||
|   }, | ||||
|   { | ||||
|     "feature": "notification_type_status", | ||||
|     "version": 3.3, | ||||
|     "reference": "https://docs.joinmastodon.org/entities/notification/#required-attributes" | ||||
|   }, | ||||
|   { | ||||
|     "feature": "notification_type_update", | ||||
|     "version": 3.5, | ||||
|     "reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0" | ||||
|   }, | ||||
|   { | ||||
|     "feature": "notification_types_positive_filter", | ||||
|     "version": 3.5, | ||||
|     "reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0" | ||||
|   } | ||||
| ] | ||||
| @@ -29,7 +29,8 @@ | ||||
|       "reblog": { | ||||
|         "default": "{{name}} boosted", | ||||
|         "notification": "{{name}} boosted your toot" | ||||
|       } | ||||
|       }, | ||||
|       "update": "Reblog has been edited" | ||||
|     }, | ||||
|     "actions": { | ||||
|       "reply": { | ||||
|   | ||||
| @@ -8,11 +8,13 @@ | ||||
|       "heading": "Show notification types", | ||||
|       "content": { | ||||
|         "follow": "$t(screenTabs:me.push.follow.heading)", | ||||
|         "follow_request": "Follow request", | ||||
|         "favourite": "$t(screenTabs:me.push.favourite.heading)", | ||||
|         "reblog": "$t(screenTabs:me.push.reblog.heading)", | ||||
|         "mention": "$t(screenTabs:me.push.mention.heading)", | ||||
|         "poll": "$t(screenTabs:me.push.poll.heading)", | ||||
|         "follow_request": "Follow request" | ||||
|         "status": "Toot from subscribed users", | ||||
|         "update": "Reblog has been edited" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import MenuHeader from '@components/Menu/Header' | ||||
| import MenuRow from '@components/Menu/Row' | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import { | ||||
|   checkInstanceFeature, | ||||
|   getInstanceNotificationsFilter, | ||||
|   updateInstanceNotificationsFilter | ||||
| } from '@utils/slices/instancesSlice' | ||||
| @@ -33,42 +34,63 @@ const ActionsNotificationsFilter: React.FC = () => { | ||||
|     return null | ||||
|   } | ||||
|  | ||||
|   const hasTypeStatus = useSelector( | ||||
|     checkInstanceFeature('notification_type_status') | ||||
|   ) | ||||
|   const hasTypeUpdate = useSelector( | ||||
|     checkInstanceFeature('notification_type_update') | ||||
|   ) | ||||
|   const options = useMemo(() => { | ||||
|     return ( | ||||
|       instanceNotificationsFilter && | ||||
|       ( | ||||
|         [ | ||||
|           'follow', | ||||
|           'follow_request', | ||||
|           'favourite', | ||||
|           'reblog', | ||||
|           'mention', | ||||
|           'poll', | ||||
|           'follow_request' | ||||
|           'status', | ||||
|           'update' | ||||
|         ] as [ | ||||
|           'follow', | ||||
|           'follow_request', | ||||
|           'favourite', | ||||
|           'reblog', | ||||
|           'mention', | ||||
|           'poll', | ||||
|           'follow_request' | ||||
|           'status', | ||||
|           'update' | ||||
|         ] | ||||
|       ).map(type => ( | ||||
|         <MenuRow | ||||
|           key={type} | ||||
|           title={t(`content.notificationsFilter.content.${type}`)} | ||||
|           switchValue={instanceNotificationsFilter[type]} | ||||
|           switchOnValueChange={() => | ||||
|             dispatch( | ||||
|               updateInstanceNotificationsFilter({ | ||||
|                 ...instanceNotificationsFilter, | ||||
|                 [type]: !instanceNotificationsFilter[type] | ||||
|               }) | ||||
|             ) | ||||
|       ) | ||||
|         .filter(type => { | ||||
|           switch (type) { | ||||
|             case 'status': | ||||
|               return hasTypeStatus | ||||
|             case 'update': | ||||
|               return hasTypeUpdate | ||||
|             default: | ||||
|               return true | ||||
|           } | ||||
|         /> | ||||
|       )) | ||||
|         }) | ||||
|         .map(type => ( | ||||
|           <MenuRow | ||||
|             key={type} | ||||
|             title={t(`content.notificationsFilter.content.${type}`)} | ||||
|             switchValue={instanceNotificationsFilter[type]} | ||||
|             switchOnValueChange={() => | ||||
|               dispatch( | ||||
|                 updateInstanceNotificationsFilter({ | ||||
|                   ...instanceNotificationsFilter, | ||||
|                   [type]: !instanceNotificationsFilter[type] | ||||
|                 }) | ||||
|               ) | ||||
|             } | ||||
|           /> | ||||
|         )) | ||||
|     ) | ||||
|   }, [instanceNotificationsFilter]) | ||||
|   }, [instanceNotificationsFilter, hasTypeStatus, hasTypeUpdate]) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|   | ||||
| @@ -5,11 +5,8 @@ import ComponentInstance from '@components/Instance' | ||||
| import CustomText from '@components/Text' | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import initQuery from '@utils/initQuery' | ||||
| import { | ||||
|   getInstanceActive, | ||||
|   getInstances, | ||||
|   Instance | ||||
| } from '@utils/slices/instancesSlice' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import React, { useEffect, useRef } from 'react' | ||||
| @@ -19,7 +16,7 @@ import { ScrollView } from 'react-native-gesture-handler' | ||||
| import { useSelector } from 'react-redux' | ||||
|  | ||||
| interface Props { | ||||
|   instance: Instance | ||||
|   instance: InstanceLatest | ||||
|   selected?: boolean | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import { isRelease } from '@utils/checkEnvironment' | ||||
| import * as Sentry from 'sentry-expo' | ||||
| import log from './log' | ||||
|  | ||||
| @@ -7,7 +6,7 @@ const sentry = () => { | ||||
|   Sentry.init({ | ||||
|     dsn: 'https://53348b60ff844d52886e90251b3a5f41@o917354.ingest.sentry.io/6410576', | ||||
|     enableInExpoDevelopment: false, | ||||
|     debug: !isRelease, | ||||
|     // debug: !isRelease, | ||||
|     autoSessionTracking: true | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -39,7 +39,7 @@ const instancesPersistConfig = { | ||||
|   key: 'instances', | ||||
|   prefix, | ||||
|   storage: Platform.OS === 'ios' ? secureStorage : AsyncStorage, | ||||
|   version: 9, | ||||
|   version: 10, | ||||
|   // @ts-ignore | ||||
|   migrate: createMigrate(instancesMigration) | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| import queryClient from '@helpers/queryClient' | ||||
| import { store } from '@root/store' | ||||
| import { InstanceLatest } from './migrations/instances/migration' | ||||
| // import { prefetchTimelineQuery } from './queryHooks/timeline' | ||||
| import { Instance, updateInstanceActive } from './slices/instancesSlice' | ||||
| import { updateInstanceActive } from './slices/instancesSlice' | ||||
|  | ||||
| const initQuery = async ({ | ||||
|   instance, | ||||
|   prefetch | ||||
| }: { | ||||
|   instance: Instance | ||||
|   instance: InstanceLatest | ||||
|   prefetch?: { enabled: boolean; page?: 'Following' | 'LocalPublic' } | ||||
| }) => { | ||||
|   store.dispatch(updateInstanceActive(instance)) | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { InstanceV6 } from './v6' | ||||
| import { InstanceV7 } from './v7' | ||||
| import { InstanceV8 } from './v8' | ||||
| import { InstanceV9 } from './v9' | ||||
| import { InstanceV10 } from './v10' | ||||
|  | ||||
| const instancesMigration = { | ||||
|   4: (state: InstanceV3): InstanceV4 => { | ||||
| @@ -99,7 +100,23 @@ const instancesMigration = { | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   10: (state: { instances: InstanceV9[] }): { instances: InstanceV10[] } => { | ||||
|     return { | ||||
|       instances: state.instances.map(instance => { | ||||
|         return { | ||||
|           ...instance, | ||||
|           notifications_filter: { | ||||
|             ...instance.notifications_filter, | ||||
|             status: true, | ||||
|             update: true | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| export { InstanceV10 as InstanceLatest } | ||||
|  | ||||
| export default instancesMigration | ||||
|   | ||||
							
								
								
									
										81
									
								
								src/utils/migrations/instances/v10.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/utils/migrations/instances/v10.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| import { ComposeStateDraft } from '@screens/Compose/utils/types' | ||||
| import { QueryKeyTimeline } from '@utils/queryHooks/timeline' | ||||
|  | ||||
| export type InstanceV10 = { | ||||
|   active: boolean | ||||
|   appData: { | ||||
|     clientId: string | ||||
|     clientSecret: string | ||||
|   } | ||||
|   url: string | ||||
|   token: string | ||||
|   uri: Mastodon.Instance['uri'] | ||||
|   urls: Mastodon.Instance['urls'] | ||||
|   account: { | ||||
|     id: Mastodon.Account['id'] | ||||
|     acct: Mastodon.Account['acct'] | ||||
|     avatarStatic: Mastodon.Account['avatar_static'] | ||||
|     preferences: Mastodon.Preferences | ||||
|   } | ||||
|   version: string | ||||
|   configuration?: Mastodon.Instance['configuration'] | ||||
|   filters: Mastodon.Filter[] | ||||
|   notifications_filter: { | ||||
|     follow: boolean | ||||
|     follow_request: boolean | ||||
|     favourite: boolean | ||||
|     reblog: boolean | ||||
|     mention: boolean | ||||
|     poll: boolean | ||||
|     status: boolean | ||||
|     update: boolean | ||||
|   } | ||||
|   push: { | ||||
|     global: { loading: boolean; value: boolean } | ||||
|     decode: { loading: boolean; value: boolean } | ||||
|     alerts: { | ||||
|       follow: { | ||||
|         loading: boolean | ||||
|         value: Mastodon.PushSubscription['alerts']['follow'] | ||||
|       } | ||||
|       favourite: { | ||||
|         loading: boolean | ||||
|         value: Mastodon.PushSubscription['alerts']['favourite'] | ||||
|       } | ||||
|       reblog: { | ||||
|         loading: boolean | ||||
|         value: Mastodon.PushSubscription['alerts']['reblog'] | ||||
|       } | ||||
|       mention: { | ||||
|         loading: boolean | ||||
|         value: Mastodon.PushSubscription['alerts']['mention'] | ||||
|       } | ||||
|       poll: { | ||||
|         loading: boolean | ||||
|         value: Mastodon.PushSubscription['alerts']['poll'] | ||||
|       } | ||||
|     } | ||||
|     keys: { | ||||
|       auth?: string | ||||
|       public?: string // legacy | ||||
|       private?: string // legacy | ||||
|     } | ||||
|   } | ||||
|   timelinesLookback?: { | ||||
|     [key: string]: { | ||||
|       queryKey: QueryKeyTimeline | ||||
|       ids: Mastodon.Status['id'][] | ||||
|     } | ||||
|   } | ||||
|   mePage: { | ||||
|     lists: { shown: boolean } | ||||
|     announcements: { shown: boolean; unread: number } | ||||
|   } | ||||
|   drafts: ComposeStateDraft[] | ||||
|   frequentEmojis: { | ||||
|     emoji: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'> | ||||
|     score: number | ||||
|     count: number | ||||
|     lastUsed: number | ||||
|   }[] | ||||
| } | ||||
| @@ -4,7 +4,8 @@ import { displayMessage } from '@components/Message' | ||||
| import navigationRef from '@helpers/navigationRef' | ||||
| import { useAppDispatch } from '@root/store' | ||||
| import { isDevelopment } from '@utils/checkEnvironment' | ||||
| import { disableAllPushes, Instance } from '@utils/slices/instancesSlice' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { disableAllPushes } from '@utils/slices/instancesSlice' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import { useEffect } from 'react' | ||||
| @@ -12,7 +13,7 @@ import { TFunction } from 'react-i18next' | ||||
|  | ||||
| export interface Params { | ||||
|   t: TFunction<'screens'> | ||||
|   instances: Instance[] | ||||
|   instances: InstanceLatest[] | ||||
| } | ||||
|  | ||||
| const pushUseConnect = ({ t, instances }: Params) => { | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| import { displayMessage } from '@components/Message' | ||||
| import queryClient from '@helpers/queryClient' | ||||
| import initQuery from '@utils/initQuery' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { QueryKeyTimeline } from '@utils/queryHooks/timeline' | ||||
| import { Instance } from '@utils/slices/instancesSlice' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import { useEffect } from 'react' | ||||
| import pushUseNavigate from './useNavigate' | ||||
|  | ||||
| export interface Params { | ||||
|   instances: Instance[] | ||||
|   instances: InstanceLatest[] | ||||
| } | ||||
|  | ||||
| const pushUseReceive = ({ instances }: Params) => { | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| import queryClient from '@helpers/queryClient' | ||||
| import initQuery from '@utils/initQuery' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { QueryKeyTimeline } from '@utils/queryHooks/timeline' | ||||
| import { Instance } from '@utils/slices/instancesSlice' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import { useEffect } from 'react' | ||||
| import pushUseNavigate from './useNavigate' | ||||
|  | ||||
| export interface Params { | ||||
|   instances: Instance[] | ||||
|   instances: InstanceLatest[] | ||||
| } | ||||
|  | ||||
| const pushUseRespond = ({ instances }: Params) => { | ||||
|   | ||||
| @@ -2,7 +2,10 @@ import apiInstance, { InstanceResponse } from '@api/instance' | ||||
| import haptics from '@components/haptics' | ||||
| import queryClient from '@helpers/queryClient' | ||||
| import { store } from '@root/store' | ||||
| import { getInstanceNotificationsFilter } from '@utils/slices/instancesSlice' | ||||
| import { | ||||
|   checkInstanceFeature, | ||||
|   getInstanceNotificationsFilter | ||||
| } from '@utils/slices/instancesSlice' | ||||
| import { AxiosError } from 'axios' | ||||
| import { uniqBy } from 'lodash' | ||||
| import { | ||||
| @@ -62,16 +65,26 @@ const queryFunction = async ({ | ||||
|     case 'Notifications': | ||||
|       const rootStore = store.getState() | ||||
|       const notificationsFilter = getInstanceNotificationsFilter(rootStore) | ||||
|       const usePositiveFilter = checkInstanceFeature( | ||||
|         'notification_types_positive_filter' | ||||
|       )(rootStore) | ||||
|       return apiInstance<Mastodon.Notification[]>({ | ||||
|         method: 'get', | ||||
|         url: 'notifications', | ||||
|         params: { | ||||
|           ...params, | ||||
|           ...(notificationsFilter && { | ||||
|             exclude_types: Object.keys(notificationsFilter) | ||||
|               // @ts-ignore | ||||
|               .filter(filter => notificationsFilter[filter] === false) | ||||
|           }) | ||||
|           ...(notificationsFilter && | ||||
|             (usePositiveFilter | ||||
|               ? { | ||||
|                   types: Object.keys(notificationsFilter) | ||||
|                     // @ts-ignore | ||||
|                     .filter(filter => notificationsFilter[filter] === true) | ||||
|                 } | ||||
|               : { | ||||
|                   exclude_types: Object.keys(notificationsFilter) | ||||
|                     // @ts-ignore | ||||
|                     .filter(filter => notificationsFilter[filter] === false) | ||||
|                 })) | ||||
|         } | ||||
|       }) | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import apiGeneral from '@api/general' | ||||
| import { createAsyncThunk } from '@reduxjs/toolkit' | ||||
| import { RootState } from '@root/store' | ||||
| import { Instance } from '../instancesSlice' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
|  | ||||
| const addInstance = createAsyncThunk( | ||||
|   'instances/add', | ||||
| @@ -11,11 +11,11 @@ const addInstance = createAsyncThunk( | ||||
|     instance, | ||||
|     appData | ||||
|   }: { | ||||
|     domain: Instance['url'] | ||||
|     token: Instance['token'] | ||||
|     domain: InstanceLatest['url'] | ||||
|     token: InstanceLatest['token'] | ||||
|     instance: Mastodon.Instance | ||||
|     appData: Instance['appData'] | ||||
|   }): Promise<{ type: 'add' | 'overwrite'; data: Instance }> => { | ||||
|     appData: InstanceLatest['appData'] | ||||
|   }): Promise<{ type: 'add' | 'overwrite'; data: InstanceLatest }> => { | ||||
|     const { store } = require('@root/store') | ||||
|     const instances = (store.getState() as RootState).instances.instances | ||||
|  | ||||
| @@ -81,11 +81,13 @@ const addInstance = createAsyncThunk( | ||||
|         filters, | ||||
|         notifications_filter: { | ||||
|           follow: true, | ||||
|           follow_request: true, | ||||
|           favourite: true, | ||||
|           reblog: true, | ||||
|           mention: true, | ||||
|           poll: true, | ||||
|           follow_request: true | ||||
|           status: true, | ||||
|           update: true | ||||
|         }, | ||||
|         push: { | ||||
|           global: { loading: false, value: false }, | ||||
|   | ||||
| @@ -2,7 +2,8 @@ import apiInstance from '@api/instance' | ||||
| import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot' | ||||
| import i18n from '@root/i18n/i18n' | ||||
| import { RootState } from '@root/store' | ||||
| import { getInstance, Instance } from '@utils/slices/instancesSlice' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import { getInstance } from '@utils/slices/instancesSlice' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import * as Random from 'expo-random' | ||||
| import { Platform } from 'react-native' | ||||
| @@ -34,7 +35,7 @@ const subscribe = async ({ | ||||
| const pushRegister = async ( | ||||
|   state: RootState, | ||||
|   expoToken: string | ||||
| ): Promise<Instance['push']['keys']['auth']> => { | ||||
| ): Promise<InstanceLatest['push']['keys']['auth']> => { | ||||
|   const instance = getInstance(state) | ||||
|   const instanceUrl = instance?.url | ||||
|   const instanceUri = instance?.uri | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { createAsyncThunk } from '@reduxjs/toolkit' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import * as AuthSession from 'expo-auth-session' | ||||
| import { Instance } from '../instancesSlice' | ||||
| import { updateInstancePush } from './updatePush' | ||||
|  | ||||
| const removeInstance = createAsyncThunk( | ||||
|   'instances/remove', | ||||
|   async (instance: Instance, { dispatch }): Promise<Instance> => { | ||||
|   async (instance: InstanceLatest, { dispatch }): Promise<InstanceLatest> => { | ||||
|     if (instance.push.global.value) { | ||||
|       dispatch(updateInstancePush(false)) | ||||
|     } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { createAsyncThunk } from '@reduxjs/toolkit' | ||||
| import { RootState } from '@root/store' | ||||
| import { isDevelopment } from '@utils/checkEnvironment' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import { Instance } from '../instancesSlice' | ||||
| import pushRegister from './push/register' | ||||
| import pushUnregister from './push/unregister' | ||||
|  | ||||
| @@ -11,7 +11,7 @@ export const updateInstancePush = createAsyncThunk( | ||||
|   async ( | ||||
|     disable: boolean, | ||||
|     { getState } | ||||
|   ): Promise<Instance['push']['keys']['auth'] | undefined> => { | ||||
|   ): Promise<InstanceLatest['push']['keys']['auth'] | undefined> => { | ||||
|     const state = getState() as RootState | ||||
|     const expoToken = isDevelopment | ||||
|       ? 'DEVELOPMENT_TOKEN_1' | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| import apiInstance from '@api/instance' | ||||
| import { createAsyncThunk } from '@reduxjs/toolkit' | ||||
| import { Instance } from '../instancesSlice' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
|  | ||||
| export const updateInstancePushAlert = createAsyncThunk( | ||||
|   'instances/updatePushAlert', | ||||
|   async ({ | ||||
|     alerts | ||||
|   }: { | ||||
|     changed: keyof Instance['push']['alerts'] | ||||
|     alerts: Instance['push']['alerts'] | ||||
|   }): Promise<Instance['push']['alerts']> => { | ||||
|     changed: keyof InstanceLatest['push']['alerts'] | ||||
|     alerts: InstanceLatest['push']['alerts'] | ||||
|   }): Promise<InstanceLatest['push']['alerts']> => { | ||||
|     const formData = new FormData() | ||||
|     Object.keys(alerts).map(alert => | ||||
|       // @ts-ignore | ||||
|   | ||||
| @@ -3,9 +3,10 @@ import { createAsyncThunk } from '@reduxjs/toolkit' | ||||
| import i18n from '@root/i18n/i18n' | ||||
| import { RootState } from '@root/store' | ||||
| import { isDevelopment } from '@utils/checkEnvironment' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import * as Notifications from 'expo-notifications' | ||||
| import { Platform } from 'react-native' | ||||
| import { getInstance, Instance } from '../instancesSlice' | ||||
| import { getInstance } from '../instancesSlice' | ||||
| import androidDefaults from './push/androidDefaults' | ||||
|  | ||||
| export const updateInstancePushDecode = createAsyncThunk( | ||||
| @@ -13,7 +14,7 @@ export const updateInstancePushDecode = createAsyncThunk( | ||||
|   async ( | ||||
|     disable: boolean, | ||||
|     { getState } | ||||
|   ): Promise<{ disable: Instance['push']['decode']['value'] }> => { | ||||
|   ): Promise<{ disable: InstanceLatest['push']['decode']['value'] }> => { | ||||
|     const state = getState() as RootState | ||||
|     const instance = getInstance(state) | ||||
|     if (!instance?.url || !instance.account.id || !instance.push.keys) { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import features from '@helpers/features' | ||||
| import { createSlice, PayloadAction } from '@reduxjs/toolkit' | ||||
| import { RootState } from '@root/store' | ||||
| import { ComposeStateDraft } from '@screens/Compose/utils/types' | ||||
| import { InstanceV9 } from '@utils/migrations/instances/v9' | ||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||
| import addInstance from './instances/add' | ||||
| import { checkEmojis } from './instances/checkEmojis' | ||||
| import removeInstance from './instances/remove' | ||||
| @@ -14,24 +14,25 @@ import { updateInstancePush } from './instances/updatePush' | ||||
| import { updateInstancePushAlert } from './instances/updatePushAlert' | ||||
| import { updateInstancePushDecode } from './instances/updatePushDecode' | ||||
|  | ||||
| export type Instance = InstanceV9 | ||||
|  | ||||
| export type InstancesState = { | ||||
|   instances: Instance[] | ||||
|   instances: InstanceLatest[] | ||||
| } | ||||
|  | ||||
| export const instancesInitialState: InstancesState = { | ||||
|   instances: [] | ||||
| } | ||||
|  | ||||
| const findInstanceActive = (instances: Instance[]) => | ||||
| const findInstanceActive = (instances: InstanceLatest[]) => | ||||
|   instances.findIndex(instance => instance.active) | ||||
|  | ||||
| const instancesSlice = createSlice({ | ||||
|   name: 'instances', | ||||
|   initialState: instancesInitialState, | ||||
|   reducers: { | ||||
|     updateInstanceActive: ({ instances }, action: PayloadAction<Instance>) => { | ||||
|     updateInstanceActive: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<InstanceLatest> | ||||
|     ) => { | ||||
|       instances = instances.map(instance => { | ||||
|         instance.active = | ||||
|           instance.url === action.payload.url && | ||||
| @@ -42,7 +43,9 @@ const instancesSlice = createSlice({ | ||||
|     }, | ||||
|     updateInstanceAccount: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<Pick<Instance['account'], 'acct' & 'avatarStatic'>> | ||||
|       action: PayloadAction< | ||||
|         Pick<InstanceLatest['account'], 'acct' & 'avatarStatic'> | ||||
|       > | ||||
|     ) => { | ||||
|       const activeIndex = findInstanceActive(instances) | ||||
|       instances[activeIndex].account = { | ||||
| @@ -52,7 +55,7 @@ const instancesSlice = createSlice({ | ||||
|     }, | ||||
|     updateInstanceNotificationsFilter: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<Instance['notifications_filter']> | ||||
|       action: PayloadAction<InstanceLatest['notifications_filter']> | ||||
|     ) => { | ||||
|       const activeIndex = findInstanceActive(instances) | ||||
|       instances[activeIndex].notifications_filter = action.payload | ||||
| @@ -99,7 +102,7 @@ const instancesSlice = createSlice({ | ||||
|     }, | ||||
|     updateInstanceTimelineLookback: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<Instance['timelinesLookback']> | ||||
|       action: PayloadAction<InstanceLatest['timelinesLookback']> | ||||
|     ) => { | ||||
|       const activeIndex = findInstanceActive(instances) | ||||
|       instances[activeIndex] && | ||||
| @@ -110,7 +113,7 @@ const instancesSlice = createSlice({ | ||||
|     }, | ||||
|     updateInstanceMePage: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<Partial<Instance['mePage']>> | ||||
|       action: PayloadAction<Partial<InstanceLatest['mePage']>> | ||||
|     ) => { | ||||
|       const activeIndex = findInstanceActive(instances) | ||||
|       instances[activeIndex].mePage = { | ||||
| @@ -120,10 +123,12 @@ const instancesSlice = createSlice({ | ||||
|     }, | ||||
|     countInstanceEmoji: ( | ||||
|       { instances }, | ||||
|       action: PayloadAction<Instance['frequentEmojis'][0]['emoji']> | ||||
|       action: PayloadAction<InstanceLatest['frequentEmojis'][0]['emoji']> | ||||
|     ) => { | ||||
|       const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week | ||||
|       const calculateScore = (emoji: Instance['frequentEmojis'][0]): number => { | ||||
|       const calculateScore = ( | ||||
|         emoji: InstanceLatest['frequentEmojis'][0] | ||||
|       ): number => { | ||||
|         var seconds = (new Date().getTime() - emoji.lastUsed) / 1000 | ||||
|         var score = emoji.count + 1 | ||||
|         var order = Math.log(Math.max(score, 1)) / Math.LN10 | ||||
| @@ -136,7 +141,7 @@ const instancesSlice = createSlice({ | ||||
|           e.emoji.shortcode === action.payload.shortcode && | ||||
|           e.emoji.url === action.payload.url | ||||
|       ) | ||||
|       let newEmojisSort: Instance['frequentEmojis'] | ||||
|       let newEmojisSort: InstanceLatest['frequentEmojis'] | ||||
|       if (foundEmojiIndex > -1) { | ||||
|         newEmojisSort = instances[activeIndex].frequentEmojis | ||||
|           .map((e, i) => | ||||
|   | ||||
		Reference in New Issue
	
	Block a user