mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Lots of updates
This commit is contained in:
@ -58,7 +58,7 @@ const Button: React.FC<Props> = ({
|
||||
} else {
|
||||
mounted.current = true
|
||||
}
|
||||
}, [content, loading, disabled])
|
||||
}, [content, loading, disabled, active])
|
||||
|
||||
const loadingSpinkit = useMemo(
|
||||
() => (
|
||||
|
@ -5,15 +5,19 @@ import { useNavigation } from '@react-navigation/native'
|
||||
import hookApps from '@utils/queryHooks/apps'
|
||||
import hookInstance from '@utils/queryHooks/instance'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { InstanceLocal, remoteUpdate } from '@utils/slices/instancesSlice'
|
||||
import {
|
||||
getLocalInstances,
|
||||
InstanceLocal,
|
||||
remoteUpdate
|
||||
} from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Image, StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import { Alert, Image, StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import InstanceAuth from './Instance/Auth'
|
||||
import InstanceInfo from './Instance/Info'
|
||||
import { toast } from './toast'
|
||||
@ -36,6 +40,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
const { theme } = useTheme()
|
||||
const [instanceDomain, setInstanceDomain] = useState<string | undefined>()
|
||||
const [appData, setApplicationData] = useState<InstanceLocal['appData']>()
|
||||
const localInstances = useSelector(getLocalInstances)
|
||||
|
||||
const instanceQuery = hookInstance({
|
||||
instanceDomain,
|
||||
@ -79,12 +84,32 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
|
||||
const processUpdate = useCallback(() => {
|
||||
if (instanceDomain) {
|
||||
haptics('Success')
|
||||
switch (type) {
|
||||
case 'local':
|
||||
applicationQuery.refetch()
|
||||
return
|
||||
if (
|
||||
localInstances &&
|
||||
localInstances.filter(instance => instance.url === instanceDomain)
|
||||
.length
|
||||
) {
|
||||
Alert.alert(
|
||||
'域名已存在',
|
||||
'可以登录同个域名的另外一个账户,现有账户🈚️用',
|
||||
[
|
||||
{ text: '取消', style: 'cancel' },
|
||||
{
|
||||
text: '继续',
|
||||
onPress: () => {
|
||||
applicationQuery.refetch()
|
||||
}
|
||||
}
|
||||
]
|
||||
)
|
||||
} else {
|
||||
applicationQuery.refetch()
|
||||
}
|
||||
break
|
||||
case 'remote':
|
||||
haptics('Success')
|
||||
const queryKey: QueryKeyTimeline = [
|
||||
'Timeline',
|
||||
{ page: 'RemotePublic' }
|
||||
@ -92,8 +117,8 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
dispatch(remoteUpdate(instanceDomain))
|
||||
queryClient.resetQueries(queryKey)
|
||||
toast({ type: 'success', message: '重置成功' })
|
||||
navigation.navigate('Screen-Remote-Root')
|
||||
return
|
||||
navigation.navigate('Screen-Public', { screen: 'Screen-Public-Root' })
|
||||
break
|
||||
}
|
||||
}
|
||||
}, [instanceDomain])
|
||||
@ -160,7 +185,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
content={buttonContent}
|
||||
onPress={processUpdate}
|
||||
disabled={!instanceQuery.data?.uri}
|
||||
loading={instanceQuery.isFetching || applicationQuery.isFetching}
|
||||
loading={instanceQuery.isLoading || applicationQuery.isLoading}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
|
@ -1,12 +1,12 @@
|
||||
import Icon from '@components/Icon'
|
||||
import openLink from '@components/openLink'
|
||||
import ParseEmojis from '@components/Parse/Emojis'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useNavigation, useRoute } from '@react-navigation/native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Image, Pressable, Text, View } from 'react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import HTMLView from 'react-native-htmlview'
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
@ -16,6 +16,7 @@ import Animated, {
|
||||
|
||||
// Prevent going to the same hashtag multiple times
|
||||
const renderNode = ({
|
||||
routeParams,
|
||||
theme,
|
||||
node,
|
||||
index,
|
||||
@ -26,6 +27,7 @@ const renderNode = ({
|
||||
showFullLink,
|
||||
disableDetails
|
||||
}: {
|
||||
routeParams?: any
|
||||
theme: any
|
||||
node: any
|
||||
index: number
|
||||
@ -42,6 +44,10 @@ const renderNode = ({
|
||||
const href = node.attribs.href
|
||||
if (classes) {
|
||||
if (classes.includes('hashtag')) {
|
||||
const tag = href.split(new RegExp(/\/tag\/(.*)|\/tags\/(.*)/))
|
||||
const differentTag = routeParams?.hashtag
|
||||
? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2]
|
||||
: true
|
||||
return (
|
||||
<Text
|
||||
key={index}
|
||||
@ -50,8 +56,8 @@ const renderNode = ({
|
||||
...StyleConstants.FontStyle[size]
|
||||
}}
|
||||
onPress={() => {
|
||||
const tag = href.split(new RegExp(/\/tag\/(.*)|\/tags\/(.*)/))
|
||||
!disableDetails &&
|
||||
differentTag &&
|
||||
navigation.push('Screen-Shared-Hashtag', {
|
||||
hashtag: tag[1] || tag[2]
|
||||
})
|
||||
@ -65,6 +71,9 @@ const renderNode = ({
|
||||
const accountIndex = mentions.findIndex(
|
||||
mention => mention.url === href
|
||||
)
|
||||
const differentAccount = routeParams?.account
|
||||
? routeParams.account.id !== mentions[accountIndex].id
|
||||
: true
|
||||
return (
|
||||
<Text
|
||||
key={index}
|
||||
@ -75,6 +84,7 @@ const renderNode = ({
|
||||
onPress={() => {
|
||||
accountIndex !== -1 &&
|
||||
!disableDetails &&
|
||||
differentAccount &&
|
||||
navigation.push('Screen-Shared-Account', {
|
||||
account: mentions[accountIndex]
|
||||
})
|
||||
@ -151,11 +161,13 @@ const ParseHTML: React.FC<Props> = ({
|
||||
disableDetails = false
|
||||
}) => {
|
||||
const navigation = useNavigation()
|
||||
const route = useRoute()
|
||||
const { theme } = useTheme()
|
||||
|
||||
const renderNodeCallback = useCallback(
|
||||
(node, index) =>
|
||||
renderNode({
|
||||
routeParams: route.params,
|
||||
theme,
|
||||
node,
|
||||
index,
|
||||
|
@ -2,6 +2,7 @@ import client from '@api/client'
|
||||
import Button from '@components/Button'
|
||||
import haptics from '@components/haptics'
|
||||
import { toast } from '@components/toast'
|
||||
import { QueryKeyRelationship } from '@utils/queryHooks/relationship'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -15,7 +16,7 @@ export interface Props {
|
||||
const RelationshipIncoming: React.FC<Props> = ({ id }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const relationshipQueryKey = ['Relationship', { id }]
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const fireMutation = useCallback(
|
||||
@ -31,7 +32,7 @@ const RelationshipIncoming: React.FC<Props> = ({ id }) => {
|
||||
const mutation = useMutation(fireMutation, {
|
||||
onSuccess: res => {
|
||||
haptics('Success')
|
||||
queryClient.setQueryData(relationshipQueryKey, res)
|
||||
queryClient.setQueryData(queryKeyRelationship, res)
|
||||
queryClient.refetchQueries(['Notifications'])
|
||||
},
|
||||
onError: (err: any, { type }) => {
|
||||
|
@ -2,7 +2,9 @@ import client from '@api/client'
|
||||
import Button from '@components/Button'
|
||||
import haptics from '@components/haptics'
|
||||
import { toast } from '@components/toast'
|
||||
import hookRelationship from '@utils/queryHooks/relationship'
|
||||
import hookRelationship, {
|
||||
QueryKeyRelationship
|
||||
} from '@utils/queryHooks/relationship'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMutation, useQueryClient } from 'react-query'
|
||||
@ -14,7 +16,7 @@ export interface Props {
|
||||
const RelationshipOutgoing: React.FC<Props> = ({ id }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const relationshipQueryKey = ['Relationship', { id }]
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
|
||||
const query = hookRelationship({ id })
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
@ -31,7 +33,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }) => {
|
||||
const mutation = useMutation(fireMutation, {
|
||||
onSuccess: res => {
|
||||
haptics('Success')
|
||||
queryClient.setQueryData(relationshipQueryKey, res)
|
||||
queryClient.setQueryData(queryKeyRelationship, res)
|
||||
},
|
||||
onError: (err: any, { type }) => {
|
||||
haptics('Error')
|
||||
|
@ -16,11 +16,12 @@ const Stack = createNativeStackNavigator<
|
||||
>()
|
||||
|
||||
export interface Props {
|
||||
name: 'Screen-Local-Root' | 'Screen-Public-Root'
|
||||
content: { title: string; page: App.Pages }[]
|
||||
name: 'Local' | 'Public'
|
||||
content: { title: string; page: App.Pages; remote?: boolean }[]
|
||||
}
|
||||
|
||||
const Timelines: React.FC<Props> = ({ name, content }) => {
|
||||
const remoteUrl = useSelector(getRemoteUrl)
|
||||
const navigation = useNavigation()
|
||||
const { mode } = useTheme()
|
||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||
@ -71,16 +72,19 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
|
||||
<Stack.Navigator screenOptions={{ headerHideShadow: true }}>
|
||||
<Stack.Screen
|
||||
// @ts-ignore
|
||||
name={name}
|
||||
name={`Screen-${name}-Root`}
|
||||
component={screenComponent}
|
||||
options={{
|
||||
headerTitle: name === 'Screen-Public-Root' ? publicDomain : '',
|
||||
headerTitle: name === 'Public' ? publicDomain : '',
|
||||
...(localActiveIndex !== null && {
|
||||
headerCenter: () => (
|
||||
<View style={styles.segmentsContainer}>
|
||||
<SegmentedControl
|
||||
appearance={mode}
|
||||
values={[content[0].title, content[1].title]}
|
||||
values={[
|
||||
content[0].title,
|
||||
content[1].remote ? remoteUrl : content[1].title
|
||||
]}
|
||||
selectedIndex={segment}
|
||||
onChange={({ nativeEvent }) =>
|
||||
setSegment(nativeEvent.selectedSegmentIndex)
|
||||
@ -102,7 +106,7 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
segmentsContainer: {
|
||||
flexBasis: '60%'
|
||||
flexBasis: '65%'
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -16,11 +16,9 @@ const TimelineHeader = React.memo(
|
||||
一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字{' '}
|
||||
<Text
|
||||
style={{ color: theme.blue }}
|
||||
onPress={() =>
|
||||
navigation.navigate('Screen-Me', {
|
||||
screen: 'Screen-Me-Settings-UpdateRemote'
|
||||
})
|
||||
}
|
||||
onPress={() => {
|
||||
navigation.navigate('Screen-Me')
|
||||
}}
|
||||
>
|
||||
前往设置{' '}
|
||||
<Icon
|
||||
|
@ -13,22 +13,14 @@ export interface Props {
|
||||
const TimelineCard: React.FC<Props> = ({ card }) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
let isMounted = false
|
||||
useEffect(() => {
|
||||
isMounted = true
|
||||
|
||||
return () => {
|
||||
isMounted = false
|
||||
}
|
||||
})
|
||||
const [imageLoaded, setImageLoaded] = useState(false)
|
||||
useEffect(() => {
|
||||
const preFetch = () =>
|
||||
card.image &&
|
||||
isMounted &&
|
||||
Image.getSize(card.image, () => isMounted && setImageLoaded(true))
|
||||
preFetch()
|
||||
}, [isMounted])
|
||||
const preFetch = () => Image.getSize(card.image, () => setImageLoaded(true))
|
||||
|
||||
if (card.image) {
|
||||
preFetch()
|
||||
}
|
||||
}, [])
|
||||
const cardVisual = useMemo(() => {
|
||||
if (imageLoaded) {
|
||||
return <Image source={{ uri: card.image }} style={styles.image} />
|
||||
@ -45,12 +37,18 @@ const TimelineCard: React.FC<Props> = ({ card }) => {
|
||||
<Pressable
|
||||
style={[styles.card, { borderColor: theme.border }]}
|
||||
onPress={async () => await openLink(card.url)}
|
||||
testID='base'
|
||||
>
|
||||
{card.image && <View style={styles.left}>{cardVisual}</View>}
|
||||
{card.image && (
|
||||
<View style={styles.left} testID='image'>
|
||||
{cardVisual}
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.right}>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={[styles.rightTitle, { color: theme.primary }]}
|
||||
testID='title'
|
||||
>
|
||||
{card.title}
|
||||
</Text>
|
||||
@ -58,6 +56,7 @@ const TimelineCard: React.FC<Props> = ({ card }) => {
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[styles.rightDescription, { color: theme.primary }]}
|
||||
testID='description'
|
||||
>
|
||||
{card.description}
|
||||
</Text>
|
||||
|
Reference in New Issue
Block a user