mirror of https://github.com/tooot-app/app
Fixed #495
This commit is contained in:
parent
a3a0bf523f
commit
29fd36a581
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { View } from 'react-native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
|
||||
export interface Props {
|
||||
|
@ -7,14 +7,16 @@ export interface Props {
|
|||
}
|
||||
|
||||
const MenuContainer: React.FC<Props> = ({ children }) => {
|
||||
return <View style={styles.base}>{children}</View>
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
||||
}
|
||||
})
|
||||
|
||||
export default MenuContainer
|
||||
|
|
|
@ -5,7 +5,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { ColorDefinitions } from '@utils/styles/themes'
|
||||
import React, { useMemo } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { Text, View } from 'react-native'
|
||||
import { Flow } from 'react-native-animated-spinkit'
|
||||
import { State, Switch, TapGestureHandler } from 'react-native-gesture-handler'
|
||||
|
||||
|
@ -65,6 +65,7 @@ const MenuRow: React.FC<Props> = ({
|
|||
>
|
||||
<TapGestureHandler
|
||||
onHandlerStateChange={async ({ nativeEvent }) => {
|
||||
if (typeof iconBack !== 'string') return // Let icon back handles the gesture
|
||||
if (nativeEvent.state === State.ACTIVE && !loading) {
|
||||
if (screenReaderEnabled && switchOnValueChange) {
|
||||
switchOnValueChange()
|
||||
|
@ -79,12 +80,13 @@ const MenuRow: React.FC<Props> = ({
|
|||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingTop: StyleConstants.Spacing.S
|
||||
justifyContent: 'space-between',
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexGrow: 3,
|
||||
flex: 3,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
|
|
|
@ -28,8 +28,9 @@ const TimelineHeaderAndroid: React.FC<Props> = ({ queryKey, rootQueryKey, status
|
|||
url: status.url || status.uri
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
type: 'status',
|
||||
openChange,
|
||||
id: status.account.id,
|
||||
account: status.account,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||
|
|
|
@ -45,8 +45,9 @@ const TimelineHeaderDefault: React.FC<Props> = ({
|
|||
copiableContent
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
type: 'status',
|
||||
openChange,
|
||||
id: status.account.id,
|
||||
account: status.account,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||
|
|
|
@ -31,8 +31,9 @@ const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
|||
url: notification.status?.url || notification.status?.uri
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
type: 'status',
|
||||
openChange,
|
||||
id: notification.status?.account.id,
|
||||
account: notification.status?.account,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status: notification.status, queryKey })
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import haptics from '@components/haptics'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||
import { TabSharedStackParamList } from '@utils/navigation/navigators'
|
||||
import {
|
||||
QueryKeyRelationship,
|
||||
useRelationshipMutation,
|
||||
|
@ -19,25 +22,29 @@ import { useQueryClient } from 'react-query'
|
|||
import { useSelector } from 'react-redux'
|
||||
|
||||
const menuAccount = ({
|
||||
type,
|
||||
openChange,
|
||||
id,
|
||||
account,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
}: {
|
||||
type: 'status' | 'account' // Where the action is coming from
|
||||
openChange: boolean
|
||||
id?: Mastodon.Account['id']
|
||||
account?: Pick<Mastodon.Account, 'id' | 'username'>
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}): ContextMenu[][] => {
|
||||
if (!id) return []
|
||||
if (!account) return []
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
const menus: ContextMenu[][] = [[]]
|
||||
|
||||
const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id)
|
||||
const ownAccount = instanceAccount?.id === id
|
||||
const ownAccount = instanceAccount?.id === account.id
|
||||
|
||||
const [enabled, setEnabled] = useState(openChange)
|
||||
useEffect(() => {
|
||||
|
@ -45,12 +52,12 @@ const menuAccount = ({
|
|||
setEnabled(true)
|
||||
}
|
||||
}, [openChange, enabled])
|
||||
const { data, isFetching } = useRelationshipQuery({ id, options: { enabled } })
|
||||
const { data, isFetching } = useRelationshipQuery({ id: account.id, options: { enabled } })
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const timelineMutation = useTimelineMutation({
|
||||
onSuccess: (_, params) => {
|
||||
queryClient.refetchQueries(['Relationship', { id }])
|
||||
queryClient.refetchQueries(['Relationship', { id: account.id }])
|
||||
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||
displayMessage({
|
||||
theme,
|
||||
|
@ -90,7 +97,7 @@ const menuAccount = ({
|
|||
rootQueryKey && queryClient.invalidateQueries(rootQueryKey)
|
||||
}
|
||||
})
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id: account.id }]
|
||||
const relationshipMutation = useRelationshipMutation({
|
||||
onSuccess: (res, { payload: { action } }) => {
|
||||
haptics('Success')
|
||||
|
@ -118,14 +125,14 @@ const menuAccount = ({
|
|||
}
|
||||
})
|
||||
|
||||
if (!ownAccount && Platform.OS !== 'android') {
|
||||
if (!ownAccount && Platform.OS !== 'android' && type !== 'account') {
|
||||
menus[0].push({
|
||||
key: 'account-following',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
data &&
|
||||
relationshipMutation.mutate({
|
||||
id,
|
||||
id: account.id,
|
||||
type: 'outgoing',
|
||||
payload: { action: 'follow', state: !data?.requested ? data.following : true }
|
||||
}),
|
||||
|
@ -146,6 +153,17 @@ const menuAccount = ({
|
|||
})
|
||||
}
|
||||
if (!ownAccount) {
|
||||
menus[0].push({
|
||||
key: 'account-list',
|
||||
item: {
|
||||
onSelect: () => navigation.navigate('Tab-Shared-Account-In-Lists', { account }),
|
||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
||||
destructive: false,
|
||||
hidden: isFetching ? false : !data?.following
|
||||
},
|
||||
title: t('account.inLists'),
|
||||
icon: 'checklist'
|
||||
})
|
||||
menus[0].push({
|
||||
key: 'account-mute',
|
||||
item: {
|
||||
|
@ -153,7 +171,7 @@ const menuAccount = ({
|
|||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
id: account.id,
|
||||
payload: { property: 'mute', currentValue: data?.muting }
|
||||
}),
|
||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
||||
|
@ -176,7 +194,7 @@ const menuAccount = ({
|
|||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
id: account.id,
|
||||
payload: { property: 'block', currentValue: data?.blocking }
|
||||
}),
|
||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
||||
|
@ -195,13 +213,13 @@ const menuAccount = ({
|
|||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
id: account.id,
|
||||
payload: { property: 'reports' }
|
||||
})
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
id: account.id,
|
||||
payload: { property: 'block', currentValue: false }
|
||||
})
|
||||
},
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"continue": "Continue",
|
||||
"delete": "Delete"
|
||||
"delete": "Delete",
|
||||
"done": "Done"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Custom emoji {{emoji}}"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"action_false": "Follow user",
|
||||
"action_true": "Unfollow user"
|
||||
},
|
||||
"inLists": "Manage user of lists",
|
||||
"mute": {
|
||||
"action_false": "Mute user",
|
||||
"action_true": "Unmute user"
|
||||
|
|
|
@ -320,6 +320,11 @@
|
|||
},
|
||||
"suspended": "Account suspended by the moderators of your server"
|
||||
},
|
||||
"accountInLists": {
|
||||
"name": "Lists of @{{username}}",
|
||||
"inLists": "In lists",
|
||||
"notInLists": "Other lists"
|
||||
},
|
||||
"attachments": {
|
||||
"name": "<0 /><1>\"s media</1>"
|
||||
},
|
||||
|
|
|
@ -11,7 +11,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { Text, View } from 'react-native'
|
||||
import { useSharedValue } from 'react-native-reanimated'
|
||||
import { useIsFetching } from 'react-query'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
|
@ -30,21 +30,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||
const { colors, mode } = useTheme()
|
||||
|
||||
const mShare = menuShare({ type: 'account', url: account.url })
|
||||
const mAccount = menuAccount({ openChange: true, id: account.id })
|
||||
const mAccount = menuAccount({ type: 'account', openChange: true, account })
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerRight: () => {
|
||||
// const shareOnPress = contextMenuShare({
|
||||
// actions,
|
||||
// type: 'account',
|
||||
// url: account.url
|
||||
// })
|
||||
// const accountOnPress = contextMenuAccount({
|
||||
// actions,
|
||||
// type: 'account',
|
||||
// id: account.id
|
||||
// })
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
|
@ -107,7 +96,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||
const ListHeaderComponent = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<View style={[styles.header, { borderBottomColor: colors.border }]}>
|
||||
<View style={{ borderBottomWidth: 1, borderBottomColor: colors.border }}>
|
||||
<AccountHeader account={data} />
|
||||
<AccountInformation account={data} />
|
||||
{!data?.suspended && fetchedTimeline.current ? (
|
||||
|
@ -129,7 +118,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||
break
|
||||
}
|
||||
}}
|
||||
style={styles.segmentsContainer}
|
||||
style={{
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{data?.suspended ? (
|
||||
|
@ -178,14 +170,4 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
borderBottomWidth: 1
|
||||
},
|
||||
segmentsContainer: {
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
}
|
||||
})
|
||||
|
||||
export default TabSharedAccount
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
import Button from '@components/Button'
|
||||
import haptics from '@components/haptics'
|
||||
import { HeaderRight } from '@components/Header'
|
||||
import { MenuRow } from '@components/Menu'
|
||||
import CustomText from '@components/Text'
|
||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||
import { useAccountInListsQuery } from '@utils/queryHooks/account'
|
||||
import { useListAccountsMutation, useListsQuery } from '@utils/queryHooks/lists'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SectionList, Text, View } from 'react-native'
|
||||
|
||||
const TabSharedAccountInLists: React.FC<
|
||||
TabSharedStackScreenProps<'Tab-Shared-Account-In-Lists'>
|
||||
> = ({
|
||||
navigation,
|
||||
route: {
|
||||
params: { account }
|
||||
}
|
||||
}) => {
|
||||
const { colors } = useTheme()
|
||||
const { t } = useTranslation('screenTabs')
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
presentation: 'modal',
|
||||
title: t('shared.accountInLists.name', { username: account.username }),
|
||||
headerLeft: () => null,
|
||||
headerRight: () => {
|
||||
return (
|
||||
<HeaderRight
|
||||
type='text'
|
||||
content={t('common:buttons.done')}
|
||||
onPress={() => navigation.pop(1)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const listsQuery = useListsQuery({})
|
||||
const accountInListsQuery = useAccountInListsQuery({ id: account.id })
|
||||
|
||||
const sections = [
|
||||
{ id: 'in', title: t('shared.accountInLists.inLists'), data: accountInListsQuery.data || [] },
|
||||
{
|
||||
id: 'out',
|
||||
title: t('shared.accountInLists.notInLists'),
|
||||
data:
|
||||
listsQuery.data?.filter(
|
||||
({ id }) => !accountInListsQuery.data?.filter(d => d.id === id).length
|
||||
) || []
|
||||
}
|
||||
]
|
||||
|
||||
const listAccountsMutation = useListAccountsMutation({})
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
style={{ padding: StyleConstants.Spacing.Global.PagePadding }}
|
||||
sections={sections}
|
||||
SectionSeparatorComponent={props => {
|
||||
return props.leadingItem && props.trailingSection ? (
|
||||
<View style={{ flex: 1, height: StyleConstants.Spacing.Global.PagePadding * 2 }} />
|
||||
) : null
|
||||
}}
|
||||
renderSectionHeader={({ section: { title, data } }) =>
|
||||
data.length ? (
|
||||
<CustomText fontStyle='S' style={{ color: colors.secondary }} children={title} />
|
||||
) : null
|
||||
}
|
||||
renderItem={({ index, item, section }) => (
|
||||
<MenuRow
|
||||
key={index}
|
||||
iconFront='List'
|
||||
content={
|
||||
<Button
|
||||
type='icon'
|
||||
content={section.id === 'in' ? 'Minus' : 'Plus'}
|
||||
round
|
||||
disabled={accountInListsQuery.isFetching}
|
||||
onPress={() => {
|
||||
switch (section.id) {
|
||||
case 'in':
|
||||
listAccountsMutation
|
||||
.mutateAsync({
|
||||
type: 'delete',
|
||||
payload: { id: item.id, accounts: [account.id] }
|
||||
})
|
||||
.then(() => {
|
||||
haptics('Light')
|
||||
accountInListsQuery.refetch()
|
||||
})
|
||||
break
|
||||
case 'out':
|
||||
listAccountsMutation
|
||||
.mutateAsync({
|
||||
type: 'add',
|
||||
payload: { id: item.id, accounts: [account.id] }
|
||||
})
|
||||
.then(() => {
|
||||
haptics('Light')
|
||||
accountInListsQuery.refetch()
|
||||
})
|
||||
break
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
title={item.title}
|
||||
/>
|
||||
)}
|
||||
></SectionList>
|
||||
)
|
||||
}
|
||||
|
||||
export default TabSharedAccountInLists
|
|
@ -16,6 +16,7 @@ import { debounce } from 'lodash'
|
|||
import React from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Platform, TextInput, View } from 'react-native'
|
||||
import TabSharedAccountInLists from './AccountInLists'
|
||||
|
||||
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
||||
const { colors, mode } = useTheme()
|
||||
|
@ -48,6 +49,12 @@ const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNaviga
|
|||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
key='Tab-Shared-Account-In-Lists'
|
||||
name='Tab-Shared-Account-In-Lists'
|
||||
component={TabSharedAccountInLists}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
key='Tab-Shared-Attachments'
|
||||
name='Tab-Shared-Attachments'
|
||||
|
|
|
@ -92,6 +92,9 @@ export type TabSharedStackParamList = {
|
|||
'Tab-Shared-Account': {
|
||||
account: Mastodon.Account | Mastodon.Mention
|
||||
}
|
||||
'Tab-Shared-Account-In-Lists': {
|
||||
account: Pick<Mastodon.Account, 'id' | 'username'>
|
||||
}
|
||||
'Tab-Shared-Attachments': { account: Mastodon.Account }
|
||||
'Tab-Shared-Hashtag': {
|
||||
hashtag: Mastodon.Tag['name']
|
||||
|
|
|
@ -4,7 +4,7 @@ import { QueryFunctionContext, useQuery, UseQueryOptions } from 'react-query'
|
|||
|
||||
export type QueryKeyAccount = ['Account', { id: Mastodon.Account['id'] }]
|
||||
|
||||
const queryFunction = ({ queryKey }: QueryFunctionContext<QueryKeyAccount>) => {
|
||||
const accountQueryFunction = ({ queryKey }: QueryFunctionContext<QueryKeyAccount>) => {
|
||||
const { id } = queryKey[1]
|
||||
|
||||
return apiInstance<Mastodon.Account>({
|
||||
|
@ -20,7 +20,30 @@ const useAccountQuery = ({
|
|||
options?: UseQueryOptions<Mastodon.Account, AxiosError>
|
||||
}) => {
|
||||
const queryKey: QueryKeyAccount = ['Account', { ...queryKeyParams }]
|
||||
return useQuery(queryKey, queryFunction, options)
|
||||
return useQuery(queryKey, accountQueryFunction, options)
|
||||
}
|
||||
|
||||
export { useAccountQuery }
|
||||
/* ----- */
|
||||
|
||||
export type QueryKeyAccountInLists = ['AccountInLists', { id: Mastodon.Account['id'] }]
|
||||
|
||||
const accountInListsQueryFunction = ({ queryKey }: QueryFunctionContext<QueryKeyAccount>) => {
|
||||
const { id } = queryKey[1]
|
||||
|
||||
return apiInstance<Mastodon.List[]>({
|
||||
method: 'get',
|
||||
url: `accounts/${id}/lists`
|
||||
}).then(res => res.body)
|
||||
}
|
||||
|
||||
const useAccountInListsQuery = ({
|
||||
options,
|
||||
...queryKeyParams
|
||||
}: QueryKeyAccount[1] & {
|
||||
options?: UseQueryOptions<Mastodon.List[], AxiosError>
|
||||
}) => {
|
||||
const queryKey: QueryKeyAccount = ['Account', { ...queryKeyParams }]
|
||||
return useQuery(queryKey, accountInListsQueryFunction, options)
|
||||
}
|
||||
|
||||
export { useAccountQuery, useAccountInListsQuery }
|
||||
|
|
Loading…
Reference in New Issue