mirror of
https://github.com/tooot-app/app
synced 2025-01-24 07:30:46 +01:00
Actions are working well!
This commit is contained in:
parent
631636db15
commit
c825895b92
4150
package-lock.json
generated
Normal file
4150
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@
|
||||
"expo-splash-screen": "~0.6.1",
|
||||
"expo-status-bar": "~1.0.2",
|
||||
"ky": "^0.24.0",
|
||||
"lodash": "^4.17.20",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz",
|
||||
@ -44,6 +45,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
||||
"@types/lodash": "^4.14.164",
|
||||
"@types/react": "^16.9.55",
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"@types/react-native": "^0.63.30",
|
||||
|
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':
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "react-native",
|
||||
"lib": ["dom", "esnext"],
|
||||
|
@ -1380,6 +1380,11 @@
|
||||
"@types/istanbul-lib-coverage" "*"
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/lodash@^4.14.164":
|
||||
version "4.14.164"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.164.tgz#52348bcf909ac7b4c1bcbeda5c23135176e5dfa0"
|
||||
integrity sha512-fXCEmONnrtbYUc5014avwBeMdhHHO8YJCkOBflUL9EoJBSKZ1dei+VO74fA7JkTHZ1GvZack2TyIw5U+1lT8jg==
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
|
Loading…
Reference in New Issue
Block a user