mirror of
https://github.com/tooot-app/app
synced 2025-02-09 00:18:38 +01:00
Moving to using zeego
This commit is contained in:
parent
f619d1bb6a
commit
a3a0bf523f
@ -297,8 +297,6 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-cameraroll (5.1.0):
|
||||
- React-Core
|
||||
- react-native-context-menu-view (1.5.4):
|
||||
- React
|
||||
- react-native-image-picker (4.10.1):
|
||||
- React-Core
|
||||
- react-native-ios-context-menu (1.15.1):
|
||||
@ -494,7 +492,6 @@ DEPENDENCIES:
|
||||
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
|
||||
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
|
||||
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
|
||||
- react-native-context-menu-view (from `../node_modules/react-native-context-menu-view`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
- react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`)
|
||||
- react-native-language-detection (from `../node_modules/react-native-language-detection`)
|
||||
@ -627,8 +624,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-blurhash"
|
||||
react-native-cameraroll:
|
||||
:path: "../node_modules/@react-native-camera-roll/camera-roll"
|
||||
react-native-context-menu-view:
|
||||
:path: "../node_modules/react-native-context-menu-view"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-ios-context-menu:
|
||||
@ -742,7 +737,6 @@ SPEC CHECKSUMS:
|
||||
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
||||
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
||||
react-native-cameraroll: a40b082318eb1ecd0336a2f29d9f74b7f2c8cae8
|
||||
react-native-context-menu-view: b0beca02aad4bd9f9d7d932bf437e0a03baa69ef
|
||||
react-native-image-picker: f2ab1215d17bcfe27b0eb6417cc236fd1f4775e7
|
||||
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
|
||||
react-native-language-detection: 0e43195ad014974f1b7a31b64820eff34a243f2d
|
||||
|
@ -71,7 +71,6 @@
|
||||
"react-native-animated-spinkit": "^1.5.2",
|
||||
"react-native-base64": "^0.2.1",
|
||||
"react-native-blurhash": "^1.1.10",
|
||||
"react-native-context-menu-view": "xmflsct/react-native-context-menu-view",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-feather": "^1.1.2",
|
||||
"react-native-flash-message": "^0.3.1",
|
||||
|
@ -1,175 +0,0 @@
|
||||
import { displayMessage } from '@components/Message'
|
||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
||||
import {
|
||||
MutationVarsTimelineUpdateAccountProperty,
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
export interface Props {
|
||||
actions: ContextMenuAction[]
|
||||
type: 'status' | 'account' // Do not need to fetch relationship in timeline
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
id: Mastodon.Account['id']
|
||||
}
|
||||
|
||||
const contextMenuAccount = ({ actions, type, queryKey, rootQueryKey, id: accountId }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const mutation = useTimelineMutation({
|
||||
onSuccess: (_, params) => {
|
||||
queryClient.refetchQueries(['Relationship', { id: accountId }])
|
||||
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'success',
|
||||
message: t('common:message.success.message', {
|
||||
function: t(`account.${theParams.payload.property}.action`, {
|
||||
...(theParams.payload.property !== 'reports' && {
|
||||
context: (theParams.payload.currentValue || false).toString()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onError: (err: any, params) => {
|
||||
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'error',
|
||||
message: t('common:message.error.message', {
|
||||
function: t(`account.${theParams.payload.property}.action`, {
|
||||
...(theParams.payload.property !== 'reports' && {
|
||||
context: (theParams.payload.currentValue || false).toString()
|
||||
})
|
||||
})
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
err.data &&
|
||||
err.data.error &&
|
||||
typeof err.data.error === 'string' && {
|
||||
description: err.data.error
|
||||
})
|
||||
})
|
||||
},
|
||||
onSettled: () => {
|
||||
queryKey && queryClient.invalidateQueries(queryKey)
|
||||
rootQueryKey && queryClient.invalidateQueries(rootQueryKey)
|
||||
}
|
||||
})
|
||||
|
||||
const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id)
|
||||
const ownAccount = instanceAccount?.id === accountId
|
||||
|
||||
const { data: relationship } = useRelationshipQuery({
|
||||
id: accountId,
|
||||
options: { enabled: type === 'account' }
|
||||
})
|
||||
|
||||
if (!ownAccount) {
|
||||
actions.push({
|
||||
id: 'account-mute',
|
||||
title: t('account.mute.action', {
|
||||
context: (relationship?.muting || false).toString()
|
||||
}),
|
||||
systemIcon: 'eye.slash'
|
||||
})
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
actions.push({
|
||||
id: 'account',
|
||||
title: t('account.title'),
|
||||
actions: [
|
||||
{
|
||||
id: 'account-block',
|
||||
title: t('account.block.action', {
|
||||
context: (relationship?.blocking || false).toString()
|
||||
}),
|
||||
systemIcon: 'xmark.circle',
|
||||
destructive: true
|
||||
},
|
||||
{
|
||||
id: 'account-reports',
|
||||
title: t('account.reports.action'),
|
||||
systemIcon: 'flag',
|
||||
destructive: true
|
||||
}
|
||||
]
|
||||
})
|
||||
break
|
||||
default:
|
||||
actions.push(
|
||||
{
|
||||
id: 'account-block',
|
||||
title: t('account.block.action', {
|
||||
context: (relationship?.blocking || false).toString()
|
||||
}),
|
||||
systemIcon: 'xmark.circle',
|
||||
destructive: true
|
||||
},
|
||||
{
|
||||
id: 'account-reports',
|
||||
title: t('account.reports.action'),
|
||||
systemIcon: 'flag',
|
||||
destructive: true
|
||||
}
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (index: number) => {
|
||||
if (typeof index !== 'number' || !actions[index]) {
|
||||
return // For Android
|
||||
}
|
||||
if (actions[index].id === 'account-mute') {
|
||||
mutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id: accountId,
|
||||
payload: { property: 'mute', currentValue: relationship?.muting }
|
||||
})
|
||||
}
|
||||
if (
|
||||
actions[index].id === 'account-block' ||
|
||||
(actions[index].id === 'account' && actions[index].actions?.[0].id === 'account-block')
|
||||
) {
|
||||
mutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id: accountId,
|
||||
payload: { property: 'block', currentValue: relationship?.blocking }
|
||||
})
|
||||
}
|
||||
if (
|
||||
actions[index].id === 'account-reports' ||
|
||||
(actions[index].id === 'account' && actions[index].actions?.[0].id === 'account-reports')
|
||||
) {
|
||||
mutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id: accountId,
|
||||
payload: { property: 'reports' }
|
||||
})
|
||||
mutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id: accountId,
|
||||
payload: { property: 'block', currentValue: false }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default contextMenuAccount
|
@ -3,24 +3,23 @@ import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timelin
|
||||
import { getInstanceUrl } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Alert, Platform } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
import { Alert } from 'react-native'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
export interface Props {
|
||||
actions: ContextMenuAction[]
|
||||
status: Mastodon.Status
|
||||
queryKey: QueryKeyTimeline
|
||||
const menuInstance = ({
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
}: {
|
||||
status?: Mastodon.Status
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}
|
||||
}): ContextMenu[][] => {
|
||||
if (!status || !queryKey) return []
|
||||
|
||||
const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props) => {
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const currentInstance = useSelector(getInstanceUrl)
|
||||
const instance = status?.uri && status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const mutation = useTimelineMutation({
|
||||
@ -37,61 +36,48 @@ const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props)
|
||||
}
|
||||
})
|
||||
|
||||
const menus: ContextMenu[][] = []
|
||||
|
||||
const currentInstance = useSelector(getInstanceUrl)
|
||||
const instance = status.uri && status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||
|
||||
if (currentInstance !== instance && instance) {
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
actions.push({
|
||||
id: 'instance',
|
||||
title: t('instance.title'),
|
||||
actions: [
|
||||
{
|
||||
id: 'instance-block',
|
||||
title: t('instance.block.action', { instance }),
|
||||
destructive: true
|
||||
}
|
||||
]
|
||||
})
|
||||
break
|
||||
default:
|
||||
actions.push({
|
||||
id: 'instance-block',
|
||||
title: t('instance.block.action', { instance }),
|
||||
destructive: true
|
||||
})
|
||||
break
|
||||
}
|
||||
menus.push([
|
||||
{
|
||||
key: 'instance-block',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
Alert.alert(
|
||||
t('instance.block.alert.title', { instance }),
|
||||
t('instance.block.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t('instance.block.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
mutation.mutate({
|
||||
type: 'domainBlock',
|
||||
queryKey,
|
||||
domain: instance
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel')
|
||||
}
|
||||
]
|
||||
),
|
||||
disabled: false,
|
||||
destructive: true,
|
||||
hidden: false
|
||||
},
|
||||
title: t('instance.block.action', { instance }),
|
||||
icon: ''
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
return (index: number) => {
|
||||
if (typeof index !== 'number' || !actions[index]) {
|
||||
return // For Android
|
||||
}
|
||||
if (
|
||||
actions[index].id === 'instance-block' ||
|
||||
(actions[index].id === 'instance' && actions[index].actions?.[0].id === 'instance-block')
|
||||
) {
|
||||
Alert.alert(
|
||||
t('instance.block.alert.title', { instance }),
|
||||
t('instance.block.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t('instance.block.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
mutation.mutate({
|
||||
type: 'domainBlock',
|
||||
queryKey,
|
||||
domain: instance
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel')
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
return menus
|
||||
}
|
||||
|
||||
export default contextMenuInstance
|
||||
export default menuInstance
|
||||
|
@ -3,59 +3,74 @@ import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, Share } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
|
||||
export interface Props {
|
||||
copiableContent?: React.MutableRefObject<{
|
||||
content?: string | undefined
|
||||
complete: boolean
|
||||
}>
|
||||
actions: ContextMenuAction[]
|
||||
type: 'status' | 'account'
|
||||
url: string
|
||||
}
|
||||
const menuShare = (
|
||||
params:
|
||||
| {
|
||||
visibility?: Mastodon.Status['visibility']
|
||||
copiableContent?: React.MutableRefObject<{
|
||||
content?: string | undefined
|
||||
complete: boolean
|
||||
}>
|
||||
type: 'status'
|
||||
url?: string
|
||||
}
|
||||
| {
|
||||
type: 'account'
|
||||
url: string
|
||||
}
|
||||
): ContextMenu[][] => {
|
||||
if (params.type === 'status' && params.visibility === 'direct') return []
|
||||
|
||||
const contextMenuShare = ({ copiableContent, actions, type, url }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
actions.push({
|
||||
id: 'share',
|
||||
title: t(`share.${type}.action`),
|
||||
systemIcon: 'square.and.arrow.up'
|
||||
})
|
||||
Platform.OS !== 'android' &&
|
||||
type === 'status' &&
|
||||
actions.push({
|
||||
id: 'copy',
|
||||
title: t(`copy.action`),
|
||||
systemIcon: 'doc.on.doc',
|
||||
disabled: !copiableContent?.current.content?.length
|
||||
const menus: ContextMenu[][] = [[]]
|
||||
|
||||
if (params.url) {
|
||||
const url = params.url
|
||||
menus[0].push({
|
||||
key: 'share',
|
||||
item: {
|
||||
onSelect: () => {
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
Share.share({ url })
|
||||
break
|
||||
case 'android':
|
||||
Share.share({ message: url })
|
||||
break
|
||||
}
|
||||
},
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: t(`share.${params.type}.action`),
|
||||
icon: 'square.and.arrow.up'
|
||||
})
|
||||
}
|
||||
if (params.type === 'status' && Platform.OS === 'ios')
|
||||
menus[0].push({
|
||||
key: 'copy',
|
||||
item: {
|
||||
onSelect: () => {
|
||||
Clipboard.setString(params.copiableContent?.current.content || '')
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'success',
|
||||
message: t(`copy.succeed`)
|
||||
})
|
||||
},
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: !params.copiableContent?.current.content?.length
|
||||
},
|
||||
title: t('copy.action'),
|
||||
icon: 'doc.on.doc'
|
||||
})
|
||||
|
||||
return (index: number) => {
|
||||
if (typeof index !== 'number' || !actions[index]) {
|
||||
return // For Android
|
||||
}
|
||||
if (actions[index].id === 'copy') {
|
||||
Clipboard.setString(copiableContent?.current.content || '')
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'success',
|
||||
message: t(`copy.succeed`)
|
||||
})
|
||||
}
|
||||
if (actions[index].id === 'share') {
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
Share.share({ url })
|
||||
break
|
||||
case 'android':
|
||||
Share.share({ message: url })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return menus
|
||||
}
|
||||
|
||||
export default contextMenuShare
|
||||
export default menuShare
|
||||
|
@ -12,18 +12,20 @@ import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instance
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Alert } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
export interface Props {
|
||||
actions: ContextMenuAction[]
|
||||
status: Mastodon.Status
|
||||
queryKey: QueryKeyTimeline
|
||||
const menuStatus = ({
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
}: {
|
||||
status?: Mastodon.Status
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}
|
||||
}): ContextMenu[][] => {
|
||||
if (!status || !queryKey) return []
|
||||
|
||||
const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) => {
|
||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
@ -53,85 +55,19 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) =
|
||||
}
|
||||
})
|
||||
|
||||
const menus: ContextMenu[][] = []
|
||||
|
||||
const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id)
|
||||
const ownAccount = instanceAccount?.id === status?.account?.id
|
||||
const ownAccount = instanceAccount?.id === status.account?.id
|
||||
|
||||
const canEditPost = useSelector(checkInstanceFeature('edit_post'))
|
||||
|
||||
if (ownAccount) {
|
||||
const accountMenuItems: ContextMenuAction[] = [
|
||||
menus.push([
|
||||
{
|
||||
id: 'status-delete',
|
||||
title: t('status.delete.action'),
|
||||
systemIcon: 'trash',
|
||||
destructive: true
|
||||
},
|
||||
{
|
||||
id: 'status-delete-edit',
|
||||
title: t('status.deleteEdit.action'),
|
||||
systemIcon: 'pencil.and.outline',
|
||||
destructive: true
|
||||
},
|
||||
{
|
||||
id: 'status-mute',
|
||||
title: t('status.mute.action', {
|
||||
context: (status.muted || false).toString()
|
||||
}),
|
||||
systemIcon: status.muted ? 'speaker' : 'speaker.slash'
|
||||
}
|
||||
]
|
||||
|
||||
const canEditPost = useSelector(checkInstanceFeature('edit_post'))
|
||||
if (canEditPost) {
|
||||
accountMenuItems.unshift({
|
||||
id: 'status-edit',
|
||||
title: t('status.edit.action'),
|
||||
systemIcon: 'square.and.pencil'
|
||||
})
|
||||
}
|
||||
|
||||
if (status.visibility === 'public' || status.visibility === 'unlisted') {
|
||||
accountMenuItems.push({
|
||||
id: 'status-pin',
|
||||
title: t('status.pin.action', {
|
||||
context: (status.pinned || false).toString()
|
||||
}),
|
||||
systemIcon: status.pinned ? 'pin.slash' : 'pin'
|
||||
})
|
||||
}
|
||||
|
||||
actions.push(...accountMenuItems)
|
||||
}
|
||||
|
||||
return async (index: number) => {
|
||||
if (typeof index !== 'number' || !actions[index]) {
|
||||
return // For Android
|
||||
}
|
||||
if (actions[index].id === 'status-delete') {
|
||||
Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [
|
||||
{
|
||||
text: t('status.delete.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
mutation.mutate({
|
||||
type: 'deleteItem',
|
||||
source: 'statuses',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel'),
|
||||
style: 'default'
|
||||
}
|
||||
])
|
||||
}
|
||||
if (actions[index].id === 'status-delete-edit') {
|
||||
Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [
|
||||
{
|
||||
text: t('status.deleteEdit.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
key: 'status-edit',
|
||||
item: {
|
||||
onSelect: async () => {
|
||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||
if (status.in_reply_to_id) {
|
||||
replyToStatus = await apiInstance<Mastodon.Status>({
|
||||
@ -139,87 +75,166 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) =
|
||||
url: `statuses/${status.in_reply_to_id}`
|
||||
}).then(res => res.body)
|
||||
}
|
||||
mutation
|
||||
.mutateAsync({
|
||||
type: 'deleteItem',
|
||||
source: 'statuses',
|
||||
apiInstance<{
|
||||
id: Mastodon.Status['id']
|
||||
text: NonNullable<Mastodon.Status['text']>
|
||||
spoiler_text: Mastodon.Status['spoiler_text']
|
||||
}>({
|
||||
method: 'get',
|
||||
url: `statuses/${status.id}/source`
|
||||
}).then(res => {
|
||||
navigation.navigate('Screen-Compose', {
|
||||
type: 'edit',
|
||||
incomingStatus: {
|
||||
...status,
|
||||
text: res.body.text,
|
||||
spoiler_text: res.body.spoiler_text
|
||||
},
|
||||
...(replyToStatus && { replyToStatus }),
|
||||
queryKey,
|
||||
id: status.id
|
||||
rootQueryKey
|
||||
})
|
||||
.then(res => {
|
||||
navigation.navigate('Screen-Compose', {
|
||||
type: 'deleteEdit',
|
||||
incomingStatus: res.body as Mastodon.Status,
|
||||
...(replyToStatus && { replyToStatus }),
|
||||
queryKey
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel')
|
||||
}
|
||||
])
|
||||
}
|
||||
if (actions[index].id === 'status-mute') {
|
||||
mutation.mutate({
|
||||
type: 'updateStatusProperty',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id,
|
||||
payload: {
|
||||
property: 'muted',
|
||||
currentValue: status.muted,
|
||||
propertyCount: undefined,
|
||||
countValue: undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
if (actions[index].id === 'status-edit') {
|
||||
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)
|
||||
}
|
||||
apiInstance<{
|
||||
id: Mastodon.Status['id']
|
||||
text: NonNullable<Mastodon.Status['text']>
|
||||
spoiler_text: Mastodon.Status['spoiler_text']
|
||||
}>({
|
||||
method: 'get',
|
||||
url: `statuses/${status.id}/source`
|
||||
}).then(res => {
|
||||
navigation.navigate('Screen-Compose', {
|
||||
type: 'edit',
|
||||
incomingStatus: {
|
||||
...status,
|
||||
text: res.body.text,
|
||||
spoiler_text: res.body.spoiler_text
|
||||
})
|
||||
},
|
||||
...(replyToStatus && { replyToStatus }),
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
})
|
||||
})
|
||||
}
|
||||
if (actions[index].id === 'status-pin') {
|
||||
// Also note that reblogs cannot be pinned.
|
||||
mutation.mutate({
|
||||
type: 'updateStatusProperty',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id,
|
||||
payload: {
|
||||
property: 'pinned',
|
||||
currentValue: status.pinned,
|
||||
propertyCount: undefined,
|
||||
countValue: undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: !canEditPost
|
||||
},
|
||||
title: t('status.edit.action'),
|
||||
icon: 'square.and.pencil'
|
||||
},
|
||||
{
|
||||
key: 'status-delete-edit',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [
|
||||
{
|
||||
text: t('status.deleteEdit.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
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')
|
||||
}
|
||||
]),
|
||||
disabled: false,
|
||||
destructive: true,
|
||||
hidden: false
|
||||
},
|
||||
title: t('status.deleteEdit.action'),
|
||||
icon: 'pencil.and.outline'
|
||||
},
|
||||
{
|
||||
key: 'status-delete',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [
|
||||
{
|
||||
text: t('status.delete.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
mutation.mutate({
|
||||
type: 'deleteItem',
|
||||
source: 'statuses',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel'),
|
||||
style: 'default'
|
||||
}
|
||||
]),
|
||||
disabled: false,
|
||||
destructive: true,
|
||||
hidden: false
|
||||
},
|
||||
title: t('status.delete.action'),
|
||||
icon: 'trash'
|
||||
}
|
||||
])
|
||||
|
||||
menus.push([
|
||||
{
|
||||
key: 'status-mute',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
mutation.mutate({
|
||||
type: 'updateStatusProperty',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id,
|
||||
payload: {
|
||||
property: 'muted',
|
||||
currentValue: status.muted,
|
||||
propertyCount: undefined,
|
||||
countValue: undefined
|
||||
}
|
||||
}),
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: t('status.mute.action', {
|
||||
context: (status.muted || false).toString()
|
||||
}),
|
||||
icon: status.muted ? 'speaker' : 'speaker.slash'
|
||||
},
|
||||
{
|
||||
key: 'status-pin',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
// Also note that reblogs cannot be pinned.
|
||||
mutation.mutate({
|
||||
type: 'updateStatusProperty',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id,
|
||||
payload: {
|
||||
property: 'pinned',
|
||||
currentValue: status.pinned,
|
||||
propertyCount: undefined,
|
||||
countValue: undefined
|
||||
}
|
||||
}),
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: status.visibility !== 'public' && status.visibility !== 'unlisted'
|
||||
},
|
||||
title: t('status.pin.action', {
|
||||
context: (status.pinned || false).toString()
|
||||
}),
|
||||
icon: status.pinned ? 'pin.slash' : 'pin'
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
return menus
|
||||
}
|
||||
|
||||
export default contextMenuStatus
|
||||
export default menuStatus
|
||||
|
@ -1,10 +1,12 @@
|
||||
import menuInstance from '@components/contextMenu/instance'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import menuStatus from '@components/contextMenu/status'
|
||||
import TimelineActioned from '@components/Timeline/Shared/Actioned'
|
||||
import TimelineActions from '@components/Timeline/Shared/Actions'
|
||||
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
||||
import TimelineCard from '@components/Timeline/Shared/Card'
|
||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||
// @ts-ignore
|
||||
import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
@ -18,10 +20,11 @@ import { uniqBy } from 'lodash'
|
||||
import React, { useRef } from 'react'
|
||||
import { Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineContextMenu from './Shared/ContextMenu'
|
||||
import * as ContextMenu from 'zeego/context-menu'
|
||||
import TimelineFeedback from './Shared/Feedback'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||
import TimelineTranslate from './Shared/Translate'
|
||||
|
||||
export interface Props {
|
||||
@ -89,8 +92,10 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
/>
|
||||
<TimelineHeaderDefault
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
rootQueryKey={rootQueryKey}
|
||||
status={actualStatus}
|
||||
highlighted={highlighted}
|
||||
copiableContent={copiableContent}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@ -149,24 +154,71 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
</>
|
||||
)
|
||||
|
||||
const mShare = menuShare({
|
||||
visibility: actualStatus.visibility,
|
||||
type: 'status',
|
||||
url: actualStatus.url || actualStatus.uri,
|
||||
copiableContent
|
||||
})
|
||||
const mStatus = menuStatus({ status: actualStatus, queryKey, rootQueryKey })
|
||||
const mInstance = menuInstance({ status: actualStatus, queryKey, rootQueryKey })
|
||||
|
||||
return disableOnPress ? (
|
||||
<View style={mainStyle}>{main()}</View>
|
||||
) : (
|
||||
<TimelineContextMenu
|
||||
copiableContent={copiableContent}
|
||||
status={actualStatus}
|
||||
queryKey={queryKey}
|
||||
rootQueryKey={rootQueryKey}
|
||||
>
|
||||
<Pressable
|
||||
accessible={highlighted ? false : true}
|
||||
style={mainStyle}
|
||||
onPress={onPress}
|
||||
onLongPress={() => {}}
|
||||
>
|
||||
{main()}
|
||||
</Pressable>
|
||||
</TimelineContextMenu>
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Pressable
|
||||
accessible={highlighted ? false : true}
|
||||
style={mainStyle}
|
||||
onPress={onPress}
|
||||
onLongPress={() => {}}
|
||||
children={main()}
|
||||
/>
|
||||
</ContextMenu.Trigger>
|
||||
|
||||
<ContextMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
|
||||
{mStatus.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
|
||||
{mInstance.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Root>
|
||||
<TimelineHeaderAndroid
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
rootQueryKey={rootQueryKey}
|
||||
status={actualStatus}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
import menuInstance from '@components/contextMenu/instance'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import menuStatus from '@components/contextMenu/status'
|
||||
import TimelineActioned from '@components/Timeline/Shared/Actioned'
|
||||
import TimelineActions from '@components/Timeline/Shared/Actions'
|
||||
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
||||
import TimelineCard from '@components/Timeline/Shared/Card'
|
||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||
// @ts-ignore
|
||||
import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotification'
|
||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
@ -16,11 +18,12 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { uniqBy } from 'lodash'
|
||||
import React, { useCallback, useRef } from 'react'
|
||||
import { Platform, Pressable, View } from 'react-native'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineContextMenu from './Shared/ContextMenu'
|
||||
import * as ContextMenu from 'zeego/context-menu'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||
|
||||
export interface Props {
|
||||
notification: Mastodon.Notification
|
||||
@ -136,36 +139,68 @@ const TimelineNotifications: React.FC<Props> = ({
|
||||
)
|
||||
}
|
||||
|
||||
return Platform.OS === 'android' ? (
|
||||
<Pressable
|
||||
style={{
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
backgroundColor: colors.backgroundDefault,
|
||||
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
onPress={onPress}
|
||||
onLongPress={() => {}}
|
||||
>
|
||||
{main()}
|
||||
</Pressable>
|
||||
) : (
|
||||
<TimelineContextMenu
|
||||
copiableContent={copiableContent}
|
||||
status={notification.status}
|
||||
queryKey={queryKey}
|
||||
>
|
||||
<Pressable
|
||||
style={{
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
backgroundColor: colors.backgroundDefault,
|
||||
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
onPress={onPress}
|
||||
onLongPress={() => {}}
|
||||
>
|
||||
{main()}
|
||||
</Pressable>
|
||||
</TimelineContextMenu>
|
||||
const mShare = menuShare({
|
||||
visibility: notification.status?.visibility,
|
||||
type: 'status',
|
||||
url: notification.status?.url || notification.status?.uri,
|
||||
copiableContent
|
||||
})
|
||||
const mStatus = menuStatus({ status: notification.status, queryKey })
|
||||
const mInstance = menuInstance({ status: notification.status, queryKey })
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Pressable
|
||||
style={{
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
backgroundColor: colors.backgroundDefault,
|
||||
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
onPress={onPress}
|
||||
onLongPress={() => {}}
|
||||
children={main()}
|
||||
/>
|
||||
</ContextMenu.Trigger>
|
||||
|
||||
<ContextMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
|
||||
{mStatus.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
|
||||
{mInstance.map((mGroup, index) => (
|
||||
<ContextMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
||||
<ContextMenu.ItemTitle children={menu.title} />
|
||||
<ContextMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</ContextMenu.Item>
|
||||
))}
|
||||
</ContextMenu.Group>
|
||||
))}
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Root>
|
||||
<TimelineHeaderAndroid queryKey={queryKey} status={notification.status} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
import contextMenuAccount from '@components/ContextMenu/account'
|
||||
import contextMenuInstance from '@components/ContextMenu/instance'
|
||||
import contextMenuShare from '@components/ContextMenu/share'
|
||||
import contextMenuStatus from '@components/ContextMenu/status'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import React from 'react'
|
||||
import { createContext } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
import ContextMenu, { ContextMenuAction, ContextMenuProps } from 'react-native-context-menu-view'
|
||||
|
||||
export interface Props {
|
||||
copiableContent: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
status?: Mastodon.Status
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}
|
||||
|
||||
export const ContextMenuContext = createContext<ContextMenuAction[]>([])
|
||||
|
||||
const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
|
||||
children,
|
||||
copiableContent,
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
...props
|
||||
}) => {
|
||||
if (!status || !queryKey || Platform.OS === 'android') {
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
const actions: ContextMenuAction[] = []
|
||||
|
||||
const shareOnPress =
|
||||
status.visibility !== 'direct'
|
||||
? contextMenuShare({
|
||||
copiableContent,
|
||||
actions,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
})
|
||||
: null
|
||||
const statusOnPress = contextMenuStatus({
|
||||
actions,
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
})
|
||||
const accountOnPress = status?.account?.id
|
||||
? contextMenuAccount({
|
||||
actions,
|
||||
type: 'status',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.account.id
|
||||
})
|
||||
: null
|
||||
const instanceOnPress = contextMenuInstance({
|
||||
actions,
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
})
|
||||
|
||||
return (
|
||||
<ContextMenuContext.Provider value={actions}>
|
||||
<ContextMenu
|
||||
actions={actions}
|
||||
onPress={({ nativeEvent: { index } }) => {
|
||||
for (const on of [shareOnPress, statusOnPress, accountOnPress, instanceOnPress]) {
|
||||
on && on(index)
|
||||
}
|
||||
}}
|
||||
children={children}
|
||||
{...props}
|
||||
/>
|
||||
</ContextMenuContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineContextMenu
|
103
src/components/Timeline/Shared/HeaderAndroid.tsx
Normal file
103
src/components/Timeline/Shared/HeaderAndroid.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
import menuAccount from '@components/contextMenu/account'
|
||||
import menuInstance from '@components/contextMenu/instance'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import menuStatus from '@components/contextMenu/status'
|
||||
import Icon from '@components/Icon'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useState } from 'react'
|
||||
import { Platform, View } from 'react-native'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
|
||||
export interface Props {
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
status?: Mastodon.Status
|
||||
}
|
||||
|
||||
const TimelineHeaderAndroid: React.FC<Props> = ({ queryKey, rootQueryKey, status }) => {
|
||||
if (Platform.OS !== 'android' || !status) return null
|
||||
|
||||
const { colors } = useTheme()
|
||||
|
||||
const [openChange, setOpenChange] = useState(false)
|
||||
const mShare = menuShare({
|
||||
visibility: status.visibility,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
openChange,
|
||||
id: status.account.id,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||
const mInstance = menuInstance({ status, queryKey, rootQueryKey })
|
||||
|
||||
return (
|
||||
<View style={{ position: 'absolute', top: 0, right: 0 }}>
|
||||
{queryKey ? (
|
||||
<DropdownMenu.Root onOpenChange={setOpenChange}>
|
||||
<DropdownMenu.Trigger>
|
||||
<View style={{ padding: StyleConstants.Spacing.L }}>
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
</View>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mAccount.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mStatus.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mInstance.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderAndroid
|
@ -1,114 +0,0 @@
|
||||
import contextMenuAccount from '@components/ContextMenu/account'
|
||||
import contextMenuInstance from '@components/ContextMenu/instance'
|
||||
import contextMenuShare from '@components/ContextMenu/share'
|
||||
import contextMenuStatus from '@components/ContextMenu/status'
|
||||
import Icon from '@components/Icon'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey?: QueryKeyTimeline
|
||||
status: Mastodon.Status
|
||||
highlighted: boolean
|
||||
}
|
||||
|
||||
const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => {
|
||||
if (!queryKey) return null
|
||||
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
const { colors } = useTheme()
|
||||
|
||||
const actions: ContextMenuAction[] = []
|
||||
|
||||
const shareOnPress =
|
||||
status.visibility !== 'direct'
|
||||
? contextMenuShare({
|
||||
actions,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
})
|
||||
: null
|
||||
const statusOnPress = contextMenuStatus({
|
||||
actions,
|
||||
status,
|
||||
queryKey
|
||||
})
|
||||
const accountOnPress = contextMenuAccount({
|
||||
actions,
|
||||
type: 'status',
|
||||
queryKey,
|
||||
id: status.account.id
|
||||
})
|
||||
const instanceOnPress = contextMenuInstance({
|
||||
actions,
|
||||
status,
|
||||
queryKey
|
||||
})
|
||||
|
||||
const { showActionSheetWithOptions } = useActionSheet()
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View style={{ flex: 7 }}>
|
||||
<HeaderSharedAccount account={status.account} />
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={status.created_at}
|
||||
edited_at={status.edited_at}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
<HeaderSharedVisibility visibility={status.visibility} />
|
||||
<HeaderSharedMuted muted={status.muted} />
|
||||
<HeaderSharedApplication application={status.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{queryKey ? (
|
||||
<Pressable
|
||||
accessibilityHint={t('accessibilityHint')}
|
||||
style={{ flex: 1, flexBasis: StyleConstants.Font.Size.L }}
|
||||
onPress={() =>
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: actions.map(action => action.title),
|
||||
cancelButtonIndex: 999,
|
||||
destructiveButtonIndex: actions
|
||||
.map((action, index) => (action.destructive ? index : 999))
|
||||
.filter(num => num !== 999)
|
||||
},
|
||||
index => {
|
||||
if (index !== undefined) {
|
||||
for (const on of [shareOnPress, statusOnPress, accountOnPress, instanceOnPress]) {
|
||||
on && on(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon name='MoreHorizontal' color={colors.secondary} size={StyleConstants.Font.Size.L} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderDefault
|
@ -1,75 +0,0 @@
|
||||
import Icon from '@components/Icon'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import ContextMenu from 'react-native-context-menu-view'
|
||||
import { ContextMenuContext } from './ContextMenu'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey?: QueryKeyTimeline
|
||||
status: Mastodon.Status
|
||||
highlighted: boolean
|
||||
}
|
||||
|
||||
const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => {
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
const { colors } = useTheme()
|
||||
|
||||
const contextMenuContext = useContext(ContextMenuContext)
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View style={{ flex: 7 }}>
|
||||
<HeaderSharedAccount account={status.account} />
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={status.created_at}
|
||||
edited_at={status.edited_at}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
<HeaderSharedVisibility visibility={status.visibility} />
|
||||
<HeaderSharedMuted muted={status.muted} />
|
||||
<HeaderSharedApplication application={status.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{queryKey ? (
|
||||
<Pressable
|
||||
accessibilityHint={t('accessibilityHint')}
|
||||
style={{ flex: 1, flexBasis: StyleConstants.Font.Size.L }}
|
||||
>
|
||||
<ContextMenu
|
||||
style={{ flex: 1, alignItems: 'center' }}
|
||||
dropdownMenuMode
|
||||
actions={contextMenuContext}
|
||||
onPress={() => {}}
|
||||
children={
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderDefault
|
144
src/components/Timeline/Shared/HeaderDefault.tsx
Normal file
144
src/components/Timeline/Shared/HeaderDefault.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import menuAccount from '@components/contextMenu/account'
|
||||
import menuInstance from '@components/contextMenu/instance'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import menuStatus from '@components/contextMenu/status'
|
||||
import Icon from '@components/Icon'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, Pressable, View } from 'react-native'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
status: Mastodon.Status
|
||||
highlighted: boolean
|
||||
copiableContent: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
}
|
||||
|
||||
const TimelineHeaderDefault: React.FC<Props> = ({
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
status,
|
||||
highlighted,
|
||||
copiableContent
|
||||
}) => {
|
||||
const { colors } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
const [openChange, setOpenChange] = useState(false)
|
||||
const mShare = menuShare({
|
||||
visibility: status.visibility,
|
||||
type: 'status',
|
||||
url: status.url || status.uri,
|
||||
copiableContent
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
openChange,
|
||||
id: status.account.id,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||
const mInstance = menuInstance({ status, queryKey, rootQueryKey })
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View style={{ flex: 7 }}>
|
||||
<HeaderSharedAccount account={status.account} />
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={status.created_at}
|
||||
edited_at={status.edited_at}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
<HeaderSharedVisibility visibility={status.visibility} />
|
||||
<HeaderSharedMuted muted={status.muted} />
|
||||
<HeaderSharedApplication application={status.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{Platform.OS !== 'android' && queryKey ? (
|
||||
<Pressable
|
||||
accessibilityHint={t('accessibilityHint')}
|
||||
style={{ flex: 1, alignItems: 'center' }}
|
||||
>
|
||||
<DropdownMenu.Root onOpenChange={setOpenChange}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mAccount.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mStatus.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mInstance.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderDefault
|
@ -1,157 +0,0 @@
|
||||
import contextMenuAccount from '@components/ContextMenu/account'
|
||||
import contextMenuInstance from '@components/ContextMenu/instance'
|
||||
import contextMenuShare from '@components/ContextMenu/share'
|
||||
import contextMenuStatus from '@components/ContextMenu/status'
|
||||
import Icon from '@components/Icon'
|
||||
import { RelationshipIncoming, RelationshipOutgoing } from '@components/Relationship'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey: QueryKeyTimeline
|
||||
notification: Mastodon.Notification
|
||||
}
|
||||
|
||||
const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
const contextMenuActions: ContextMenuAction[] = []
|
||||
const status = notification.status
|
||||
const shareOnPress =
|
||||
status && status?.visibility !== 'direct'
|
||||
? contextMenuShare({
|
||||
actions: contextMenuActions,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
})
|
||||
: null
|
||||
const statusOnPress =
|
||||
status &&
|
||||
contextMenuStatus({
|
||||
actions: contextMenuActions,
|
||||
status: status,
|
||||
queryKey
|
||||
})
|
||||
const accountOnPress =
|
||||
status &&
|
||||
contextMenuAccount({
|
||||
actions: contextMenuActions,
|
||||
type: 'status',
|
||||
queryKey,
|
||||
id: status.account.id
|
||||
})
|
||||
const instanceOnPress =
|
||||
status &&
|
||||
contextMenuInstance({
|
||||
actions: contextMenuActions,
|
||||
status: status,
|
||||
queryKey
|
||||
})
|
||||
|
||||
const { showActionSheetWithOptions } = useActionSheet()
|
||||
|
||||
const actions = useMemo(() => {
|
||||
switch (notification.type) {
|
||||
case 'follow':
|
||||
return <RelationshipOutgoing id={notification.account.id} />
|
||||
case 'follow_request':
|
||||
return <RelationshipIncoming id={notification.account.id} />
|
||||
default:
|
||||
if (notification.status) {
|
||||
return (
|
||||
<Pressable
|
||||
style={{ flex: 1, flexBasis: StyleConstants.Font.Size.L }}
|
||||
onPress={() =>
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: contextMenuActions.map(action => action.title),
|
||||
cancelButtonIndex: 999,
|
||||
destructiveButtonIndex: contextMenuActions
|
||||
.map((action, index) => (action.destructive ? index : 999))
|
||||
.filter(num => num !== 999)
|
||||
},
|
||||
index => {
|
||||
if (index !== undefined) {
|
||||
for (const on of [
|
||||
shareOnPress,
|
||||
statusOnPress,
|
||||
accountOnPress,
|
||||
instanceOnPress
|
||||
]) {
|
||||
on && on(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
children={
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [notification.type])
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View
|
||||
style={{
|
||||
flex: notification.type === 'follow' || notification.type === 'follow_request' ? 1 : 4
|
||||
}}
|
||||
>
|
||||
<HeaderSharedAccount
|
||||
account={notification.status ? notification.status.account : notification.account}
|
||||
{...((notification.type === 'follow' || notification.type === 'follow_request') && {
|
||||
withoutName: true
|
||||
})}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={notification.status?.created_at || notification.created_at}
|
||||
edited_at={notification.status?.edited_at}
|
||||
/>
|
||||
{notification.status?.visibility ? (
|
||||
<HeaderSharedVisibility visibility={notification.status.visibility} />
|
||||
) : null}
|
||||
<HeaderSharedMuted muted={notification.status?.muted} />
|
||||
<HeaderSharedApplication application={notification.status?.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
{ marginLeft: StyleConstants.Spacing.M },
|
||||
notification.type === 'follow' || notification.type === 'follow_request'
|
||||
? { flexShrink: 1 }
|
||||
: { flex: 1 }
|
||||
]}
|
||||
>
|
||||
{actions}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderNotification
|
@ -1,105 +0,0 @@
|
||||
import Icon from '@components/Icon'
|
||||
import { RelationshipIncoming, RelationshipOutgoing } from '@components/Relationship'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext, useMemo } from 'react'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import ContextMenu from 'react-native-context-menu-view'
|
||||
import { ContextMenuContext } from './ContextMenu'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey: QueryKeyTimeline
|
||||
notification: Mastodon.Notification
|
||||
}
|
||||
|
||||
const TimelineHeaderNotification = ({ notification }: Props) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
const contextMenuContext = useContext(ContextMenuContext)
|
||||
|
||||
const actions = useMemo(() => {
|
||||
switch (notification.type) {
|
||||
case 'follow':
|
||||
return <RelationshipOutgoing id={notification.account.id} />
|
||||
case 'follow_request':
|
||||
return <RelationshipIncoming id={notification.account.id} />
|
||||
default:
|
||||
if (notification.status) {
|
||||
return (
|
||||
<Pressable
|
||||
style={{ flex: 1, flexBasis: StyleConstants.Font.Size.L }}
|
||||
children={
|
||||
<ContextMenu
|
||||
style={{ flex: 1, alignItems: 'center' }}
|
||||
dropdownMenuMode
|
||||
actions={contextMenuContext}
|
||||
onPress={() => {}}
|
||||
children={
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [notification.type])
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View
|
||||
style={{
|
||||
flex: notification.type === 'follow' || notification.type === 'follow_request' ? 1 : 4
|
||||
}}
|
||||
>
|
||||
<HeaderSharedAccount
|
||||
account={notification.status ? notification.status.account : notification.account}
|
||||
{...((notification.type === 'follow' || notification.type === 'follow_request') && {
|
||||
withoutName: true
|
||||
})}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={notification.status?.created_at || notification.created_at}
|
||||
edited_at={notification.status?.edited_at}
|
||||
/>
|
||||
{notification.status?.visibility ? (
|
||||
<HeaderSharedVisibility visibility={notification.status.visibility} />
|
||||
) : null}
|
||||
<HeaderSharedMuted muted={notification.status?.muted} />
|
||||
<HeaderSharedApplication application={notification.status?.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
{ marginLeft: StyleConstants.Spacing.M },
|
||||
notification.type === 'follow' || notification.type === 'follow_request'
|
||||
? { flexShrink: 1 }
|
||||
: { flex: 1 }
|
||||
]}
|
||||
>
|
||||
{actions}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderNotification
|
163
src/components/Timeline/Shared/HeaderNotification.tsx
Normal file
163
src/components/Timeline/Shared/HeaderNotification.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
import menuAccount from '@components/contextMenu/account'
|
||||
import menuInstance from '@components/contextMenu/instance'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import menuStatus from '@components/contextMenu/status'
|
||||
import Icon from '@components/Icon'
|
||||
import { RelationshipIncoming, RelationshipOutgoing } from '@components/Relationship'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useState } from 'react'
|
||||
import { Platform, Pressable, View } from 'react-native'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
import HeaderSharedCreated from './HeaderShared/Created'
|
||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
export interface Props {
|
||||
queryKey: QueryKeyTimeline
|
||||
notification: Mastodon.Notification
|
||||
}
|
||||
|
||||
const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
const [openChange, setOpenChange] = useState(false)
|
||||
const mShare = menuShare({
|
||||
visibility: notification.status?.visibility,
|
||||
type: 'status',
|
||||
url: notification.status?.url || notification.status?.uri
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
openChange,
|
||||
id: notification.status?.account.id,
|
||||
queryKey
|
||||
})
|
||||
const mStatus = menuStatus({ status: notification.status, queryKey })
|
||||
const mInstance = menuInstance({ status: notification.status, queryKey })
|
||||
|
||||
const actions = () => {
|
||||
switch (notification.type) {
|
||||
case 'follow':
|
||||
return <RelationshipOutgoing id={notification.account.id} />
|
||||
case 'follow_request':
|
||||
return <RelationshipIncoming id={notification.account.id} />
|
||||
default:
|
||||
if (notification.status) {
|
||||
return (
|
||||
<Pressable
|
||||
style={{ flex: 1, alignItems: 'center' }}
|
||||
children={
|
||||
<DropdownMenu.Root onOpenChange={setOpenChange}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Icon
|
||||
name='MoreHorizontal'
|
||||
color={colors.secondary}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
/>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mAccount.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mStatus.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mInstance.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View
|
||||
style={{
|
||||
flex: notification.type === 'follow' || notification.type === 'follow_request' ? 1 : 4
|
||||
}}
|
||||
>
|
||||
<HeaderSharedAccount
|
||||
account={notification.status ? notification.status.account : notification.account}
|
||||
{...((notification.type === 'follow' || notification.type === 'follow_request') && {
|
||||
withoutName: true
|
||||
})}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: StyleConstants.Spacing.XS,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated
|
||||
created_at={notification.status?.created_at || notification.created_at}
|
||||
edited_at={notification.status?.edited_at}
|
||||
/>
|
||||
{notification.status?.visibility ? (
|
||||
<HeaderSharedVisibility visibility={notification.status.visibility} />
|
||||
) : null}
|
||||
<HeaderSharedMuted muted={notification.status?.muted} />
|
||||
<HeaderSharedApplication application={notification.status?.application} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{Platform.OS !== 'android' ? (
|
||||
<View
|
||||
style={[
|
||||
{ marginLeft: StyleConstants.Spacing.M },
|
||||
notification.type === 'follow' || notification.type === 'follow_request'
|
||||
? { flexShrink: 1 }
|
||||
: { flex: 1 }
|
||||
]}
|
||||
children={actions()}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineHeaderNotification
|
220
src/components/contextMenu/account.tsx
Normal file
220
src/components/contextMenu/account.tsx
Normal file
@ -0,0 +1,220 @@
|
||||
import haptics from '@components/haptics'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import {
|
||||
QueryKeyRelationship,
|
||||
useRelationshipMutation,
|
||||
useRelationshipQuery
|
||||
} from '@utils/queryHooks/relationship'
|
||||
import {
|
||||
MutationVarsTimelineUpdateAccountProperty,
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform } from 'react-native'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
const menuAccount = ({
|
||||
openChange,
|
||||
id,
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
}: {
|
||||
openChange: boolean
|
||||
id?: Mastodon.Account['id']
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}): ContextMenu[][] => {
|
||||
if (!id) return []
|
||||
|
||||
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 [enabled, setEnabled] = useState(openChange)
|
||||
useEffect(() => {
|
||||
if (!ownAccount && enabled === false && openChange === true) {
|
||||
setEnabled(true)
|
||||
}
|
||||
}, [openChange, enabled])
|
||||
const { data, isFetching } = useRelationshipQuery({ id, options: { enabled } })
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const timelineMutation = useTimelineMutation({
|
||||
onSuccess: (_, params) => {
|
||||
queryClient.refetchQueries(['Relationship', { id }])
|
||||
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'success',
|
||||
message: t('common:message.success.message', {
|
||||
function: t(`account.${theParams.payload.property}.action`, {
|
||||
...(theParams.payload.property !== 'reports' && {
|
||||
context: (theParams.payload.currentValue || false).toString()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onError: (err: any, params) => {
|
||||
const theParams = params as MutationVarsTimelineUpdateAccountProperty
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'error',
|
||||
message: t('common:message.error.message', {
|
||||
function: t(`account.${theParams.payload.property}.action`, {
|
||||
...(theParams.payload.property !== 'reports' && {
|
||||
context: (theParams.payload.currentValue || false).toString()
|
||||
})
|
||||
})
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
err.data &&
|
||||
err.data.error &&
|
||||
typeof err.data.error === 'string' && {
|
||||
description: err.data.error
|
||||
})
|
||||
})
|
||||
},
|
||||
onSettled: () => {
|
||||
queryKey && queryClient.invalidateQueries(queryKey)
|
||||
rootQueryKey && queryClient.invalidateQueries(rootQueryKey)
|
||||
}
|
||||
})
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
|
||||
const relationshipMutation = useRelationshipMutation({
|
||||
onSuccess: (res, { payload: { action } }) => {
|
||||
haptics('Success')
|
||||
queryClient.setQueryData<Mastodon.Relationship[]>(queryKeyRelationship, [res])
|
||||
if (action === 'block') {
|
||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
||||
queryClient.invalidateQueries(queryKey)
|
||||
}
|
||||
},
|
||||
onError: (err: any, { payload: { action } }) => {
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'error',
|
||||
message: t('common:message.error.message', {
|
||||
function: t(`${action}.function`)
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
err.data &&
|
||||
err.data.error &&
|
||||
typeof err.data.error === 'string' && {
|
||||
description: err.data.error
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!ownAccount && Platform.OS !== 'android') {
|
||||
menus[0].push({
|
||||
key: 'account-following',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
data &&
|
||||
relationshipMutation.mutate({
|
||||
id,
|
||||
type: 'outgoing',
|
||||
payload: { action: 'follow', state: !data?.requested ? data.following : true }
|
||||
}),
|
||||
disabled: !data || isFetching,
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: !data?.requested
|
||||
? t('account.following.action', {
|
||||
context: (data?.following || false).toString()
|
||||
})
|
||||
: t('componentRelationship:button.requested'),
|
||||
icon: !data?.requested
|
||||
? data?.following
|
||||
? 'person.badge.minus'
|
||||
: 'person.badge.plus'
|
||||
: 'person.badge.minus'
|
||||
})
|
||||
}
|
||||
if (!ownAccount) {
|
||||
menus[0].push({
|
||||
key: 'account-mute',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
payload: { property: 'mute', currentValue: data?.muting }
|
||||
}),
|
||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: t('account.mute.action', {
|
||||
context: (data?.muting || false).toString()
|
||||
}),
|
||||
icon: data?.muting ? 'eye' : 'eye.slash'
|
||||
})
|
||||
}
|
||||
|
||||
!ownAccount &&
|
||||
menus.push([
|
||||
{
|
||||
key: 'account-block',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
payload: { property: 'block', currentValue: data?.blocking }
|
||||
}),
|
||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
||||
destructive: !data?.blocking,
|
||||
hidden: false
|
||||
},
|
||||
title: t('account.block.action', {
|
||||
context: (data?.blocking || false).toString()
|
||||
}),
|
||||
icon: data?.blocking ? 'checkmark.circle' : 'xmark.circle'
|
||||
},
|
||||
{
|
||||
key: 'account-reports',
|
||||
item: {
|
||||
onSelect: () => {
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
payload: { property: 'reports' }
|
||||
})
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id,
|
||||
payload: { property: 'block', currentValue: false }
|
||||
})
|
||||
},
|
||||
disabled: false,
|
||||
destructive: true,
|
||||
hidden: false
|
||||
},
|
||||
title: t('account.reports.action'),
|
||||
icon: 'flag'
|
||||
}
|
||||
])
|
||||
|
||||
return menus
|
||||
}
|
||||
|
||||
export default menuAccount
|
6
src/components/contextMenu/index.d.ts
vendored
Normal file
6
src/components/contextMenu/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
type ContextMenu = {
|
||||
key: string
|
||||
item: { onSelect: () => void; disabled: boolean; destructive: boolean; hidden: boolean }
|
||||
title: string
|
||||
icon: string
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
"accessibilityHint": "Actions for this toot, such as its posted user, toot itself",
|
||||
"account": {
|
||||
"title": "User actions",
|
||||
"following": {
|
||||
"action_false": "Follow user",
|
||||
"action_true": "Unfollow user"
|
||||
},
|
||||
"mute": {
|
||||
"action_false": "Mute user",
|
||||
"action_true": "Unmute user"
|
||||
@ -11,7 +15,7 @@
|
||||
"action_true": "Unblock user"
|
||||
},
|
||||
"reports": {
|
||||
"action": "Report and block"
|
||||
"action": "Report and block user"
|
||||
}
|
||||
},
|
||||
"copy": {
|
||||
|
@ -9,8 +9,7 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform } from 'react-native'
|
||||
import ContextMenu from 'react-native-context-menu-view'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
import TabShared from './Shared'
|
||||
|
||||
const Stack = createNativeStackNavigator<TabLocalStackParamList>()
|
||||
@ -34,31 +33,8 @@ const TabLocal = React.memo(
|
||||
name='Tab-Local-Root'
|
||||
options={{
|
||||
headerTitle: () => (
|
||||
<ContextMenu
|
||||
dropdownMenuMode
|
||||
style={{ maxWidth: '80%', flex: Platform.OS === 'android' ? 1 : undefined }}
|
||||
actions={
|
||||
lists?.length
|
||||
? [
|
||||
{
|
||||
id: '',
|
||||
title: t('tabs.local.name'),
|
||||
disabled: queryKey[1].page === 'Following'
|
||||
},
|
||||
...lists?.map(list => ({
|
||||
id: list.id,
|
||||
title: list.title,
|
||||
disabled: queryKey[1].page === 'List' && queryKey[1].list === list.id
|
||||
}))
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
onPress={({ nativeEvent: { index } }) => {
|
||||
lists && index
|
||||
? setQueryKey(['Timeline', { page: 'List', list: lists[index - 1].id }])
|
||||
: setQueryKey(['Timeline', { page: 'Following' }])
|
||||
}}
|
||||
children={
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<HeaderCenter
|
||||
dropdown={(lists?.length ?? 0) > 0}
|
||||
content={
|
||||
@ -67,8 +43,43 @@ const TabLocal = React.memo(
|
||||
: t('tabs.local.name')
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
{lists?.length
|
||||
? [
|
||||
{
|
||||
key: 'default',
|
||||
item: {
|
||||
onSelect: () => setQueryKey(['Timeline', { page: 'Following' }]),
|
||||
disabled: queryKey[1].page === 'Following',
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: t('tabs.local.name'),
|
||||
icon: ''
|
||||
},
|
||||
...lists?.map(list => ({
|
||||
key: list.id,
|
||||
item: {
|
||||
onSelect: () =>
|
||||
setQueryKey(['Timeline', { page: 'List', list: list.id }]),
|
||||
disabled: queryKey[1].page === 'List' && queryKey[1].list === list.id,
|
||||
destructive: false,
|
||||
hidden: false
|
||||
},
|
||||
title: list.title,
|
||||
icon: ''
|
||||
}))
|
||||
].map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))
|
||||
: undefined}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
),
|
||||
headerRight: () => (
|
||||
<HeaderRight
|
||||
|
@ -1,3 +1,6 @@
|
||||
import menuAccount from '@components/contextMenu/account'
|
||||
import menuShare from '@components/contextMenu/share'
|
||||
import { HeaderRight } from '@components/Header'
|
||||
import Timeline from '@components/Timeline'
|
||||
import TimelineDefault from '@components/Timeline/Default'
|
||||
import SegmentedControl from '@react-native-community/segmented-control'
|
||||
@ -11,12 +14,14 @@ import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { useSharedValue } from 'react-native-reanimated'
|
||||
import { useIsFetching } from 'react-query'
|
||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
import AccountAttachments from './Account/Attachments'
|
||||
import AccountHeader from './Account/Header'
|
||||
import AccountInformation from './Account/Information'
|
||||
import AccountNav from './Account/Nav'
|
||||
|
||||
const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>> = ({
|
||||
navigation,
|
||||
route: {
|
||||
params: { account }
|
||||
}
|
||||
@ -24,6 +29,65 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
const { t, i18n } = useTranslation('screenTabs')
|
||||
const { colors, mode } = useTheme()
|
||||
|
||||
const mShare = menuShare({ type: 'account', url: account.url })
|
||||
const mAccount = menuAccount({ openChange: true, id: account.id })
|
||||
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>
|
||||
<HeaderRight
|
||||
accessibilityLabel={t('shared.account.actions.accessibilityLabel', {
|
||||
user: account.acct
|
||||
})}
|
||||
accessibilityHint={t('shared.account.actions.accessibilityHint')}
|
||||
content='MoreHorizontal'
|
||||
onPress={() => {}}
|
||||
background
|
||||
/>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
{mShare.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
|
||||
{mAccount.map((mGroup, index) => (
|
||||
<DropdownMenu.Group key={index}>
|
||||
{mGroup.map(menu => (
|
||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||
<DropdownMenu.ItemTitle children={menu.title} />
|
||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const { data } = useAccountQuery({ id: account.id })
|
||||
|
||||
const scrollY = useSharedValue(0)
|
||||
@ -77,7 +141,13 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
>
|
||||
<Text style={{ ...StyleConstants.FontStyle.M, color: colors.secondary, textAlign: 'center' }}>
|
||||
<Text
|
||||
style={{
|
||||
...StyleConstants.FontStyle.M,
|
||||
color: colors.secondary,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{t('shared.account.suspended')}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -1,6 +1,4 @@
|
||||
import contextMenuAccount from '@components/ContextMenu/account'
|
||||
import contextMenuShare from '@components/ContextMenu/share'
|
||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
||||
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
||||
import { ParseEmojis } from '@components/Parse'
|
||||
import CustomText from '@components/Text'
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||
@ -18,7 +16,6 @@ import { debounce } from 'lodash'
|
||||
import React from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Platform, TextInput, View } from 'react-native'
|
||||
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
|
||||
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
||||
const { colors, mode } = useTheme()
|
||||
@ -46,42 +43,7 @@ const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNaviga
|
||||
backgroundColor: `rgba(255, 255, 255, 0)`
|
||||
},
|
||||
title: '',
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} background />,
|
||||
headerRight: () => {
|
||||
const actions: ContextMenuAction[] = []
|
||||
|
||||
const shareOnPress = contextMenuShare({
|
||||
actions,
|
||||
type: 'account',
|
||||
url: account.url
|
||||
})
|
||||
const accountOnPress = contextMenuAccount({
|
||||
actions,
|
||||
type: 'account',
|
||||
id: account.id
|
||||
})
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
actions={actions}
|
||||
onPress={({ nativeEvent: { index } }) => {
|
||||
shareOnPress(index)
|
||||
accountOnPress(index)
|
||||
}}
|
||||
dropdownMenuMode
|
||||
>
|
||||
<HeaderRight
|
||||
accessibilityLabel={t('shared.account.actions.accessibilityLabel', {
|
||||
user: account.acct
|
||||
})}
|
||||
accessibilityHint={t('shared.account.actions.accessibilityHint')}
|
||||
content='MoreHorizontal'
|
||||
onPress={() => {}}
|
||||
background
|
||||
/>
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} background />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -10793,10 +10793,6 @@ react-native-codegen@^0.70.6:
|
||||
jscodeshift "^0.13.1"
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
react-native-context-menu-view@xmflsct/react-native-context-menu-view:
|
||||
version "1.5.4"
|
||||
resolved "https://codeload.github.com/xmflsct/react-native-context-menu-view/tar.gz/bff5773d318970cd67b5cf114d4bec1e200178eb"
|
||||
|
||||
react-native-fast-image@^8.6.3:
|
||||
version "8.6.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz#6edc3f9190092a909d636d93eecbcc54a8822255"
|
||||
|
Loading…
x
Reference in New Issue
Block a user