Avoid leaking previous messages via replies (#11017)
This commit is contained in:
@@ -74,16 +74,17 @@ const BORDER_RADIUS = 20
|
|||||||
const SQUARED_BORDER_RADIUS = 4
|
const SQUARED_BORDER_RADIUS = 4
|
||||||
const DISPLAY_NAME_INSET = 20
|
const DISPLAY_NAME_INSET = 20
|
||||||
|
|
||||||
function messageIsReply(
|
export type MessageItemNeighbor =
|
||||||
message:
|
|
||||||
| ChatBskyConvoDefs.MessageView
|
| ChatBskyConvoDefs.MessageView
|
||||||
| ChatBskyConvoDefs.DeletedMessageView
|
| ChatBskyConvoDefs.DeletedMessageView
|
||||||
| null,
|
| null
|
||||||
): boolean {
|
|
||||||
|
function messageIsReply(message: MessageItemNeighbor): boolean {
|
||||||
return (
|
return (
|
||||||
ChatBskyConvoDefs.isMessageView(message) &&
|
ChatBskyConvoDefs.isMessageView(message) &&
|
||||||
(ChatBskyConvoDefs.isMessageView(message.replyTo) ||
|
(ChatBskyConvoDefs.isMessageView(message.replyTo) ||
|
||||||
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo))
|
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo) ||
|
||||||
|
ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(message.replyTo))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,10 +97,7 @@ function isWithinClusterBoundary({
|
|||||||
}: {
|
}: {
|
||||||
isPending: boolean
|
isPending: boolean
|
||||||
message: ChatBskyConvoDefs.MessageView
|
message: ChatBskyConvoDefs.MessageView
|
||||||
adjacentMessage:
|
adjacentMessage: MessageItemNeighbor
|
||||||
| ChatBskyConvoDefs.MessageView
|
|
||||||
| ChatBskyConvoDefs.DeletedMessageView
|
|
||||||
| null
|
|
||||||
isFromSameSender: boolean
|
isFromSameSender: boolean
|
||||||
direction: 'prev' | 'next'
|
direction: 'prev' | 'next'
|
||||||
}): boolean {
|
}): boolean {
|
||||||
@@ -135,14 +133,8 @@ let MessageItem = ({
|
|||||||
}: {
|
}: {
|
||||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||||
isGroupChat?: boolean
|
isGroupChat?: boolean
|
||||||
prevMessage:
|
prevMessage: MessageItemNeighbor
|
||||||
| ChatBskyConvoDefs.MessageView
|
nextMessage: MessageItemNeighbor
|
||||||
| ChatBskyConvoDefs.DeletedMessageView
|
|
||||||
| null
|
|
||||||
nextMessage:
|
|
||||||
| ChatBskyConvoDefs.MessageView
|
|
||||||
| ChatBskyConvoDefs.DeletedMessageView
|
|
||||||
| null
|
|
||||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
@@ -157,14 +149,22 @@ let MessageItem = ({
|
|||||||
const {openReactions} = useMessageDialogs()
|
const {openReactions} = useMessageDialogs()
|
||||||
const {scrollToMessage, highlightedMessage} = useMessageReplies()
|
const {scrollToMessage, highlightedMessage} = useMessageReplies()
|
||||||
|
|
||||||
// `replyTo` comes back hydrated as the referenced message (or a deleted-
|
// `replyTo` comes back hydrated as the referenced message, a deleted-message
|
||||||
// message tombstone). Narrow away the open-union fallback so we only render
|
// tombstone, or a before-joined placeholder. Narrow away the open-union
|
||||||
// shapes we understand.
|
// fallback so we only render shapes we understand.
|
||||||
const replyTo =
|
const replyTo =
|
||||||
ChatBskyConvoDefs.isMessageView(message.replyTo) ||
|
ChatBskyConvoDefs.isMessageView(message.replyTo) ||
|
||||||
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo)
|
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo) ||
|
||||||
|
ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(message.replyTo)
|
||||||
? message.replyTo
|
? message.replyTo
|
||||||
: undefined
|
: undefined
|
||||||
|
const replyToMessageId =
|
||||||
|
replyTo && !ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(replyTo)
|
||||||
|
? replyTo.id
|
||||||
|
: undefined
|
||||||
|
const onPressReplyTo = replyToMessageId
|
||||||
|
? () => scrollToMessage(replyToMessageId)
|
||||||
|
: undefined
|
||||||
|
|
||||||
const isPending = item.type === 'pending-message'
|
const isPending = item.type === 'pending-message'
|
||||||
|
|
||||||
@@ -468,7 +468,7 @@ let MessageItem = ({
|
|||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
replierDisplayName={displayName}
|
replierDisplayName={displayName}
|
||||||
relatedProfiles={relatedProfiles}
|
relatedProfiles={relatedProfiles}
|
||||||
onPress={() => scrollToMessage(replyTo.id)}
|
onPress={onPressReplyTo}
|
||||||
/>
|
/>
|
||||||
) : displayName && showDisplayName ? (
|
) : displayName && showDisplayName ? (
|
||||||
<Text
|
<Text
|
||||||
@@ -541,7 +541,7 @@ let MessageItem = ({
|
|||||||
replyTo={replyTo}
|
replyTo={replyTo}
|
||||||
isFromSelf={isFromSelf}
|
isFromSelf={isFromSelf}
|
||||||
relatedProfiles={relatedProfiles}
|
relatedProfiles={relatedProfiles}
|
||||||
onPress={() => scrollToMessage(replyTo.id)}
|
onPress={onPressReplyTo}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<RichText
|
<RichText
|
||||||
@@ -751,17 +751,25 @@ function ReplyCaption({
|
|||||||
relatedProfiles,
|
relatedProfiles,
|
||||||
onPress,
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
replyTo: ChatBskyConvoDefs.MessageView | ChatBskyConvoDefs.DeletedMessageView
|
replyTo:
|
||||||
|
| ChatBskyConvoDefs.MessageView
|
||||||
|
| ChatBskyConvoDefs.DeletedMessageView
|
||||||
|
| ChatBskyConvoDefs.MessageBeforeUserJoinedGroupView
|
||||||
isFromSelf: boolean
|
isFromSelf: boolean
|
||||||
isGroupChat: boolean
|
isGroupChat: boolean
|
||||||
replierDisplayName: string | null
|
replierDisplayName: string | null
|
||||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||||
onPress: () => void
|
onPress?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
|
let caption: string = ''
|
||||||
|
if (
|
||||||
|
ChatBskyConvoDefs.isMessageView(replyTo) ||
|
||||||
|
ChatBskyConvoDefs.isDeletedMessageView(replyTo)
|
||||||
|
) {
|
||||||
const originalSenderIsSelf = replyTo.sender.did === currentAccount?.did
|
const originalSenderIsSelf = replyTo.sender.did === currentAccount?.did
|
||||||
const originalProfile = relatedProfiles.get(replyTo.sender.did)
|
const originalProfile = relatedProfiles.get(replyTo.sender.did)
|
||||||
const originalName = originalSenderIsSelf
|
const originalName = originalSenderIsSelf
|
||||||
@@ -770,9 +778,29 @@ function ReplyCaption({
|
|||||||
? createSanitizedDisplayName(originalProfile)
|
? createSanitizedDisplayName(originalProfile)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
caption = isFromSelf
|
||||||
|
? originalSenderIsSelf
|
||||||
|
? l`You replied to yourself`
|
||||||
|
: originalName
|
||||||
|
? l`You replied to ${originalName}`
|
||||||
|
: l`You replied`
|
||||||
|
: originalSenderIsSelf
|
||||||
|
? l`${replierDisplayName} replied to you`
|
||||||
|
: originalName
|
||||||
|
? l`${replierDisplayName} replied to ${originalName}`
|
||||||
|
: l`${replierDisplayName} replied`
|
||||||
|
} else {
|
||||||
|
caption = l`Someone replied`
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
label={l`Scroll to the message this is replying to`}
|
label={
|
||||||
|
onPress
|
||||||
|
? l`Scroll to the message this is replying to`
|
||||||
|
: l`A reply to a message sent before you joined`
|
||||||
|
}
|
||||||
|
disabled={!onPress}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[
|
style={[
|
||||||
a.w_full,
|
a.w_full,
|
||||||
@@ -796,23 +824,7 @@ function ReplyCaption({
|
|||||||
style={[a.text_xs, a.flex_shrink, t.atoms.text_contrast_medium]}
|
style={[a.text_xs, a.flex_shrink, t.atoms.text_contrast_medium]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
emoji>
|
emoji>
|
||||||
{isFromSelf ? (
|
{caption}
|
||||||
originalSenderIsSelf ? (
|
|
||||||
<Trans>You replied to yourself</Trans>
|
|
||||||
) : originalName ? (
|
|
||||||
<Trans>You replied to {originalName}</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>You replied</Trans>
|
|
||||||
)
|
|
||||||
) : originalSenderIsSelf ? (
|
|
||||||
<Trans>{replierDisplayName} replied to you</Trans>
|
|
||||||
) : originalName ? (
|
|
||||||
<Trans>
|
|
||||||
{replierDisplayName} replied to {originalName}
|
|
||||||
</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>{replierDisplayName} replied</Trans>
|
|
||||||
)}
|
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
@@ -828,17 +840,25 @@ function ReplyQuote({
|
|||||||
relatedProfiles,
|
relatedProfiles,
|
||||||
onPress,
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
replyTo: ChatBskyConvoDefs.MessageView | ChatBskyConvoDefs.DeletedMessageView
|
replyTo:
|
||||||
|
| ChatBskyConvoDefs.MessageView
|
||||||
|
| ChatBskyConvoDefs.DeletedMessageView
|
||||||
|
| ChatBskyConvoDefs.MessageBeforeUserJoinedGroupView
|
||||||
isFromSelf: boolean
|
isFromSelf: boolean
|
||||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||||
onPress: () => void
|
onPress?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const getReplyPreviewText = useReplyPreviewText()
|
const getReplyPreviewText = useReplyPreviewText()
|
||||||
|
|
||||||
|
const senderDid =
|
||||||
|
ChatBskyConvoDefs.isMessageView(replyTo) ||
|
||||||
|
ChatBskyConvoDefs.isDeletedMessageView(replyTo)
|
||||||
|
? replyTo.sender.did
|
||||||
|
: undefined
|
||||||
const senderProfile = useMaybeProfileShadow(
|
const senderProfile = useMaybeProfileShadow(
|
||||||
relatedProfiles.get(replyTo.sender.did),
|
senderDid ? relatedProfiles.get(senderDid) : undefined,
|
||||||
)
|
)
|
||||||
// Hide the quoted content if we block, or are blocked by, the original
|
// Hide the quoted content if we block, or are blocked by, the original
|
||||||
// sender - mirroring how the message bubble itself is hidden.
|
// sender - mirroring how the message bubble itself is hidden.
|
||||||
@@ -866,6 +886,12 @@ function ReplyQuote({
|
|||||||
subtle = true
|
subtle = true
|
||||||
} else if (ChatBskyConvoDefs.isMessageView(replyTo)) {
|
} else if (ChatBskyConvoDefs.isMessageView(replyTo)) {
|
||||||
;({text, subtle} = getReplyPreviewText(replyTo))
|
;({text, subtle} = getReplyPreviewText(replyTo))
|
||||||
|
} else if (ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(replyTo)) {
|
||||||
|
text = l({
|
||||||
|
message: `(message sent before you joined)`,
|
||||||
|
comment: 'A reply summary in chat',
|
||||||
|
})
|
||||||
|
subtle = true
|
||||||
} else {
|
} else {
|
||||||
text = l({message: '(deleted message)', comment: 'A reply summary in chat'})
|
text = l({message: '(deleted message)', comment: 'A reply summary in chat'})
|
||||||
subtle = true
|
subtle = true
|
||||||
@@ -874,10 +900,13 @@ function ReplyQuote({
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
label={
|
label={
|
||||||
senderName
|
!onPress
|
||||||
|
? l`Replied-to message was sent before you joined`
|
||||||
|
: senderName
|
||||||
? l`Replied-to message from ${senderName}, tap to scroll to it`
|
? l`Replied-to message from ${senderName}, tap to scroll to it`
|
||||||
: l`Replied-to message, tap to scroll to it`
|
: l`Replied-to message, tap to scroll to it`
|
||||||
}
|
}
|
||||||
|
disabled={!onPress}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[
|
style={[
|
||||||
a.mb_xs,
|
a.mb_xs,
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
|||||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||||
import {atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
import {atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
||||||
import {DateDivider} from '#/components/dms/DateDivider'
|
import {DateDivider} from '#/components/dms/DateDivider'
|
||||||
import {MessageItem} from '#/components/dms/MessageItem'
|
import {
|
||||||
|
MessageItem,
|
||||||
|
type MessageItemNeighbor,
|
||||||
|
} from '#/components/dms/MessageItem'
|
||||||
import {MessageOverlays} from '#/components/dms/MessageOverlays'
|
import {MessageOverlays} from '#/components/dms/MessageOverlays'
|
||||||
import {MessageRepliesProvider} from '#/components/dms/MessageReplies'
|
import {MessageRepliesProvider} from '#/components/dms/MessageReplies'
|
||||||
import {NewMessagesPill} from '#/components/dms/NewMessagesPill'
|
import {NewMessagesPill} from '#/components/dms/NewMessagesPill'
|
||||||
@@ -104,7 +107,7 @@ function keyExtractor(item: RenderItem) {
|
|||||||
function getNeighborMessage(
|
function getNeighborMessage(
|
||||||
items: RenderItem[],
|
items: RenderItem[],
|
||||||
index: number,
|
index: number,
|
||||||
): ChatBskyConvoDefs.MessageView | ChatBskyConvoDefs.DeletedMessageView | null {
|
): MessageItemNeighbor {
|
||||||
const neighbor = items[index]
|
const neighbor = items[index]
|
||||||
if (!neighbor) return null
|
if (!neighbor) return null
|
||||||
if (
|
if (
|
||||||
|
|||||||
Reference in New Issue
Block a user