diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 5e983aa2..4745108d 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,3 +1,4 @@ Enjoy toooting! This version includes following improvements and fixes: - Added following remote instance -- Added set note of followed users \ No newline at end of file +- Added set note of followed users +- Best effort load remote user's toots \ No newline at end of file diff --git a/fastlane/metadata/zh-Hans/release_notes.txt b/fastlane/metadata/zh-Hans/release_notes.txt index 8d2d6a1b..97fbf0b1 100644 --- a/fastlane/metadata/zh-Hans/release_notes.txt +++ b/fastlane/metadata/zh-Hans/release_notes.txt @@ -1,3 +1,4 @@ toooting愉快!此版本包括以下改进和修复: - 新增关注远程实例功能 -- 新增关注用户备注功能 \ No newline at end of file +- 新增关注用户备注功能 +- 加载远程用户的嘟文 \ No newline at end of file diff --git a/src/@types/mastodon.d.ts b/src/@types/mastodon.d.ts index 2a90a73a..37ed36fd 100644 --- a/src/@types/mastodon.d.ts +++ b/src/@types/mastodon.d.ts @@ -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 = diff --git a/src/screens/Tabs/Me/Root/index.tsx b/src/screens/Tabs/Me/Root/index.tsx index 1bd737e5..584c5bba 100644 --- a/src/screens/Tabs/Me/Root/index.tsx +++ b/src/screens/Tabs/Me/Root/index.tsx @@ -28,7 +28,7 @@ const TabMeRoot: React.FC = () => { }) return ( - + {accountActive && data ? : null} { +export type Props = { + remote_id?: Mastodon.Status['id'] + remote_domain?: string +} + +const AccountAttachments: React.FC = ({ 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 ( { diff --git a/src/screens/Tabs/Shared/Account/Context.tsx b/src/screens/Tabs/Shared/Account/Context.tsx index d5ee7832..3e2c4b90 100644 --- a/src/screens/Tabs/Shared/Account/Context.tsx +++ b/src/screens/Tabs/Shared/Account/Context.tsx @@ -4,6 +4,7 @@ type AccountContextType = { account?: Mastodon.Account relationship?: Mastodon.Relationship pageMe?: boolean + localInstance: boolean } const AccountContext = createContext({} as AccountContextType) diff --git a/src/screens/Tabs/Shared/Account/Information/Account.tsx b/src/screens/Tabs/Shared/Account/Information/Account.tsx index 53a885dd..93ee0c3f 100644 --- a/src/screens/Tabs/Shared/Account/Information/Account.tsx +++ b/src/screens/Tabs/Shared/Account/Information/Account.tsx @@ -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 ( { } }} dim + withoutTransition /> ) } diff --git a/src/screens/Tabs/Shared/Account/index.tsx b/src/screens/Tabs/Shared/Account/index.tsx index 2c947477..527b5c1d 100644 --- a/src/screens/Tabs/Shared/Account/index.tsx +++ b/src/screens/Tabs/Shared/Account/index.tsx @@ -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 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 '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 useEffect(() => { navigation.setOptions({ headerTransparent: true, - headerStyle: { - backgroundColor: `rgba(255, 255, 255, 0)` - }, + headerStyle: { backgroundColor: `rgba(255, 255, 255, 0)` }, title: '', headerLeft: () => navigation.goBack()} background />, headerRight: () => { @@ -179,7 +165,9 @@ const TabSharedAccount: React.FC - + {!data?.suspended ? ( // @ts-ignore @@ -246,8 +234,18 @@ const TabSharedAccount: React.FC ) }, [segment, dataUpdatedAt, mode]) + const [domain] = useAccountStorage.string('auth.account.domain') + return ( - + {data?.suspended ? ( @@ -256,7 +254,6 @@ const TabSharedAccount: React.FC , diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index 719e8e77..b28acf09 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -206,7 +206,7 @@ const TabSharedToot: React.FC> = ({ if (localMatch) { return localMatch } else { - return appendRemote.status(ancestor) + return appendRemote.status(ancestor, domain) } }) } @@ -256,7 +256,7 @@ const TabSharedToot: React.FC> = ({ if (localMatch) { return { ...localMatch, _level: remote._level } } else { - return appendRemote.status(remote) + return appendRemote.status(remote, match!.domain) } }) } diff --git a/src/utils/helpers/appendRemote.ts b/src/utils/helpers/appendRemote.ts index 5447abe1..4e8370f3 100644 --- a/src/utils/helpers/appendRemote.ts +++ b/src/utils/helpers/appendRemote.ts @@ -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 })) } diff --git a/src/utils/helpers/urlMatcher.ts b/src/utils/helpers/urlMatcher.ts index eb3b91c5..0d39cc14 100644 --- a/src/utils/helpers/urlMatcher.ts +++ b/src/utils/helpers/urlMatcher.ts @@ -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 } }) } } diff --git a/src/utils/queryHooks/account.ts b/src/utils/queryHooks/account.ts index 852336b3..5dec9dd7 100644 --- a/src/utils/queryHooks/account.ts +++ b/src/utils/queryHooks/account.ts @@ -33,16 +33,16 @@ const accountQueryFunction = async ({ queryKey }: QueryFunctionContext({ 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({ 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 {} } diff --git a/src/utils/queryHooks/status.ts b/src/utils/queryHooks/status.ts index 602c039c..7287c6ed 100644 --- a/src/utils/queryHooks/status.ts +++ b/src/utils/queryHooks/status.ts @@ -27,7 +27,7 @@ const queryFunction = async ({ queryKey }: QueryFunctionContext) method: 'get', domain, url: `api/v1/statuses/${id}` - }).then(res => appendRemote.status(res.body)) + }).then(res => appendRemote.status(res.body, domain)) } catch {} } diff --git a/src/utils/queryHooks/timeline.ts b/src/utils/queryHooks/timeline.ts index 0f9389e2..8755c692 100644 --- a/src/utils/queryHooks/timeline.ts +++ b/src/utils/queryHooks/timeline.ts @@ -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({ 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({ 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({ - 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({ + let res + if (page.remote_domain && page.remote_id) { + res = await apiGeneral({ 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({ 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({ - 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({ + 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({ + 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({ + 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({ + 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({ - 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({ + 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({ + method: 'get', + url: `accounts/${page.id}/statuses`, + params: { + ...params, + exclude_replies: false, + only_media: false + } + }) + } + return res || reject } case 'Hashtag': diff --git a/src/utils/queryHooks/users.ts b/src/utils/queryHooks/users.ts index 6b9a43e0..2b96f056 100644 --- a/src/utils/queryHooks/users.ts +++ b/src/utils/queryHooks/users.ts @@ -57,7 +57,7 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext appendRemote.account(account)), + body: res.body.map(account => appendRemote.account(account, domain)), remoteData: true } } else {