mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-02-14 23:18:54 +00:00
Ability to click megaphone in narrow sidebar to expand sidebar
This commit is contained in:
@@ -10154,6 +10154,10 @@
|
||||
"messageformat": "Thanks for your feedback!",
|
||||
"description": "Toast message shown when call quality survey submission succeeds"
|
||||
},
|
||||
"icu:Megaphone__ExpandNarrowSidebar": {
|
||||
"messageformat": "Expand sidebar to interact with megaphone",
|
||||
"description": "Aria label for collapsed megaphone in the narrow sidebar state. Indicates that you can click it to expand the sidebar and the megaphone."
|
||||
},
|
||||
"icu:WhatsNew__bugfixes": {
|
||||
"messageformat": "This version contains a number of small tweaks and bug fixes to keep Signal running smoothly.",
|
||||
"description": "Release notes for releases that only include bug fixes",
|
||||
|
||||
@@ -19,12 +19,12 @@ import type {
|
||||
} from '../state/ducks/calling.preload.js';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog.dom.js';
|
||||
import type { UnreadStats } from '../util/countUnreadStats.std.js';
|
||||
import type { WidthBreakpoint } from './_util.std.js';
|
||||
import type { CallLinkType } from '../types/CallLink.std.js';
|
||||
import type { CallStateType } from '../state/selectors/calling.std.js';
|
||||
import type { StartCallData } from './ConfirmLeaveCallModal.dom.js';
|
||||
import { I18n } from './I18n.dom.js';
|
||||
import { AxoDropdownMenu } from '../axo/AxoDropdownMenu.dom.js';
|
||||
import type { SmartPropsType as SmartToastManagerPropsType } from '../state/smart/ToastManager.preload.js';
|
||||
|
||||
enum CallsTabSidebarView {
|
||||
CallsListView,
|
||||
@@ -70,9 +70,7 @@ type CallsTabProps = Readonly<{
|
||||
conversationId: string,
|
||||
callHistoryGroup: CallHistoryGroup | null
|
||||
) => React.JSX.Element;
|
||||
renderToastManager: (_: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}) => React.JSX.Element;
|
||||
renderToastManager: (_: SmartToastManagerPropsType) => React.JSX.Element;
|
||||
regionCode: string | undefined;
|
||||
savePreferredLeftPaneWidth: (preferredLeftPaneWidth: number) => void;
|
||||
startCallLinkLobbyByRoomId: (options: { roomId: string }) => void;
|
||||
|
||||
@@ -164,6 +164,7 @@ export function DebugLogWindow({
|
||||
setDidResumeDonation={shouldNeverBeCalled}
|
||||
toast={toast}
|
||||
containerWidthBreakpoint={null}
|
||||
expandNarrowLeftPane={shouldNeverBeCalled}
|
||||
isInFullScreenCall={false}
|
||||
/>
|
||||
</div>
|
||||
@@ -230,6 +231,7 @@ export function DebugLogWindow({
|
||||
setDidResumeDonation={shouldNeverBeCalled}
|
||||
toast={toast}
|
||||
containerWidthBreakpoint={null}
|
||||
expandNarrowLeftPane={shouldNeverBeCalled}
|
||||
isInFullScreenCall={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -318,6 +318,7 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
|
||||
toast={undefined}
|
||||
megaphone={undefined}
|
||||
containerWidthBreakpoint={containerWidthBreakpoint}
|
||||
expandNarrowLeftPane={action('expandNarrowLeftPane')}
|
||||
isInFullScreenCall={false}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -36,6 +36,7 @@ import * as KeyboardLayout from '../services/keyboardLayout.dom.js';
|
||||
import type { LookupConversationWithoutServiceIdActionsType } from '../util/lookupConversationWithoutServiceId.preload.js';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations.preload.js';
|
||||
import type { PropsType as UnsupportedOSDialogPropsType } from '../state/smart/UnsupportedOSDialog.preload.js';
|
||||
import type { SmartPropsType as SmartToastManagerPropsType } from '../state/smart/ToastManager.preload.js';
|
||||
|
||||
import { ConversationList } from './ConversationList.dom.js';
|
||||
import { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox.dom.js';
|
||||
@@ -209,9 +210,9 @@ export type PropsType = {
|
||||
) => React.JSX.Element;
|
||||
renderLeftPaneChatFolders: () => React.JSX.Element;
|
||||
renderNotificationProfilesMenu: () => React.JSX.Element;
|
||||
renderToastManager: (_: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}) => React.JSX.Element;
|
||||
renderToastManager: (
|
||||
_: Readonly<SmartToastManagerPropsType>
|
||||
) => React.JSX.Element;
|
||||
} & LookupConversationWithoutServiceIdActionsType;
|
||||
|
||||
export function LeftPane({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import React, { createContext, useEffect, useState } from 'react';
|
||||
import React, { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useMove } from 'react-aria';
|
||||
import { NavTabsToggle } from './NavTabs.dom.js';
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '../util/leftPaneWidth.std.js';
|
||||
import { WidthBreakpoint, getNavSidebarWidthBreakpoint } from './_util.std.js';
|
||||
import type { UnreadStats } from '../util/countUnreadStats.std.js';
|
||||
import type { SmartPropsType as SmartToastManagerPropsType } from '../state/smart/ToastManager.preload.js';
|
||||
|
||||
export const NavSidebarWidthBreakpointContext =
|
||||
createContext<WidthBreakpoint | null>(null);
|
||||
@@ -60,9 +61,7 @@ export type NavSidebarProps = Readonly<{
|
||||
savePreferredLeftPaneWidth: (width: number) => void;
|
||||
title: string;
|
||||
otherTabsUnreadStats: UnreadStats;
|
||||
renderToastManager: (_: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}) => React.JSX.Element;
|
||||
renderToastManager: (_: SmartToastManagerPropsType) => React.JSX.Element;
|
||||
}>;
|
||||
|
||||
enum DragState {
|
||||
@@ -103,6 +102,13 @@ export function NavSidebar({
|
||||
|
||||
const widthBreakpoint = getNavSidebarWidthBreakpoint(width);
|
||||
|
||||
const expandNarrowLeftPane = useCallback(() => {
|
||||
if (preferredWidth < MIN_FULL_WIDTH) {
|
||||
setPreferredWidth(MIN_FULL_WIDTH);
|
||||
savePreferredLeftPaneWidth(MIN_FULL_WIDTH);
|
||||
}
|
||||
}, [preferredWidth, savePreferredLeftPaneWidth]);
|
||||
|
||||
// `useMove` gives us keyboard and mouse dragging support.
|
||||
const { moveProps } = useMove({
|
||||
onMoveStart() {
|
||||
@@ -229,7 +235,10 @@ export function NavSidebar({
|
||||
{...moveProps}
|
||||
/>
|
||||
|
||||
{renderToastManager({ containerWidthBreakpoint: widthBreakpoint })}
|
||||
{renderToastManager({
|
||||
containerWidthBreakpoint: widthBreakpoint,
|
||||
expandNarrowLeftPane,
|
||||
})}
|
||||
</div>
|
||||
</NavSidebarWidthBreakpointContext.Provider>
|
||||
);
|
||||
|
||||
@@ -28,6 +28,7 @@ export default {
|
||||
body: 'Signal is powered by people like you. Show your support today!',
|
||||
imagePath: '/fixtures/donate-heart.png',
|
||||
isFullSize: true,
|
||||
onClickNarrowMegaphone: action('onClickNarrowMegaphone'),
|
||||
onInteractWithMegaphone: action('onInteractWithMegaphone'),
|
||||
},
|
||||
} satisfies ComponentMeta<PropsType>;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { offsetDistanceModifier } from '../util/popperUtil.std.js';
|
||||
export type PropsType = Omit<RemoteActionableMegaphoneType, 'type'> & {
|
||||
isFullSize: boolean;
|
||||
i18n: LocalizerType;
|
||||
onClickNarrowMegaphone: () => void;
|
||||
};
|
||||
|
||||
export function RemoteMegaphone({
|
||||
@@ -25,6 +26,7 @@ export function RemoteMegaphone({
|
||||
secondaryCtaText,
|
||||
remoteMegaphoneId,
|
||||
isFullSize,
|
||||
onClickNarrowMegaphone,
|
||||
onInteractWithMegaphone,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const isRTL = i18n.getLocaleDirection() === 'rtl';
|
||||
@@ -37,7 +39,12 @@ export function RemoteMegaphone({
|
||||
'bg-elevated-background-primary dark:bg-elevated-background-tertiary'
|
||||
);
|
||||
const image: React.JSX.Element = (
|
||||
<div className={tw('size-[48px] shrink-0 @min-[88px]:size-[64px]')}>
|
||||
<div
|
||||
className={tw(
|
||||
'size-[48px] shrink-0',
|
||||
isFullSize ? 'size-[64px]' : 'm-auto'
|
||||
)}
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
className={tw('object-cover')}
|
||||
@@ -100,7 +107,6 @@ export function RemoteMegaphone({
|
||||
}
|
||||
|
||||
// Narrow collapsed sidebar
|
||||
// TODO: DESKTOP-9540
|
||||
const tooltipContent: React.JSX.Element = (
|
||||
<div className={tw('text-start text-label-primary')}>
|
||||
<h2 className={tw('mt-1 type-body-medium font-semibold')}>{title}</h2>
|
||||
@@ -112,11 +118,17 @@ export function RemoteMegaphone({
|
||||
<Tooltip
|
||||
content={tooltipContent}
|
||||
className="RemoteMegaphoneTooltip"
|
||||
wrapperClassName={wrapperClassName}
|
||||
direction={isRTL ? TooltipPlacement.Left : TooltipPlacement.Right}
|
||||
popperModifiers={[offsetDistanceModifier(15)]}
|
||||
>
|
||||
<div className={tw('m-auto')}>{image}</div>
|
||||
<button
|
||||
aria-label={i18n('icu:Megaphone__ExpandNarrowSidebar')}
|
||||
className={wrapperClassName}
|
||||
onClick={onClickNarrowMegaphone}
|
||||
type="button"
|
||||
>
|
||||
{image}
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { RemoteMegaphone } from './RemoteMegaphone.dom.js';
|
||||
|
||||
export type PropsType = {
|
||||
changeLocation: (newLocation: Location) => unknown;
|
||||
expandNarrowLeftPane: () => void;
|
||||
hideToast: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
openFileInFolder: (target: string) => unknown;
|
||||
@@ -948,6 +949,7 @@ export function renderMegaphone({
|
||||
i18n,
|
||||
megaphone,
|
||||
containerWidthBreakpoint,
|
||||
expandNarrowLeftPane,
|
||||
}: PropsType): React.JSX.Element | null {
|
||||
if (!megaphone) {
|
||||
return null;
|
||||
@@ -963,6 +965,7 @@ export function renderMegaphone({
|
||||
{...megaphone}
|
||||
i18n={i18n}
|
||||
isFullSize={containerWidthBreakpoint !== WidthBreakpoint.Narrow}
|
||||
onClickNarrowMegaphone={expandNarrowLeftPane}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
getPreferredLeftPaneWidth,
|
||||
} from '../selectors/items.dom.js';
|
||||
import { getIntl, getRegionCode } from '../selectors/user.std.js';
|
||||
import type { WidthBreakpoint } from '../../components/_util.std.js';
|
||||
import { CallsTab } from '../../components/CallsTab.preload.js';
|
||||
import {
|
||||
getAllConversations,
|
||||
@@ -24,7 +23,7 @@ import type {
|
||||
} from '../../types/CallDisposition.std.js';
|
||||
import type { ConversationType } from '../ducks/conversations.preload.js';
|
||||
import { SmartConversationDetails } from './ConversationDetails.preload.js';
|
||||
import { SmartToastManager } from './ToastManager.preload.js';
|
||||
import { renderToastManagerWithoutMegaphone } from './ToastManager.preload.js';
|
||||
import { useCallingActions } from '../ducks/calling.preload.js';
|
||||
import {
|
||||
getActiveCallState,
|
||||
@@ -131,12 +130,6 @@ function renderConversationDetails(
|
||||
);
|
||||
}
|
||||
|
||||
function renderToastManager(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
return <SmartToastManager disableMegaphone {...props} />;
|
||||
}
|
||||
|
||||
export const SmartCallsTab = memo(function SmartCallsTab() {
|
||||
const i18n = useSelector(getIntl);
|
||||
const navTabsCollapsed = useSelector(getNavTabsCollapsed);
|
||||
@@ -253,7 +246,7 @@ export const SmartCallsTab = memo(function SmartCallsTab() {
|
||||
preferredLeftPaneWidth={preferredLeftPaneWidth}
|
||||
renderCallLinkDetails={renderCallLinkDetails}
|
||||
renderConversationDetails={renderConversationDetails}
|
||||
renderToastManager={renderToastManager}
|
||||
renderToastManager={renderToastManagerWithoutMegaphone}
|
||||
regionCode={regionCode}
|
||||
savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
|
||||
startCallLinkLobbyByRoomId={startCallLinkLobbyByRoomId}
|
||||
|
||||
@@ -20,6 +20,7 @@ import OS from '../../util/os/osMain.node.js';
|
||||
import { isStagingServer } from '../../util/isStagingServer.dom.js';
|
||||
import { createLogger } from '../../logging/log.std.js';
|
||||
import { SmartToastManager } from './ToastManager.preload.js';
|
||||
import { shouldNeverBeCalled } from '../../util/shouldNeverBeCalled.std.js';
|
||||
|
||||
const log = createLogger('InstallScreen');
|
||||
|
||||
@@ -103,6 +104,7 @@ export const SmartInstallScreen = memo(function SmartInstallScreen() {
|
||||
<InstallScreen {...props} />
|
||||
<SmartToastManager
|
||||
disableMegaphone
|
||||
expandNarrowLeftPane={shouldNeverBeCalled}
|
||||
containerWidthBreakpoint={WidthBreakpoint.Narrow}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -103,7 +103,11 @@ import { SmartCrashReportDialog } from './CrashReportDialog.preload.js';
|
||||
import { SmartMessageSearchResult } from './MessageSearchResult.preload.js';
|
||||
import { SmartNetworkStatus } from './NetworkStatus.preload.js';
|
||||
import { SmartRelinkDialog } from './RelinkDialog.dom.js';
|
||||
import { SmartToastManager } from './ToastManager.preload.js';
|
||||
import {
|
||||
renderToastManagerWithoutMegaphone,
|
||||
SmartToastManager,
|
||||
} from './ToastManager.preload.js';
|
||||
import type { SmartPropsType as SmartToastManagerPropsType } from './ToastManager.preload.js';
|
||||
import type { PropsType as SmartUnsupportedOSDialogPropsType } from './UnsupportedOSDialog.preload.js';
|
||||
import { SmartUnsupportedOSDialog } from './UnsupportedOSDialog.preload.js';
|
||||
import { SmartUpdateDialog } from './UpdateDialog.preload.js';
|
||||
@@ -172,18 +176,12 @@ function renderUnsupportedOSDialog(
|
||||
): React.JSX.Element {
|
||||
return <SmartUnsupportedOSDialog {...props} />;
|
||||
}
|
||||
function renderToastManagerWithMegaphone(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
function renderToastManagerWithMegaphone(
|
||||
props: Readonly<SmartToastManagerPropsType>
|
||||
): React.JSX.Element {
|
||||
return <SmartToastManager {...props} />;
|
||||
}
|
||||
|
||||
function renderToastManagerWithoutMegaphone(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
return <SmartToastManager disableMegaphone {...props} />;
|
||||
}
|
||||
|
||||
function renderNotificationProfilesMenu(): React.JSX.Element {
|
||||
return <SmartNotificationProfilesMenu />;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ import { getPreferredBadgeSelector } from '../selectors/badges.preload.js';
|
||||
import { SmartProfileEditor } from './ProfileEditor.preload.js';
|
||||
import { useNavActions } from '../ducks/nav.std.js';
|
||||
import { NavTab } from '../../types/Nav.std.js';
|
||||
import { SmartToastManager } from './ToastManager.preload.js';
|
||||
import { renderToastManagerWithoutMegaphone } from './ToastManager.preload.js';
|
||||
import { useToastActions } from '../ducks/toast.preload.js';
|
||||
import { DataReader, DataWriter } from '../../sql/Client.preload.js';
|
||||
import { deleteAllMyStories } from '../../util/deleteAllMyStories.preload.js';
|
||||
@@ -157,12 +157,6 @@ function renderProfileEditor(options: {
|
||||
return <SmartProfileEditor contentsRef={options.contentsRef} />;
|
||||
}
|
||||
|
||||
function renderToastManager(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
return <SmartToastManager disableMegaphone {...props} />;
|
||||
}
|
||||
|
||||
function renderDonationsPane({
|
||||
contentsRef,
|
||||
settingsLocation,
|
||||
@@ -937,7 +931,7 @@ export function SmartPreferences(): React.JSX.Element | null {
|
||||
renderNotificationProfilesCreateFlow
|
||||
}
|
||||
renderProfileEditor={renderProfileEditor}
|
||||
renderToastManager={renderToastManager}
|
||||
renderToastManager={renderToastManagerWithoutMegaphone}
|
||||
renderUpdateDialog={renderUpdateDialog}
|
||||
renderPreferencesChatFoldersPage={renderPreferencesChatFoldersPage}
|
||||
renderPreferencesEditChatFolderPage={
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
import React, { memo, useCallback, useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SmartStoryCreator } from './StoryCreator.preload.js';
|
||||
import { SmartToastManager } from './ToastManager.preload.js';
|
||||
import type { WidthBreakpoint } from '../../components/_util.std.js';
|
||||
import { renderToastManagerWithoutMegaphone } from './ToastManager.preload.js';
|
||||
import { StoriesTab } from '../../components/StoriesTab.dom.js';
|
||||
import { getMaximumOutgoingAttachmentSizeInKb } from '../../types/AttachmentSize.std.js';
|
||||
import type { ConfigKeyType } from '../../RemoteConfig.dom.js';
|
||||
@@ -40,12 +39,6 @@ function renderStoryCreator(): React.JSX.Element {
|
||||
return <SmartStoryCreator />;
|
||||
}
|
||||
|
||||
function renderToastManager(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
return <SmartToastManager disableMegaphone {...props} />;
|
||||
}
|
||||
|
||||
export const SmartStoriesTab = memo(function SmartStoriesTab() {
|
||||
const storiesActions = useStoriesActions();
|
||||
const {
|
||||
@@ -133,7 +126,7 @@ export const SmartStoriesTab = memo(function SmartStoriesTab() {
|
||||
preferredLeftPaneWidth={preferredLeftPaneWidth}
|
||||
preferredWidthFromStorage={preferredWidthFromStorage}
|
||||
renderStoryCreator={renderStoryCreator}
|
||||
renderToastManager={renderToastManager}
|
||||
renderToastManager={renderToastManagerWithoutMegaphone}
|
||||
retryMessageSend={retryMessageSend}
|
||||
savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
|
||||
showConversation={showConversation}
|
||||
|
||||
@@ -35,19 +35,34 @@ import { useDonationsActions } from '../ducks/donations.preload.js';
|
||||
import { itemStorage } from '../../textsecure/Storage.preload.js';
|
||||
import { getVisibleMegaphonesForDisplay } from '../selectors/megaphones.preload.js';
|
||||
import { useMegaphonesActions } from '../ducks/megaphones.preload.js';
|
||||
import { shouldNeverBeCalled } from '../../util/shouldNeverBeCalled.std.js';
|
||||
|
||||
export type SmartPropsType = Readonly<{
|
||||
disableMegaphone?: boolean;
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
expandNarrowLeftPane: () => void;
|
||||
}>;
|
||||
|
||||
function handleShowDebugLog() {
|
||||
window.IPC.showDebugLog();
|
||||
}
|
||||
|
||||
export function renderToastManagerWithoutMegaphone(props: {
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<SmartToastManager
|
||||
disableMegaphone
|
||||
expandNarrowLeftPane={shouldNeverBeCalled}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const SmartToastManager = memo(function SmartToastManager({
|
||||
disableMegaphone = false,
|
||||
containerWidthBreakpoint,
|
||||
expandNarrowLeftPane,
|
||||
}: SmartPropsType) {
|
||||
const i18n = useSelector(getIntl);
|
||||
const hasCompletedUsernameOnboarding = useSelector(
|
||||
@@ -119,6 +134,7 @@ export const SmartToastManager = memo(function SmartToastManager({
|
||||
setDidResumeDonation={setDidResume}
|
||||
centerToast={centerToast}
|
||||
containerWidthBreakpoint={containerWidthBreakpoint}
|
||||
expandNarrowLeftPane={expandNarrowLeftPane}
|
||||
isCompositionAreaVisible={isCompositionAreaVisible}
|
||||
isInFullScreenCall={isInFullScreenCall}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user