1
0
mirror of https://github.com/tooot-app/app synced 2025-04-19 04:37:24 +02:00
Also Android cannot read from ID, using index instead
This commit is contained in:
Zhiyuan Zheng 2022-06-14 23:32:35 +02:00
parent 85039db137
commit 236ccbe45d
9 changed files with 274 additions and 296 deletions

View File

@ -4,7 +4,7 @@
"native": "220603", "native": "220603",
"major": 4, "major": 4,
"minor": 1, "minor": 1,
"patch": 1, "patch": 2,
"expo": "45.0.0" "expo": "45.0.0"
}, },
"description": "tooot app for Mastodon", "description": "tooot app for Mastodon",

View File

@ -1,5 +1,6 @@
import analytics from '@components/analytics' import analytics from '@components/analytics'
import { displayMessage } from '@components/Message' import { displayMessage } from '@components/Message'
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
import { import {
MutationVarsTimelineUpdateAccountProperty, MutationVarsTimelineUpdateAccountProperty,
QueryKeyTimeline, QueryKeyTimeline,
@ -8,13 +9,13 @@ import {
import { getInstanceAccount } from '@utils/slices/instancesSlice' import { getInstanceAccount } from '@utils/slices/instancesSlice'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Platform } from 'react-native'
import { ContextMenuAction } from 'react-native-context-menu-view' import { ContextMenuAction } from 'react-native-context-menu-view'
import { useQueryClient } from 'react-query' import { useQueryClient } from 'react-query'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
export interface Props { export interface Props {
actions: ContextMenuAction[] actions: ContextMenuAction[]
type: 'status' | 'account' // Do not need to fetch relationship in timeline
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline
id: Mastodon.Account['id'] id: Mastodon.Account['id']
@ -22,6 +23,7 @@ export interface Props {
const contextMenuAccount = ({ const contextMenuAccount = ({
actions, actions,
type,
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: accountId id: accountId
@ -32,12 +34,17 @@ const contextMenuAccount = ({
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutateion = useTimelineMutation({ const mutateion = useTimelineMutation({
onSuccess: (_, params) => { onSuccess: (_, params) => {
queryClient.refetchQueries(['Relationship', { id: accountId }])
const theParams = params as MutationVarsTimelineUpdateAccountProperty const theParams = params as MutationVarsTimelineUpdateAccountProperty
displayMessage({ displayMessage({
theme, theme,
type: 'success', type: 'success',
message: t('common:message.success.message', { message: t('common:message.success.message', {
function: t(`account.${theParams.payload.property}.action`) function: t(`account.${theParams.payload.property}.action`, {
...(typeof theParams.payload.currentValue === 'boolean' && {
context: theParams.payload.currentValue.toString()
})
})
}) })
}) })
}, },
@ -47,7 +54,11 @@ const contextMenuAccount = ({
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`account.${theParams.payload.property}.action`) function: t(`account.${theParams.payload.property}.action`, {
...(typeof theParams.payload.currentValue === 'boolean' && {
context: theParams.payload.currentValue.toString()
})
})
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -70,93 +81,70 @@ const contextMenuAccount = ({
) )
const ownAccount = instanceAccount?.id === accountId const ownAccount = instanceAccount?.id === accountId
const { data: relationship } = useRelationshipQuery({
id: accountId,
options: { enabled: type === 'account' }
})
if (!ownAccount) { if (!ownAccount) {
switch (Platform.OS) { actions.push(
case 'ios': {
actions.push({ id: 'account-mute',
id: 'account', title: t('account.mute.action', {
title: t('account.title'), context: (relationship?.muting || false).toString()
inlineChildren: true, }),
actions: [ systemIcon: 'eye.slash'
{ },
id: 'account-mute', {
title: t('account.mute.action'), id: 'account-block',
systemIcon: 'eye.slash' title: t('account.block.action', {
}, context: (relationship?.blocking || false).toString()
{ }),
id: 'account-block', systemIcon: 'xmark.circle',
title: t('account.block.action'), destructive: true
systemIcon: 'xmark.circle', },
destructive: true {
}, id: 'account-reports',
{ title: t('account.reports.action'),
id: 'account-reports', systemIcon: 'flag',
title: t('account.reports.action'), destructive: true
systemIcon: 'flag', }
destructive: true )
}
]
})
break
default:
actions.push(
{
id: 'account-mute',
title: t('account.mute.action'),
systemIcon: 'eye.slash'
},
{
id: 'account-block',
title: t('account.block.action'),
systemIcon: 'xmark.circle',
destructive: true
},
{
id: 'account-reports',
title: t('account.reports.action'),
systemIcon: 'flag',
destructive: true
}
)
break
}
} }
return (id: string) => { return (index: number) => {
switch (id) { if (actions[index].id === 'account-mute') {
case 'account-mute': analytics('timeline_shared_headeractions_account_mute_press', {
analytics('timeline_shared_headeractions_account_mute_press', { page: queryKey && queryKey[1].page
page: queryKey && queryKey[1].page })
}) mutateion.mutate({
mutateion.mutate({ type: 'updateAccountProperty',
type: 'updateAccountProperty', queryKey,
queryKey, id: accountId,
id: accountId, payload: { property: 'mute', currentValue: relationship?.muting }
payload: { property: 'mute' } })
}) }
break if (actions[index].id === 'account-block') {
case 'account-block': analytics('timeline_shared_headeractions_account_block_press', {
analytics('timeline_shared_headeractions_account_block_press', { page: queryKey && queryKey[1].page
page: queryKey && queryKey[1].page })
}) mutateion.mutate({
mutateion.mutate({ type: 'updateAccountProperty',
type: 'updateAccountProperty', queryKey,
queryKey, id: accountId,
id: accountId, payload: { property: 'block', currentValue: relationship?.blocking }
payload: { property: 'block' } })
}) }
break if (actions[index].id === 'account-report') {
case 'account-report': analytics('timeline_shared_headeractions_account_reports_press', {
analytics('timeline_shared_headeractions_account_reports_press', { page: queryKey && queryKey[1].page
page: queryKey && queryKey[1].page })
}) mutateion.mutate({
mutateion.mutate({ type: 'updateAccountProperty',
type: 'updateAccountProperty', queryKey,
queryKey, id: accountId,
id: accountId, payload: { property: 'reports' }
payload: { property: 'reports' } })
})
break
} }
} }
} }

View File

@ -71,36 +71,38 @@ const contextMenuInstance = ({
} }
} }
return (id: string) => { return (index: number) => {
switch (id) { if (
case 'instance-block': actions[index].id === 'instance-block' ||
analytics('timeline_shared_headeractions_domain_block_press', { (actions[index].id === 'instance' &&
page: queryKey[1].page actions[index].actions?.[0].id === 'instance-block')
}) ) {
Alert.alert( analytics('timeline_shared_headeractions_domain_block_press', {
t('instance.block.alert.title', { instance }), page: queryKey[1].page
t('instance.block.alert.message'), })
[ Alert.alert(
{ t('instance.block.alert.title', { instance }),
text: t('instance.block.alert.buttons.confirm'), t('instance.block.alert.message'),
style: 'destructive', [
onPress: () => { {
analytics( text: t('instance.block.alert.buttons.confirm'),
'timeline_shared_headeractions_domain_block_confirm', style: 'destructive',
{ page: queryKey && queryKey[1].page } onPress: () => {
) analytics('timeline_shared_headeractions_domain_block_confirm', {
mutation.mutate({ page: queryKey && queryKey[1].page
type: 'domainBlock', })
queryKey, mutation.mutate({
domain: instance type: 'domainBlock',
}) queryKey,
} domain: instance
}, })
{
text: t('common:buttons.cancel')
} }
] },
) {
text: t('common:buttons.cancel')
}
]
)
} }
} }
} }

View File

@ -18,19 +18,17 @@ const contextMenuShare = ({ actions, type, url }: Props) => {
systemIcon: 'square.and.arrow.up' systemIcon: 'square.and.arrow.up'
}) })
return (id: string) => { return (index: number) => {
switch (id) { if (actions[index].id === 'share') {
case 'share': analytics('timeline_shared_headeractions_share_press')
analytics('timeline_shared_headeractions_share_press') switch (Platform.OS) {
switch (Platform.OS) { case 'ios':
case 'ios': Share.share({ url })
Share.share({ url }) break
break case 'android':
case 'android': Share.share({ message: url })
Share.share({ message: url }) break
break }
}
break
} }
} }
} }

View File

@ -15,7 +15,7 @@ import {
} from '@utils/slices/instancesSlice' } from '@utils/slices/instancesSlice'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Alert, Platform } from 'react-native' import { Alert } from 'react-native'
import { ContextMenuAction } from 'react-native-context-menu-view' import { ContextMenuAction } from 'react-native-context-menu-view'
import { useQueryClient } from 'react-query' import { useQueryClient } from 'react-query'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
@ -88,7 +88,7 @@ const contextMenuStatus = ({
}, },
{ {
id: 'status-mute', id: 'status-mute',
title: t('status.mute.action-muted', { title: t('status.mute.action', {
context: status.muted.toString() context: status.muted.toString()
}), }),
systemIcon: status.muted ? 'speaker' : 'speaker.slash' systemIcon: status.muted ? 'speaker' : 'speaker.slash'
@ -107,178 +107,161 @@ const contextMenuStatus = ({
if (status.visibility === 'public' || status.visibility === 'unlisted') { if (status.visibility === 'public' || status.visibility === 'unlisted') {
accountMenuItems.push({ accountMenuItems.push({
id: 'status-pin', id: 'status-pin',
title: t('status.pin.action-pinned', { title: t('status.pin.action', {
context: status.pinned.toString() context: status.pinned.toString()
}), }),
systemIcon: status.pinned ? 'pin.slash' : 'pin' systemIcon: status.pinned ? 'pin.slash' : 'pin'
}) })
} }
switch (Platform.OS) { actions.push(...accountMenuItems)
case 'ios':
actions.push({
id: 'status',
title: t('status.title'),
inlineChildren: true,
actions: accountMenuItems
})
break
default:
actions.push(...accountMenuItems)
break
}
} }
return async (id: string) => { return async (index: number) => {
switch (id) { if (actions[index].id === 'status-delete') {
case 'status-delete': analytics('timeline_shared_headeractions_status_delete_press', {
analytics('timeline_shared_headeractions_status_delete_press', { page: queryKey && queryKey[1].page
page: queryKey && queryKey[1].page })
}) Alert.alert(
Alert.alert( t('status.delete.alert.title'),
t('status.delete.alert.title'), t('status.delete.alert.message'),
t('status.delete.alert.message'), [
[ {
{ text: t('status.delete.alert.buttons.confirm'),
text: t('status.delete.alert.buttons.confirm'), style: 'destructive',
style: 'destructive', onPress: async () => {
onPress: async () => { analytics('timeline_shared_headeractions_status_delete_confirm', {
analytics( page: queryKey && queryKey[1].page
'timeline_shared_headeractions_status_delete_confirm', })
{ mutation.mutate({
page: queryKey && queryKey[1].page type: 'deleteItem',
} source: 'statuses',
) queryKey,
mutation.mutate({ rootQueryKey,
id: status.id
})
}
},
{
text: t('common:buttons.cancel')
}
]
)
}
if (actions[index].id === 'status-delete-edit') {
analytics('timeline_shared_headeractions_status_deleteedit_press', {
page: queryKey && queryKey[1].page
})
Alert.alert(
t('status.deleteEdit.alert.title'),
t('status.deleteEdit.alert.message'),
[
{
text: t('status.deleteEdit.alert.buttons.confirm'),
style: 'destructive',
onPress: async () => {
analytics(
'timeline_shared_headeractions_status_deleteedit_confirm',
{
page: queryKey && queryKey[1].page
}
)
let replyToStatus: Mastodon.Status | undefined = undefined
if (status.in_reply_to_id) {
replyToStatus = await apiInstance<Mastodon.Status>({
method: 'get',
url: `statuses/${status.in_reply_to_id}`
}).then(res => res.body)
}
mutation
.mutateAsync({
type: 'deleteItem', type: 'deleteItem',
source: 'statuses', source: 'statuses',
queryKey, queryKey,
rootQueryKey,
id: status.id id: status.id
}) })
} .then(res => {
}, navigation.navigate('Screen-Compose', {
{ type: 'deleteEdit',
text: t('common:buttons.cancel') incomingStatus: res.body as Mastodon.Status,
} ...(replyToStatus && { replyToStatus }),
] queryKey
)
break
case 'status-delete-edit':
analytics('timeline_shared_headeractions_status_deleteedit_press', {
page: queryKey && queryKey[1].page
})
Alert.alert(
t('status.deleteEdit.alert.title'),
t('status.deleteEdit.alert.message'),
[
{
text: t('status.deleteEdit.alert.buttons.confirm'),
style: 'destructive',
onPress: async () => {
analytics(
'timeline_shared_headeractions_status_deleteedit_confirm',
{
page: queryKey && queryKey[1].page
}
)
let replyToStatus: Mastodon.Status | undefined = undefined
if (status.in_reply_to_id) {
replyToStatus = await apiInstance<Mastodon.Status>({
method: 'get',
url: `statuses/${status.in_reply_to_id}`
}).then(res => res.body)
}
mutation
.mutateAsync({
type: 'deleteItem',
source: 'statuses',
queryKey,
id: status.id
}) })
.then(res => { })
navigation.navigate('Screen-Compose', {
type: 'deleteEdit',
incomingStatus: res.body as Mastodon.Status,
...(replyToStatus && { replyToStatus }),
queryKey
})
})
}
},
{
text: t('common:buttons.cancel')
} }
] },
) {
break text: t('common:buttons.cancel')
case 'status-mute':
analytics('timeline_shared_headeractions_status_mute_press', {
page: queryKey && queryKey[1].page
})
mutation.mutate({
type: 'updateStatusProperty',
queryKey,
rootQueryKey,
id: status.id,
payload: {
property: 'muted',
currentValue: status.muted,
propertyCount: undefined,
countValue: undefined
} }
}) ]
break )
case 'status-edit': }
analytics('timeline_shared_headeractions_status_edit_press', { if (actions[index].id === 'status-mute') {
page: queryKey && queryKey[1].page analytics('timeline_shared_headeractions_status_mute_press', {
}) page: queryKey && queryKey[1].page
let replyToStatus: Mastodon.Status | undefined = undefined })
if (status.in_reply_to_id) { mutation.mutate({
replyToStatus = await apiInstance<Mastodon.Status>({ type: 'updateStatusProperty',
method: 'get', queryKey,
url: `statuses/${status.in_reply_to_id}` rootQueryKey,
}).then(res => res.body) id: status.id,
payload: {
property: 'muted',
currentValue: status.muted,
propertyCount: undefined,
countValue: undefined
} }
apiInstance<{ })
id: Mastodon.Status['id'] }
text: NonNullable<Mastodon.Status['text']> if (actions[index].id === 'status-edit') {
spoiler_text: Mastodon.Status['spoiler_text'] analytics('timeline_shared_headeractions_status_edit_press', {
}>({ page: queryKey && queryKey[1].page
})
let replyToStatus: Mastodon.Status | undefined = undefined
if (status.in_reply_to_id) {
replyToStatus = await apiInstance<Mastodon.Status>({
method: 'get', method: 'get',
url: `statuses/${status.id}/source` url: `statuses/${status.in_reply_to_id}`
}).then(res => { }).then(res => res.body)
navigation.navigate('Screen-Compose', { }
type: 'edit', apiInstance<{
incomingStatus: { id: Mastodon.Status['id']
...status, text: NonNullable<Mastodon.Status['text']>
text: res.body.text, spoiler_text: Mastodon.Status['spoiler_text']
spoiler_text: res.body.spoiler_text }>({
}, method: 'get',
...(replyToStatus && { replyToStatus }), url: `statuses/${status.id}/source`
queryKey, }).then(res => {
rootQueryKey navigation.navigate('Screen-Compose', {
}) type: 'edit',
}) incomingStatus: {
break ...status,
case 'status-pin': text: res.body.text,
// Also note that reblogs cannot be pinned. spoiler_text: res.body.spoiler_text
analytics('timeline_shared_headeractions_status_pin_press', { },
page: queryKey && queryKey[1].page ...(replyToStatus && { replyToStatus }),
})
mutation.mutate({
type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey
id: status.id,
payload: {
property: 'pinned',
currentValue: status.pinned,
propertyCount: undefined,
countValue: undefined
}
}) })
break })
}
if (actions[index].id === 'status-pin') {
// Also note that reblogs cannot be pinned.
analytics('timeline_shared_headeractions_status_pin_press', {
page: queryKey && queryKey[1].page
})
mutation.mutate({
type: 'updateStatusProperty',
queryKey,
rootQueryKey,
id: status.id,
payload: {
property: 'pinned',
currentValue: status.pinned,
propertyCount: undefined,
countValue: undefined
}
})
} }
} }
} }

View File

@ -47,6 +47,7 @@ const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
}) })
const accountOnPress = contextMenuAccount({ const accountOnPress = contextMenuAccount({
actions, actions,
type: 'status',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.account.id id: status.account.id
@ -62,14 +63,14 @@ const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
<ContextMenuContext.Provider value={actions}> <ContextMenuContext.Provider value={actions}>
<ContextMenu <ContextMenu
actions={actions} actions={actions}
onPress={({ nativeEvent: { id } }) => { onPress={({ nativeEvent: { index } }) => {
for (const on of [ for (const on of [
shareOnPress, shareOnPress,
statusOnPress, statusOnPress,
accountOnPress, accountOnPress,
instanceOnPress instanceOnPress
]) { ]) {
on && on(id) on && on(index)
} }
}} }}
children={children} children={children}

View File

@ -3,10 +3,12 @@
"account": { "account": {
"title": "User actions", "title": "User actions",
"mute": { "mute": {
"action": "Mute user" "action_false": "Mute user",
"action_true": "Unmute user"
}, },
"block": { "block": {
"action": "Block user" "action_false": "Block user",
"action_true": "Unblock user"
}, },
"reports": { "reports": {
"action": "Report user" "action": "Report user"
@ -59,12 +61,12 @@
} }
}, },
"mute": { "mute": {
"action-muted_false": "Mute toot and replies", "action_false": "Mute toot and replies",
"action-muted_true": "Unmute toot and replies" "action_true": "Unmute toot and replies"
}, },
"pin": { "pin": {
"action-pinned_false": "Pin toot", "action_false": "Pin toot",
"action-pinned_true": "Unpin toot" "action_true": "Unpin toot"
} }
} }
} }

View File

@ -63,15 +63,16 @@ const TabSharedRoot = ({
}) })
const accountOnPress = contextMenuAccount({ const accountOnPress = contextMenuAccount({
actions, actions,
type: 'account',
id: account.id id: account.id
}) })
return ( return (
<ContextMenu <ContextMenu
actions={actions} actions={actions}
onPress={({ nativeEvent: { id } }) => { onPress={({ nativeEvent: { index } }) => {
shareOnPress(id) shareOnPress(index)
accountOnPress(id) accountOnPress(index)
}} }}
dropdownMenuMode dropdownMenuMode
> >

View File

@ -314,6 +314,7 @@ export type MutationVarsTimelineUpdateAccountProperty = {
id: Mastodon.Account['id'] id: Mastodon.Account['id']
payload: { payload: {
property: 'mute' | 'block' | 'reports' property: 'mute' | 'block' | 'reports'
currentValue?: boolean
} }
} }
@ -383,7 +384,9 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
case 'mute': case 'mute':
return apiInstance<Mastodon.Account>({ return apiInstance<Mastodon.Account>({
method: 'post', method: 'post',
url: `accounts/${params.id}/${params.payload.property}` url: `accounts/${params.id}/${
params.payload.currentValue ? 'un' : ''
}${params.payload.property}`
}) })
case 'reports': case 'reports':
return apiInstance<Mastodon.Account>({ return apiInstance<Mastodon.Account>({