1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

A lot of updates

This commit is contained in:
Zhiyuan Zheng
2020-11-28 17:07:30 +01:00
parent 8200375c92
commit 735cc0b903
29 changed files with 940 additions and 1275 deletions

View File

@ -1,5 +1,12 @@
import React from 'react'
import { ActivityIndicator, AppState, FlatList, Text, View } from 'react-native'
import React, { useCallback } from 'react'
import {
ActivityIndicator,
AppState,
FlatList,
StyleSheet,
Text,
View
} from 'react-native'
import { setFocusHandler, useInfiniteQuery } from 'react-query'
import TimelineNotifications from 'src/components/Timelines/Timeline/Notifications'
@ -51,6 +58,40 @@ const Timeline: React.FC<Props> = ({
const flattenData = data ? data.flatMap(d => [...d?.toots]) : []
// const flattenPointer = data ? data.flatMap(d => [d?.pointer]) : []
const flKeyExtrator = useCallback(({ id }) => id, [])
const flRenderItem = useCallback(({ item }) => {
switch (page) {
case 'Conversations':
return <TimelineConversation item={item} />
case 'Notifications':
return <TimelineNotifications notification={item} queryKey={queryKey} />
default:
return <TimelineDefault item={item} queryKey={queryKey} />
}
}, [])
const flItemSeparatorComponent = useCallback(() => <TimelineSeparator />, [])
const flOnRefresh = useCallback(
() =>
!disableRefresh &&
fetchMore(
{
direction: 'prev',
id: flattenData[0].id
},
{ previous: true }
),
[disableRefresh]
)
const flOnEndReach = useCallback(
() =>
!disableRefresh &&
fetchMore({
direction: 'next',
id: flattenData[flattenData.length - 1].id
}),
[disableRefresh]
)
let content
if (!isSuccess) {
content = <ActivityIndicator />
@ -60,53 +101,18 @@ const Timeline: React.FC<Props> = ({
content = (
<>
<FlatList
style={{ minHeight: '100%' }}
scrollEnabled={scrollEnabled} // For timeline in Account view
data={flattenData}
keyExtractor={({ id }) => id}
renderItem={({ item, index, separators }) => {
switch (page) {
case 'Conversations':
return <TimelineConversation key={index} item={item} />
case 'Notifications':
return (
<TimelineNotifications
key={index}
notification={item}
queryKey={queryKey}
/>
)
default:
return (
<TimelineDefault
key={index}
item={item}
queryKey={queryKey}
/>
)
}
}}
ItemSeparatorComponent={() => <TimelineSeparator />}
onRefresh={flOnRefresh}
renderItem={flRenderItem}
onEndReached={flOnEndReach}
keyExtractor={flKeyExtrator}
style={styles.flatList}
scrollEnabled={scrollEnabled} // For timeline in Account view
ItemSeparatorComponent={flItemSeparatorComponent}
refreshing={!disableRefresh && isLoading}
onEndReachedThreshold={!disableRefresh ? 0.5 : null}
// require getItemLayout
// {...(flattenPointer[0] && { initialScrollIndex: flattenPointer[0] })}
{...(!disableRefresh && {
onRefresh: () =>
fetchMore(
{
direction: 'prev',
id: flattenData[0].id
},
{ previous: true }
),
refreshing: isLoading,
onEndReached: () => {
fetchMore({
direction: 'next',
id: flattenData[flattenData.length - 1].id
})
},
onEndReachedThreshold: 0.5
})}
/>
{isFetchingMore && <ActivityIndicator />}
</>
@ -116,4 +122,10 @@ const Timeline: React.FC<Props> = ({
return <View>{content}</View>
}
const styles = StyleSheet.create({
flatList: {
minHeight: '100%'
}
})
export default Timeline

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import React, { useCallback, useMemo } from 'react'
import { Dimensions, Pressable, StyleSheet, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
@ -24,6 +24,43 @@ const TimelineDefault: React.FC<Props> = ({ item, queryKey }) => {
let actualStatus = item.reblog ? item.reblog : item
const pressableToot = useCallback(
() =>
navigation.navigate('Screen-Shared-Toot', {
toot: actualStatus.id
}),
[]
)
const childrenToot = useMemo(
() => (
<>
{actualStatus.content ? (
<Content
content={actualStatus.content}
emojis={actualStatus.emojis}
mentions={actualStatus.mentions}
spoiler_text={actualStatus.spoiler_text}
// tags={actualStatus.tags}
/>
) : (
<></>
)}
{actualStatus.poll && <Poll poll={actualStatus.poll} />}
{actualStatus.media_attachments.length > 0 && (
<Attachment
media_attachments={actualStatus.media_attachments}
sensitive={actualStatus.sensitive}
width={
Dimensions.get('window').width - constants.SPACING_M * 2 - 50 - 8
}
/>
)}
{actualStatus.card && <Card card={actualStatus.card} />}
</>
),
[]
)
const statusView = useMemo(() => {
return (
<View style={styles.statusView}>
@ -54,40 +91,7 @@ const TimelineDefault: React.FC<Props> = ({ item, queryKey }) => {
application={item.application}
/>
{/* Can pass toot info to next page to speed up performance */}
<Pressable
onPress={() =>
navigation.navigate('Screen-Shared-Toot', {
toot: actualStatus.id
})
}
>
{actualStatus.content ? (
<Content
content={actualStatus.content}
emojis={actualStatus.emojis}
mentions={actualStatus.mentions}
spoiler_text={actualStatus.spoiler_text}
// tags={actualStatus.tags}
// style={{ flex: 1 }}
/>
) : (
<></>
)}
{actualStatus.poll && <Poll poll={actualStatus.poll} />}
{actualStatus.media_attachments.length > 0 && (
<Attachment
media_attachments={actualStatus.media_attachments}
sensitive={actualStatus.sensitive}
width={
Dimensions.get('window').width -
constants.SPACING_M * 2 -
50 -
8
}
/>
)}
{actualStatus.card && <Card card={actualStatus.card} />}
</Pressable>
<Pressable onPress={pressableToot} children={childrenToot} />
<ActionsStatus queryKey={queryKey} status={actualStatus} />
</View>
</View>
@ -114,4 +118,12 @@ const styles = StyleSheet.create({
}
})
export default TimelineDefault
export default React.memo(TimelineDefault, (prev, next) => {
let skipUpdate = true
skipUpdate = prev.item.id === next.item.id
skipUpdate = prev.item.replies_count === next.item.replies_count
skipUpdate = prev.item.favourited === next.item.favourited
skipUpdate = prev.item.reblogged === next.item.reblogged
skipUpdate = prev.item.bookmarked === next.item.bookmarked
return skipUpdate
})

View File

@ -108,4 +108,4 @@ const styles = StyleSheet.create({
}
})
export default Actioned
export default React.memo(Actioned)

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useCallback, useMemo, useState } from 'react'
import {
ActionSheetIOS,
Clipboard,
@ -11,12 +11,14 @@ import {
import Toast from 'react-native-toast-message'
import { useMutation, useQueryCache } from 'react-query'
import { Feather } from '@expo/vector-icons'
import { findIndex } from 'lodash'
import client from 'src/api/client'
import { getLocalAccountId } from 'src/utils/slices/instancesSlice'
import { store } from 'src/store'
import { useTheme } from 'src/utils/styles/ThemeManager'
import constants from 'src/utils/styles/constants'
import { toast } from 'src/components/toast'
const fireMutation = async ({
id,
@ -25,14 +27,7 @@ const fireMutation = async ({
prevState
}: {
id: string
type:
| 'favourite'
| 'reblog'
| 'bookmark'
| 'mute'
| 'pin'
| 'delete'
| 'account/mute'
type: 'favourite' | 'reblog' | 'bookmark' | 'mute' | 'pin' | 'delete'
stateKey:
| 'favourited'
| 'reblogged'
@ -53,27 +48,13 @@ const fireMutation = async ({
method: 'post',
instance: 'local',
endpoint: `statuses/${id}/${prevState ? 'un' : ''}${type}`
})
}) // bug in response from Mastodon
if (!res.body[stateKey] === prevState) {
if (type === 'bookmark' || 'mute' || 'pin')
Toast.show({
type: 'success',
position: 'bottom',
text1: '功能成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
toast({ type: 'success', content: '功能成功' })
return Promise.resolve(res.body)
} else {
Toast.show({
type: 'error',
position: 'bottom',
text1: '请重试',
autoHide: false,
bottomOffset: 65
})
toast({ type: 'error', content: '功能错误' })
return Promise.reject()
}
break
@ -85,23 +66,10 @@ const fireMutation = async ({
})
if (res.body[stateKey] === id) {
Toast.show({
type: 'success',
position: 'bottom',
text1: '删除成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
toast({ type: 'success', content: '删除成功' })
return Promise.resolve(res.body)
} else {
Toast.show({
type: 'error',
position: 'bottom',
text1: '请重试',
autoHide: false,
bottomOffset: 65
})
toast({ type: 'error', content: '删除失败' })
return Promise.reject()
}
break
@ -120,136 +88,204 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
state ? theme.primary : theme.secondary
const localAccountId = getLocalAccountId(store.getState())
const [modalVisible, setModalVisible] = useState(false)
const [bottomSheetVisible, setBottomSheetVisible] = useState(false)
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
onMutate: () => {
onMutate: ({ id, type, stateKey, prevState }) => {
queryCache.cancelQueries(queryKey)
const prevData = queryCache.getQueryData(queryKey)
return prevData
},
onSuccess: (newData, params) => {
if (params.type === 'reblog') {
queryCache.invalidateQueries(['Following', { page: 'Following' }])
const oldData = queryCache.getQueryData(queryKey)
switch (type) {
case 'favourite':
case 'reblog':
case 'bookmark':
case 'mute':
case 'pin':
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
toots: paging.toots.map((toot: any) => {
if (toot.id === id) {
toot[stateKey] =
typeof prevState === 'boolean' ? !prevState : true
}
return toot
}),
pointer: paging.pointer
}))
)
break
case 'delete':
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
toots: paging.toots.map((toot: any, index: number) => {
if (toot.id === id) {
paging.toots.splice(index, 1)
}
return toot
}),
pointer: paging.pointer
}))
)
break
}
// queryCache.setQueryData(queryKey, (oldData: any) => {
// oldData &&
// oldData.map((paging: any) => {
// paging.toots.map(
// (status: Mastodon.Status | Mastodon.Notification, i: number) => {
// if (status.id === newData.id) {
// paging.toots[i] = newData
// }
// }
// )
// })
// return oldData
// })
return Promise.resolve()
return oldData
},
onError: (err, variables, prevData) => {
queryCache.setQueryData(queryKey, prevData)
},
onSettled: () => {
queryCache.invalidateQueries(queryKey)
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试' })
queryCache.setQueryData(queryKey, oldData)
}
})
const onPressReply = useCallback(() => {}, [])
const onPressReblog = useCallback(
() =>
mutateAction({
id: status.id,
type: 'reblog',
stateKey: 'reblogged',
prevState: status.reblogged
}),
[status.reblogged]
)
const onPressFavourite = useCallback(
() =>
mutateAction({
id: status.id,
type: 'favourite',
stateKey: 'favourited',
prevState: status.favourited
}),
[status.favourited]
)
const onPressBookmark = useCallback(
() =>
mutateAction({
id: status.id,
type: 'bookmark',
stateKey: 'bookmarked',
prevState: status.bookmarked
}),
[status.bookmarked]
)
const onPressShare = useCallback(() => setBottomSheetVisible(true), [])
const childrenReply = useMemo(
() => (
<>
<Feather
name='message-circle'
color={iconColor}
size={constants.FONT_SIZE_M + 2}
/>
{status.replies_count > 0 && (
<Text
style={{
color: theme.secondary,
fontSize: constants.FONT_SIZE_M,
marginLeft: constants.SPACING_XS
}}
>
{status.replies_count}
</Text>
)}
</>
),
[status.replies_count]
)
const childrenReblog = useMemo(
() => (
<Feather
name='repeat'
color={
status.visibility === 'public' || status.visibility === 'unlisted'
? iconColorAction(status.reblogged)
: theme.disabled
}
size={constants.FONT_SIZE_M + 2}
/>
),
[status.reblogged]
)
const childrenFavourite = useMemo(
() => (
<Feather
name='heart'
color={iconColorAction(status.favourited)}
size={constants.FONT_SIZE_M + 2}
/>
),
[status.favourited]
)
const childrenBookmark = useMemo(
() => (
<Feather
name='bookmark'
color={iconColorAction(status.bookmarked)}
size={constants.FONT_SIZE_M + 2}
/>
),
[status.bookmarked]
)
const childrenShare = useMemo(
() => (
<Feather
name='share-2'
color={iconColor}
size={constants.FONT_SIZE_M + 2}
/>
),
[]
)
return (
<>
<View style={styles.actions}>
<Pressable style={styles.action}>
<Feather
name='message-circle'
color={iconColor}
size={constants.FONT_SIZE_M + 2}
/>
{status.replies_count > 0 && (
<Text
style={{
color: theme.secondary,
fontSize: constants.FONT_SIZE_M,
marginLeft: constants.SPACING_XS
}}
>
{status.replies_count}
</Text>
)}
</Pressable>
<Pressable
style={styles.action}
onPress={onPressReply}
children={childrenReply}
/>
<Pressable
style={styles.action}
onPress={() =>
mutateAction({
id: status.id,
type: 'reblog',
stateKey: 'reblogged',
prevState: status.reblogged
})
onPress={
status.visibility === 'public' || status.visibility === 'unlisted'
? onPressReblog
: null
}
>
<Feather
name='repeat'
color={iconColorAction(status.reblogged)}
size={constants.FONT_SIZE_M + 2}
/>
</Pressable>
children={childrenReblog}
/>
<Pressable
style={styles.action}
onPress={() =>
mutateAction({
id: status.id,
type: 'favourite',
stateKey: 'favourited',
prevState: status.favourited
})
}
>
<Feather
name='heart'
color={iconColorAction(status.favourited)}
size={constants.FONT_SIZE_M + 2}
/>
</Pressable>
onPress={onPressFavourite}
children={childrenFavourite}
/>
<Pressable
style={styles.action}
onPress={() =>
mutateAction({
id: status.id,
type: 'bookmark',
stateKey: 'bookmarked',
prevState: status.bookmarked
})
}
>
<Feather
name='bookmark'
color={iconColorAction(status.bookmarked)}
size={constants.FONT_SIZE_M + 2}
/>
</Pressable>
onPress={onPressBookmark}
children={childrenBookmark}
/>
<Pressable style={styles.action} onPress={() => setModalVisible(true)}>
<Feather
name='share-2'
color={iconColor}
size={constants.FONT_SIZE_M + 2}
/>
</Pressable>
<Pressable
style={styles.action}
onPress={onPressShare}
children={childrenShare}
/>
</View>
<Modal
animationType='fade'
presentationStyle='overFullScreen'
transparent
visible={modalVisible}
visible={bottomSheetVisible}
>
<Pressable
style={styles.modalBackground}
onPress={() => setModalVisible(false)}
onPress={() => setBottomSheetVisible(false)}
>
<View style={styles.modalSheet}>
<Pressable
@ -266,15 +302,8 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
},
() => {},
() => {
setModalVisible(false)
Toast.show({
type: 'success',
position: 'bottom',
text1: '分享成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
setBottomSheetVisible(false)
toast({ type: 'success', content: '分享成功' })
}
)
}
@ -284,15 +313,8 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
<Pressable
onPress={() => {
Clipboard.setString(status.uri)
setModalVisible(false)
Toast.show({
type: 'success',
position: 'bottom',
text1: '链接复制成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
setBottomSheetVisible(false)
toast({ type: 'success', content: '链接复制成功' })
}}
>
<Text></Text>
@ -300,7 +322,7 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
{status.account.id === localAccountId && (
<Pressable
onPress={() => {
setModalVisible(false)
setBottomSheetVisible(false)
mutateAction({
id: status.id,
type: 'delete',
@ -314,7 +336,7 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
<Text></Text>
<Pressable
onPress={() => {
setModalVisible(false)
setBottomSheetVisible(false)
mutateAction({
id: status.id,
type: 'mute',
@ -325,10 +347,11 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
>
<Text>{status.muted ? '取消静音' : '静音'}</Text>
</Pressable>
{/* Also note that reblogs cannot be pinned. */}
{status.account.id === localAccountId && (
<Pressable
onPress={() => {
setModalVisible(false)
setBottomSheetVisible(false)
mutateAction({
id: status.id,
type: 'pin',

View File

@ -84,4 +84,4 @@ const Attachment: React.FC<Props> = ({
)
}
export default Attachment
export default React.memo(Attachment, () => true)

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, { useCallback } from 'react'
import { Image, Pressable, StyleSheet } from 'react-native'
import { useNavigation } from '@react-navigation/native'
@ -12,15 +12,14 @@ export interface Props {
const Avatar: React.FC<Props> = ({ uri, id }) => {
const navigation = useNavigation()
// Need to fix go back root
const onPress = useCallback(() => {
navigation.navigate('Screen-Shared-Account', {
id: id
})
}, [])
return (
<Pressable
style={styles.avatar}
onPress={() => {
navigation.navigate('Screen-Shared-Account', {
id: id
})
}}
>
<Pressable style={styles.avatar} onPress={onPress}>
<Image source={{ uri: uri }} style={styles.image} />
</Pressable>
)
@ -39,4 +38,4 @@ const styles = StyleSheet.create({
}
})
export default Avatar
export default React.memo(Avatar, () => true)

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, { useCallback } from 'react'
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
@ -8,32 +8,29 @@ export interface Props {
const Card: React.FC<Props> = ({ card }) => {
const navigation = useNavigation()
const onPress = useCallback(() => {
navigation.navigate('Screen-Shared-Webview', {
uri: card.url
})
}, [])
return (
card && (
<Pressable
style={styles.card}
onPress={() => {
navigation.navigate('Webview', {
uri: card.url
})
}}
>
{card.image && (
<View style={styles.left}>
<Image source={{ uri: card.image }} style={styles.image} />
</View>
)}
<View style={styles.right}>
<Text numberOfLines={1}>{card.title}</Text>
{card.description ? (
<Text numberOfLines={2}>{card.description}</Text>
) : (
<></>
)}
<Text numberOfLines={1}>{card.url}</Text>
<Pressable style={styles.card} onPress={onPress}>
{card.image && (
<View style={styles.left}>
<Image source={{ uri: card.image }} style={styles.image} />
</View>
</Pressable>
)
)}
<View style={styles.right}>
<Text numberOfLines={1}>{card.title}</Text>
{card.description ? (
<Text numberOfLines={2}>{card.description}</Text>
) : (
<></>
)}
<Text numberOfLines={1}>{card.url}</Text>
</View>
</Pressable>
)
}
@ -56,4 +53,4 @@ const styles = StyleSheet.create({
}
})
export default Card
export default React.memo(Card, () => true)

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import { Modal, Pressable, StyleSheet, Text, View } from 'react-native'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { Feather } from '@expo/vector-icons'
import Toast from 'react-native-toast-message'
@ -12,6 +12,9 @@ import { getLocalAccountId, getLocalUrl } from 'src/utils/slices/instancesSlice'
import { store } from 'src/store'
import { useTheme } from 'src/utils/styles/ThemeManager'
import constants from 'src/utils/styles/constants'
import BottomSheet from 'src/components/BottomSheet'
import BottomSheetRow from 'src/components/BottomSheet/Row'
import { toast } from 'src/components/toast'
const fireMutation = async ({
id,
@ -32,24 +35,11 @@ const fireMutation = async ({
endpoint: `accounts/${id}/${type}`
})
if (res.body[stateKey] === true) {
Toast.show({
type: 'success',
position: 'bottom',
text1: '功能成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
if (res.body[stateKey!] === true) {
toast({ type: 'success', content: '功能成功' })
return Promise.resolve()
} else {
Toast.show({
type: 'error',
position: 'bottom',
text1: '请重试',
autoHide: false,
bottomOffset: 65
})
toast({ type: 'error', content: '功能错误', autoHide: false })
return Promise.reject()
}
break
@ -64,57 +54,40 @@ const fireMutation = async ({
})
if (!res.body.error) {
Toast.show({
type: 'success',
position: 'bottom',
text1: '隐藏域名成功',
visibilityTime: 2000,
autoHide: true,
bottomOffset: 65
})
toast({ type: 'success', content: '隐藏域名成功' })
return Promise.resolve()
} else {
Toast.show({
toast({
type: 'error',
position: 'bottom',
text1: '隐藏域名失败,请重试',
autoHide: false,
bottomOffset: 65
content: '隐藏域名失败,请重试',
autoHide: false
})
return Promise.reject()
}
break
case 'reports':
console.log('reporting')
res = await client({
method: 'post',
instance: 'local',
endpoint: `reports`,
query: {
account_id: id || ''
}
})
console.log(res.body)
if (!res.body.error) {
toast({ type: 'success', content: '举报账户成功' })
return Promise.resolve()
} else {
toast({
type: 'error',
content: '举报账户失败,请重试',
autoHide: false
})
return Promise.reject()
}
break
// case 'reports':
// res = await client({
// method: 'post',
// instance: 'local',
// endpoint: `reports`,
// query: {
// domain: id || ''
// }
// })
// if (!res.body.error) {
// Toast.show({
// type: 'success',
// position: 'bottom',
// text1: '隐藏域名成功',
// visibilityTime: 2000,
// autoHide: true,
// bottomOffset: 65
// })
// return Promise.resolve()
// } else {
// Toast.show({
// type: 'error',
// position: 'bottom',
// text1: '隐藏域名失败,请重试',
// autoHide: false,
// bottomOffset: 65
// })
// return Promise.reject()
// }
// break
}
}
@ -151,31 +124,12 @@ const HeaderDefault: React.FC<Props> = ({
const [mutateAction] = useMutation(fireMutation, {
onMutate: () => {
queryCache.cancelQueries(queryKey)
const prevData = queryCache.getQueryData(queryKey)
return prevData
const oldData = queryCache.getQueryData(queryKey)
return oldData
},
onSuccess: (newData, params) => {
if (params.type === 'domain_blocks') {
console.log('clearing cache')
queryCache.invalidateQueries(['Following', { page: 'Following' }])
}
// queryCache.setQueryData(queryKey, (oldData: any) => {
// oldData &&
// oldData.map((paging: any) => {
// paging.toots.map(
// (status: Mastodon.Status | Mastodon.Notification, i: number) => {
// if (status.id === newData.id) {
// paging.toots[i] = newData
// }
// }
// )
// })
// return oldData
// })
return Promise.resolve()
},
onError: (err, variables, prevData) => {
queryCache.setQueryData(queryKey, prevData)
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试', autoHide: false })
queryCache.setQueryData(queryKey, oldData)
},
onSettled: () => {
queryCache.invalidateQueries(queryKey)
@ -189,6 +143,24 @@ const HeaderDefault: React.FC<Props> = ({
}, 1000)
}, [since])
const onPressAction = useCallback(() => setModalVisible(true), [])
const onPressApplication = useCallback(() => {
navigation.navigate('Webview', {
uri: application!.website
})
}, [])
const pressableAction = useMemo(
() => (
<Feather
name='more-horizontal'
color={theme.secondary}
size={constants.FONT_SIZE_M + 2}
/>
),
[]
)
return (
<View>
<View style={styles.nameAndAction}>
@ -212,17 +184,12 @@ const HeaderDefault: React.FC<Props> = ({
@{account}
</Text>
</View>
{accountId !== localAccountId && domain !== localDomain && (
{(accountId !== localAccountId || domain !== localDomain) && (
<Pressable
style={styles.action}
onPress={() => setModalVisible(true)}
>
<Feather
name='more-horizontal'
color={theme.secondary}
size={constants.FONT_SIZE_M + 2}
/>
</Pressable>
onPress={onPressAction}
children={pressableAction}
/>
)}
</View>
<View style={styles.meta}>
@ -234,11 +201,7 @@ const HeaderDefault: React.FC<Props> = ({
{application && application.name !== 'Web' && (
<View>
<Text
onPress={() => {
navigation.navigate('Webview', {
uri: application.website
})
}}
onPress={onPressApplication}
style={[styles.application, { color: theme.secondary }]}
>
- {application.name}
@ -246,75 +209,65 @@ const HeaderDefault: React.FC<Props> = ({
</View>
)}
</View>
<Modal
animationType='fade'
presentationStyle='overFullScreen'
transparent
<BottomSheet
visible={modalVisible}
handleDismiss={() => setModalVisible(false)}
>
<Pressable
style={styles.modalBackground}
onPress={() => setModalVisible(false)}
>
<View style={styles.modalSheet}>
{accountId !== localAccountId && (
<Pressable
onPress={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'mute',
stateKey: 'muting'
})
}}
>
<Text></Text>
</Pressable>
)}
{accountId !== localAccountId && (
<Pressable
onPress={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'block',
stateKey: 'blocking'
})
}}
>
<Text></Text>
</Pressable>
)}
{domain !== localDomain && (
<Pressable
onPress={() => {
setModalVisible(false)
mutateAction({
id: domain,
type: 'domain_blocks'
})
}}
>
<Text></Text>
</Pressable>
)}
{accountId !== localAccountId && (
<Pressable
onPress={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'reports'
})
}}
>
<Text></Text>
</Pressable>
)}
</View>
</Pressable>
</Modal>
{accountId !== localAccountId && (
<BottomSheetRow
onPressFunction={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'mute',
stateKey: 'muting'
})
}}
icon='eye-off'
text={`隐藏 @${account} 的嘟嘟`}
/>
)}
{accountId !== localAccountId && (
<BottomSheetRow
onPressFunction={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'block',
stateKey: 'blocking'
})
}}
icon='x-circle'
text={`屏蔽用户 @${account}`}
/>
)}
{domain !== localDomain && (
<BottomSheetRow
onPressFunction={() => {
setModalVisible(false)
mutateAction({
id: domain,
type: 'domain_blocks'
})
}}
icon='cloud-off'
text={`屏蔽域名 ${domain}`}
/>
)}
{accountId !== localAccountId && (
<BottomSheetRow
onPressFunction={() => {
setModalVisible(false)
mutateAction({
id: accountId,
type: 'reports'
})
}}
icon='alert-triangle'
text={`举报 @${account}`}
/>
)}
</BottomSheet>
</View>
)
}
@ -349,22 +302,7 @@ const styles = StyleSheet.create({
application: {
fontSize: constants.FONT_SIZE_S,
marginLeft: constants.SPACING_S
},
modalBackground: {
width: '100%',
height: '100%',
backgroundColor: 'rgba(0, 0, 0, 0.75)',
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'flex-end'
},
modalSheet: {
width: '100%',
height: '50%',
backgroundColor: 'white',
flex: 1
}
})
export default HeaderDefault
export default React.memo(HeaderDefault, () => true)