mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Moving to using zeego
This commit is contained in:
@ -297,8 +297,6 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- react-native-cameraroll (5.1.0):
|
- react-native-cameraroll (5.1.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-context-menu-view (1.5.4):
|
|
||||||
- React
|
|
||||||
- react-native-image-picker (4.10.1):
|
- react-native-image-picker (4.10.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-ios-context-menu (1.15.1):
|
- 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-blur (from `../node_modules/@react-native-community/blur`)"
|
||||||
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
|
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
|
||||||
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
|
- "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-image-picker (from `../node_modules/react-native-image-picker`)
|
||||||
- react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`)
|
- react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`)
|
||||||
- react-native-language-detection (from `../node_modules/react-native-language-detection`)
|
- react-native-language-detection (from `../node_modules/react-native-language-detection`)
|
||||||
@ -627,8 +624,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native-blurhash"
|
:path: "../node_modules/react-native-blurhash"
|
||||||
react-native-cameraroll:
|
react-native-cameraroll:
|
||||||
:path: "../node_modules/@react-native-camera-roll/camera-roll"
|
: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:
|
react-native-image-picker:
|
||||||
:path: "../node_modules/react-native-image-picker"
|
:path: "../node_modules/react-native-image-picker"
|
||||||
react-native-ios-context-menu:
|
react-native-ios-context-menu:
|
||||||
@ -742,7 +737,6 @@ SPEC CHECKSUMS:
|
|||||||
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
||||||
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
|
||||||
react-native-cameraroll: a40b082318eb1ecd0336a2f29d9f74b7f2c8cae8
|
react-native-cameraroll: a40b082318eb1ecd0336a2f29d9f74b7f2c8cae8
|
||||||
react-native-context-menu-view: b0beca02aad4bd9f9d7d932bf437e0a03baa69ef
|
|
||||||
react-native-image-picker: f2ab1215d17bcfe27b0eb6417cc236fd1f4775e7
|
react-native-image-picker: f2ab1215d17bcfe27b0eb6417cc236fd1f4775e7
|
||||||
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
|
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
|
||||||
react-native-language-detection: 0e43195ad014974f1b7a31b64820eff34a243f2d
|
react-native-language-detection: 0e43195ad014974f1b7a31b64820eff34a243f2d
|
||||||
|
@ -71,7 +71,6 @@
|
|||||||
"react-native-animated-spinkit": "^1.5.2",
|
"react-native-animated-spinkit": "^1.5.2",
|
||||||
"react-native-base64": "^0.2.1",
|
"react-native-base64": "^0.2.1",
|
||||||
"react-native-blurhash": "^1.1.10",
|
"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-fast-image": "^8.6.3",
|
||||||
"react-native-feather": "^1.1.2",
|
"react-native-feather": "^1.1.2",
|
||||||
"react-native-flash-message": "^0.3.1",
|
"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 { getInstanceUrl } 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 { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
const menuInstance = ({
|
||||||
actions: ContextMenuAction[]
|
status,
|
||||||
status: Mastodon.Status
|
queryKey,
|
||||||
queryKey: QueryKeyTimeline
|
rootQueryKey
|
||||||
|
}: {
|
||||||
|
status?: Mastodon.Status
|
||||||
|
queryKey?: QueryKeyTimeline
|
||||||
rootQueryKey?: QueryKeyTimeline
|
rootQueryKey?: QueryKeyTimeline
|
||||||
}
|
}): ContextMenu[][] => {
|
||||||
|
if (!status || !queryKey) return []
|
||||||
|
|
||||||
const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props) => {
|
|
||||||
const { t } = useTranslation('componentContextMenu')
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
const { t } = useTranslation('componentContextMenu')
|
||||||
const currentInstance = useSelector(getInstanceUrl)
|
|
||||||
const instance = status?.uri && status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const mutation = useTimelineMutation({
|
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) {
|
if (currentInstance !== instance && instance) {
|
||||||
switch (Platform.OS) {
|
menus.push([
|
||||||
case 'ios':
|
{
|
||||||
actions.push({
|
key: 'instance-block',
|
||||||
id: 'instance',
|
item: {
|
||||||
title: t('instance.title'),
|
onSelect: () =>
|
||||||
actions: [
|
Alert.alert(
|
||||||
{
|
t('instance.block.alert.title', { instance }),
|
||||||
id: 'instance-block',
|
t('instance.block.alert.message'),
|
||||||
title: t('instance.block.action', { instance }),
|
[
|
||||||
destructive: true
|
{
|
||||||
}
|
text: t('instance.block.alert.buttons.confirm'),
|
||||||
]
|
style: 'destructive',
|
||||||
})
|
onPress: () => {
|
||||||
break
|
mutation.mutate({
|
||||||
default:
|
type: 'domainBlock',
|
||||||
actions.push({
|
queryKey,
|
||||||
id: 'instance-block',
|
domain: instance
|
||||||
title: t('instance.block.action', { instance }),
|
})
|
||||||
destructive: true
|
}
|
||||||
})
|
},
|
||||||
break
|
{
|
||||||
}
|
text: t('common:buttons.cancel')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
disabled: false,
|
||||||
|
destructive: true,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('instance.block.action', { instance }),
|
||||||
|
icon: ''
|
||||||
|
}
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
return (index: number) => {
|
return menus
|
||||||
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')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default contextMenuInstance
|
export default menuInstance
|
||||||
|
@ -3,59 +3,74 @@ import Clipboard from '@react-native-clipboard/clipboard'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform, Share } from 'react-native'
|
import { Platform, Share } from 'react-native'
|
||||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
|
||||||
|
|
||||||
export interface Props {
|
const menuShare = (
|
||||||
copiableContent?: React.MutableRefObject<{
|
params:
|
||||||
content?: string | undefined
|
| {
|
||||||
complete: boolean
|
visibility?: Mastodon.Status['visibility']
|
||||||
}>
|
copiableContent?: React.MutableRefObject<{
|
||||||
actions: ContextMenuAction[]
|
content?: string | undefined
|
||||||
type: 'status' | 'account'
|
complete: boolean
|
||||||
url: string
|
}>
|
||||||
}
|
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 { theme } = useTheme()
|
||||||
const { t } = useTranslation('componentContextMenu')
|
const { t } = useTranslation('componentContextMenu')
|
||||||
|
|
||||||
actions.push({
|
const menus: ContextMenu[][] = [[]]
|
||||||
id: 'share',
|
|
||||||
title: t(`share.${type}.action`),
|
if (params.url) {
|
||||||
systemIcon: 'square.and.arrow.up'
|
const url = params.url
|
||||||
})
|
menus[0].push({
|
||||||
Platform.OS !== 'android' &&
|
key: 'share',
|
||||||
type === 'status' &&
|
item: {
|
||||||
actions.push({
|
onSelect: () => {
|
||||||
id: 'copy',
|
switch (Platform.OS) {
|
||||||
title: t(`copy.action`),
|
case 'ios':
|
||||||
systemIcon: 'doc.on.doc',
|
Share.share({ url })
|
||||||
disabled: !copiableContent?.current.content?.length
|
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) => {
|
return menus
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default contextMenuShare
|
export default menuShare
|
||||||
|
@ -12,18 +12,20 @@ import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instance
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
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 {
|
const menuStatus = ({
|
||||||
actions: ContextMenuAction[]
|
status,
|
||||||
status: Mastodon.Status
|
queryKey,
|
||||||
queryKey: QueryKeyTimeline
|
rootQueryKey
|
||||||
|
}: {
|
||||||
|
status?: Mastodon.Status
|
||||||
|
queryKey?: QueryKeyTimeline
|
||||||
rootQueryKey?: QueryKeyTimeline
|
rootQueryKey?: QueryKeyTimeline
|
||||||
}
|
}): ContextMenu[][] => {
|
||||||
|
if (!status || !queryKey) return []
|
||||||
|
|
||||||
const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) => {
|
|
||||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t } = useTranslation('componentContextMenu')
|
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 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) {
|
if (ownAccount) {
|
||||||
const accountMenuItems: ContextMenuAction[] = [
|
menus.push([
|
||||||
{
|
{
|
||||||
id: 'status-delete',
|
key: 'status-edit',
|
||||||
title: t('status.delete.action'),
|
item: {
|
||||||
systemIcon: 'trash',
|
onSelect: async () => {
|
||||||
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 () => {
|
|
||||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||||
if (status.in_reply_to_id) {
|
if (status.in_reply_to_id) {
|
||||||
replyToStatus = await apiInstance<Mastodon.Status>({
|
replyToStatus = await apiInstance<Mastodon.Status>({
|
||||||
@ -139,87 +75,166 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) =
|
|||||||
url: `statuses/${status.in_reply_to_id}`
|
url: `statuses/${status.in_reply_to_id}`
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
}
|
}
|
||||||
mutation
|
apiInstance<{
|
||||||
.mutateAsync({
|
id: Mastodon.Status['id']
|
||||||
type: 'deleteItem',
|
text: NonNullable<Mastodon.Status['text']>
|
||||||
source: 'statuses',
|
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,
|
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 }),
|
disabled: false,
|
||||||
queryKey,
|
destructive: false,
|
||||||
rootQueryKey
|
hidden: !canEditPost
|
||||||
})
|
},
|
||||||
})
|
title: t('status.edit.action'),
|
||||||
}
|
icon: 'square.and.pencil'
|
||||||
if (actions[index].id === 'status-pin') {
|
},
|
||||||
// Also note that reblogs cannot be pinned.
|
{
|
||||||
mutation.mutate({
|
key: 'status-delete-edit',
|
||||||
type: 'updateStatusProperty',
|
item: {
|
||||||
queryKey,
|
onSelect: () =>
|
||||||
rootQueryKey,
|
Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [
|
||||||
id: status.id,
|
{
|
||||||
payload: {
|
text: t('status.deleteEdit.alert.buttons.confirm'),
|
||||||
property: 'pinned',
|
style: 'destructive',
|
||||||
currentValue: status.pinned,
|
onPress: async () => {
|
||||||
propertyCount: undefined,
|
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||||
countValue: 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 TimelineActioned from '@components/Timeline/Shared/Actioned'
|
||||||
import TimelineActions from '@components/Timeline/Shared/Actions'
|
import TimelineActions from '@components/Timeline/Shared/Actions'
|
||||||
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||||
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
||||||
import TimelineCard from '@components/Timeline/Shared/Card'
|
import TimelineCard from '@components/Timeline/Shared/Card'
|
||||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||||
// @ts-ignore
|
|
||||||
import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
@ -18,10 +20,11 @@ import { uniqBy } from 'lodash'
|
|||||||
import React, { useRef } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import { Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
import { Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineContextMenu from './Shared/ContextMenu'
|
import * as ContextMenu from 'zeego/context-menu'
|
||||||
import TimelineFeedback from './Shared/Feedback'
|
import TimelineFeedback from './Shared/Feedback'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
|
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||||
import TimelineTranslate from './Shared/Translate'
|
import TimelineTranslate from './Shared/Translate'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -89,8 +92,10 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<TimelineHeaderDefault
|
<TimelineHeaderDefault
|
||||||
queryKey={disableOnPress ? undefined : queryKey}
|
queryKey={disableOnPress ? undefined : queryKey}
|
||||||
|
rootQueryKey={rootQueryKey}
|
||||||
status={actualStatus}
|
status={actualStatus}
|
||||||
highlighted={highlighted}
|
highlighted={highlighted}
|
||||||
|
copiableContent={copiableContent}
|
||||||
/>
|
/>
|
||||||
</View>
|
</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 ? (
|
return disableOnPress ? (
|
||||||
<View style={mainStyle}>{main()}</View>
|
<View style={mainStyle}>{main()}</View>
|
||||||
) : (
|
) : (
|
||||||
<TimelineContextMenu
|
<>
|
||||||
copiableContent={copiableContent}
|
<ContextMenu.Root>
|
||||||
status={actualStatus}
|
<ContextMenu.Trigger>
|
||||||
queryKey={queryKey}
|
<Pressable
|
||||||
rootQueryKey={rootQueryKey}
|
accessible={highlighted ? false : true}
|
||||||
>
|
style={mainStyle}
|
||||||
<Pressable
|
onPress={onPress}
|
||||||
accessible={highlighted ? false : true}
|
onLongPress={() => {}}
|
||||||
style={mainStyle}
|
children={main()}
|
||||||
onPress={onPress}
|
/>
|
||||||
onLongPress={() => {}}
|
</ContextMenu.Trigger>
|
||||||
>
|
|
||||||
{main()}
|
<ContextMenu.Content>
|
||||||
</Pressable>
|
{mShare.map((mGroup, index) => (
|
||||||
</TimelineContextMenu>
|
<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 TimelineActioned from '@components/Timeline/Shared/Actioned'
|
||||||
import TimelineActions from '@components/Timeline/Shared/Actions'
|
import TimelineActions from '@components/Timeline/Shared/Actions'
|
||||||
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||||
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
import TimelineAvatar from '@components/Timeline/Shared/Avatar'
|
||||||
import TimelineCard from '@components/Timeline/Shared/Card'
|
import TimelineCard from '@components/Timeline/Shared/Card'
|
||||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||||
// @ts-ignore
|
|
||||||
import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotification'
|
import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotification'
|
||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
@ -16,11 +18,12 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
import React, { useCallback, useRef } from 'react'
|
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 { useSelector } from 'react-redux'
|
||||||
import TimelineContextMenu from './Shared/ContextMenu'
|
import * as ContextMenu from 'zeego/context-menu'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
|
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
notification: Mastodon.Notification
|
notification: Mastodon.Notification
|
||||||
@ -136,36 +139,68 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Platform.OS === 'android' ? (
|
const mShare = menuShare({
|
||||||
<Pressable
|
visibility: notification.status?.visibility,
|
||||||
style={{
|
type: 'status',
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
url: notification.status?.url || notification.status?.uri,
|
||||||
backgroundColor: colors.backgroundDefault,
|
copiableContent
|
||||||
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
})
|
||||||
}}
|
const mStatus = menuStatus({ status: notification.status, queryKey })
|
||||||
onPress={onPress}
|
const mInstance = menuInstance({ status: notification.status, queryKey })
|
||||||
onLongPress={() => {}}
|
|
||||||
>
|
return (
|
||||||
{main()}
|
<>
|
||||||
</Pressable>
|
<ContextMenu.Root>
|
||||||
) : (
|
<ContextMenu.Trigger>
|
||||||
<TimelineContextMenu
|
<Pressable
|
||||||
copiableContent={copiableContent}
|
style={{
|
||||||
status={notification.status}
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
queryKey={queryKey}
|
backgroundColor: colors.backgroundDefault,
|
||||||
>
|
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
||||||
<Pressable
|
}}
|
||||||
style={{
|
onPress={onPress}
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
onLongPress={() => {}}
|
||||||
backgroundColor: colors.backgroundDefault,
|
children={main()}
|
||||||
paddingBottom: notification.status ? 0 : StyleConstants.Spacing.Global.PagePadding
|
/>
|
||||||
}}
|
</ContextMenu.Trigger>
|
||||||
onPress={onPress}
|
|
||||||
onLongPress={() => {}}
|
<ContextMenu.Content>
|
||||||
>
|
{mShare.map((mGroup, index) => (
|
||||||
{main()}
|
<ContextMenu.Group key={index}>
|
||||||
</Pressable>
|
{mGroup.map(menu => (
|
||||||
</TimelineContextMenu>
|
<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",
|
"accessibilityHint": "Actions for this toot, such as its posted user, toot itself",
|
||||||
"account": {
|
"account": {
|
||||||
"title": "User actions",
|
"title": "User actions",
|
||||||
|
"following": {
|
||||||
|
"action_false": "Follow user",
|
||||||
|
"action_true": "Unfollow user"
|
||||||
|
},
|
||||||
"mute": {
|
"mute": {
|
||||||
"action_false": "Mute user",
|
"action_false": "Mute user",
|
||||||
"action_true": "Unmute user"
|
"action_true": "Unmute user"
|
||||||
@ -11,7 +15,7 @@
|
|||||||
"action_true": "Unblock user"
|
"action_true": "Unblock user"
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"action": "Report and block"
|
"action": "Report and block user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
|
@ -9,8 +9,7 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
|||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
import ContextMenu from 'react-native-context-menu-view'
|
|
||||||
import TabShared from './Shared'
|
import TabShared from './Shared'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabLocalStackParamList>()
|
const Stack = createNativeStackNavigator<TabLocalStackParamList>()
|
||||||
@ -34,31 +33,8 @@ const TabLocal = React.memo(
|
|||||||
name='Tab-Local-Root'
|
name='Tab-Local-Root'
|
||||||
options={{
|
options={{
|
||||||
headerTitle: () => (
|
headerTitle: () => (
|
||||||
<ContextMenu
|
<DropdownMenu.Root>
|
||||||
dropdownMenuMode
|
<DropdownMenu.Trigger>
|
||||||
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={
|
|
||||||
<HeaderCenter
|
<HeaderCenter
|
||||||
dropdown={(lists?.length ?? 0) > 0}
|
dropdown={(lists?.length ?? 0) > 0}
|
||||||
content={
|
content={
|
||||||
@ -67,8 +43,43 @@ const TabLocal = React.memo(
|
|||||||
: t('tabs.local.name')
|
: 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: () => (
|
||||||
<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 Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
import SegmentedControl from '@react-native-community/segmented-control'
|
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 { StyleSheet, Text, View } from 'react-native'
|
||||||
import { useSharedValue } from 'react-native-reanimated'
|
import { useSharedValue } from 'react-native-reanimated'
|
||||||
import { useIsFetching } from 'react-query'
|
import { useIsFetching } from 'react-query'
|
||||||
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
import AccountAttachments from './Account/Attachments'
|
import AccountAttachments from './Account/Attachments'
|
||||||
import AccountHeader from './Account/Header'
|
import AccountHeader from './Account/Header'
|
||||||
import AccountInformation from './Account/Information'
|
import AccountInformation from './Account/Information'
|
||||||
import AccountNav from './Account/Nav'
|
import AccountNav from './Account/Nav'
|
||||||
|
|
||||||
const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>> = ({
|
const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>> = ({
|
||||||
|
navigation,
|
||||||
route: {
|
route: {
|
||||||
params: { account }
|
params: { account }
|
||||||
}
|
}
|
||||||
@ -24,6 +29,65 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t, i18n } = useTranslation('screenTabs')
|
||||||
const { colors, mode } = useTheme()
|
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 { data } = useAccountQuery({ id: account.id })
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
const scrollY = useSharedValue(0)
|
||||||
@ -77,7 +141,13 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
|
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')}
|
{t('shared.account.suspended')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import contextMenuAccount from '@components/ContextMenu/account'
|
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
||||||
import contextMenuShare from '@components/ContextMenu/share'
|
|
||||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
|
||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||||
@ -18,7 +16,6 @@ import { debounce } from 'lodash'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import { Platform, TextInput, View } from 'react-native'
|
import { Platform, TextInput, View } from 'react-native'
|
||||||
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
|
|
||||||
|
|
||||||
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
||||||
const { colors, mode } = useTheme()
|
const { colors, mode } = useTheme()
|
||||||
@ -46,42 +43,7 @@ const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNaviga
|
|||||||
backgroundColor: `rgba(255, 255, 255, 0)`
|
backgroundColor: `rgba(255, 255, 255, 0)`
|
||||||
},
|
},
|
||||||
title: '',
|
title: '',
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} background />,
|
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -10793,10 +10793,6 @@ react-native-codegen@^0.70.6:
|
|||||||
jscodeshift "^0.13.1"
|
jscodeshift "^0.13.1"
|
||||||
nullthrows "^1.1.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:
|
react-native-fast-image@^8.6.3:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz#6edc3f9190092a909d636d93eecbcc54a8822255"
|
||||||
|
Reference in New Issue
Block a user