mirror of https://github.com/tooot-app/app
Updates
This commit is contained in:
parent
98a60df9d1
commit
09b960d368
|
@ -57,7 +57,6 @@ declare namespace QueryKey {
|
|||
type Timeline = [
|
||||
Pages,
|
||||
{
|
||||
page: Pages
|
||||
hashtag?: Mastodon.Tag['name']
|
||||
list?: Mastodon.List['id']
|
||||
toot?: Mastodon.Status
|
||||
|
|
|
@ -84,8 +84,7 @@ const renderNode = ({
|
|||
}}
|
||||
onPress={() => {
|
||||
navigation.navigate('Screen-Shared-Webview', {
|
||||
uri: href,
|
||||
domain: domain[1]
|
||||
uri: href
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -39,7 +39,12 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
|
|||
})
|
||||
}, [])
|
||||
|
||||
const [routes] = useState(content.map(p => ({ key: p.page })))
|
||||
const routes = content
|
||||
.filter(p =>
|
||||
localRegistered ? p : p.page === 'RemotePublic' ? p : undefined
|
||||
)
|
||||
.map(p => ({ key: p.page }))
|
||||
|
||||
const renderScene = ({
|
||||
route
|
||||
}: {
|
||||
|
@ -47,7 +52,11 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
|
|||
key: App.Pages
|
||||
}
|
||||
}) => {
|
||||
return <Timeline page={route.key} />
|
||||
return (
|
||||
(localRegistered || route.key === 'RemotePublic') && (
|
||||
<Timeline page={route.key} />
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -40,7 +40,6 @@ const Timeline: React.FC<Props> = ({
|
|||
const queryKey: QueryKey.Timeline = [
|
||||
page,
|
||||
{
|
||||
page,
|
||||
...(hashtag && { hashtag }),
|
||||
...(list && { list }),
|
||||
...(toot && { toot }),
|
||||
|
@ -91,27 +90,32 @@ const Timeline: React.FC<Props> = ({
|
|||
}, [status])
|
||||
|
||||
const flKeyExtrator = useCallback(({ id }) => id, [])
|
||||
const flRenderItem = useCallback(({ item, index }) => {
|
||||
switch (page) {
|
||||
case 'Conversations':
|
||||
return <TimelineConversation item={item} queryKey={queryKey} />
|
||||
case 'Notifications':
|
||||
return <TimelineNotifications notification={item} queryKey={queryKey} />
|
||||
default:
|
||||
return (
|
||||
<TimelineDefault
|
||||
item={item}
|
||||
queryKey={queryKey}
|
||||
index={index}
|
||||
{...(flattenPinnedLength &&
|
||||
flattenPinnedLength[0] && {
|
||||
pinnedLength: flattenPinnedLength[0]
|
||||
})}
|
||||
{...(toot && toot.id === item.id && { highlighted: true })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}, [])
|
||||
const flRenderItem = useCallback(
|
||||
({ item, index }) => {
|
||||
switch (page) {
|
||||
case 'Conversations':
|
||||
return <TimelineConversation item={item} queryKey={queryKey} />
|
||||
case 'Notifications':
|
||||
return (
|
||||
<TimelineNotifications notification={item} queryKey={queryKey} />
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<TimelineDefault
|
||||
item={item}
|
||||
queryKey={queryKey}
|
||||
index={index}
|
||||
{...(flattenPinnedLength &&
|
||||
flattenPinnedLength[0] && {
|
||||
pinnedLength: flattenPinnedLength[0]
|
||||
})}
|
||||
{...(toot && toot.id === item.id && { highlighted: true })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
[flattenPinnedLength[0]]
|
||||
)
|
||||
const flItemSeparatorComponent = useCallback(
|
||||
({ leadingItem }) => (
|
||||
<TimelineSeparator
|
||||
|
|
|
@ -74,7 +74,12 @@ const TimelineConversation: React.FC<Props> = ({
|
|||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<TimelineActions queryKey={queryKey} status={item.last_status!} />
|
||||
<TimelineActions
|
||||
queryKey={queryKey}
|
||||
status={item.last_status!}
|
||||
reblog={false}
|
||||
sameAccountRoot={false}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -12,6 +12,8 @@ import TimelineHeaderDefault from '@components/Timelines/Timeline/Shared/HeaderD
|
|||
import TimelinePoll from '@components/Timelines/Timeline/Shared/Poll'
|
||||
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { getLocalAccountId } from '@root/utils/slices/instancesSlice'
|
||||
|
||||
export interface Props {
|
||||
item: Mastodon.Status
|
||||
|
@ -29,6 +31,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
pinnedLength,
|
||||
highlighted = false
|
||||
}) => {
|
||||
const localAccountId = useSelector(getLocalAccountId)
|
||||
const isRemotePublic = queryKey[0] === 'RemotePublic'
|
||||
const navigation = useNavigation()
|
||||
|
||||
|
@ -68,6 +71,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
queryKey={queryKey}
|
||||
poll={actualStatus.poll}
|
||||
reblog={item.reblog ? true : false}
|
||||
sameAccount={actualStatus.account.id === localAccountId}
|
||||
/>
|
||||
)}
|
||||
{actualStatus.media_attachments.length > 0 && (
|
||||
|
@ -76,7 +80,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
{actualStatus.card && <TimelineCard card={actualStatus.card} />}
|
||||
</View>
|
||||
),
|
||||
[actualStatus.poll?.voted]
|
||||
[actualStatus.poll]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -95,6 +99,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
<TimelineHeaderDefault
|
||||
{...(!isRemotePublic && { queryKey })}
|
||||
status={actualStatus}
|
||||
sameAccount={actualStatus.account.id === localAccountId}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
@ -112,6 +117,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
queryKey={queryKey}
|
||||
status={actualStatus}
|
||||
reblog={item.reblog ? true : false}
|
||||
sameAccountRoot={item.account.id === localAccountId}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
@ -12,6 +12,8 @@ import TimelineHeaderNotification from '@components/Timelines/Timeline/Shared/He
|
|||
import TimelinePoll from '@components/Timelines/Timeline/Shared/Poll'
|
||||
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { getLocalAccountId } from '@root/utils/slices/instancesSlice'
|
||||
|
||||
export interface Props {
|
||||
notification: Mastodon.Notification
|
||||
|
@ -24,6 +26,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||
queryKey,
|
||||
highlighted = false
|
||||
}) => {
|
||||
const localAccountId = useSelector(getLocalAccountId)
|
||||
const navigation = useNavigation()
|
||||
const actualAccount = notification.status
|
||||
? notification.status.account
|
||||
|
@ -61,7 +64,12 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||
/>
|
||||
)}
|
||||
{notification.status.poll && (
|
||||
<TimelinePoll queryKey={queryKey} status={notification.status} />
|
||||
<TimelinePoll
|
||||
queryKey={queryKey}
|
||||
poll={notification.status.poll}
|
||||
reblog={false}
|
||||
sameAccount={notification.account.id === localAccountId}
|
||||
/>
|
||||
)}
|
||||
{notification.status.media_attachments.length > 0 && (
|
||||
<TimelineAttachment
|
||||
|
@ -100,7 +108,12 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<TimelineActions queryKey={queryKey} status={notification.status} />
|
||||
<TimelineActions
|
||||
queryKey={queryKey}
|
||||
status={notification.status}
|
||||
reblog={false}
|
||||
sameAccountRoot={notification.account.id === localAccountId}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
|
|
@ -11,6 +11,8 @@ import { useNavigation } from '@react-navigation/native'
|
|||
import getCurrentTab from '@utils/getCurrentTab'
|
||||
import { findIndex } from 'lodash'
|
||||
import { TimelineData } from '../../Timeline'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { getLocalAccountId } from '@root/utils/slices/instancesSlice'
|
||||
|
||||
const fireMutation = async ({
|
||||
id,
|
||||
|
@ -49,9 +51,15 @@ export interface Props {
|
|||
queryKey: QueryKey.Timeline
|
||||
status: Mastodon.Status
|
||||
reblog: boolean
|
||||
sameAccountRoot: boolean
|
||||
}
|
||||
|
||||
const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
||||
const TimelineActions: React.FC<Props> = ({
|
||||
queryKey,
|
||||
status,
|
||||
reblog,
|
||||
sameAccountRoot
|
||||
}) => {
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
const iconColor = theme.secondary
|
||||
|
@ -65,9 +73,28 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
const oldData = queryClient.getQueryData(queryKey)
|
||||
|
||||
switch (type) {
|
||||
// Update each specific page
|
||||
case 'favourite':
|
||||
case 'reblog':
|
||||
case 'bookmark':
|
||||
if (type === 'favourite' && queryKey[0] === 'Favourites') {
|
||||
queryClient.invalidateQueries(['Favourites'])
|
||||
break
|
||||
}
|
||||
if (
|
||||
type === 'reblog' &&
|
||||
queryKey[0] === 'Following' &&
|
||||
prevState === true &&
|
||||
sameAccountRoot
|
||||
) {
|
||||
queryClient.invalidateQueries(['Following'])
|
||||
break
|
||||
}
|
||||
if (type === 'bookmark' && queryKey[0] === 'Bookmarks') {
|
||||
queryClient.invalidateQueries(['Bookmarks'])
|
||||
break
|
||||
}
|
||||
|
||||
queryClient.setQueryData<TimelineData>(queryKey, old => {
|
||||
let tootIndex = -1
|
||||
const pageIndex = findIndex(old?.pages, page => {
|
||||
|
|
|
@ -23,7 +23,7 @@ const fireMutation = async ({ id }: { id: string }) => {
|
|||
instance: 'local',
|
||||
url: `conversations/${id}`
|
||||
})
|
||||
console.log(res)
|
||||
|
||||
if (!res.body.error) {
|
||||
toast({ type: 'success', content: '删除私信成功' })
|
||||
return Promise.resolve()
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Feather } from '@expo/vector-icons'
|
|||
|
||||
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
|
||||
import relativeTime from '@utils/relativeTime'
|
||||
import { getLocalAccountId, getLocalUrl } from '@utils/slices/instancesSlice'
|
||||
import { getLocalUrl } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import BottomSheet from '@components/BottomSheet'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
@ -17,9 +17,14 @@ import HeaderDefaultActionsDomain from '@components/Timelines/Timeline/Shared/He
|
|||
export interface Props {
|
||||
queryKey?: QueryKey.Timeline
|
||||
status: Mastodon.Status
|
||||
sameAccount: boolean
|
||||
}
|
||||
|
||||
const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
||||
const TimelineHeaderDefault: React.FC<Props> = ({
|
||||
queryKey,
|
||||
status,
|
||||
sameAccount
|
||||
}) => {
|
||||
const domain = status.uri
|
||||
? status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||
: ''
|
||||
|
@ -29,7 +34,6 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||
const { theme } = useTheme()
|
||||
|
||||
const navigation = useNavigation()
|
||||
const localAccountId = useSelector(getLocalAccountId)
|
||||
const localDomain = useSelector(getLocalUrl)
|
||||
const [since, setSince] = useState(relativeTime(status.created_at))
|
||||
const [modalVisible, setBottomSheetVisible] = useState(false)
|
||||
|
@ -46,9 +50,10 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||
|
||||
const onPressAction = useCallback(() => setBottomSheetVisible(true), [])
|
||||
const onPressApplication = useCallback(() => {
|
||||
navigation.navigate('Screen-Shared-Webview', {
|
||||
uri: status.application!.website
|
||||
})
|
||||
status.application!.website &&
|
||||
navigation.navigate('Screen-Shared-Webview', {
|
||||
uri: status.application!.website
|
||||
})
|
||||
}, [])
|
||||
|
||||
const pressableAction = useMemo(
|
||||
|
@ -128,7 +133,7 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||
visible={modalVisible}
|
||||
handleDismiss={() => setBottomSheetVisible(false)}
|
||||
>
|
||||
{status.account.id !== localAccountId && (
|
||||
{!sameAccount && (
|
||||
<HeaderDefaultActionsAccount
|
||||
queryKey={queryKey}
|
||||
accountId={status.account.id}
|
||||
|
@ -137,7 +142,7 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||
/>
|
||||
)}
|
||||
|
||||
{status.account.id === localAccountId && (
|
||||
{sameAccount && (
|
||||
<HeaderDefaultActionsStatus
|
||||
queryKey={queryKey}
|
||||
status={status}
|
||||
|
|
|
@ -99,7 +99,6 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
|
|||
})
|
||||
break
|
||||
case 'delete':
|
||||
console.log('deleting toot')
|
||||
queryClient.setQueryData<TimelineData>(
|
||||
queryKey,
|
||||
old =>
|
||||
|
|
|
@ -18,26 +18,26 @@ const fireMutation = async ({
|
|||
options
|
||||
}: {
|
||||
id: string
|
||||
options: { [key: number]: boolean }
|
||||
options?: { [key: number]: boolean }
|
||||
}) => {
|
||||
const formData = new FormData()
|
||||
Object.keys(options).forEach(option => {
|
||||
// @ts-ignore
|
||||
if (options[option]) {
|
||||
formData.append('choices[]', option)
|
||||
}
|
||||
})
|
||||
options &&
|
||||
Object.keys(options).forEach(option => {
|
||||
// @ts-ignore
|
||||
if (options[option]) {
|
||||
formData.append('choices[]', option)
|
||||
}
|
||||
})
|
||||
|
||||
const res = await client({
|
||||
method: 'post',
|
||||
method: options ? 'post' : 'get',
|
||||
instance: 'local',
|
||||
url: `polls/${id}/votes`,
|
||||
body: formData
|
||||
url: options ? `polls/${id}/votes` : `polls/${id}`,
|
||||
...(options && { body: formData })
|
||||
})
|
||||
|
||||
if (res.body.id === id) {
|
||||
toast({ type: 'success', content: '投票成功成功' })
|
||||
return Promise.resolve()
|
||||
return Promise.resolve(res.body as Mastodon.Poll)
|
||||
} else {
|
||||
toast({
|
||||
type: 'error',
|
||||
|
@ -52,40 +52,20 @@ export interface Props {
|
|||
queryKey: QueryKey.Timeline
|
||||
poll: NonNullable<Mastodon.Status['poll']>
|
||||
reblog: boolean
|
||||
sameAccount: boolean
|
||||
}
|
||||
|
||||
const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => {
|
||||
const TimelinePoll: React.FC<Props> = ({
|
||||
queryKey,
|
||||
poll,
|
||||
reblog,
|
||||
sameAccount
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const { mutate } = useMutation(fireMutation, {
|
||||
onMutate: ({ id, options }) => {
|
||||
const mutation = useMutation(fireMutation, {
|
||||
onSuccess: (data, { id }) => {
|
||||
queryClient.cancelQueries(queryKey)
|
||||
const oldData = queryClient.getQueryData(queryKey)
|
||||
|
||||
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))
|
||||
|
||||
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,
|
||||
options: poll.options.map((o, i) => {
|
||||
if (myVotesInt.includes(i)) {
|
||||
o.votes_count = o.votes_count + 1
|
||||
}
|
||||
return o
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
queryClient.setQueryData<TimelineData>(queryKey, old => {
|
||||
let tootIndex = -1
|
||||
|
@ -104,28 +84,49 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => {
|
|||
|
||||
if (pageIndex >= 0 && tootIndex >= 0) {
|
||||
if (reblog) {
|
||||
old!.pages[pageIndex].toots[tootIndex].reblog!.poll = updatePoll(
|
||||
old!.pages[pageIndex].toots[tootIndex].reblog!.poll!
|
||||
)
|
||||
old!.pages[pageIndex].toots[tootIndex].reblog!.poll = data
|
||||
} else {
|
||||
old!.pages[pageIndex].toots[tootIndex].poll = updatePoll(
|
||||
old!.pages[pageIndex].toots[tootIndex].poll!
|
||||
)
|
||||
old!.pages[pageIndex].toots[tootIndex].poll = data
|
||||
}
|
||||
}
|
||||
return old
|
||||
})
|
||||
|
||||
return oldData
|
||||
},
|
||||
onError: (err, _, oldData) => {
|
||||
toast({ type: 'error', content: '请重试' })
|
||||
queryClient.setQueryData(queryKey, oldData)
|
||||
}
|
||||
})
|
||||
|
||||
const pollButton = () => {
|
||||
if (!poll.expired) {
|
||||
if (!sameAccount && !poll.voted) {
|
||||
return (
|
||||
<View style={styles.button}>
|
||||
<ButtonRow
|
||||
onPress={() => {
|
||||
if (poll.multiple) {
|
||||
mutation.mutate({ id: poll.id, options: multipleOptions })
|
||||
} else {
|
||||
mutation.mutate({ id: poll.id, options: singleOptions })
|
||||
}
|
||||
}}
|
||||
{...(mutation.isLoading ? { icon: 'loader' } : { text: '投票' })}
|
||||
disabled={mutation.isLoading}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.button}>
|
||||
<ButtonRow
|
||||
onPress={() => mutation.mutate({ id: poll.id })}
|
||||
{...(mutation.isLoading ? { icon: 'loader' } : { text: '刷新' })}
|
||||
disabled={mutation.isLoading}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pollExpiration = useMemo(() => {
|
||||
// how many voted
|
||||
if (poll.expired) {
|
||||
return (
|
||||
<Text style={[styles.expiration, { color: theme.secondary }]}>
|
||||
|
@ -178,7 +179,10 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => {
|
|||
)}
|
||||
</View>
|
||||
<Text style={[styles.percentage, { color: theme.primary }]}>
|
||||
{Math.round((option.votes_count / poll.votes_count) * 100)}%
|
||||
{poll.votes_count
|
||||
? Math.round((option.votes_count / poll.voters_count) * 100)
|
||||
: 0}
|
||||
%
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
@ -187,7 +191,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => {
|
|||
styles.background,
|
||||
{
|
||||
width: `${Math.round(
|
||||
(option.votes_count / poll.votes_count) * 100
|
||||
(option.votes_count / poll.voters_count) * 100
|
||||
)}%`,
|
||||
backgroundColor: theme.border
|
||||
}
|
||||
|
@ -234,20 +238,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, poll, reblog }) => {
|
|||
)
|
||||
)}
|
||||
<View style={styles.meta}>
|
||||
{!poll.expired && !poll.own_votes?.length && (
|
||||
<View style={styles.button}>
|
||||
<ButtonRow
|
||||
onPress={() => {
|
||||
if (poll.multiple) {
|
||||
mutate({ id: poll.id, options: multipleOptions })
|
||||
} else {
|
||||
mutate({ id: poll.id, options: singleOptions })
|
||||
}
|
||||
}}
|
||||
text='投票'
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{pollButton()}
|
||||
<Text style={[styles.votes, { color: theme.secondary }]}>
|
||||
已投{poll.voters_count || 0}人{' • '}
|
||||
</Text>
|
||||
|
@ -299,7 +290,7 @@ const styles = StyleSheet.create({
|
|||
top: 0,
|
||||
left: 0,
|
||||
height: '100%',
|
||||
minWidth: 1,
|
||||
minWidth: 2,
|
||||
borderTopRightRadius: 6,
|
||||
borderBottomRightRadius: 6
|
||||
},
|
||||
|
@ -320,7 +311,4 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
})
|
||||
|
||||
export default React.memo(
|
||||
TimelinePoll,
|
||||
(prev, next) => prev.poll.voted === next.poll.voted
|
||||
)
|
||||
export default TimelinePoll
|
||||
|
|
|
@ -29,6 +29,7 @@ import { HeaderLeft, HeaderRight } from '@components/Header'
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import formatText from '@screens/Shared/Compose/formatText'
|
||||
import { useQueryClient } from 'react-query'
|
||||
|
||||
const Stack = createNativeStackNavigator()
|
||||
|
||||
|
@ -316,6 +317,7 @@ export interface Props {
|
|||
|
||||
const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
|
||||
const { theme } = useTheme()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const [hasKeyboard, setHasKeyboard] = useState(false)
|
||||
useEffect(() => {
|
||||
|
@ -449,7 +451,8 @@ const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
|
|||
composeState.poll.expire +
|
||||
composeState.attachments.sensitive +
|
||||
composeState.attachments.uploads.map(upload => upload.id) +
|
||||
composeState.visibility
|
||||
composeState.visibility +
|
||||
(params?.type === 'edit' ? Math.random() : '')
|
||||
).toString()
|
||||
},
|
||||
body: formData
|
||||
|
@ -462,7 +465,7 @@ const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
|
|||
{
|
||||
text: '好的',
|
||||
onPress: () => {
|
||||
// clear homepage cache
|
||||
queryClient.invalidateQueries(['Following'])
|
||||
navigation.goBack()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useTheme } from '@root/utils/styles/ThemeManager'
|
|||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
Pressable,
|
||||
SectionList,
|
||||
|
@ -70,42 +71,47 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
}, [searchTerm])
|
||||
|
||||
const listEmpty = useMemo(
|
||||
() => (
|
||||
<View style={styles.emptyBase}>
|
||||
<Text
|
||||
style={[
|
||||
styles.emptyDefault,
|
||||
styles.emptyFontSize,
|
||||
{ color: theme.primary }
|
||||
]}
|
||||
>
|
||||
输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、
|
||||
<Text style={styles.emptyFontBold}>话题标签</Text>或者
|
||||
<Text style={styles.emptyFontBold}>嘟文</Text>
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
高级搜索格式
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>@username@domain</Text>
|
||||
{' '}
|
||||
搜索用户
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>#example</Text>
|
||||
{' '}搜索话题标签
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定嘟文
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定用户
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
[]
|
||||
() =>
|
||||
status === 'loading' ? (
|
||||
<View style={styles.emptyBase}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.emptyBase}>
|
||||
<Text
|
||||
style={[
|
||||
styles.emptyDefault,
|
||||
styles.emptyFontSize,
|
||||
{ color: theme.primary }
|
||||
]}
|
||||
>
|
||||
输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、
|
||||
<Text style={styles.emptyFontBold}>话题标签</Text>或者
|
||||
<Text style={styles.emptyFontBold}>嘟文</Text>
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
高级搜索格式
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>@username@domain</Text>
|
||||
{' '}
|
||||
搜索用户
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>#example</Text>
|
||||
{' '}搜索话题标签
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定嘟文
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定用户
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
[status]
|
||||
)
|
||||
const sectionHeader = useCallback(
|
||||
({ section: { title } }) => (
|
||||
|
@ -247,7 +253,6 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
stickySectionHeadersEnabled
|
||||
sections={setctionData}
|
||||
ListEmptyComponent={listEmpty}
|
||||
refreshing={status === 'loading'}
|
||||
keyboardShouldPersistTaps='always'
|
||||
renderSectionHeader={sectionHeader}
|
||||
renderSectionFooter={sectionFooter}
|
||||
|
|
|
@ -20,10 +20,18 @@ export const timelineFetch = async ({
|
|||
if (pageParam) {
|
||||
switch (pageParam.direction) {
|
||||
case 'prev':
|
||||
params.min_id = pageParam.id
|
||||
if (page === 'Bookmarks' || page === 'Favourites') {
|
||||
params.max_id = pageParam.id
|
||||
} else {
|
||||
params.min_id = pageParam.id
|
||||
}
|
||||
break
|
||||
case 'next':
|
||||
params.max_id = pageParam.id
|
||||
if (page === 'Bookmarks' || page === 'Favourites') {
|
||||
params.min_id = pageParam.id
|
||||
} else {
|
||||
params.max_id = pageParam.id
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue