mirror of https://github.com/tooot-app/app
Fix #612
This commit is contained in:
parent
441090ab28
commit
127978da79
|
@ -1,9 +1,2 @@
|
||||||
Enjoy toooting! This version includes following improvements and fixes:
|
Enjoy toooting! This version includes following improvements and fixes:
|
||||||
- Auto fetch remote content in conversations!
|
- Added following remote instance
|
||||||
- Remember last read position in timeline!
|
|
||||||
- Follow a user with other logged in accounts
|
|
||||||
- Allowing adding more context of reports
|
|
||||||
- Option to disable autoplay gif
|
|
||||||
- Hide boosts from users
|
|
||||||
- Followed hashtags are underlined
|
|
||||||
- Support GoToSocial
|
|
|
@ -1,9 +1,2 @@
|
||||||
toooting愉快!此版本包括以下改进和修复:
|
toooting愉快!此版本包括以下改进和修复:
|
||||||
- 主动获取对话的远程内容
|
- 新增关注远程实例功能
|
||||||
- 自动加载上次我的关注的阅读位置
|
|
||||||
- 用其它已登陆的账户关注用户
|
|
||||||
- 可添加举报细节
|
|
||||||
- 新增暂停自动播放gif动画选项
|
|
||||||
- 隐藏用户的转嘟
|
|
||||||
- 下划线高亮正在关注的话题标签
|
|
||||||
- 支持GoToSocial
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "tooot",
|
"name": "tooot",
|
||||||
"version": "4.8.9",
|
"version": "4.9.0",
|
||||||
"description": "tooot for Mastodon",
|
"description": "tooot for Mastodon",
|
||||||
"author": "xmflsct <me@xmflsct.com>",
|
"author": "xmflsct <me@xmflsct.com>",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
|
|
|
@ -3,7 +3,7 @@ declare namespace App {
|
||||||
| 'Following'
|
| 'Following'
|
||||||
| 'Local'
|
| 'Local'
|
||||||
| 'LocalPublic'
|
| 'LocalPublic'
|
||||||
| 'Trending'
|
| 'Explore'
|
||||||
| 'Notifications'
|
| 'Notifications'
|
||||||
| 'Hashtag'
|
| 'Hashtag'
|
||||||
| 'List'
|
| 'List'
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface Props {
|
||||||
switchDisabled?: boolean
|
switchDisabled?: boolean
|
||||||
switchOnValueChange?: () => void
|
switchOnValueChange?: () => void
|
||||||
|
|
||||||
iconBack?: 'chevron-right' | 'external-link' | 'check'
|
iconBack?: 'chevron-right' | 'chevron-down' | 'external-link' | 'check'
|
||||||
iconBackColor?: ColorDefinitions
|
iconBackColor?: ColorDefinitions
|
||||||
|
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
@ -66,14 +66,7 @@ const MenuRow: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{ flex: 1 }}>
|
<View style={{ flex: 1 }}>
|
||||||
<View
|
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
marginTop: StyleConstants.Spacing.S
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexShrink: 3,
|
flexShrink: 3,
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"confirm": "Confirm"
|
"confirm": "Confirm",
|
||||||
|
"add": "Add"
|
||||||
},
|
},
|
||||||
"customEmoji": {
|
"customEmoji": {
|
||||||
"accessibilityLabel": "Custom emoji {{emoji}}"
|
"accessibilityLabel": "Custom emoji {{emoji}}"
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
"button": "Login",
|
"button": "Login",
|
||||||
"information": {
|
"information": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"accounts": "Users",
|
"description": "Description"
|
||||||
"statuses": "Toots",
|
|
||||||
"domains": "Universes"
|
|
||||||
},
|
},
|
||||||
"disclaimer": {
|
"disclaimer": {
|
||||||
"base": "Logging in process uses system browser that, your account information won't be visible to tooot app."
|
"base": "Logging in process uses system browser that, your account information won't be visible to tooot app."
|
||||||
|
|
|
@ -11,7 +11,13 @@
|
||||||
"segments": {
|
"segments": {
|
||||||
"federated": "Federated",
|
"federated": "Federated",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
"trending": "Trending"
|
"explore": "Explore"
|
||||||
|
},
|
||||||
|
"exploring": {
|
||||||
|
"heading": "Exploring",
|
||||||
|
"trending": "Trending",
|
||||||
|
"followRemote": "Follow remote instance",
|
||||||
|
"noTitle": "No Title"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
|
|
@ -39,7 +39,7 @@ const ComposePoll: React.FC = () => {
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
borderRadius: 6,
|
borderRadius: StyleConstants.Spacing.S,
|
||||||
margin: StyleConstants.Spacing.Global.PagePadding,
|
margin: StyleConstants.Spacing.Global.PagePadding,
|
||||||
borderColor: colors.border
|
borderColor: colors.border
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,26 +1,384 @@
|
||||||
import { HeaderRight } from '@components/Header'
|
import Button from '@components/Button'
|
||||||
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import SegmentedControl from '@react-native-segmented-control/segmented-control'
|
import SegmentedControl from '@react-native-segmented-control/segmented-control'
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
|
import apiGeneral from '@utils/api/general'
|
||||||
import { TabPublicStackParamList } from '@utils/navigation/navigators'
|
import { TabPublicStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { useInstanceQuery } from '@utils/queryHooks/instance'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getGlobalStorage, setGlobalStorage } from '@utils/storage/actions'
|
import { getGlobalStorage, setGlobalStorage, useGlobalStorage } from '@utils/storage/actions'
|
||||||
import { StorageGlobal } from '@utils/storage/global'
|
import { StorageGlobal } from '@utils/storage/global'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { useEffect, useState } from 'react'
|
import { debounce } from 'lodash'
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dimensions } from 'react-native'
|
import { Dimensions, FlatList, Platform, Pressable, TextInput, View } from 'react-native'
|
||||||
import { SceneMap, TabView } from 'react-native-tab-view'
|
import { SceneMap, TabView } from 'react-native-tab-view'
|
||||||
|
import { Placeholder, PlaceholderLine } from 'rn-placeholder'
|
||||||
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
|
|
||||||
const Route = ({ route: { key: page } }: { route: any }) => {
|
const Explore = ({ route: { key: page } }: { route: { key: 'Explore' } }) => {
|
||||||
|
const { t } = useTranslation(['common', 'componentInstance', 'screenTabs'])
|
||||||
|
const { colors, mode } = useTheme()
|
||||||
|
|
||||||
|
const [addingRemote, setAddingRemote] = useState(false)
|
||||||
|
const [domain, setDomain] = useState<string>('')
|
||||||
|
const [domainValid, setDomainValid] = useState<boolean>()
|
||||||
|
const instanceQuery = useInstanceQuery({
|
||||||
|
domain,
|
||||||
|
options: {
|
||||||
|
enabled: false,
|
||||||
|
retry: false,
|
||||||
|
keepPreviousData: false,
|
||||||
|
cacheTime: 1000 * 30,
|
||||||
|
onSuccess: () =>
|
||||||
|
apiGeneral<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
domain: domain,
|
||||||
|
url: 'api/v1/timelines/public',
|
||||||
|
params: { local: 'true', limit: 1 }
|
||||||
|
})
|
||||||
|
.then(({ body }) => {
|
||||||
|
if (Array.isArray(body)) {
|
||||||
|
setDomainValid(true)
|
||||||
|
} else {
|
||||||
|
setDomainValid(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => setDomainValid(false))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const debounceFetch = useCallback(
|
||||||
|
debounce(() => {
|
||||||
|
instanceQuery.refetch()
|
||||||
|
}, 1000),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const [accountActive] = useGlobalStorage.string('account.active')
|
||||||
|
const [remoteActive, setRemoteActive] = useGlobalStorage.string('remote.active')
|
||||||
|
const [remotes, setRemotes] = useGlobalStorage.object('remotes')
|
||||||
|
|
||||||
|
const flRef = useRef<FlatList>(null)
|
||||||
|
const queryKey: QueryKeyTimeline = [
|
||||||
|
'Timeline',
|
||||||
|
{ page, ...(remoteActive && { domain: remoteActive }) }
|
||||||
|
]
|
||||||
|
|
||||||
|
const info = ({
|
||||||
|
heading,
|
||||||
|
content,
|
||||||
|
lines,
|
||||||
|
potentialWidth = 6
|
||||||
|
}: {
|
||||||
|
heading: string
|
||||||
|
content?: string
|
||||||
|
lines?: number
|
||||||
|
potentialWidth?: number
|
||||||
|
}) => (
|
||||||
|
<View style={{ flex: 1, marginTop: StyleConstants.Spacing.M }} accessible>
|
||||||
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
|
style={{
|
||||||
|
marginBottom: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
|
fontWeight='Bold'
|
||||||
|
children={heading}
|
||||||
|
/>
|
||||||
|
{content ? (
|
||||||
|
<CustomText
|
||||||
|
fontStyle='M'
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
|
children={content}
|
||||||
|
numberOfLines={lines}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
Array.from({ length: lines || 1 }).map((_, index) => (
|
||||||
|
<PlaceholderLine
|
||||||
|
key={index}
|
||||||
|
width={potentialWidth ? potentialWidth * StyleConstants.Font.Size.M : undefined}
|
||||||
|
height={StyleConstants.Font.LineHeight.M}
|
||||||
|
color={colors.shimmerDefault}
|
||||||
|
noMargin
|
||||||
|
style={{ borderRadius: 0 }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Timeline
|
||||||
|
flRef={flRef}
|
||||||
|
queryKey={queryKey}
|
||||||
|
disableRefresh={!remoteActive}
|
||||||
|
customProps={{
|
||||||
|
ListHeaderComponent: (
|
||||||
|
<View
|
||||||
|
style={{ backgroundColor: colors.backgroundDefault }}
|
||||||
|
children={
|
||||||
|
addingRemote ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
marginTop: StyleConstants.Spacing.S,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: StyleConstants.Spacing.S,
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
borderColor: colors.border
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingVertical: StyleConstants.Spacing.XS
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeaderLeft
|
||||||
|
onPress={() => {
|
||||||
|
setDomain('')
|
||||||
|
setAddingRemote(false)
|
||||||
|
layoutAnimation().then(() =>
|
||||||
|
flRef.current?.scrollToOffset({ animated: true, offset: 0 })
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<CustomText
|
||||||
|
fontSize='M'
|
||||||
|
fontWeight='Bold'
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
|
children={t('screenTabs:tabs.public.exploring.followRemote')}
|
||||||
|
/>
|
||||||
|
<HeaderRight type='text' content='' onPress={() => {}} />
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
accessible={false}
|
||||||
|
accessibilityRole='none'
|
||||||
|
style={{
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
...StyleConstants.FontStyle.M,
|
||||||
|
color: colors.primaryDefault,
|
||||||
|
borderBottomColor:
|
||||||
|
instanceQuery.isError ||
|
||||||
|
(!!domain.length && instanceQuery.isFetched && domainValid === false)
|
||||||
|
? colors.red
|
||||||
|
: colors.border,
|
||||||
|
paddingVertical: StyleConstants.Spacing.S,
|
||||||
|
...(Platform.OS === 'android' && { paddingRight: 0 })
|
||||||
|
}}
|
||||||
|
editable={false}
|
||||||
|
defaultValue='https://'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
marginRight: StyleConstants.Spacing.M,
|
||||||
|
...StyleConstants.FontStyle.M,
|
||||||
|
color: colors.primaryDefault,
|
||||||
|
borderBottomColor:
|
||||||
|
instanceQuery.isError ||
|
||||||
|
(!!domain.length && instanceQuery.isFetched && domainValid === false)
|
||||||
|
? colors.red
|
||||||
|
: colors.border,
|
||||||
|
paddingVertical: StyleConstants.Spacing.S,
|
||||||
|
...(Platform.OS === 'android' && { paddingLeft: 0 })
|
||||||
|
}}
|
||||||
|
value={domain}
|
||||||
|
onChangeText={text => {
|
||||||
|
setDomain(text.replace(/^http(s)?\:\/\//i, ''))
|
||||||
|
setDomainValid(undefined)
|
||||||
|
debounceFetch()
|
||||||
|
}}
|
||||||
|
autoCapitalize='none'
|
||||||
|
clearButtonMode='never'
|
||||||
|
keyboardType='url'
|
||||||
|
textContentType='URL'
|
||||||
|
onSubmitEditing={() => instanceQuery.refetch()}
|
||||||
|
placeholder={' ' + t('componentInstance:server.textInput.placeholder')}
|
||||||
|
placeholderTextColor={colors.secondary}
|
||||||
|
returnKeyType='go'
|
||||||
|
keyboardAppearance={mode}
|
||||||
|
autoCorrect={false}
|
||||||
|
spellCheck={false}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
content={t('common:buttons.add')}
|
||||||
|
loading={instanceQuery.isFetching}
|
||||||
|
disabled={
|
||||||
|
!!domain.length
|
||||||
|
? domainValid === false ||
|
||||||
|
accountActive === domain ||
|
||||||
|
!!remotes?.find(r => r.domain === domain)
|
||||||
|
: true
|
||||||
|
}
|
||||||
|
onPress={() => {
|
||||||
|
setRemotes([
|
||||||
|
...(remotes || []),
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
instanceQuery.data?.title ||
|
||||||
|
t('screenTabs:tabs.public.exploring.noTitle'),
|
||||||
|
domain
|
||||||
|
}
|
||||||
|
])
|
||||||
|
setRemoteActive(domain)
|
||||||
|
setAddingRemote(false)
|
||||||
|
layoutAnimation().then(() =>
|
||||||
|
flRef.current?.scrollToOffset({ animated: true, offset: 0 })
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Placeholder style={{ marginBottom: StyleConstants.Spacing.M }}>
|
||||||
|
{info({
|
||||||
|
heading: t('componentInstance:server.information.name'),
|
||||||
|
content: !!domain.length ? instanceQuery.data?.title : undefined,
|
||||||
|
potentialWidth: 2
|
||||||
|
})}
|
||||||
|
{info({
|
||||||
|
heading: t('componentInstance:server.information.description'),
|
||||||
|
content: !!domain.length
|
||||||
|
? (instanceQuery.data as Mastodon.Instance_V1)?.short_description ||
|
||||||
|
instanceQuery.data?.description
|
||||||
|
: undefined,
|
||||||
|
lines: 2
|
||||||
|
})}
|
||||||
|
</Placeholder>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger>
|
||||||
|
<Pressable
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: StyleConstants.Spacing.XS,
|
||||||
|
paddingTop: StyleConstants.Spacing.M
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomText
|
||||||
|
fontSize='S'
|
||||||
|
style={{ color: colors.secondary }}
|
||||||
|
children={t('screenTabs:tabs.public.exploring.heading')}
|
||||||
|
/>
|
||||||
|
<CustomText
|
||||||
|
fontSize='S'
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
|
children={
|
||||||
|
!remoteActive
|
||||||
|
? t('screenTabs:tabs.public.exploring.trending').toLocaleLowerCase()
|
||||||
|
: remotes?.find(r => r.domain === remoteActive)?.title
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name='chevron-down'
|
||||||
|
color={colors.primaryDefault}
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
|
<DropdownMenu.Content>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
key={`explore_trending`}
|
||||||
|
value={!remoteActive ? 'on' : 'off'}
|
||||||
|
onValueChange={next => {
|
||||||
|
if (next === 'on') {
|
||||||
|
setRemoteActive(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemIndicator />
|
||||||
|
<DropdownMenu.ItemTitle
|
||||||
|
children={t('screenTabs:tabs.public.exploring.trending')}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
{remotes?.map((item: NonNullable<StorageGlobal['remotes']>[0], index) => (
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
key={`explore_${index}`}
|
||||||
|
value={
|
||||||
|
index === remotes?.findIndex(r => r.domain === remoteActive)
|
||||||
|
? 'on'
|
||||||
|
: 'off'
|
||||||
|
}
|
||||||
|
onValueChange={next => {
|
||||||
|
if (next === 'on') {
|
||||||
|
setRemoteActive(item.domain)
|
||||||
|
} else if (next === 'off') {
|
||||||
|
const nextRemotes = remotes?.filter(r => r.domain !== item.domain)
|
||||||
|
setRemotes(nextRemotes)
|
||||||
|
setRemoteActive(nextRemotes.at(-1)?.domain)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemIndicator />
|
||||||
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
<DropdownMenu.ItemSubtitle children={item.domain} />
|
||||||
|
{index === remotes?.findIndex(r => r.domain === remoteActive) ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: 'trash' }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
))}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
key='explore_add'
|
||||||
|
onSelect={() => {
|
||||||
|
setDomain('')
|
||||||
|
setAddingRemote(true)
|
||||||
|
layoutAnimation().then(() =>
|
||||||
|
flRef.current?.scrollToOffset({ animated: true, offset: 0 })
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle
|
||||||
|
children={t('screenTabs:tabs.public.exploring.followRemote')}
|
||||||
|
/>
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: 'plus' }} />
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Route = ({ route: { key: page } }: { route: { key: any } }) => {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { page }]
|
||||||
return <Timeline queryKey={queryKey} disableRefresh={page === 'Trending'} />
|
return <Timeline queryKey={queryKey} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderScene = SceneMap({
|
const renderScene = SceneMap({
|
||||||
Local: Route,
|
Local: Route,
|
||||||
LocalPublic: Route,
|
LocalPublic: Route,
|
||||||
Trending: Route
|
Explore
|
||||||
})
|
})
|
||||||
|
|
||||||
const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public-Root'>> = ({
|
const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public-Root'>> = ({
|
||||||
|
@ -29,7 +387,7 @@ const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public
|
||||||
const { mode } = useTheme()
|
const { mode } = useTheme()
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
const segments: StorageGlobal['app.prev_public_segment'][] = ['Local', 'LocalPublic', 'Trending']
|
const segments: StorageGlobal['app.prev_public_segment'][] = ['Local', 'LocalPublic', 'Explore']
|
||||||
const [segment, setSegment] = useState<number>(
|
const [segment, setSegment] = useState<number>(
|
||||||
Math.max(
|
Math.max(
|
||||||
0,
|
0,
|
||||||
|
@ -39,7 +397,7 @@ const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public
|
||||||
const [routes] = useState([
|
const [routes] = useState([
|
||||||
{ key: 'Local', title: t('tabs.public.segments.local') },
|
{ key: 'Local', title: t('tabs.public.segments.local') },
|
||||||
{ key: 'LocalPublic', title: t('tabs.public.segments.federated') },
|
{ key: 'LocalPublic', title: t('tabs.public.segments.federated') },
|
||||||
{ key: 'Trending', title: t('tabs.public.segments.trending') }
|
{ key: 'Explore', title: t('tabs.public.segments.explore') }
|
||||||
])
|
])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const page = segments[segment]
|
const page = segments[segment]
|
||||||
|
|
|
@ -54,12 +54,16 @@ const useInstanceQuery = (
|
||||||
options?: UseQueryOptions<Mastodon.Instance<any>, AxiosError>
|
options?: UseQueryOptions<Mastodon.Instance<any>, AxiosError>
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const queryKey: QueryKeyInstance = params?.domain ? ['Instance', params] : ['Instance']
|
const queryKey: QueryKeyInstance = params?.domain
|
||||||
|
? ['Instance', { domain: params.domain }]
|
||||||
|
: ['Instance']
|
||||||
return useQuery(queryKey, queryFunction, {
|
return useQuery(queryKey, queryFunction, {
|
||||||
...params?.options,
|
...params?.options,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
cacheTime: Infinity,
|
cacheTime: Infinity,
|
||||||
onSuccess: data => setAccountStorage([{ key: 'version', value: data.version }])
|
...(!params?.domain && {
|
||||||
|
onSuccess: data => setAccountStorage([{ key: 'version', value: data.version }])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@ import {
|
||||||
UseInfiniteQueryOptions,
|
UseInfiniteQueryOptions,
|
||||||
useMutation
|
useMutation
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
import apiGeneral from '@utils/api/general'
|
||||||
import { PagedResponse } from '@utils/api/helpers'
|
import { PagedResponse } from '@utils/api/helpers'
|
||||||
import apiInstance from '@utils/api/instance'
|
import apiInstance from '@utils/api/instance'
|
||||||
|
import { appendRemote } from '@utils/helpers/appendRemote'
|
||||||
import { featureCheck } from '@utils/helpers/featureCheck'
|
import { featureCheck } from '@utils/helpers/featureCheck'
|
||||||
import { useNavState } from '@utils/navigation/navigators'
|
import { useNavState } from '@utils/navigation/navigators'
|
||||||
import { queryClient } from '@utils/queryHooks'
|
import { queryClient } from '@utils/queryHooks'
|
||||||
|
@ -24,7 +26,7 @@ export type QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
(
|
(
|
||||||
| {
|
| {
|
||||||
page: Exclude<App.Pages, 'Following' | 'Hashtag' | 'List' | 'Toot' | 'Account'>
|
page: Exclude<App.Pages, 'Following' | 'Hashtag' | 'List' | 'Toot' | 'Account' | 'Explore'>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
page: 'Following'
|
page: 'Following'
|
||||||
|
@ -50,6 +52,7 @@ export type QueryKeyTimeline = [
|
||||||
toot: Mastodon.Status['id']
|
toot: Mastodon.Status['id']
|
||||||
remote: boolean
|
remote: boolean
|
||||||
}
|
}
|
||||||
|
| { page: 'Explore'; domain?: string }
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -117,12 +120,24 @@ export const queryFunctionTimeline = async ({
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Trending':
|
case 'Explore':
|
||||||
return apiInstance<Mastodon.Status[]>({
|
if (page.domain) {
|
||||||
method: 'get',
|
return apiGeneral<Mastodon.Status[]>({
|
||||||
url: 'trends/statuses',
|
method: 'get',
|
||||||
params
|
domain: page.domain,
|
||||||
})
|
url: 'api/v1/timelines/public',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
local: 'true'
|
||||||
|
}
|
||||||
|
}).then(res => ({ ...res, body: res.body.map(status => appendRemote.status(status)) }))
|
||||||
|
} else {
|
||||||
|
return apiInstance<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: 'trends/statuses',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
case 'Notifications':
|
case 'Notifications':
|
||||||
const notificationsFilter = getAccountStorage.object('notifications')
|
const notificationsFilter = getAccountStorage.object('notifications')
|
||||||
|
|
|
@ -5,7 +5,7 @@ export type GlobalV0 = {
|
||||||
// string
|
// string
|
||||||
'app.expo_token'?: string
|
'app.expo_token'?: string
|
||||||
'app.prev_tab'?: keyof ScreenTabsStackParamList
|
'app.prev_tab'?: keyof ScreenTabsStackParamList
|
||||||
'app.prev_public_segment'?: Extract<App.Pages, 'Local' | 'LocalPublic' | 'Trending'>
|
'app.prev_public_segment'?: Extract<App.Pages, 'Local' | 'LocalPublic' | 'Explore'>
|
||||||
'app.language'?: string
|
'app.language'?: string
|
||||||
'app.theme'?: 'light' | 'dark' | 'auto'
|
'app.theme'?: 'light' | 'dark' | 'auto'
|
||||||
'app.theme.dark'?: 'lighter' | 'darker'
|
'app.theme.dark'?: 'lighter' | 'darker'
|
||||||
|
@ -24,4 +24,10 @@ export type GlobalV0 = {
|
||||||
'account.active'?: string
|
'account.active'?: string
|
||||||
// object
|
// object
|
||||||
accounts?: string[]
|
accounts?: string[]
|
||||||
|
|
||||||
|
//// remote
|
||||||
|
// string
|
||||||
|
'remote.active'?: string
|
||||||
|
// object
|
||||||
|
remotes?: { title: string; domain: string }[]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue