d6ca9e6b41
* start adding new report reasons * cleanup * use updated types * cleanup open/closed logic * other fix * state fix * rename type * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/index.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/components/moderation/ReportDialog/utils/useReportOptions.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * add mapping * Replace consts and maps using exported values * Map to deprecated reason if necessary, send some reports only to Bluesky * Use only new report reason types * Cleanup * Bump package, integrate new reasons * Adjust sorting, return type, integrate feedback * Just remove sort * Finish 'other' handling * Update descriptions * Just invert filter logic, no functional change but feels more correct * Clarify handling * fix deep imports * fix type error * [Reporting] Integrate new `ReportDialog` into DMs (#9213) * Update types, fork copy for convo vs message * Integrate in remaining spots * Replace DoneStep with separate dialog that opens after reporting * Handle profile error state by just showing a success message * Delete old dms ReportDialog * Chats only go to bsky * Only open block or delete dialog if submitted successfully * make close button always large * prevent expansion of blockordelete dialog * Delete old report dialog (#9214)e * Skip labeler selection for Bluesky-only cases * Rename AfterReportDialog --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com> --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Samuel Newman <mozzius@protonmail.com>
210 lines
7.0 KiB
TypeScript
210 lines
7.0 KiB
TypeScript
import {memo, useCallback} from 'react'
|
|
import {LayoutAnimation} from 'react-native'
|
|
import * as Clipboard from 'expo-clipboard'
|
|
import {type ChatBskyConvoDefs, RichText} from '@atproto/api'
|
|
import {msg} from '@lingui/macro'
|
|
import {useLingui} from '@lingui/react'
|
|
|
|
import {useTranslate} from '#/lib/hooks/useTranslate'
|
|
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
|
import {logger} from '#/logger'
|
|
import {isNative} from '#/platform/detection'
|
|
import {useConvoActive} from '#/state/messages/convo'
|
|
import {useLanguagePrefs} from '#/state/preferences'
|
|
import {useSession} from '#/state/session'
|
|
import * as Toast from '#/view/com/util/Toast'
|
|
import * as ContextMenu from '#/components/ContextMenu'
|
|
import {type TriggerProps} from '#/components/ContextMenu/types'
|
|
import {AfterReportDialog} from '#/components/dms/AfterReportDialog'
|
|
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
|
|
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
|
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
|
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
|
|
import {ReportDialog} from '#/components/moderation/ReportDialog'
|
|
import * as Prompt from '#/components/Prompt'
|
|
import {usePromptControl} from '#/components/Prompt'
|
|
import {EmojiReactionPicker} from './EmojiReactionPicker'
|
|
import {hasReachedReactionLimit} from './util'
|
|
|
|
export let MessageContextMenu = ({
|
|
message,
|
|
children,
|
|
}: {
|
|
message: ChatBskyConvoDefs.MessageView
|
|
children: TriggerProps['children']
|
|
}): React.ReactNode => {
|
|
const {_} = useLingui()
|
|
const {currentAccount} = useSession()
|
|
const convo = useConvoActive()
|
|
const deleteControl = usePromptControl()
|
|
const reportControl = usePromptControl()
|
|
const blockOrDeleteControl = usePromptControl()
|
|
const langPrefs = useLanguagePrefs()
|
|
const translate = useTranslate()
|
|
|
|
const isFromSelf = message.sender?.did === currentAccount?.did
|
|
|
|
const onCopyMessage = useCallback(() => {
|
|
const str = richTextToString(
|
|
new RichText({
|
|
text: message.text,
|
|
facets: message.facets,
|
|
}),
|
|
true,
|
|
)
|
|
|
|
Clipboard.setStringAsync(str)
|
|
Toast.show(_(msg`Copied to clipboard`), 'clipboard-check')
|
|
}, [_, message.text, message.facets])
|
|
|
|
const onPressTranslateMessage = useCallback(() => {
|
|
translate(message.text, langPrefs.primaryLanguage)
|
|
|
|
logger.metric(
|
|
'translate',
|
|
{
|
|
sourceLanguages: [],
|
|
targetLanguage: langPrefs.primaryLanguage,
|
|
textLength: message.text.length,
|
|
},
|
|
{statsig: false},
|
|
)
|
|
}, [langPrefs.primaryLanguage, message.text, translate])
|
|
|
|
const onDelete = useCallback(() => {
|
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
|
convo
|
|
.deleteMessage(message.id)
|
|
.then(() =>
|
|
Toast.show(_(msg({message: 'Message deleted', context: 'toast'}))),
|
|
)
|
|
.catch(() => Toast.show(_(msg`Failed to delete message`)))
|
|
}, [_, convo, message.id])
|
|
|
|
const onEmojiSelect = useCallback(
|
|
(emoji: string) => {
|
|
if (
|
|
message.reactions?.find(
|
|
reaction =>
|
|
reaction.value === emoji &&
|
|
reaction.sender.did === currentAccount?.did,
|
|
)
|
|
) {
|
|
convo
|
|
.removeReaction(message.id, emoji)
|
|
.catch(() => Toast.show(_(msg`Failed to remove emoji reaction`)))
|
|
} else {
|
|
if (hasReachedReactionLimit(message, currentAccount?.did)) return
|
|
convo
|
|
.addReaction(message.id, emoji)
|
|
.catch(() =>
|
|
Toast.show(_(msg`Failed to add emoji reaction`), 'xmark'),
|
|
)
|
|
}
|
|
},
|
|
[_, convo, message, currentAccount?.did],
|
|
)
|
|
|
|
const sender = convo.convo.members.find(
|
|
member => member.did === message.sender.did,
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<ContextMenu.Root>
|
|
{isNative && (
|
|
<ContextMenu.AuxiliaryView align={isFromSelf ? 'right' : 'left'}>
|
|
<EmojiReactionPicker
|
|
message={message}
|
|
onEmojiSelect={onEmojiSelect}
|
|
/>
|
|
</ContextMenu.AuxiliaryView>
|
|
)}
|
|
|
|
<ContextMenu.Trigger
|
|
label={_(msg`Message options`)}
|
|
contentLabel={_(
|
|
msg`Message from @${
|
|
sender?.handle ?? 'unknown' // should always be defined
|
|
}: ${message.text}`,
|
|
)}>
|
|
{children}
|
|
</ContextMenu.Trigger>
|
|
|
|
<ContextMenu.Outer align={isFromSelf ? 'right' : 'left'}>
|
|
{message.text.length > 0 && (
|
|
<>
|
|
<ContextMenu.Item
|
|
testID="messageDropdownTranslateBtn"
|
|
label={_(msg`Translate`)}
|
|
onPress={onPressTranslateMessage}>
|
|
<ContextMenu.ItemText>{_(msg`Translate`)}</ContextMenu.ItemText>
|
|
<ContextMenu.ItemIcon icon={Translate} position="right" />
|
|
</ContextMenu.Item>
|
|
<ContextMenu.Item
|
|
testID="messageDropdownCopyBtn"
|
|
label={_(msg`Copy message text`)}
|
|
onPress={onCopyMessage}>
|
|
<ContextMenu.ItemText>
|
|
{_(msg`Copy message text`)}
|
|
</ContextMenu.ItemText>
|
|
<ContextMenu.ItemIcon icon={ClipboardIcon} position="right" />
|
|
</ContextMenu.Item>
|
|
<ContextMenu.Divider />
|
|
</>
|
|
)}
|
|
<ContextMenu.Item
|
|
testID="messageDropdownDeleteBtn"
|
|
label={_(msg`Delete message for me`)}
|
|
onPress={() => deleteControl.open()}>
|
|
<ContextMenu.ItemText>{_(msg`Delete for me`)}</ContextMenu.ItemText>
|
|
<ContextMenu.ItemIcon icon={Trash} position="right" />
|
|
</ContextMenu.Item>
|
|
{!isFromSelf && (
|
|
<ContextMenu.Item
|
|
testID="messageDropdownReportBtn"
|
|
label={_(msg`Report message`)}
|
|
onPress={() => reportControl.open()}>
|
|
<ContextMenu.ItemText>{_(msg`Report`)}</ContextMenu.ItemText>
|
|
<ContextMenu.ItemIcon icon={Warning} position="right" />
|
|
</ContextMenu.Item>
|
|
)}
|
|
</ContextMenu.Outer>
|
|
</ContextMenu.Root>
|
|
|
|
<ReportDialog
|
|
// currentScreen="conversation"
|
|
control={reportControl}
|
|
subject={{
|
|
view: 'message',
|
|
convoId: convo.convo.id,
|
|
message,
|
|
}}
|
|
onAfterSubmit={() => {
|
|
blockOrDeleteControl.open()
|
|
}}
|
|
/>
|
|
<AfterReportDialog
|
|
control={blockOrDeleteControl}
|
|
currentScreen="conversation"
|
|
params={{
|
|
convoId: convo.convo.id,
|
|
message,
|
|
}}
|
|
/>
|
|
|
|
<Prompt.Basic
|
|
control={deleteControl}
|
|
title={_(msg`Delete message`)}
|
|
description={_(
|
|
msg`Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant.`,
|
|
)}
|
|
confirmButtonCta={_(msg`Delete`)}
|
|
confirmButtonColor="negative"
|
|
onConfirm={onDelete}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
MessageContextMenu = memo(MessageContextMenu)
|