PinnedMessagesPanel: Add footer with Unpin all messages button

This commit is contained in:
Jamie
2025-11-21 07:13:05 -08:00
committed by GitHub
parent 025d5d5011
commit 954bb8591b
4 changed files with 72 additions and 30 deletions

View File

@@ -1682,6 +1682,10 @@
"messageformat": "Pinned messages",
"description": "Conversation > Pinned messages panel (view all) > Title"
},
"icu:PinnedMessagesPanel__UnpinAllMessages": {
"messageformat": "Unpin all messages",
"description": "Conversation > Pinned messages panel (view all) > Unpin all messages button"
},
"icu:sessionEnded": {
"messageformat": "Secure session reset",
"description": "This is a past tense, informational message. In other words, your secure session has been reset."

View File

@@ -10,7 +10,6 @@
height: 100%;
inset-inline-start: 0;
overflow-y: auto;
position: absolute;
top: 0;
width: 100%;
@@ -28,12 +27,13 @@
// Used for centering EmptyState in All Media view
position: relative;
overflow-y: auto;
flex-grow: 1;
padding-top: calc(
#{variables.$header-height} + var(--title-bar-drag-area-height)
);
&--padding {
padding-inline: 24px;
}
}
&__header {
flex-shrink: 0;
@@ -44,7 +44,6 @@
#{variables.$header-height} + var(--title-bar-drag-area-height)
);
padding-top: var(--title-bar-drag-area-height);
position: fixed;
width: 100%;
z-index: variables.$z-index-base;

View File

@@ -1,6 +1,14 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { Fragment, memo, useMemo, useRef, useState } from 'react';
import type { ForwardedRef, ReactNode } from 'react';
import React, {
forwardRef,
Fragment,
memo,
useMemo,
useRef,
useState,
} from 'react';
import { useLayoutEffect } from '@react-aria/utils';
import type { LocalizerType } from '../../../types/I18N.std.js';
import type { ConversationType } from '../../../state/ducks/conversations.preload.js';
@@ -16,6 +24,8 @@ import { getWidthBreakpoint } from '../../../util/timelineUtil.std.js';
import { strictAssert } from '../../../util/assert.std.js';
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';
export type PinnedMessagesPanelProps = Readonly<{
i18n: LocalizerType;
@@ -27,6 +37,7 @@ export type PinnedMessagesPanelProps = Readonly<{
export const PinnedMessagesPanel = memo(function PinnedMessagesPanel(
props: PinnedMessagesPanelProps
) {
const { i18n } = props;
const containerElementRef = useRef<HTMLDivElement>(null);
const [containerWidthBreakpoint, setContainerWidthBreakpoint] = useState(
WidthBreakpoint.Wide
@@ -42,19 +53,9 @@ export const PinnedMessagesPanel = memo(function PinnedMessagesPanel(
setContainerWidthBreakpoint(getWidthBreakpoint(size.width));
});
const scrollerLock = useMemo(() => {
return createScrollerLock('PinnedMessagesPanel', () => {
// noop - we probably don't need to do anything here because the only
// thing that can happen is the pinned messages getting removed/added
});
}, []);
return (
<AxoScrollArea.Root scrollbarWidth="wide">
<AxoScrollArea.Viewport>
<AxoScrollArea.Content>
<div ref={containerElementRef}>
<ScrollerLockContext.Provider value={scrollerLock}>
<div className={tw('flex h-full flex-col')}>
<ScrollArea ref={containerElementRef}>
{props.pinnedMessages.map((pinnedMessage, pinnedMessageIndex) => {
const next = props.pinnedMessages[pinnedMessageIndex + 1];
const prev = props.pinnedMessages[pinnedMessageIndex - 1];
@@ -76,6 +77,36 @@ export const PinnedMessagesPanel = memo(function PinnedMessagesPanel(
</Fragment>
);
})}
</ScrollArea>
<div className={tw('flex items-center justify-center p-2.5')}>
<AxoButton.Root variant="borderless-primary" size="lg">
{i18n('icu:PinnedMessagesPanel__UnpinAllMessages')}
</AxoButton.Root>
</div>
</div>
);
});
const ScrollArea = forwardRef(function ScrollArea(
props: { children: ReactNode },
ref: ForwardedRef<HTMLDivElement>
) {
const scrollerLock = useMemo(() => {
return createScrollerLock('PinnedMessagesPanel', () => {
// noop - we probably don't need to do anything here because the only
// thing that can happen is the pinned messages getting removed/added
});
}, []);
return (
<AxoScrollArea.Root scrollbarWidth="wide">
<AxoScrollArea.Hint edge="top" />
<AxoScrollArea.Hint edge="bottom" />
<AxoScrollArea.Viewport>
<AxoScrollArea.Content>
<div ref={ref}>
<ScrollerLockContext.Provider value={scrollerLock}>
{props.children}
</ScrollerLockContext.Provider>
</div>
</AxoScrollArea.Content>

View File

@@ -11,6 +11,7 @@ import React, {
useState,
} from 'react';
import { useSelector } from 'react-redux';
import classNames from 'classnames';
import type { PanelRenderType } from '../../types/Panels.std.js';
import { createLogger } from '../../logging/log.std.js';
import { PanelType } from '../../types/Panels.std.js';
@@ -322,7 +323,14 @@ const PanelContainer = forwardRef<
</div>
)}
</div>
<div className="ConversationPanel__body" ref={focusRef}>
<div
className={classNames(
'ConversationPanel__body',
panel.type !== PanelType.PinnedMessages &&
'ConversationPanel__body--padding'
)}
ref={focusRef}
>
<PanelElement conversationId={conversationId} panel={panel} />
</div>
</div>