mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Fixed #354
This commit is contained in:
@ -1,15 +1,23 @@
|
||||
import analytics from '@components/analytics'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, Share } from 'react-native'
|
||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||
|
||||
export interface Props {
|
||||
copiableContent?: React.MutableRefObject<{
|
||||
content?: string | undefined
|
||||
complete: boolean
|
||||
}>
|
||||
actions: ContextMenuAction[]
|
||||
type: 'status' | 'account'
|
||||
url: string
|
||||
}
|
||||
|
||||
const contextMenuShare = ({ actions, type, url }: Props) => {
|
||||
const contextMenuShare = ({ copiableContent, actions, type, url }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentContextMenu')
|
||||
|
||||
actions.push({
|
||||
@ -17,8 +25,24 @@ const contextMenuShare = ({ actions, type, url }: Props) => {
|
||||
title: t(`share.${type}.action`),
|
||||
systemIcon: 'square.and.arrow.up'
|
||||
})
|
||||
Platform.OS !== 'android' &&
|
||||
actions.push({
|
||||
id: 'copy',
|
||||
title: t(`copy.action`),
|
||||
systemIcon: 'doc.on.doc',
|
||||
disabled: !copiableContent?.current.content?.length
|
||||
})
|
||||
|
||||
return (index: number) => {
|
||||
if (actions[index].id === 'copy') {
|
||||
analytics('timeline_shared_headeractions_copy_press')
|
||||
Clipboard.setString(copiableContent?.current.content || '')
|
||||
displayMessage({
|
||||
theme,
|
||||
type: 'success',
|
||||
message: t(`copy.succeed`)
|
||||
})
|
||||
}
|
||||
if (actions[index].id === 'share') {
|
||||
analytics('timeline_shared_headeractions_share_press')
|
||||
switch (Platform.OS) {
|
||||
|
@ -15,7 +15,7 @@ import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { uniqBy } from 'lodash'
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { useCallback, useEffect, useRef } from 'react'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineContextMenu from './Shared/ContextMenu'
|
||||
@ -53,10 +53,15 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
|
||||
const ownAccount = actualStatus.account?.id === instanceAccount?.id
|
||||
|
||||
const copiableContent = useRef<{ content: string; complete: boolean }>({
|
||||
content: '',
|
||||
complete: false
|
||||
})
|
||||
|
||||
if (
|
||||
!highlighted &&
|
||||
queryKey &&
|
||||
shouldFilter({ status: actualStatus, queryKey })
|
||||
shouldFilter({ copiableContent, status: actualStatus, queryKey })
|
||||
) {
|
||||
return <TimelineFiltered />
|
||||
}
|
||||
@ -75,10 +80,10 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<TimelineContextMenu
|
||||
copiableContent={copiableContent}
|
||||
status={actualStatus}
|
||||
queryKey={queryKey}
|
||||
rootQueryKey={rootQueryKey}
|
||||
disabled={highlighted}
|
||||
>
|
||||
<Pressable
|
||||
accessible={highlighted ? false : true}
|
||||
|
@ -15,7 +15,7 @@ import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { isEqual, uniqBy } from 'lodash'
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { useCallback, useRef } from 'react'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineContextMenu from './Shared/ContextMenu'
|
||||
@ -30,9 +30,14 @@ export interface Props {
|
||||
|
||||
const TimelineNotifications = React.memo(
|
||||
({ notification, queryKey, highlighted = false }: Props) => {
|
||||
const copiableContent = useRef<{ content: string; complete: boolean }>({
|
||||
content: '',
|
||||
complete: false
|
||||
})
|
||||
|
||||
if (
|
||||
notification.status &&
|
||||
shouldFilter({ status: notification.status, queryKey })
|
||||
shouldFilter({ copiableContent, status: notification.status, queryKey })
|
||||
) {
|
||||
return <TimelineFiltered />
|
||||
}
|
||||
@ -60,9 +65,9 @@ const TimelineNotifications = React.memo(
|
||||
|
||||
return (
|
||||
<TimelineContextMenu
|
||||
copiableContent={copiableContent}
|
||||
status={notification.status}
|
||||
queryKey={queryKey}
|
||||
disabled={highlighted}
|
||||
>
|
||||
<Pressable
|
||||
style={{
|
||||
|
@ -38,7 +38,6 @@ const TimelineContent = React.memo(
|
||||
numberOfLines={999}
|
||||
highlighted={highlighted}
|
||||
disableDetails={disableDetails}
|
||||
selectable={highlighted}
|
||||
/>
|
||||
<ParseHTML
|
||||
content={status.content}
|
||||
@ -53,7 +52,6 @@ const TimelineContent = React.memo(
|
||||
expandHint={t('shared.content.expandHint')}
|
||||
highlighted={highlighted}
|
||||
disableDetails={disableDetails}
|
||||
selectable={highlighted}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@ -66,7 +64,6 @@ const TimelineContent = React.memo(
|
||||
tags={status.tags}
|
||||
numberOfLines={highlighted ? 999 : numberOfLines}
|
||||
disableDetails={disableDetails}
|
||||
selectable={highlighted}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -12,23 +12,26 @@ import ContextMenu, {
|
||||
} from 'react-native-context-menu-view'
|
||||
|
||||
export interface Props {
|
||||
copiableContent: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
status?: Mastodon.Status
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
disabled?: boolean // Allowing toot to be copied when highlighted
|
||||
}
|
||||
|
||||
export const ContextMenuContext = createContext<ContextMenuAction[]>([])
|
||||
|
||||
const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
|
||||
children,
|
||||
copiableContent,
|
||||
status,
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
disabled,
|
||||
...props
|
||||
}) => {
|
||||
if (!status || !queryKey || disabled || Platform.OS === 'android') {
|
||||
if (!status || !queryKey || Platform.OS === 'android') {
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
@ -37,6 +40,7 @@ const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
|
||||
const shareOnPress =
|
||||
status.visibility !== 'direct'
|
||||
? contextMenuShare({
|
||||
copiableContent,
|
||||
actions,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
|
@ -34,9 +34,14 @@ const TimelineFiltered = React.memo(
|
||||
)
|
||||
|
||||
export const shouldFilter = ({
|
||||
copiableContent,
|
||||
status,
|
||||
queryKey
|
||||
}: {
|
||||
copiableContent: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
status: Mastodon.Status
|
||||
queryKey: QueryKeyTimeline
|
||||
}) => {
|
||||
@ -48,6 +53,11 @@ export const shouldFilter = ({
|
||||
if (!ownAccount) {
|
||||
const parser = new htmlparser2.Parser({
|
||||
ontext: (text: string) => {
|
||||
if (!copiableContent.current.complete) {
|
||||
copiableContent.current.content =
|
||||
copiableContent.current.content + text
|
||||
}
|
||||
|
||||
const checkFilter = (filter: Mastodon.Filter) => {
|
||||
const escapedPhrase = filter.phrase.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
@ -103,6 +113,7 @@ export const shouldFilter = ({
|
||||
status.spoiler_text && parser.write(status.spoiler_text)
|
||||
parser.write(status.content)
|
||||
parser.end()
|
||||
copiableContent.current.complete = true
|
||||
}
|
||||
|
||||
return shouldFilter
|
||||
|
@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, Pressable, View } from 'react-native'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import ContextMenu from 'react-native-context-menu-view'
|
||||
import { ContextMenuContext } from './ContextMenu'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
@ -48,7 +48,7 @@ const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{queryKey && !highlighted ? (
|
||||
{queryKey ? (
|
||||
<Pressable
|
||||
accessibilityHint={t('accessibilityHint')}
|
||||
style={{
|
||||
|
@ -141,13 +141,7 @@ const TimelineTranslate = React.memo(
|
||||
</Pressable>
|
||||
{data && data.error === undefined
|
||||
? data.text.map((d, i) => (
|
||||
<ParseHTML
|
||||
key={i}
|
||||
content={d}
|
||||
size={'M'}
|
||||
numberOfLines={999}
|
||||
selectable
|
||||
/>
|
||||
<ParseHTML key={i} content={d} size={'M'} numberOfLines={999} />
|
||||
))
|
||||
: null}
|
||||
</>
|
||||
|
Reference in New Issue
Block a user