mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Rewrite header actions
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| import React, { useCallback, useEffect, useMemo, useRef } from 'react' | ||||
| import { StyleSheet } from 'react-native' | ||||
| import { useInfiniteQuery } from 'react-query' | ||||
| import { InfiniteData, useInfiniteQuery } from 'react-query' | ||||
|  | ||||
| import TimelineNotifications from '@components/Timelines/Timeline/Notifications' | ||||
| import TimelineDefault from '@components/Timelines/Timeline/Default' | ||||
| @@ -12,6 +12,14 @@ import TimelineEnd from '@components/Timelines/Timeline/Shared/End' | ||||
| import { useScrollToTop } from '@react-navigation/native' | ||||
| import { FlatList } from 'react-native-gesture-handler' | ||||
|  | ||||
| export type TimelineData = | ||||
|   | InfiniteData<{ | ||||
|       toots: Mastodon.Status[] | ||||
|       pointer?: number | undefined | ||||
|       pinnedLength?: number | undefined | ||||
|     }> | ||||
|   | undefined | ||||
|  | ||||
| export interface Props { | ||||
|   page: App.Pages | ||||
|   hashtag?: string | ||||
|   | ||||
| @@ -44,6 +44,7 @@ const TimelineDefault: React.FC<Props> = ({ | ||||
|   const tootOnPress = useCallback( | ||||
|     () => | ||||
|       !isRemotePublic && | ||||
|       !highlighted && | ||||
|       navigation.push('Screen-Shared-Toot', { | ||||
|         toot: actualStatus | ||||
|       }), | ||||
| @@ -63,7 +64,11 @@ const TimelineDefault: React.FC<Props> = ({ | ||||
|           <TimelineContent status={actualStatus} highlighted={highlighted} /> | ||||
|         )} | ||||
|         {actualStatus.poll && ( | ||||
|           <TimelinePoll queryKey={queryKey} status={actualStatus} /> | ||||
|           <TimelinePoll | ||||
|             queryKey={queryKey} | ||||
|             poll={actualStatus.poll} | ||||
|             reblog={item.reblog ? true : false} | ||||
|           /> | ||||
|         )} | ||||
|         {actualStatus.media_attachments.length > 0 && ( | ||||
|           <TimelineAttachment status={actualStatus} width={contentWidth} /> | ||||
| @@ -103,7 +108,11 @@ const TimelineDefault: React.FC<Props> = ({ | ||||
|               : StyleConstants.Avatar.M + StyleConstants.Spacing.S | ||||
|           }} | ||||
|         > | ||||
|           <TimelineActions queryKey={queryKey} status={actualStatus} /> | ||||
|           <TimelineActions | ||||
|             queryKey={queryKey} | ||||
|             status={actualStatus} | ||||
|             reblog={item.reblog ? true : false} | ||||
|           /> | ||||
|         </View> | ||||
|       )} | ||||
|     </View> | ||||
| @@ -122,16 +131,4 @@ const styles = StyleSheet.create({ | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default React.memo(TimelineDefault, (prev, next) => { | ||||
|   let skipUpdate = true | ||||
|   skipUpdate = | ||||
|     prev.item.id === next.item.id && | ||||
|     prev.item.replies_count === next.item.replies_count && | ||||
|     prev.item.favourited === next.item.favourited && | ||||
|     prev.item.reblogged === next.item.reblogged && | ||||
|     prev.item.bookmarked === next.item.bookmarked && | ||||
|     prev.item.poll?.voted === next.item.poll?.voted && | ||||
|     prev.item.reblog?.poll?.voted === next.item.reblog?.poll?.voted | ||||
|  | ||||
|   return skipUpdate | ||||
| }) | ||||
| export default TimelineDefault | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import React, { useCallback, useMemo } from 'react' | ||||
| import { ActionSheetIOS, Pressable, StyleSheet, Text, View } from 'react-native' | ||||
| import { useMutation, useQueryClient } from 'react-query' | ||||
| import { InfiniteData, useMutation, useQueryClient } from 'react-query' | ||||
| import { Feather } from '@expo/vector-icons' | ||||
|  | ||||
| import client from '@api/client' | ||||
| @@ -9,6 +9,8 @@ import { toast } from '@components/toast' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import getCurrentTab from '@utils/getCurrentTab' | ||||
| import { findIndex } from 'lodash' | ||||
| import { TimelineData } from '../../Timeline' | ||||
|  | ||||
| const fireMutation = async ({ | ||||
|   id, | ||||
| @@ -46,9 +48,10 @@ const fireMutation = async ({ | ||||
| export interface Props { | ||||
|   queryKey: QueryKey.Timeline | ||||
|   status: Mastodon.Status | ||||
|   reblog: boolean | ||||
| } | ||||
|  | ||||
| const TimelineActions: React.FC<Props> = ({ queryKey, status }) => { | ||||
| const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|   const navigation = useNavigation() | ||||
|   const { theme } = useTheme() | ||||
|   const iconColor = theme.secondary | ||||
| @@ -65,18 +68,31 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => { | ||||
|         case 'favourite': | ||||
|         case 'reblog': | ||||
|         case 'bookmark': | ||||
|           queryClient.setQueryData(queryKey, (old: any) => { | ||||
|             old.pages.map((paging: any) => ({ | ||||
|               toots: paging.toots.map((toot: any) => { | ||||
|                 if (toot.id === id) { | ||||
|                   console.log(toot[stateKey]) | ||||
|                   toot[stateKey] = | ||||
|           queryClient.setQueryData<TimelineData>(queryKey, old => { | ||||
|             let tootIndex = -1 | ||||
|             const pageIndex = findIndex(old?.pages, page => { | ||||
|               const tempIndex = findIndex(page.toots, [ | ||||
|                 reblog ? 'reblog.id' : 'id', | ||||
|                 id | ||||
|               ]) | ||||
|               if (tempIndex >= 0) { | ||||
|                 tootIndex = tempIndex | ||||
|                 return true | ||||
|               } else { | ||||
|                 return false | ||||
|               } | ||||
|             }) | ||||
|  | ||||
|             if (pageIndex >= 0 && tootIndex >= 0) { | ||||
|               if (reblog) { | ||||
|                 old!.pages[pageIndex].toots[tootIndex].reblog![stateKey] = | ||||
|                   typeof prevState === 'boolean' ? !prevState : true | ||||
|               } else { | ||||
|                 old!.pages[pageIndex].toots[tootIndex][stateKey] = | ||||
|                   typeof prevState === 'boolean' ? !prevState : true | ||||
|               } | ||||
|                 return toot | ||||
|               }), | ||||
|               pointer: paging.pointer | ||||
|             })) | ||||
|             } | ||||
|  | ||||
|             return old | ||||
|           }) | ||||
|           break | ||||
|   | ||||
| @@ -36,9 +36,12 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => { | ||||
|  | ||||
|   // causing full re-render | ||||
|   useEffect(() => { | ||||
|     setTimeout(() => { | ||||
|     const timer = setTimeout(() => { | ||||
|       setSince(relativeTime(status.created_at)) | ||||
|     }, 1000) | ||||
|     return () => { | ||||
|       clearTimeout(timer) | ||||
|     } | ||||
|   }, [since]) | ||||
|  | ||||
|   const onPressAction = useCallback(() => setBottomSheetVisible(true), []) | ||||
|   | ||||
| @@ -70,14 +70,8 @@ const HeaderDefaultActionsAccount: React.FC<Props> = ({ | ||||
| }) => { | ||||
|   const queryClient = useQueryClient() | ||||
|   const { mutate } = useMutation(fireMutation, { | ||||
|     onMutate: () => { | ||||
|       queryClient.cancelQueries(queryKey) | ||||
|       const oldData = queryClient.getQueryData(queryKey) | ||||
|       return oldData | ||||
|     }, | ||||
|     onError: (err, _, oldData) => { | ||||
|       toast({ type: 'error', content: '请重试', autoHide: false }) | ||||
|       queryClient.setQueryData(queryKey, oldData) | ||||
|     onSettled: () => { | ||||
|       queryClient.invalidateQueries(queryKey) | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   | ||||
| @@ -42,15 +42,6 @@ const HeaderDefaultActionsDomain: React.FC<Props> = ({ | ||||
| }) => { | ||||
|   const queryClient = useQueryClient() | ||||
|   const { mutate } = useMutation(fireMutation, { | ||||
|     onMutate: () => { | ||||
|       queryClient.cancelQueries(queryKey) | ||||
|       const oldData = queryClient.getQueryData(queryKey) | ||||
|       return oldData | ||||
|     }, | ||||
|     onError: (err, _, oldData) => { | ||||
|       toast({ type: 'error', content: '请重试', autoHide: false }) | ||||
|       queryClient.setQueryData(queryKey, oldData) | ||||
|     }, | ||||
|     onSettled: () => { | ||||
|       queryClient.invalidateQueries(queryKey) | ||||
|     } | ||||
|   | ||||
| @@ -6,6 +6,8 @@ import client from '@api/client' | ||||
| import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu' | ||||
| import { toast } from '@components/toast' | ||||
| import getCurrentTab from '@utils/getCurrentTab' | ||||
| import { TimelineData } from '@root/components/Timelines/Timeline' | ||||
| import { findIndex } from 'lodash' | ||||
|  | ||||
| const fireMutation = async ({ | ||||
|   id, | ||||
| @@ -75,25 +77,39 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({ | ||||
|       switch (type) { | ||||
|         case 'mute': | ||||
|         case 'pin': | ||||
|           queryClient.setQueryData(queryKey, (old: any) => | ||||
|             old.pages.map((paging: any) => ({ | ||||
|               toots: paging.toots.map((toot: any) => { | ||||
|                 if (toot.id === id) { | ||||
|                   toot[stateKey] = | ||||
|                     typeof prevState === 'boolean' ? !prevState : true | ||||
|           queryClient.setQueryData<TimelineData>(queryKey, old => { | ||||
|             let tootIndex = -1 | ||||
|             const pageIndex = findIndex(old?.pages, page => { | ||||
|               const tempIndex = findIndex(page.toots, ['id', id]) | ||||
|               if (tempIndex >= 0) { | ||||
|                 tootIndex = tempIndex | ||||
|                 return true | ||||
|               } else { | ||||
|                 return false | ||||
|               } | ||||
|                 return toot | ||||
|               }), | ||||
|               pointer: paging.pointer | ||||
|             })) | ||||
|           ) | ||||
|             }) | ||||
|  | ||||
|             if (pageIndex >= 0 && tootIndex >= 0) { | ||||
|               old!.pages[pageIndex].toots[tootIndex][ | ||||
|                 stateKey as 'muted' | 'pinned' | ||||
|               ] = typeof prevState === 'boolean' ? !prevState : true | ||||
|             } | ||||
|  | ||||
|             return old | ||||
|           }) | ||||
|           break | ||||
|         case 'delete': | ||||
|           queryClient.setQueryData(queryKey, (old: any) => | ||||
|             old.pages.map((paging: any) => ({ | ||||
|               toots: paging.toots.filter((toot: any) => toot.id !== id), | ||||
|               pointer: paging.pointer | ||||
|           console.log('deleting toot') | ||||
|           queryClient.setQueryData<TimelineData>( | ||||
|             queryKey, | ||||
|             old => | ||||
|               old && { | ||||
|                 ...old, | ||||
|                 pages: old?.pages.map(paging => ({ | ||||
|                   ...paging, | ||||
|                   toots: paging.toots.filter(toot => toot.id !== id) | ||||
|                 })) | ||||
|               } | ||||
|           ) | ||||
|           break | ||||
|       } | ||||
| @@ -167,7 +183,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({ | ||||
|           }) | ||||
|         }} | ||||
|         iconFront='volume-x' | ||||
|         title={status.muted ? '取消隐藏对话' : '隐藏对话'} | ||||
|         title={status.muted ? '取消静音对话' : '静音对话'} | ||||
|       /> | ||||
|       {/* Also note that reblogs cannot be pinned. */} | ||||
|       {(status.visibility === 'public' || status.visibility === 'unlisted') && ( | ||||
|   | ||||
| @@ -10,6 +10,8 @@ import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
|  | ||||
| import Emojis from './Emojis' | ||||
| import { TimelineData } from '../../Timeline' | ||||
| import { findIndex } from 'lodash' | ||||
|  | ||||
| const fireMutation = async ({ | ||||
|   id, | ||||
| @@ -48,10 +50,11 @@ const fireMutation = async ({ | ||||
|  | ||||
| export interface Props { | ||||
|   queryKey: QueryKey.Timeline | ||||
|   status: Required<Mastodon.Status, 'poll'> | ||||
|   poll: NonNullable<Mastodon.Status['poll']> | ||||
|   reblog: boolean | ||||
| } | ||||
|  | ||||
| const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => { | ||||
| const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => { | ||||
|   const { theme } = useTheme() | ||||
|  | ||||
|   const queryClient = useQueryClient() | ||||
| @@ -60,26 +63,21 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => { | ||||
|       queryClient.cancelQueries(queryKey) | ||||
|       const oldData = queryClient.getQueryData(queryKey) | ||||
|  | ||||
|       queryClient.setQueryData(queryKey, (old: any) => | ||||
|         old.pages.map((paging: any) => ({ | ||||
|           toots: paging.toots.map((toot: any) => { | ||||
|             if (toot.poll?.id === id) { | ||||
|               const poll = toot.poll | ||||
|       const updatePoll = (poll: Mastodon.Poll): Mastodon.Poll => { | ||||
|         const myVotes = Object.keys(options).filter( | ||||
|           // @ts-ignore | ||||
|           option => options[option] | ||||
|         ) | ||||
|         const myVotesInt = myVotes.map(option => parseInt(option)) | ||||
|  | ||||
|               toot.poll = { | ||||
|                 ...toot.poll, | ||||
|         return { | ||||
|           ...poll, | ||||
|           votes_count: poll.votes_count | ||||
|             ? poll.votes_count + myVotes.length | ||||
|             : myVotes.length, | ||||
|           voters_count: poll.voters_count ? poll.voters_count + 1 : 1, | ||||
|           voted: true, | ||||
|           own_votes: myVotesInt, | ||||
|                 // @ts-ignore | ||||
|           options: poll.options.map((o, i) => { | ||||
|             if (myVotesInt.includes(i)) { | ||||
|               o.votes_count = o.votes_count + 1 | ||||
| @@ -88,11 +86,35 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => { | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|             return toot | ||||
|           }), | ||||
|           pointer: paging.pointer | ||||
|         })) | ||||
|  | ||||
|       queryClient.setQueryData<TimelineData>(queryKey, old => { | ||||
|         let tootIndex = -1 | ||||
|         const pageIndex = findIndex(old?.pages, page => { | ||||
|           const tempIndex = findIndex(page.toots, [ | ||||
|             reblog ? 'reblog.poll.id' : 'poll.id', | ||||
|             id | ||||
|           ]) | ||||
|           if (tempIndex >= 0) { | ||||
|             tootIndex = tempIndex | ||||
|             return true | ||||
|           } else { | ||||
|             return false | ||||
|           } | ||||
|         }) | ||||
|  | ||||
|         if (pageIndex >= 0 && tootIndex >= 0) { | ||||
|           if (reblog) { | ||||
|             old!.pages[pageIndex].toots[tootIndex].reblog!.poll = updatePoll( | ||||
|               old!.pages[pageIndex].toots[tootIndex].reblog!.poll! | ||||
|             ) | ||||
|           } else { | ||||
|             old!.pages[pageIndex].toots[tootIndex].poll = updatePoll( | ||||
|               old!.pages[pageIndex].toots[tootIndex].poll! | ||||
|             ) | ||||
|           } | ||||
|         } | ||||
|         return old | ||||
|       }) | ||||
|  | ||||
|       return oldData | ||||
|     }, | ||||
| @@ -300,5 +322,5 @@ const styles = StyleSheet.create({ | ||||
|  | ||||
| export default React.memo( | ||||
|   TimelinePoll, | ||||
|   (prev, next) => prev.status.poll.voted === next.status.poll.voted | ||||
|   (prev, next) => prev.poll.voted === next.poll.voted | ||||
| ) | ||||
|   | ||||
| @@ -179,6 +179,7 @@ const composeExistingState = ({ | ||||
| }): ComposeState => { | ||||
|   switch (type) { | ||||
|     case 'edit': | ||||
|       console.log(incomingStatus) | ||||
|       return { | ||||
|         ...composeInitialState, | ||||
|         ...(incomingStatus.spoiler_text?.length && { | ||||
| @@ -201,10 +202,10 @@ const composeExistingState = ({ | ||||
|             active: true, | ||||
|             total: incomingStatus.poll.options.length, | ||||
|             options: { | ||||
|               '0': incomingStatus.poll.options[0].title || undefined, | ||||
|               '1': incomingStatus.poll.options[1].title || undefined, | ||||
|               '2': incomingStatus.poll.options[2].title || undefined, | ||||
|               '3': incomingStatus.poll.options[3].title || undefined | ||||
|               '0': incomingStatus.poll.options[0]?.title || undefined, | ||||
|               '1': incomingStatus.poll.options[1]?.title || undefined, | ||||
|               '2': incomingStatus.poll.options[2]?.title || undefined, | ||||
|               '3': incomingStatus.poll.options[3]?.title || undefined | ||||
|             }, | ||||
|             multiple: incomingStatus.poll.multiple, | ||||
|             expire: '86400' // !!! | ||||
|   | ||||
		Reference in New Issue
	
	Block a user