diff --git a/shared/chat/conversation/messages/message-popup/attachment.tsx b/shared/chat/conversation/messages/message-popup/attachment.tsx index 0cf6fdb9efb9..31008735050e 100644 --- a/shared/chat/conversation/messages/message-popup/attachment.tsx +++ b/shared/chat/conversation/messages/message-popup/attachment.tsx @@ -3,7 +3,7 @@ import * as Chat from '@/constants/chat2' import * as React from 'react' import type * as T from '@/constants/types' import {type Position, fileUIName, type StylesCrossPlatform} from '@/styles' -import {useItems, useHeader} from './hooks' +import {useItems, useHeader, useModeration} from './hooks' import * as Kb from '@/common-adapters' import {useFSState} from '@/constants/fs' @@ -25,7 +25,7 @@ const PopAttach = (ownProps: OwnProps) => { const message = m?.type === 'attachment' ? m : emptyMessage return message }) - const {downloadPath, attachmentType, id} = message + const {author, conversationIDKey, downloadPath, attachmentType, id} = message const pending = !!message.transferState const clearModals = C.useRouterState(s => s.dispatch.clearModals) @@ -54,6 +54,9 @@ const PopAttach = (ownProps: OwnProps) => { }) ) + const {itemBlock, itemFilter, itemFlag, itemReport} = useModeration(author, conversationIDKey) + const infoPanelShowing = Chat.useChatState(s => s.infoPanelShowing) + const onJump = React.useCallback(() => { loadMessagesCentered(id, 'always') showInfoPanel(false, 'attachments') @@ -116,7 +119,9 @@ const PopAttach = (ownProps: OwnProps) => { : [] const itemMedia = [{icon: 'iconfont-camera', onClick: onAllMedia, title: 'All media'}] as const - const itemJump = [{icon: 'iconfont-search', onClick: onJump, title: 'Jump to message'}] as const + const itemJump = infoPanelShowing + ? ([{icon: 'iconfont-search', onClick: onJump, title: 'Jump to message'}] as const) + : [] const topSection = [...itemSave, ...itemShare, ...itemDelete, ...itemExplode] @@ -137,6 +142,10 @@ const PopAttach = (ownProps: OwnProps) => { ...itemProfile, ...itemKick, ...itemPin, + ...itemBlock, + ...itemFilter, + ...itemReport, + ...itemFlag, ] const header = useHeader(ordinal, onHidden) diff --git a/shared/chat/conversation/messages/message-popup/hooks.tsx b/shared/chat/conversation/messages/message-popup/hooks.tsx index 6a8a35db164b..75068acdcd16 100644 --- a/shared/chat/conversation/messages/message-popup/hooks.tsx +++ b/shared/chat/conversation/messages/message-popup/hooks.tsx @@ -270,6 +270,70 @@ export const useItems = (ordinal: T.Chat.Ordinal, onHidden: () => void) => { } } +export const useModeration = ( + author: string, + conversationIDKey: T.Chat.ConversationIDKey +) => { + const you = useCurrentUserState(s => s.username) + const yourMessage = author === you + + const {isTeam, numPart} = Chat.useChatContext( + C.useShallow(s => { + const {teamname} = s.meta + const isTeam = !!teamname + const numPart = s.participants.all.length + return {isTeam, numPart} + }) + ) + const blockModalSingle = !isTeam && numPart === 2 + const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) + + type BlockingModalProps = { + filterUserByDefault?: boolean + flagUserByDefault?: boolean + reportsUserByDefault?: boolean + } + const openBlockingModal = React.useCallback( + (extraProps?: BlockingModalProps) => { + navigateAppend({ + props: { + blockUserByDefault: true, + context: blockModalSingle ? 'message-popup-single' : 'message-popup', + conversationIDKey, + username: author, + ...extraProps, + }, + selected: 'chatBlockingModal', + }) + }, + [blockModalSingle, conversationIDKey, navigateAppend, author] + ) + const canModerate = author && !yourMessage + const onUserBlock = canModerate ? () => openBlockingModal() : undefined + const onUserFilter = C.isIOS && canModerate ? () => openBlockingModal({filterUserByDefault: true}) : undefined + const onUserReport = C.isIOS && canModerate ? () => openBlockingModal({reportsUserByDefault: true}) : undefined + const onUserFlag = + C.isIOS && canModerate + ? () => openBlockingModal({flagUserByDefault: true, reportsUserByDefault: true}) + : undefined + + const blockTitle = isTeam ? 'Report user' : 'Block user' + const itemBlock = onUserBlock + ? ([{danger: true, icon: 'iconfont-user-block', onClick: onUserBlock, title: blockTitle}] as const) + : [] + const itemFilter = onUserFilter + ? ([{danger: true, icon: 'iconfont-user-block', onClick: onUserFilter, title: 'Filter user'}] as const) + : [] + const itemReport = !isTeam && onUserReport + ? ([{danger: true, icon: 'iconfont-user-block', onClick: onUserReport, title: 'Report user'}] as const) + : [] + const itemFlag = onUserFlag + ? ([{danger: true, icon: 'iconfont-user-block', onClick: onUserFlag, title: 'Flag content'}] as const) + : [] + + return {itemBlock, itemFilter, itemFlag, itemReport} +} + export const useHeader = (ordinal: T.Chat.Ordinal, onHidden: () => void) => { const message = Chat.useChatContext(s => { return s.messageMap.get(ordinal) ?? emptyText diff --git a/shared/chat/conversation/messages/message-popup/text.tsx b/shared/chat/conversation/messages/message-popup/text.tsx index a28db313dfc2..c3321219d0f9 100644 --- a/shared/chat/conversation/messages/message-popup/text.tsx +++ b/shared/chat/conversation/messages/message-popup/text.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' import * as Chat from '@/constants/chat2' import {useConfigState} from '@/constants/config' +import {useCurrentUserState} from '@/constants/current-user' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import type {Position, StylesCrossPlatform} from '@/styles' -import {useItems, useHeader} from './hooks' +import {useItems, useHeader, useModeration} from './hooks' import openURL from '@/util/open-url' -import {useCurrentUserState} from '@/constants/current-user' type OwnProps = { attachTo?: React.RefObject @@ -28,7 +28,6 @@ const PopText = (ownProps: OwnProps) => { return message }) const you = useCurrentUserState(s => s.username) - const {conversationIDKey, author} = message const text = React.useMemo(() => { switch (message.type) { case 'text': @@ -64,24 +63,23 @@ const PopText = (ownProps: OwnProps) => { } }, [message]) + const {author, conversationIDKey} = message const yourMessage = author === you - const {isTeam, messageReplyPrivately, numPart, teamType} = Chat.useChatContext( + const {messageReplyPrivately, numPart, teamType} = Chat.useChatContext( C.useShallow(s => { - const {teamType, teamname} = s.meta - const isTeam = !!teamname - const numPart = s.participants.all.length const {messageReplyPrivately} = s.dispatch - return {isTeam, messageReplyPrivately, numPart, teamType} + const numPart = s.participants.all.length + const {teamType} = s.meta + return {messageReplyPrivately, numPart, teamType} }) ) - // you can reply privately *if* text message, someone else's message, and not in a 1-on-1 chat - const canReplyPrivately = ['small', 'big'].includes(teamType) || numPart > 2 - const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) + const {itemBlock, itemFilter, itemFlag, itemReport} = useModeration(author, conversationIDKey) const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) const onCopy = React.useCallback(() => { text && copyToClipboard(text) }, [copyToClipboard, text]) - + // you can reply privately *if* text message, someone else's message, and not in a 1-on-1 chat + const canReplyPrivately = teamType === 'small' || teamType === 'big' || numPart > 2 const _onReplyPrivately = React.useCallback(() => { messageReplyPrivately(ordinal) }, [messageReplyPrivately, ordinal]) @@ -90,63 +88,6 @@ const PopText = (ownProps: OwnProps) => { // don't pass onViewMap if we don't have a coordinate (e.g. when a location share ends) const onViewMap = mapUnfurl?.mapInfo && !mapUnfurl.mapInfo.isLiveLocationDone ? () => openURL(mapUnfurl.url) : undefined - const blockModalSingle = !isTeam && numPart === 2 - - const _onUserReport = React.useCallback(() => { - navigateAppend({ - props: { - blockUserByDefault: true, - context: blockModalSingle ? 'message-popup-single' : 'message-popup', - conversationIDKey, - reportsUserByDefault: true, - username: author, - }, - selected: 'chatBlockingModal', - }) - }, [conversationIDKey, blockModalSingle, navigateAppend, author]) - const onUserReport = C.isIOS && author && !yourMessage ? () => _onUserReport : undefined - - const _onUserFlag = React.useCallback(() => { - navigateAppend({ - props: { - blockUserByDefault: true, - context: blockModalSingle ? 'message-popup-single' : 'message-popup', - conversationIDKey, - flagUserByDefault: true, - reportsUserByDefault: true, - username: author, - }, - selected: 'chatBlockingModal', - }) - }, [conversationIDKey, blockModalSingle, navigateAppend, author]) - const onUserFlag = C.isIOS && author && !yourMessage ? _onUserFlag : undefined - - const _onUserBlock = React.useCallback(() => { - navigateAppend({ - props: { - blockUserByDefault: true, - context: blockModalSingle ? 'message-popup-single' : 'message-popup', - conversationIDKey, - username: author, - }, - selected: 'chatBlockingModal', - }) - }, [conversationIDKey, blockModalSingle, navigateAppend, author]) - const onUserBlock = author && !yourMessage ? _onUserBlock : undefined - - const _onUserFilter = React.useCallback(() => { - navigateAppend({ - props: { - blockUserByDefault: true, - context: blockModalSingle ? 'message-popup-single' : 'message-popup', - conversationIDKey, - filterUserByDefault: true, - username: author, - }, - selected: 'chatBlockingModal', - }) - }, [conversationIDKey, blockModalSingle, navigateAppend, author]) - const onUserFilter = C.isIOS && author && !yourMessage ? () => _onUserFilter : undefined const i = useItems(ordinal, onHidden) const {itemReaction, itemBot, itemCopyLink, itemReply, itemEdit, itemForward, itemPin, itemUnread} = i @@ -162,50 +103,6 @@ const PopText = (ownProps: OwnProps) => { ? ([{icon: 'iconfont-reply', onClick: onReplyPrivately, title: 'Reply privately'}] as const) : [] - const itemBlock = !yourMessage - ? ([ - { - danger: true, - icon: 'iconfont-user-block', - onClick: onUserBlock, - title: isTeam ? 'Report user' : 'Block user', - }, - ] as const) - : [] - const itemFilter = - !yourMessage && onUserFilter - ? ([ - { - danger: true, - icon: 'iconfont-user-block', - onClick: onUserFilter, - title: 'Filter user', - }, - ] as const) - : [] - const itemReport = - !yourMessage && !isTeam && onUserReport - ? ([ - { - danger: true, - icon: 'iconfont-user-block', - onClick: onUserReport, - title: 'Report user', - }, - ] as const) - : [] - const itemFlag = - !yourMessage && onUserFlag - ? ([ - { - danger: true, - icon: 'iconfont-user-block', - onClick: onUserFlag, - title: 'Flag content', - }, - ] as const) - : [] - const items = [ ...itemReaction, ...itemEdit,