mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Actions are working well!
This commit is contained in:
		
							
								
								
									
										38
									
								
								src/@types/store.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								src/@types/store.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,11 @@ | ||||
| declare namespace store { | ||||
|   type AsyncStatus = 'idle' | 'loading' | 'succeeded' | 'failed' | ||||
|  | ||||
|   type InstanceInfoState = { | ||||
|     local: string | ||||
|     localToken: string | ||||
|     remote: string | ||||
|   } | ||||
|  | ||||
|   type TimelinePage = | ||||
|   type Pages = | ||||
|     | 'Following' | ||||
|     | 'Local' | ||||
|     | 'LocalPublic' | ||||
| @@ -20,28 +18,14 @@ declare namespace store { | ||||
|     | 'Account_All' | ||||
|     | 'Account_Media' | ||||
|  | ||||
|   type TimelineState = { | ||||
|     toots: mastodon.Status[] | [] | ||||
|     pointer?: string | ||||
|     status: AsyncStatus | ||||
|   } | ||||
|  | ||||
|   type TimelinesState = { | ||||
|     Following: TimelineState | ||||
|     Local: TimelineState | ||||
|     LocalPublic: TimelineState | ||||
|     RemotePublic: TimelineState | ||||
|     Notifications: TimelineState | ||||
|     Hashtag: TimelineState | ||||
|     List: TimelineState | ||||
|     Toot: TimelineState | ||||
|     Account_Default: TimelineState | ||||
|     Account_All: TimelineState | ||||
|     Account_Media: TimelineState | ||||
|   } | ||||
|  | ||||
|   type AccountState = { | ||||
|     account: mastodon.Account | {} | ||||
|     status: AsyncStatus | ||||
|   } | ||||
|   type QueryKey = [ | ||||
|     Pages, | ||||
|     { | ||||
|       page: Pages | ||||
|       hashtag?: string | ||||
|       list?: string | ||||
|       toot?: string | ||||
|       account?: string | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import { | ||||
|   ActionSheetIOS, | ||||
|   Alert, | ||||
|   Clipboard, | ||||
|   Modal, | ||||
|   Pressable, | ||||
| @@ -8,13 +9,51 @@ import { | ||||
|   Text, | ||||
|   View | ||||
| } from 'react-native' | ||||
| import { useDispatch } from 'react-redux' | ||||
| import { useMutation, useQueryCache } from 'react-query' | ||||
| import { Feather } from '@expo/vector-icons' | ||||
|  | ||||
| import action from './action' | ||||
| import client from 'src/api/client' | ||||
|  | ||||
| import Success from './Responses/Success' | ||||
|  | ||||
| const fireMutation = async ({ | ||||
|   id, | ||||
|   type, | ||||
|   stateKey, | ||||
|   prevState | ||||
| }: { | ||||
|   id: string | ||||
|   type: 'favourite' | 'reblog' | 'bookmark' | 'mute' | 'pin' | ||||
|   stateKey: 'favourited' | 'reblogged' | 'bookmarked' | 'muted' | 'pinned' | ||||
|   prevState: boolean | ||||
| }) => { | ||||
|   let res = await client({ | ||||
|     method: 'post', | ||||
|     instance: 'local', | ||||
|     endpoint: `statuses/${id}/${prevState ? 'un' : ''}${type}` | ||||
|   }) | ||||
|   res = await client({ | ||||
|     method: 'post', | ||||
|     instance: 'local', | ||||
|     endpoint: `statuses/${id}/${prevState ? 'un' : ''}${type}` | ||||
|   }) | ||||
|  | ||||
|   if (!res.body[stateKey] === prevState) { | ||||
|     return Promise.resolve(res.body) | ||||
|   } else { | ||||
|     const alert = { | ||||
|       title: 'This is a title', | ||||
|       message: 'This is a message' | ||||
|     } | ||||
|     Alert.alert(alert.title, alert.message, [ | ||||
|       { text: 'OK', onPress: () => console.log('OK Pressed') } | ||||
|     ]) | ||||
|     return Promise.reject() | ||||
|   } | ||||
| } | ||||
|  | ||||
| export interface Props { | ||||
|   queryKey: store.QueryKey | ||||
|   id: string | ||||
|   url: string | ||||
|   replies_count: number | ||||
| @@ -22,10 +61,11 @@ export interface Props { | ||||
|   reblogged?: boolean | ||||
|   favourites_count: number | ||||
|   favourited?: boolean | ||||
|   bookmarked: boolean | ||||
|   bookmarked?: boolean | ||||
| } | ||||
|  | ||||
| const Actions: React.FC<Props> = ({ | ||||
|   queryKey, | ||||
|   id, | ||||
|   url, | ||||
|   replies_count, | ||||
| @@ -35,7 +75,6 @@ const Actions: React.FC<Props> = ({ | ||||
|   favourited, | ||||
|   bookmarked | ||||
| }) => { | ||||
|   const dispatch = useDispatch() | ||||
|   const [modalVisible, setModalVisible] = useState(false) | ||||
|  | ||||
|   const [successMessage, setSuccessMessage] = useState() | ||||
| @@ -46,10 +85,42 @@ const Actions: React.FC<Props> = ({ | ||||
|     return () => {} | ||||
|   }, [successMessage]) | ||||
|  | ||||
|   const queryCache = useQueryCache() | ||||
|   const [mutateAction] = useMutation(fireMutation, { | ||||
|     onMutate: () => { | ||||
|       queryCache.cancelQueries(queryKey) | ||||
|       const prevData = queryCache.getQueryData(queryKey) | ||||
|       return prevData | ||||
|     }, | ||||
|     onSuccess: (newData, params) => { | ||||
|       if (params.type === 'reblog') { | ||||
|         queryCache.invalidateQueries(['Following', { page: 'Following' }]) | ||||
|       } | ||||
|       // queryCache.setQueryData(queryKey, (oldData: any) => { | ||||
|       //   oldData && | ||||
|       //     oldData.map((paging: any) => { | ||||
|       //       paging.toots.map( | ||||
|       //         (status: mastodon.Status | mastodon.Notification, i: number) => { | ||||
|       //           if (status.id === newData.id) { | ||||
|       //             paging.toots[i] = newData | ||||
|       //           } | ||||
|       //         } | ||||
|       //       ) | ||||
|       //     }) | ||||
|       //   return oldData | ||||
|       // }) | ||||
|       return Promise.resolve() | ||||
|     }, | ||||
|     onError: (err, variables, prevData) => { | ||||
|       queryCache.setQueryData(queryKey, prevData) | ||||
|     }, | ||||
|     onSettled: () => { | ||||
|       queryCache.invalidateQueries(queryKey) | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Success message={successMessage} /> | ||||
|  | ||||
|       <View style={styles.actions}> | ||||
|         <Pressable style={styles.action}> | ||||
|           <Feather name='message-circle' color='gray' /> | ||||
| @@ -59,12 +130,11 @@ const Actions: React.FC<Props> = ({ | ||||
|         <Pressable | ||||
|           style={styles.action} | ||||
|           onPress={() => | ||||
|             action({ | ||||
|               dispatch, | ||||
|             mutateAction({ | ||||
|               id, | ||||
|               type: 'reblog', | ||||
|               stateKey: 'reblogged', | ||||
|               statePrev: reblogged || false | ||||
|               prevState: reblogged || false | ||||
|             }) | ||||
|           } | ||||
|         > | ||||
| @@ -74,12 +144,11 @@ const Actions: React.FC<Props> = ({ | ||||
|         <Pressable | ||||
|           style={styles.action} | ||||
|           onPress={() => | ||||
|             action({ | ||||
|               dispatch, | ||||
|             mutateAction({ | ||||
|               id, | ||||
|               type: 'favourite', | ||||
|               stateKey: 'favourited', | ||||
|               statePrev: favourited || false | ||||
|               prevState: favourited || false | ||||
|             }) | ||||
|           } | ||||
|         > | ||||
| @@ -89,12 +158,11 @@ const Actions: React.FC<Props> = ({ | ||||
|         <Pressable | ||||
|           style={styles.action} | ||||
|           onPress={() => | ||||
|             action({ | ||||
|               dispatch, | ||||
|             mutateAction({ | ||||
|               id, | ||||
|               type: 'bookmark', | ||||
|               stateKey: 'bookmarked', | ||||
|               statePrev: bookmarked | ||||
|               prevState: bookmarked || false | ||||
|             }) | ||||
|           } | ||||
|         > | ||||
|   | ||||
| @@ -1,46 +0,0 @@ | ||||
| import { Dispatch } from '@reduxjs/toolkit' | ||||
| import { Alert } from 'react-native' | ||||
|  | ||||
| import client from 'src/api/client' | ||||
| // import { updateStatus } from 'src/stacks/common/timelineSlice' | ||||
|  | ||||
| const action = async ({ | ||||
|   dispatch, | ||||
|   id, | ||||
|   type, | ||||
|   stateKey, | ||||
|   statePrev | ||||
| }: { | ||||
|   dispatch: Dispatch | ||||
|   id: string | ||||
|   type: 'favourite' | 'reblog' | 'bookmark' | 'mute' | 'pin' | ||||
|   stateKey: 'favourited' | 'reblogged' | 'bookmarked' | 'muted' | 'pinned' | ||||
|   statePrev: boolean | ||||
| }): Promise<void> => { | ||||
|   const alert = { | ||||
|     title: 'This is a title', | ||||
|     message: 'This is a message' | ||||
|   } | ||||
|  | ||||
|   // ISSUE: https://github.com/tootsuite/mastodon/issues/3166 | ||||
|   let res = await client({ | ||||
|     method: 'post', | ||||
|     instance: 'local', | ||||
|     endpoint: `statuses/${id}/${statePrev ? 'un' : ''}${type}` | ||||
|   }) | ||||
|   res = await client({ | ||||
|     method: 'post', | ||||
|     instance: 'local', | ||||
|     endpoint: `statuses/${id}/${statePrev ? 'un' : ''}${type}` | ||||
|   }) | ||||
|  | ||||
|   if (!res.body[stateKey] === statePrev) { | ||||
|     // dispatch(updateStatus(res.body)) | ||||
|   } else { | ||||
|     Alert.alert(alert.title, alert.message, [ | ||||
|       { text: 'OK', onPress: () => console.log('OK Pressed') } | ||||
|     ]) | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default action | ||||
| @@ -13,9 +13,10 @@ import Actions from './Status/Actions' | ||||
|  | ||||
| export interface Props { | ||||
|   status: mastodon.Status | ||||
|   queryKey: store.QueryKey | ||||
| } | ||||
|  | ||||
| const StatusInTimeline: React.FC<Props> = ({ status }) => { | ||||
| const StatusInTimeline: React.FC<Props> = ({ status, queryKey }) => { | ||||
|   const navigation = useNavigation() | ||||
|  | ||||
|   let actualContent = status.reblog ? status.reblog : status | ||||
| @@ -75,6 +76,7 @@ const StatusInTimeline: React.FC<Props> = ({ status }) => { | ||||
|               {actualContent.card && <Card card={actualContent.card} />} | ||||
|             </Pressable> | ||||
|             <Actions | ||||
|               queryKey={queryKey} | ||||
|               id={actualContent.id} | ||||
|               url={actualContent.url} | ||||
|               replies_count={actualContent.replies_count} | ||||
|   | ||||
| @@ -28,6 +28,7 @@ const Header = ({ | ||||
|   size: { width: number; height: number } | ||||
| }) => { | ||||
|   if (uri) { | ||||
|     const heightRatio = size ? size.height / size.width : 1 / 2 | ||||
|     return ( | ||||
|       <Image | ||||
|         source={{ uri: uri }} | ||||
| @@ -36,7 +37,7 @@ const Header = ({ | ||||
|           { | ||||
|             height: | ||||
|               Dimensions.get('window').width * | ||||
|               (size ? size.height / size.width : 1 / 2) | ||||
|               (heightRatio > 0.5 ? 1 / 2 : heightRatio) | ||||
|           } | ||||
|         ]} | ||||
|       /> | ||||
| @@ -144,6 +145,7 @@ const Toots = ({ account }: { account: string }) => { | ||||
|                 page={item} | ||||
|                 account={account} | ||||
|                 disableRefresh | ||||
|                 scrollEnabled={false} | ||||
|               /> | ||||
|             </View> | ||||
|           ) | ||||
| @@ -156,17 +158,6 @@ const Toots = ({ account }: { account: string }) => { | ||||
|           index | ||||
|         })} | ||||
|         horizontal | ||||
|         ListHeaderComponent={ | ||||
|           <View | ||||
|             style={{ | ||||
|               width: Dimensions.get('window').width, | ||||
|               height: 100, | ||||
|               position: 'absolute' | ||||
|             }} | ||||
|           > | ||||
|             <Text>Test</Text> | ||||
|           </View> | ||||
|         } | ||||
|         onMomentumScrollEnd={() => { | ||||
|           setSegmentManuallyTriggered(false) | ||||
|         }} | ||||
| @@ -204,7 +195,6 @@ const Account: React.FC<Props> = ({ | ||||
|   ) | ||||
|  | ||||
|   // const stateRelationships = useSelector(relationshipsState) | ||||
|   const [loaded, setLoaded] = useState(false) | ||||
|   interface isHeaderImageSize { | ||||
|     width: number | ||||
|     height: number | ||||
| @@ -214,19 +204,18 @@ const Account: React.FC<Props> = ({ | ||||
|   >(undefined) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (data.header) { | ||||
|     if (isSuccess && data.header) { | ||||
|       Image.getSize(data.header, (width, height) => { | ||||
|         setHeaderImageSize({ width, height }) | ||||
|         setLoaded(true) | ||||
|       }) | ||||
|     } else { | ||||
|       setLoaded(true) | ||||
|       setHeaderImageSize({ width: 3, height: 1 }) | ||||
|     } | ||||
|   }, [data]) | ||||
|   }, [data, isSuccess]) | ||||
|  | ||||
|   // add emoji support | ||||
|   return isSuccess ? ( | ||||
|     <View> | ||||
|   return isSuccess && headerImageSize ? ( | ||||
|     <ScrollView> | ||||
|       {headerImageSize && ( | ||||
|         <Header | ||||
|           uri={data.header} | ||||
| @@ -238,7 +227,7 @@ const Account: React.FC<Props> = ({ | ||||
|       )} | ||||
|       <Information account={data} emojis={data.emojis} /> | ||||
|       <Toots account={id} /> | ||||
|     </View> | ||||
|     </ScrollView> | ||||
|   ) : ( | ||||
|     <></> | ||||
|   ) | ||||
|   | ||||
| @@ -4,17 +4,19 @@ import { setFocusHandler, useInfiniteQuery } from 'react-query' | ||||
|  | ||||
| import StatusInNotifications from 'src/components/StatusInNotifications' | ||||
| import StatusInTimeline from 'src/components/StatusInTimeline' | ||||
| import store from './store' | ||||
| import { timelineFetch } from './timelineFetch' | ||||
|  | ||||
| // Opening nesting hashtag pages | ||||
|  | ||||
| export interface Props { | ||||
|   page: store.TimelinePage | ||||
|   page: store.Pages | ||||
|   hashtag?: string | ||||
|   list?: string | ||||
|   toot?: string | ||||
|   account?: string | ||||
|   disableRefresh?: boolean | ||||
|   scrollEnabled?: boolean | ||||
| } | ||||
|  | ||||
| const Timeline: React.FC<Props> = ({ | ||||
| @@ -23,7 +25,8 @@ const Timeline: React.FC<Props> = ({ | ||||
|   list, | ||||
|   toot, | ||||
|   account, | ||||
|   disableRefresh = false | ||||
|   disableRefresh = false, | ||||
|   scrollEnabled = true | ||||
| }) => { | ||||
|   setFocusHandler(handleFocus => { | ||||
|     const handleAppStateChange = (appState: string) => { | ||||
| @@ -35,6 +38,10 @@ const Timeline: React.FC<Props> = ({ | ||||
|     return () => AppState.removeEventListener('change', handleAppStateChange) | ||||
|   }) | ||||
|  | ||||
|   const queryKey: store.QueryKey = [ | ||||
|     page, | ||||
|     { page, hashtag, list, toot, account } | ||||
|   ] | ||||
|   const { | ||||
|     isLoading, | ||||
|     isFetchingMore, | ||||
| @@ -42,10 +49,7 @@ const Timeline: React.FC<Props> = ({ | ||||
|     isSuccess, | ||||
|     data, | ||||
|     fetchMore | ||||
|   } = useInfiniteQuery( | ||||
|     [page, { page, hashtag, list, toot, account }], | ||||
|     timelineFetch | ||||
|   ) | ||||
|   } = useInfiniteQuery(queryKey, timelineFetch) | ||||
|   const flattenData = data ? data.flatMap(d => [...d?.toots]) : [] | ||||
|  | ||||
|   let content | ||||
| @@ -58,13 +62,14 @@ const Timeline: React.FC<Props> = ({ | ||||
|       <> | ||||
|         <FlatList | ||||
|           style={{ minHeight: '100%' }} | ||||
|           scrollEnabled={scrollEnabled} // For timeline in Account view | ||||
|           data={flattenData} | ||||
|           keyExtractor={({ id }) => id} | ||||
|           renderItem={({ item, index, separators }) => | ||||
|             page === 'Notifications' ? ( | ||||
|               <StatusInNotifications key={index} status={item} /> | ||||
|             ) : ( | ||||
|               <StatusInTimeline key={index} status={item} /> | ||||
|               <StatusInTimeline key={index} status={item} queryKey={queryKey} /> | ||||
|             ) | ||||
|           } | ||||
|           // {...(state.pointer && { initialScrollIndex: state.pointer })} | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import { uniqBy } from 'lodash' | ||||
|  | ||||
| import client from 'src/api/client' | ||||
|  | ||||
| export const timelineFetch = async ( | ||||
| @@ -93,7 +95,7 @@ export const timelineFetch = async ( | ||||
|           pinned: 'true' | ||||
|         } | ||||
|       }) | ||||
|       const toots = res.body | ||||
|       let toots: mastodon.Status[] = res.body | ||||
|       res = await client({ | ||||
|         method: 'get', | ||||
|         instance: 'local', | ||||
| @@ -102,7 +104,7 @@ export const timelineFetch = async ( | ||||
|           exclude_replies: 'true' | ||||
|         } | ||||
|       }) | ||||
|       toots.push(...res.body) | ||||
|       toots = uniqBy([...toots, ...res.body], 'id') | ||||
|       return Promise.resolve({ toots: toots }) | ||||
|  | ||||
|     case 'Account_All': | ||||
|   | ||||
		Reference in New Issue
	
	Block a user