From 377d2728411229389d52f0d70e335a3fb9ff2ee5 Mon Sep 17 00:00:00 2001 From: Jamie <113370520+jamiebuilds-signal@users.noreply.github.com> Date: Tue, 16 Dec 2025 07:39:50 -0800 Subject: [PATCH] Allow unpinning all pinned messages --- _locales/en/messages.json | 16 ++++++ .../PinnedMessagesPanel.dom.tsx | 50 ++++++++++++++++++- .../smart/PinnedMessagesPanel.preload.tsx | 15 +++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 9b3e98d915..878faa3efd 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1750,6 +1750,22 @@ "messageformat": "Unpin all messages", "description": "Conversation > Pinned messages panel (view all) > Unpin all messages button" }, + "icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Title": { + "messageformat": "Unpin all messages?", + "description": "Conversation > Pinned messages panel (view all) > Unpin all messages button > Confirm Dialog > Title" + }, + "icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Description--Group": { + "messageformat": "Messages will be unpinned for all group members.", + "description": "Conversation > Pinned messages panel (view all) > Unpin all messages button > Confirm Dialog > Description (in group)" + }, + "icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Cancel": { + "messageformat": "Cancel", + "description": "Conversation > Pinned messages panel (view all) > Unpin all messages button > Confirm Dialog > Cancel" + }, + "icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Unpin": { + "messageformat": "Unpin", + "description": "Conversation > Pinned messages panel (view all) > Unpin all messages button > Confirm Dialog > Unpin" + }, "icu:sessionEnded": { "messageformat": "Secure session reset", "description": "This is a past tense, informational message. In other words, your secure session has been reset." diff --git a/ts/components/conversation/pinned-messages/PinnedMessagesPanel.dom.tsx b/ts/components/conversation/pinned-messages/PinnedMessagesPanel.dom.tsx index 34fae55f4a..34558412e5 100644 --- a/ts/components/conversation/pinned-messages/PinnedMessagesPanel.dom.tsx +++ b/ts/components/conversation/pinned-messages/PinnedMessagesPanel.dom.tsx @@ -5,6 +5,7 @@ import React, { forwardRef, Fragment, memo, + useCallback, useMemo, useRef, useState, @@ -26,12 +27,14 @@ import { useSizeObserver } from '../../../hooks/useSizeObserver.dom.js'; import { MessageInteractivity } from '../Message.dom.js'; import { tw } from '../../../axo/tw.dom.js'; import { AxoButton } from '../../../axo/AxoButton.dom.js'; +import { AxoAlertDialog } from '../../../axo/AxoAlertDialog.dom.js'; export type PinnedMessagesPanelProps = Readonly<{ i18n: LocalizerType; conversation: ConversationType; pinnedMessages: ReadonlyArray; canPinMessages: boolean; + onPinnedMessageRemoveAll: () => void; renderTimelineItem: (props: SmartTimelineItemProps) => JSX.Element; }>; @@ -44,6 +47,13 @@ export const PinnedMessagesPanel = memo(function PinnedMessagesPanel( WidthBreakpoint.Wide ); + const [confirmUnpinAllDialogOpen, setConfirmUnpinAllDialogOpen] = + useState(false); + + const handleClickUnpinAll = useCallback(() => { + setConfirmUnpinAllDialogOpen(true); + }, []); + useLayoutEffect(() => { strictAssert(containerElementRef.current, 'Missing container ref'); const container = containerElementRef.current; @@ -81,11 +91,49 @@ export const PinnedMessagesPanel = memo(function PinnedMessagesPanel( {props.canPinMessages && (
- + {i18n('icu:PinnedMessagesPanel__UnpinAllMessages')}
)} + + + + + {i18n( + 'icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Title' + )} + + + {i18n( + 'icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Description--Group' + )} + + + + + {i18n( + 'icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Cancel' + )} + + + {i18n( + 'icu:PinnedMessagesPanel__UnpinAllMessages__ConfirmDialog__Unpin' + )} + + + + ); }); diff --git a/ts/state/smart/PinnedMessagesPanel.preload.tsx b/ts/state/smart/PinnedMessagesPanel.preload.tsx index 5935cc2400..b8d0d6a141 100644 --- a/ts/state/smart/PinnedMessagesPanel.preload.tsx +++ b/ts/state/smart/PinnedMessagesPanel.preload.tsx @@ -1,7 +1,7 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React, { memo } from 'react'; +import React, { memo, useCallback } from 'react'; import { useSelector } from 'react-redux'; import { getIntl } from '../selectors/user.std.js'; import { getConversationByIdSelector } from '../selectors/conversations.dom.js'; @@ -11,6 +11,8 @@ import type { SmartTimelineItemProps } from './TimelineItem.preload.js'; import { SmartTimelineItem } from './TimelineItem.preload.js'; import { getPinnedMessages } from '../selectors/pinnedMessages.dom.js'; import { canPinMessages as getCanPinMessages } from '../selectors/message.preload.js'; +import { usePinnedMessagesActions } from '../ducks/pinnedMessages.preload.js'; +import { useConversationsActions } from '../ducks/conversations.preload.js'; export type SmartPinnedMessagesPanelProps = Readonly<{ conversationId: string; @@ -26,6 +28,7 @@ export const SmartPinnedMessagesPanel = memo(function SmartPinnedMessagesPanel( const i18n = useSelector(getIntl); const conversationSelector = useSelector(getConversationByIdSelector); const conversation = conversationSelector(props.conversationId); + const { popPanelForConversation } = useConversationsActions(); strictAssert( conversation, @@ -35,6 +38,15 @@ export const SmartPinnedMessagesPanel = memo(function SmartPinnedMessagesPanel( const pinnedMessages = useSelector(getPinnedMessages); const canPinMessages = getCanPinMessages(conversation); + const { onPinnedMessageRemove } = usePinnedMessagesActions(); + + const handlePinnedMessageRemoveAll = useCallback(() => { + popPanelForConversation(); + for (const { pinnedMessage } of pinnedMessages) { + onPinnedMessageRemove(pinnedMessage.messageId); + } + }, [popPanelForConversation, pinnedMessages, onPinnedMessageRemove]); + return ( ); });