mirror of
https://github.com/tooot-app/app
synced 2024-12-11 08:36:49 +01:00
Fetch remote user's toots
This commit is contained in:
parent
f505f78193
commit
702ecef243
@ -1,3 +1,4 @@
|
||||
Enjoy toooting! This version includes following improvements and fixes:
|
||||
- Added following remote instance
|
||||
- Added set note of followed users
|
||||
- Added set note of followed users
|
||||
- Best effort load remote user's toots
|
@ -1,3 +1,4 @@
|
||||
toooting愉快!此版本包括以下改进和修复:
|
||||
- 新增关注远程实例功能
|
||||
- 新增关注用户备注功能
|
||||
- 新增关注用户备注功能
|
||||
- 加载远程用户的嘟文
|
4
src/@types/mastodon.d.ts
vendored
4
src/@types/mastodon.d.ts
vendored
@ -33,7 +33,7 @@ declare namespace Mastodon {
|
||||
role?: Role
|
||||
|
||||
// Internal
|
||||
_remote?: boolean
|
||||
_remote?: string // domain
|
||||
}
|
||||
|
||||
type Announcement = {
|
||||
@ -400,7 +400,7 @@ declare namespace Mastodon {
|
||||
url: string
|
||||
|
||||
// Internal
|
||||
_remote?: boolean
|
||||
_remote?: string // domain
|
||||
}
|
||||
|
||||
type Notification =
|
||||
|
@ -28,7 +28,7 @@ const TabMeRoot: React.FC = () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<AccountContext.Provider value={{ account: data, pageMe: true }}>
|
||||
<AccountContext.Provider value={{ account: data, pageMe: true, localInstance: true }}>
|
||||
{accountActive && data ? <AccountNav scrollY={scrollY} /> : null}
|
||||
<Animated.ScrollView
|
||||
ref={scrollRef}
|
||||
|
@ -13,7 +13,12 @@ import { Dimensions, Pressable, View } from 'react-native'
|
||||
import { FlatList } from 'react-native-gesture-handler'
|
||||
import AccountContext from './Context'
|
||||
|
||||
const AccountAttachments: React.FC = () => {
|
||||
export type Props = {
|
||||
remote_id?: Mastodon.Status['id']
|
||||
remote_domain?: string
|
||||
}
|
||||
|
||||
const AccountAttachments: React.FC<Props> = ({ remote_id, remote_domain }) => {
|
||||
const { account } = useContext(AccountContext)
|
||||
|
||||
if (account?.suspended) return null
|
||||
@ -32,7 +37,8 @@ const AccountAttachments: React.FC = () => {
|
||||
id: account?.id,
|
||||
exclude_reblogs: false,
|
||||
only_media: true,
|
||||
options: { enabled: !!account?.id }
|
||||
...(remote_id && remote_domain && { remote_id, remote_domain }),
|
||||
options: { enabled: !!account?.id || (!!remote_id && !!remote_domain) }
|
||||
})
|
||||
|
||||
const flattenData = flattenPages(data)
|
||||
@ -55,7 +61,7 @@ const AccountAttachments: React.FC = () => {
|
||||
horizontal
|
||||
data={flattenData}
|
||||
renderItem={({ item, index }) => {
|
||||
if (index === DISPLAY_AMOUNT - 1) {
|
||||
if (index === DISPLAY_AMOUNT - 1 && (!remote_id || !remote_domain)) {
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
|
@ -4,6 +4,7 @@ type AccountContextType = {
|
||||
account?: Mastodon.Account
|
||||
relationship?: Mastodon.Relationship
|
||||
pageMe?: boolean
|
||||
localInstance: boolean
|
||||
}
|
||||
const AccountContext = createContext<AccountContextType>({} as AccountContextType)
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { PlaceholderLine } from 'rn-placeholder'
|
||||
import AccountContext from '../Context'
|
||||
|
||||
const AccountInformationAccount: React.FC = () => {
|
||||
const { account, relationship, pageMe } = useContext(AccountContext)
|
||||
const { account, relationship, pageMe, localInstance } = useContext(AccountContext)
|
||||
|
||||
const { t } = useTranslation('screenTabs')
|
||||
const { colors } = useTheme()
|
||||
@ -18,10 +18,6 @@ const AccountInformationAccount: React.FC = () => {
|
||||
const [acct] = useAccountStorage.string('auth.account.acct')
|
||||
const [domain] = useAccountStorage.string('auth.account.domain')
|
||||
|
||||
const localInstance = account?.acct?.includes('@')
|
||||
? account?.acct?.includes(`@${domain}`)
|
||||
: !account?._remote
|
||||
|
||||
if (account || pageMe) {
|
||||
return (
|
||||
<View
|
||||
|
@ -35,6 +35,7 @@ const AccountInformationAvatar: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
dim
|
||||
withoutTransition
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { useAccountStorage } from '@utils/storage/actions'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { Fragment, useEffect, useMemo, useState } from 'react'
|
||||
@ -31,33 +32,19 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
const { t } = useTranslation('screenTabs')
|
||||
const { colors, mode } = useTheme()
|
||||
|
||||
const { data, dataUpdatedAt } = useAccountQuery({
|
||||
const { data, dataUpdatedAt, isFetched } = useAccountQuery({
|
||||
account,
|
||||
_local: true,
|
||||
options: {
|
||||
placeholderData: (account._remote
|
||||
? { ...account, id: undefined }
|
||||
: account) as Mastodon.Account,
|
||||
onSuccess: a => {
|
||||
if (account._remote) {
|
||||
setQueryKey([
|
||||
queryKey[0],
|
||||
{
|
||||
...queryKey[1],
|
||||
page: 'Account',
|
||||
id: a.id,
|
||||
exclude_reblogs: true,
|
||||
only_media: false
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
onError: () => navigation.goBack()
|
||||
}
|
||||
})
|
||||
const { data: dataRelationship } = useRelationshipQuery({
|
||||
id: account._remote ? data?.id : account.id,
|
||||
options: { enabled: account._remote ? !!data?.id : true }
|
||||
id: data?.id,
|
||||
options: { enabled: account._remote ? isFetched : true }
|
||||
})
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
@ -65,9 +52,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
'Timeline',
|
||||
{
|
||||
page: 'Account',
|
||||
id: account._remote ? data?.id : account.id,
|
||||
id: data?.id,
|
||||
exclude_reblogs: true,
|
||||
only_media: false
|
||||
only_media: false,
|
||||
...(account._remote && { remote_id: account.id, remote_domain: account._remote })
|
||||
}
|
||||
])
|
||||
|
||||
@ -76,9 +64,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerTransparent: true,
|
||||
headerStyle: {
|
||||
backgroundColor: `rgba(255, 255, 255, 0)`
|
||||
},
|
||||
headerStyle: { backgroundColor: `rgba(255, 255, 255, 0)` },
|
||||
title: '',
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} background />,
|
||||
headerRight: () => {
|
||||
@ -179,7 +165,9 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
<View style={{ borderBottomWidth: 1, borderBottomColor: colors.border }}>
|
||||
<AccountHeader />
|
||||
<AccountInformation />
|
||||
<AccountAttachments />
|
||||
<AccountAttachments
|
||||
{...(account._remote && { remote_id: account.id, remote_domain: account._remote })}
|
||||
/>
|
||||
</View>
|
||||
{!data?.suspended ? (
|
||||
// @ts-ignore
|
||||
@ -246,8 +234,18 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
)
|
||||
}, [segment, dataUpdatedAt, mode])
|
||||
|
||||
const [domain] = useAccountStorage.string('auth.account.domain')
|
||||
|
||||
return (
|
||||
<AccountContext.Provider value={{ account: data, relationship: dataRelationship }}>
|
||||
<AccountContext.Provider
|
||||
value={{
|
||||
account: data,
|
||||
relationship: dataRelationship,
|
||||
localInstance: account?.acct?.includes('@')
|
||||
? account?.acct?.includes(`@${domain}`)
|
||||
: !account?._remote
|
||||
}}
|
||||
>
|
||||
<AccountNav scrollY={scrollY} />
|
||||
|
||||
{data?.suspended ? (
|
||||
@ -256,7 +254,6 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
<Timeline
|
||||
queryKey={queryKey}
|
||||
disableRefresh
|
||||
queryOptions={{ enabled: account._remote ? !!data?.id : true }}
|
||||
customProps={{
|
||||
keyboardShouldPersistTaps: 'always',
|
||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />,
|
||||
|
@ -206,7 +206,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
||||
if (localMatch) {
|
||||
return localMatch
|
||||
} else {
|
||||
return appendRemote.status(ancestor)
|
||||
return appendRemote.status(ancestor, domain)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -256,7 +256,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
||||
if (localMatch) {
|
||||
return { ...localMatch, _level: remote._level }
|
||||
} else {
|
||||
return appendRemote.status(remote)
|
||||
return appendRemote.status(remote, match!.domain)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
// Central place appending _remote internal prop
|
||||
|
||||
export const appendRemote = {
|
||||
status: (status: Mastodon.Status) => ({
|
||||
status: (status: Mastodon.Status, domain: string) => ({
|
||||
...status,
|
||||
...(status.reblog && {
|
||||
reblog: {
|
||||
...status.reblog,
|
||||
account: appendRemote.account(status.reblog.account),
|
||||
mentions: appendRemote.mentions(status.reblog.mentions)
|
||||
account: appendRemote.account(status.reblog.account, domain),
|
||||
mentions: appendRemote.mentions(status.reblog.mentions, domain)
|
||||
}
|
||||
}),
|
||||
account: appendRemote.account(status.account),
|
||||
mentions: appendRemote.mentions(status.mentions),
|
||||
account: appendRemote.account(status.account, domain),
|
||||
mentions: appendRemote.mentions(status.mentions, domain),
|
||||
_remote: true
|
||||
}),
|
||||
account: (account: Mastodon.Account) => ({
|
||||
account: (account: Mastodon.Account, domain: string) => ({
|
||||
...account,
|
||||
_remote: true
|
||||
_remote: domain
|
||||
}),
|
||||
mentions: (mentions: Mastodon.Mention[]) =>
|
||||
mentions?.map(mention => ({ ...mention, _remote: true }))
|
||||
mentions: (mentions: Mastodon.Mention[], domain: string) =>
|
||||
mentions?.map(mention => ({ ...mention, _remote: domain }))
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export const urlMatcher = (
|
||||
|
||||
return {
|
||||
domain,
|
||||
...(accountAcct && { account: { acct: accountAcct, _remote } }),
|
||||
...(accountAcct && { account: { acct: accountAcct, _remote: domain } }),
|
||||
...(statusId && { status: { id: statusId, _remote } })
|
||||
}
|
||||
}
|
||||
|
@ -33,16 +33,16 @@ const accountQueryFunction = async ({ queryKey }: QueryFunctionContext<QueryKeyA
|
||||
if (id) {
|
||||
matchedAccount = await apiGeneral<Mastodon.Account>({
|
||||
method: 'get',
|
||||
domain: domain,
|
||||
domain,
|
||||
url: `api/v1/accounts/${id}`
|
||||
}).then(res => appendRemote.account(res.body))
|
||||
}).then(res => appendRemote.account(res.body, domain))
|
||||
} else if (acct) {
|
||||
matchedAccount = await apiGeneral<Mastodon.Account>({
|
||||
method: 'get',
|
||||
domain: domain,
|
||||
domain,
|
||||
url: 'api/v1/accounts/lookup',
|
||||
params: { acct }
|
||||
}).then(res => appendRemote.account(res.body))
|
||||
}).then(res => appendRemote.account(res.body, domain))
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ const queryFunction = async ({ queryKey }: QueryFunctionContext<QueryKeyStatus>)
|
||||
method: 'get',
|
||||
domain,
|
||||
url: `api/v1/statuses/${id}`
|
||||
}).then(res => appendRemote.status(res.body))
|
||||
}).then(res => appendRemote.status(res.body, domain))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,9 @@ export type QueryKeyTimeline = [
|
||||
id?: Mastodon.Account['id']
|
||||
exclude_reblogs: boolean
|
||||
only_media: boolean
|
||||
// remote info
|
||||
remote_id?: Mastodon.Account['id']
|
||||
remote_domain?: string
|
||||
}
|
||||
| {
|
||||
page: 'Toot'
|
||||
@ -107,10 +110,7 @@ export const queryFunctionTimeline = async ({
|
||||
return apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: 'timelines/public',
|
||||
params: {
|
||||
...params,
|
||||
local: 'true'
|
||||
}
|
||||
params: { ...params, local: true }
|
||||
})
|
||||
|
||||
case 'LocalPublic':
|
||||
@ -126,11 +126,11 @@ export const queryFunctionTimeline = async ({
|
||||
method: 'get',
|
||||
domain: page.domain,
|
||||
url: 'api/v1/timelines/public',
|
||||
params: {
|
||||
...params,
|
||||
local: 'true'
|
||||
}
|
||||
}).then(res => ({ ...res, body: res.body.map(status => appendRemote.status(status)) }))
|
||||
params: { ...params, local: true }
|
||||
}).then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => appendRemote.status(status, page.domain!))
|
||||
}))
|
||||
} else {
|
||||
return apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
@ -163,61 +163,137 @@ export const queryFunctionTimeline = async ({
|
||||
})
|
||||
|
||||
case 'Account':
|
||||
if (!page.id) return Promise.reject('Timeline query account id not provided')
|
||||
const reject = Promise.reject('Timeline query account id not provided')
|
||||
|
||||
if (page.only_media) {
|
||||
return apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
only_media: 'true',
|
||||
...params
|
||||
}
|
||||
})
|
||||
} else if (page.exclude_reblogs) {
|
||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
||||
return apiInstance<Mastodon.Status[]>({
|
||||
let res
|
||||
if (page.remote_domain && page.remote_id) {
|
||||
res = await apiGeneral<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
domain: page.remote_domain,
|
||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||
params: {
|
||||
exclude_replies: 'true',
|
||||
only_media: true,
|
||||
...params
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const res1 = await apiInstance<(Mastodon.Status & { _pinned: boolean })[]>({
|
||||
.then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||
}))
|
||||
.catch(() => {})
|
||||
}
|
||||
if (!res && page.id) {
|
||||
res = await apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
pinned: 'true'
|
||||
only_media: true,
|
||||
...params
|
||||
}
|
||||
})
|
||||
res1.body = res1.body.map(status => {
|
||||
status._pinned = true
|
||||
return status
|
||||
})
|
||||
const res2 = await apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
exclude_replies: 'true'
|
||||
}
|
||||
})
|
||||
return {
|
||||
body: uniqBy([...res1.body, ...res2.body], 'id'),
|
||||
...(res2.links?.next && { links: { next: res2.links.next } })
|
||||
}
|
||||
return res || reject
|
||||
} else if (page.exclude_reblogs) {
|
||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
||||
let res
|
||||
if (page.remote_domain && page.remote_id) {
|
||||
res = await apiGeneral<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
domain: page.remote_domain,
|
||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||
params: {
|
||||
exclude_replies: true,
|
||||
...params
|
||||
}
|
||||
})
|
||||
.then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||
}))
|
||||
.catch(() => {})
|
||||
}
|
||||
if (!res && page.id) {
|
||||
res = await apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
exclude_replies: true,
|
||||
...params
|
||||
}
|
||||
})
|
||||
}
|
||||
return res || reject
|
||||
} else {
|
||||
let res
|
||||
if (page.remote_domain && page.remote_id) {
|
||||
res = await apiGeneral<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
domain: page.remote_domain,
|
||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||
params: { exclude_replies: true }
|
||||
})
|
||||
.then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||
}))
|
||||
.catch(() => {})
|
||||
}
|
||||
if (!res && page.id) {
|
||||
const resPinned = await apiInstance<(Mastodon.Status & { _pinned: boolean })[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: { pinned: true }
|
||||
}).then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => {
|
||||
status._pinned = true
|
||||
return status
|
||||
})
|
||||
}))
|
||||
const resDefault = await apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: { exclude_replies: true }
|
||||
})
|
||||
return {
|
||||
body: uniqBy([...resPinned.body, ...resDefault.body], 'id'),
|
||||
links: resDefault.links
|
||||
}
|
||||
}
|
||||
return res || reject
|
||||
}
|
||||
} else {
|
||||
return apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
...params,
|
||||
exclude_replies: false,
|
||||
only_media: false
|
||||
}
|
||||
})
|
||||
let res
|
||||
if (page.remote_domain && page.remote_id) {
|
||||
res = await apiGeneral<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
domain: page.remote_domain,
|
||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||
params: {
|
||||
...params,
|
||||
exclude_replies: false,
|
||||
only_media: false
|
||||
}
|
||||
})
|
||||
.then(res => ({
|
||||
...res,
|
||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||
}))
|
||||
.catch(() => {})
|
||||
}
|
||||
if (!res && page.id) {
|
||||
res = await apiInstance<Mastodon.Status[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${page.id}/statuses`,
|
||||
params: {
|
||||
...params,
|
||||
exclude_replies: false,
|
||||
only_media: false
|
||||
}
|
||||
})
|
||||
}
|
||||
return res || reject
|
||||
}
|
||||
|
||||
case 'Hashtag':
|
||||
|
@ -57,7 +57,7 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext<Query
|
||||
})
|
||||
return {
|
||||
...res,
|
||||
body: res.body.map(account => appendRemote.account(account)),
|
||||
body: res.body.map(account => appendRemote.account(account, domain)),
|
||||
remoteData: true
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user