tooot/src/components/Timeline/Shared/Actions.tsx

307 lines
9.4 KiB
TypeScript

import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import CustomText from '@components/Text'
import { useActionSheet } from '@expo/react-native-action-sheet'
import { useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { useQueryClient } from '@tanstack/react-query'
import { androidActionSheetStyles } from '@utils/helpers/androidActionSheetStyles'
import { RootStackParamList, useNavState } from '@utils/navigation/navigators'
import {
MutationVarsTimelineUpdateStatusProperty,
QueryKeyTimeline,
useTimelineMutation
} from '@utils/queryHooks/timeline'
import { useAccountStorage } from '@utils/storage/actions'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import { uniqBy } from 'lodash'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, StyleSheet, View } from 'react-native'
import StatusContext from './Context'
const TimelineActions: React.FC = () => {
const { queryKey, status, isMyAccount, highlighted, disableDetails } = useContext(StatusContext)
if (!queryKey || !status || disableDetails) return null
const navigationState = useNavState()
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
const { t } = useTranslation(['common', 'componentTimeline'])
const { colors } = useTheme()
const iconColor = colors.secondary
const queryClient = useQueryClient()
const mutation = useTimelineMutation({
onMutate: true,
onSuccess: (_, params) => {
const theParams = params as MutationVarsTimelineUpdateStatusProperty
if (
// Un-bookmark from bookmarks page
(queryKey[1].page === 'Bookmarks' && theParams.payload.type === 'bookmarked') ||
// Un-favourite from favourites page
(queryKey[1].page === 'Favourites' && theParams.payload.type === 'favourited')
) {
queryClient.invalidateQueries(queryKey)
} else if (theParams.payload.type === 'favourited') {
// When favourited, update favourited page
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }]
queryClient.invalidateQueries(tempQueryKey)
} else if (theParams.payload.type === 'bookmarked') {
// When bookmarked, update bookmark page
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }]
queryClient.invalidateQueries(tempQueryKey)
}
},
onError: (err: any, params) => {
const correctParam = params as MutationVarsTimelineUpdateStatusProperty
displayMessage({
type: 'error',
message: t('common:message.error.message', {
function: t(
`componentTimeline:shared.actions.${correctParam.payload.type}.function` as any
)
}),
...(err.status &&
typeof err.status === 'number' &&
err.data &&
err.data.error &&
typeof err.data.error === 'string' && {
description: err.data.error
})
})
queryClient.invalidateQueries(queryKey)
}
})
const [accountId] = useAccountStorage.string('auth.account.id')
const onPressReply = () => {
const accts = uniqBy(
([status.account] as Mastodon.Account[] & Mastodon.Mention[])
.concat(status.mentions)
.filter(d => d?.id !== accountId),
d => d?.id
).map(d => d?.acct)
navigation.navigate('Screen-Compose', {
type: 'reply',
incomingStatus: status,
accts,
navigationState
})
}
const { showActionSheetWithOptions } = useActionSheet()
const onPressReblog = () => {
if (!status.reblogged) {
showActionSheetWithOptions(
{
title: t('componentTimeline:shared.actions.reblogged.options.title'),
options: [
t('componentTimeline:shared.actions.reblogged.options.public'),
t('componentTimeline:shared.actions.reblogged.options.unlisted'),
t('common:buttons.cancel')
],
cancelButtonIndex: 2,
...androidActionSheetStyles(colors)
},
selectedIndex => {
switch (selectedIndex) {
case 0:
mutation.mutate({
type: 'updateStatusProperty',
status,
payload: {
type: 'reblogged',
visibility: 'public',
to: true
}
})
break
case 1:
mutation.mutate({
type: 'updateStatusProperty',
status,
payload: {
type: 'reblogged',
visibility: 'unlisted',
to: true
}
})
break
}
}
)
} else {
mutation.mutate({
type: 'updateStatusProperty',
status,
payload: {
type: 'reblogged',
visibility: 'public',
to: false
}
})
}
}
const onPressFavourite = () => {
mutation.mutate({
type: 'updateStatusProperty',
status,
payload: {
type: 'favourited',
to: !status.favourited
}
})
}
const onPressBookmark = () => {
mutation.mutate({
type: 'updateStatusProperty',
status,
payload: {
type: 'bookmarked',
to: !status.bookmarked
}
})
}
const childrenReply = () => (
<>
<Icon name='message-circle' color={iconColor} size={StyleConstants.Font.Size.L} />
{status.replies_count > 0 ? (
<CustomText
fontStyle='S'
style={{
color: colors.secondary,
marginLeft: StyleConstants.Spacing.XS
}}
>
{status.replies_count}
</CustomText>
) : null}
</>
)
const childrenReblog = () => {
const color = (state: boolean) => (state ? colors.green : colors.secondary)
const disabled =
status.visibility === 'direct' || (status.visibility === 'private' && !isMyAccount)
return (
<>
<Icon
name='repeat'
color={disabled ? colors.disabled : color(status.reblogged)}
crossOut={disabled}
size={StyleConstants.Font.Size.L}
/>
{status.reblogs_count > 0 ? (
<CustomText
fontStyle='S'
style={{
color:
status.visibility === 'private' && !isMyAccount
? colors.disabled
: color(status.reblogged),
marginLeft: StyleConstants.Spacing.XS
}}
>
{status.reblogs_count}
</CustomText>
) : null}
</>
)
}
const childrenFavourite = () => {
const color = (state: boolean) => (state ? colors.red : colors.secondary)
return (
<>
<Icon name='heart' color={color(status.favourited)} size={StyleConstants.Font.Size.L} />
{status.favourites_count > 0 ? (
<CustomText
fontStyle='S'
style={{ color: color(status.favourited), marginLeft: StyleConstants.Spacing.XS }}
>
{status.favourites_count}
</CustomText>
) : null}
</>
)
}
const childrenBookmark = () => {
const color = (state: boolean) => (state ? colors.yellow : colors.secondary)
return (
<Icon name='bookmark' color={color(status.bookmarked)} size={StyleConstants.Font.Size.L} />
)
}
return (
<View style={{ flexDirection: 'row' }}>
<Pressable
{...(highlighted
? {
accessibilityLabel: t('componentTimeline:shared.actions.reply.accessibilityLabel'),
accessibilityRole: 'button'
}
: { accessibilityLabel: '' })}
style={styles.action}
onPress={onPressReply}
children={childrenReply()}
/>
<Pressable
{...(highlighted
? {
accessibilityLabel: t(
'componentTimeline:shared.actions.reblogged.accessibilityLabel'
),
accessibilityRole: 'button'
}
: { accessibilityLabel: '' })}
style={styles.action}
onPress={onPressReblog}
children={childrenReblog()}
disabled={
status.visibility === 'direct' || (status.visibility === 'private' && !isMyAccount)
}
/>
<Pressable
{...(highlighted
? {
accessibilityLabel: t(
'componentTimeline:shared.actions.favourited.accessibilityLabel'
),
accessibilityRole: 'button'
}
: { accessibilityLabel: '' })}
style={styles.action}
onPress={onPressFavourite}
children={childrenFavourite()}
/>
<Pressable
{...(highlighted
? {
accessibilityLabel: t(
'componentTimeline:shared.actions.bookmarked.accessibilityLabel'
),
accessibilityRole: 'button'
}
: { accessibilityLabel: '' })}
style={styles.action}
onPress={onPressBookmark}
children={childrenBookmark()}
/>
</View>
)
}
const styles = StyleSheet.create({
action: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
paddingVertical: StyleConstants.Spacing.S * 1.5
}
})
export default TimelineActions