mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Fix #613
This commit is contained in:
114
src/components/Filter.tsx
Normal file
114
src/components/Filter.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { Fragment } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { View } from 'react-native'
|
||||
import { TouchableNativeFeedback } from 'react-native-gesture-handler'
|
||||
import Icon from './Icon'
|
||||
import CustomText from './Text'
|
||||
|
||||
export type Props = {
|
||||
onPress: () => void
|
||||
filter: Mastodon.Filter<'v2'>
|
||||
button?: React.ReactNode
|
||||
}
|
||||
|
||||
export const Filter: React.FC<Props> = ({ onPress, filter, button }) => {
|
||||
const { t } = useTranslation(['common', 'screenTabs'])
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<TouchableNativeFeedback onPress={onPress}>
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.backgroundDefault
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1 }}>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
children={filter.title}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
numberOfLines={1}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: StyleConstants.Spacing.XS
|
||||
}}
|
||||
>
|
||||
{filter.expires_at && new Date() > new Date(filter.expires_at) ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
fontWeight='Bold'
|
||||
children={t('screenTabs:me.preferencesFilters.expired')}
|
||||
style={{ color: colors.red, marginRight: StyleConstants.Spacing.M }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.keywords?.length ? (
|
||||
<CustomText
|
||||
children={t('screenTabs:me.preferencesFilters.keywords', {
|
||||
count: filter.keywords.length
|
||||
})}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.keywords?.length && filter.statuses?.length ? (
|
||||
<CustomText
|
||||
children={t('common:separator')}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.statuses?.length ? (
|
||||
<CustomText
|
||||
children={t('screenTabs:me.preferencesFilters.statuses', {
|
||||
count: filter.statuses.length
|
||||
})}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
<CustomText
|
||||
style={{ color: colors.secondary }}
|
||||
children={
|
||||
<Trans
|
||||
ns='screenTabs'
|
||||
i18nKey='me.preferencesFilters.context'
|
||||
components={[
|
||||
<>
|
||||
{filter.context.map((c, index) => (
|
||||
<Fragment key={index}>
|
||||
<CustomText
|
||||
style={{
|
||||
color: colors.secondary,
|
||||
textDecorationColor: colors.disabled,
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationStyle: 'solid'
|
||||
}}
|
||||
children={t(`screenTabs:me.preferencesFilters.contexts.${c}`)}
|
||||
/>
|
||||
<CustomText children={t('common:separator')} />
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
{button || (
|
||||
<Icon
|
||||
name='chevron-right'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.primaryDefault}
|
||||
style={{ marginLeft: 8 }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableNativeFeedback>
|
||||
)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const onPress = () => {
|
||||
navigation.push('Tab-Shared-Hashtag', { hashtag: hashtag.name })
|
||||
navigation.push('Tab-Shared-Hashtag', { tag_name: hashtag.name })
|
||||
}
|
||||
|
||||
const padding = StyleConstants.Spacing.Global.PagePadding
|
||||
|
||||
@@ -69,7 +69,7 @@ const ParseHTML: React.FC<Props> = ({
|
||||
|
||||
const [followedTags] = useAccountStorage.object('followed_tags')
|
||||
|
||||
const MAX_ALLOWED_LINES = 30
|
||||
const MAX_ALLOWED_LINES = 35
|
||||
const [totalLines, setTotalLines] = useState<number>()
|
||||
const [expanded, setExpanded] = useState(highlighted)
|
||||
|
||||
@@ -147,7 +147,7 @@ const ParseHTML: React.FC<Props> = ({
|
||||
tag?.length &&
|
||||
!disableDetails &&
|
||||
!sameHashtag &&
|
||||
navigation.push('Tab-Shared-Hashtag', { hashtag: tag })
|
||||
navigation.push('Tab-Shared-Hashtag', { tag_name: tag })
|
||||
}
|
||||
children={children}
|
||||
/>
|
||||
@@ -203,9 +203,7 @@ const ParseHTML: React.FC<Props> = ({
|
||||
onPress={async () => {
|
||||
if (!disableDetails) {
|
||||
if (shouldBeTag) {
|
||||
navigation.push('Tab-Shared-Hashtag', {
|
||||
hashtag: content.substring(1)
|
||||
})
|
||||
navigation.push('Tab-Shared-Hashtag', { tag_name: content.substring(1) })
|
||||
} else {
|
||||
await openLink(href, navigation)
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ const TimelinePoll: React.FC = () => {
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
name={
|
||||
`${poll.own_votes?.includes(index) ? 'Check' : ''}${
|
||||
poll.multiple ? 'Square' : 'Circle'
|
||||
`${poll.own_votes?.includes(index) ? 'check-' : ''}${
|
||||
poll.multiple ? 'square' : 'circle'
|
||||
}` as any
|
||||
}
|
||||
size={StyleConstants.Font.Size.M}
|
||||
|
||||
97
src/components/contextMenu/hashtag.ts
Normal file
97
src/components/contextMenu/hashtag.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import haptics from '@components/haptics'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { featureCheck } from '@utils/helpers/featureCheck'
|
||||
import { TabSharedStackParamList } from '@utils/navigation/navigators'
|
||||
import { QueryKeyFollowedTags, useTagMutation, useTagQuery } from '@utils/queryHooks/tags'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const menuHashtag = ({
|
||||
tag_name,
|
||||
queryKey
|
||||
}: {
|
||||
tag_name: Mastodon.Tag['name']
|
||||
queryKey?: QueryKeyTimeline
|
||||
}): ContextMenu => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
|
||||
const { t } = useTranslation(['common', 'componentContextMenu'])
|
||||
|
||||
const canFollowTags = featureCheck('follow_tags')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const {
|
||||
data,
|
||||
isFetching: tagIsFetching,
|
||||
refetch: tagRefetch
|
||||
} = useTagQuery({ tag_name, options: { enabled: canFollowTags } })
|
||||
const tagMutation = useTagMutation({
|
||||
onSuccess: () => {
|
||||
haptics('Light')
|
||||
tagRefetch()
|
||||
const queryKeyFollowedTags: QueryKeyFollowedTags = ['FollowedTags']
|
||||
queryClient.invalidateQueries({ queryKey: queryKeyFollowedTags })
|
||||
},
|
||||
onError: (err: any, { to }) => {
|
||||
displayMessage({
|
||||
type: 'error',
|
||||
message: t('common:message.error.message', {
|
||||
function: t('componentContextMenu:hashtag.follow', {
|
||||
defaultValue: 'false',
|
||||
context: to.toString()
|
||||
})
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
err.data &&
|
||||
err.data.error &&
|
||||
typeof err.data.error === 'string' && {
|
||||
description: err.data.error
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const menus: ContextMenu = []
|
||||
|
||||
menus.push([
|
||||
{
|
||||
type: 'item',
|
||||
key: 'hashtag-follow',
|
||||
props: {
|
||||
onSelect: () =>
|
||||
typeof data?.following === 'boolean' &&
|
||||
tagMutation.mutate({ tag_name, to: !data.following }),
|
||||
disabled: tagIsFetching,
|
||||
destructive: false,
|
||||
hidden: !canFollowTags
|
||||
},
|
||||
title: t('componentContextMenu:hashtag.follow.action', {
|
||||
defaultValue: 'false',
|
||||
context: (data?.following || false).toString()
|
||||
}),
|
||||
icon: data?.following ? 'rectangle.stack.badge.minus' : 'rectangle.stack.badge.plus'
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
key: 'hashtag-filter',
|
||||
props: {
|
||||
onSelect: () =>
|
||||
navigation.navigate('Tab-Shared-Filter', { source: 'hashtag', tag_name, queryKey }),
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: !canFollowTags
|
||||
},
|
||||
title: t('componentContextMenu:hashtag.filter.action'),
|
||||
icon: 'line.3.horizontal.decrease'
|
||||
}
|
||||
])
|
||||
|
||||
return menus
|
||||
}
|
||||
|
||||
export default menuHashtag
|
||||
@@ -231,6 +231,22 @@ const menuStatus = ({
|
||||
context: (status.pinned || false).toString()
|
||||
}),
|
||||
icon: status.pinned ? 'pin.slash' : 'pin'
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
key: 'status-filter',
|
||||
props: {
|
||||
// @ts-ignore
|
||||
onSelect: () => navigation.navigate('Tab-Shared-Filter', { source: 'status', status, queryKey }),
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: !('filtered' in status)
|
||||
},
|
||||
title: t('componentContextMenu:status.filter.action', {
|
||||
defaultValue: 'false',
|
||||
context: (!!status.filtered?.length).toString()
|
||||
}),
|
||||
icon: status.pinned ? 'rectangle.badge.checkmark' : 'rectangle.badge.xmark'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user