Release Note Channel: Mute/Unmute UI, hide UI elements

Co-authored-by: yash-signal <yash@signal.org>
This commit is contained in:
automated-signal 2025-02-07 09:47:11 -06:00 committed by GitHub
parent 141cf2d482
commit d93f488cbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 305 additions and 150 deletions

View file

@ -29,6 +29,8 @@
padding-bottom: 8px; padding-bottom: 8px;
padding-top: 12px; padding-top: 12px;
user-select: text; user-select: text;
display: flex;
align-items: center;
} }
&__title-contact-icon { &__title-contact-icon {

View file

@ -0,0 +1,30 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
@use '../mixins';
@use '../variables';
.SignalConversationMuteToggle {
@include mixins.light-theme() {
background-color: variables.$color-white;
border-top: 0.5px solid variables.$color-black-alpha-16;
}
@include mixins.dark-theme() {
background-color: variables.$color-gray-95;
border-top: 0.5px solid variables.$color-gray-65;
}
height: variables.$header-height;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: variables.$color-ultramarine-light;
&__text {
@include mixins.button-reset;
}
}

View file

@ -159,6 +159,7 @@
@use 'components/SendStoryModal.scss'; @use 'components/SendStoryModal.scss';
@use 'components/ShortcutGuide.scss'; @use 'components/ShortcutGuide.scss';
@use 'components/SignalConnectionsModal.scss'; @use 'components/SignalConnectionsModal.scss';
@use 'components/SignalConversationMuteToggle.scss';
@use 'components/Slider.scss'; @use 'components/Slider.scss';
@use 'components/SpinnerV2.scss'; @use 'components/SpinnerV2.scss';
@use 'components/StagedLinkPreview.scss'; @use 'components/StagedLinkPreview.scss';

View file

@ -1,7 +1,7 @@
// Copyright 2020 Signal Messenger, LLC // Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React, { useContext } from 'react'; import React, { useContext, useState } from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react'; import type { Meta } from '@storybook/react';
import { IMAGE_JPEG } from '../types/MIME'; import { IMAGE_JPEG } from '../types/MIME';
@ -125,6 +125,10 @@ export default {
selectedMessageIds: undefined, selectedMessageIds: undefined,
toggleSelectMode: action('toggleSelectMode'), toggleSelectMode: action('toggleSelectMode'),
toggleForwardMessagesModal: action('toggleForwardMessagesModal'), toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
// Signal Conversation
isSignalConversation: false,
isMuted: false,
setMuteExpiration: action('setMuteExpiration'),
}, },
} satisfies Meta<Props>; } satisfies Meta<Props>;
@ -263,3 +267,21 @@ export function NoFormattingMenu(args: Props): JSX.Element {
<CompositionArea {...args} theme={theme} isFormattingEnabled={false} /> <CompositionArea {...args} theme={theme} isFormattingEnabled={false} />
); );
} }
export function SignalConversationMuteToggle(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
const [isMuted, setIsMuted] = useState(true);
function setIsMutedByTime(_: string, muteExpiresAt: number) {
setIsMuted(muteExpiresAt > Date.now());
}
return (
<CompositionArea
{...args}
theme={theme}
isSignalConversation
isMuted={isMuted}
setMuteExpiration={setIsMutedByTime}
/>
);
}

View file

@ -76,6 +76,7 @@ import type { ShowToastAction } from '../state/ducks/toast';
import type { DraftEditMessageType } from '../model-types.d'; import type { DraftEditMessageType } from '../model-types.d';
import type { ForwardMessagesPayload } from '../state/ducks/globalModals'; import type { ForwardMessagesPayload } from '../state/ducks/globalModals';
import { ForwardMessagesModalType } from './ForwardMessagesModal'; import { ForwardMessagesModalType } from './ForwardMessagesModal';
import { SignalConversationMuteToggle } from './conversation/SignalConversationMuteToggle';
export type OwnProps = Readonly<{ export type OwnProps = Readonly<{
acceptedMessageRequest: boolean | null; acceptedMessageRequest: boolean | null;
@ -118,6 +119,7 @@ export type OwnProps = Readonly<{
recordingState: RecordingState; recordingState: RecordingState;
messageCompositionId: string; messageCompositionId: string;
shouldHidePopovers: boolean | null; shouldHidePopovers: boolean | null;
isMuted: boolean;
isSmsOnlyOrUnregistered: boolean | null; isSmsOnlyOrUnregistered: boolean | null;
left: boolean | null; left: boolean | null;
linkPreviewLoading: boolean; linkPreviewLoading: boolean;
@ -130,6 +132,7 @@ export type OwnProps = Readonly<{
conversationId: string; conversationId: string;
files: ReadonlyArray<File>; files: ReadonlyArray<File>;
}) => unknown; }) => unknown;
setMuteExpiration(conversationId: string, muteExpiresAt: number): unknown;
setMediaQualitySetting(conversationId: string, isHQ: boolean): unknown; setMediaQualitySetting(conversationId: string, isHQ: boolean): unknown;
sendStickerMessage( sendStickerMessage(
id: string, id: string,
@ -239,6 +242,7 @@ export const CompositionArea = memo(function CompositionArea({
imageToBlurHash, imageToBlurHash,
isDisabled, isDisabled,
isSignalConversation, isSignalConversation,
isMuted,
isActive, isActive,
lastEditableMessageId, lastEditableMessageId,
messageCompositionId, messageCompositionId,
@ -254,6 +258,7 @@ export const CompositionArea = memo(function CompositionArea({
shouldHidePopovers, shouldHidePopovers,
showToast, showToast,
theme, theme,
setMuteExpiration,
// AttachmentList // AttachmentList
draftAttachments, draftAttachments,
@ -737,8 +742,14 @@ export const CompositionArea = memo(function CompositionArea({
useEscapeHandling(handleEscape); useEscapeHandling(handleEscape);
if (isSignalConversation) { if (isSignalConversation) {
// TODO DESKTOP-4547 return (
return <div />; <SignalConversationMuteToggle
conversationId={conversationId}
isMuted={isMuted}
i18n={i18n}
setMuteExpiration={setMuteExpiration}
/>
);
} }
if (selectedMessageIds != null) { if (selectedMessageIds != null) {

View file

@ -47,6 +47,7 @@ function HeaderInfoTitle({
type, type,
i18n, i18n,
isMe, isMe,
isSignalConversation,
headerRef, headerRef,
}: { }: {
name: string | null; name: string | null;
@ -54,8 +55,18 @@ function HeaderInfoTitle({
type: ConversationTypeType; type: ConversationTypeType;
i18n: LocalizerType; i18n: LocalizerType;
isMe: boolean; isMe: boolean;
isSignalConversation: boolean;
headerRef: React.RefObject<HTMLDivElement>; headerRef: React.RefObject<HTMLDivElement>;
}) { }) {
if (isSignalConversation) {
return (
<div className="module-ConversationHeader__header__info__title">
<UserText text={title} />
<span className="ContactModal__official-badge" />
</div>
);
}
if (isMe) { if (isMe) {
return ( return (
<div className="module-ConversationHeader__header__info__title"> <div className="module-ConversationHeader__header__info__title">
@ -294,6 +305,7 @@ export const ConversationHeader = memo(function ConversationHeader({
theme={theme} theme={theme}
onViewUserStories={onViewUserStories} onViewUserStories={onViewUserStories}
onViewConversationDetails={onViewConversationDetails} onViewConversationDetails={onViewConversationDetails}
isSignalConversation={isSignalConversation ?? false}
/> />
{!isSmsOnlyOrUnregistered && !isSignalConversation && ( {!isSmsOnlyOrUnregistered && !isSignalConversation && (
<OutgoingCallButtons <OutgoingCallButtons
@ -415,6 +427,7 @@ function HeaderContent({
i18n, i18n,
sharedGroupNames, sharedGroupNames,
theme, theme,
isSignalConversation,
onViewUserStories, onViewUserStories,
onViewConversationDetails, onViewConversationDetails,
}: { }: {
@ -425,6 +438,7 @@ function HeaderContent({
i18n: LocalizerType; i18n: LocalizerType;
sharedGroupNames: ReadonlyArray<string>; sharedGroupNames: ReadonlyArray<string>;
theme: ThemeType; theme: ThemeType;
isSignalConversation: boolean;
onViewUserStories: () => void; onViewUserStories: () => void;
onViewConversationDetails: () => void; onViewConversationDetails: () => void;
}) { }) {
@ -476,6 +490,7 @@ function HeaderContent({
type={conversation.type} type={conversation.type}
i18n={i18n} i18n={i18n}
isMe={conversation.isMe} isMe={conversation.isMe}
isSignalConversation={isSignalConversation}
headerRef={headerRef} headerRef={headerRef}
/> />
{(conversation.expireTimer != null || conversation.isVerified) && ( {(conversation.expireTimer != null || conversation.isVerified) && (

View file

@ -0,0 +1,34 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { LocalizerType } from '../../types/I18N';
type Props = {
isMuted: boolean;
i18n: LocalizerType;
setMuteExpiration: (conversationId: string, muteExpiresAt: number) => unknown;
conversationId: string;
};
export function SignalConversationMuteToggle({
isMuted,
i18n,
setMuteExpiration,
conversationId,
}: Props): JSX.Element {
const onMuteToggleClicked = () => {
setMuteExpiration(conversationId, isMuted ? 0 : Number.MAX_SAFE_INTEGER);
};
return (
<div className="SignalConversationMuteToggle">
<button
onClick={onMuteToggleClicked}
type="button"
className="SignalConversationMuteToggle__text"
>
{isMuted ? i18n('icu:unmute') : i18n('icu:mute')}
</button>
</div>
);
}

View file

@ -65,6 +65,7 @@ const createProps = (
i18n, i18n,
isAdmin: false, isAdmin: false,
isGroup: true, isGroup: true,
isSignalConversation: false,
leaveGroup: action('leaveGroup'), leaveGroup: action('leaveGroup'),
loadRecentMediaItems: action('loadRecentMediaItems'), loadRecentMediaItems: action('loadRecentMediaItems'),
memberships: times(32, i => ({ memberships: times(32, i => ({
@ -244,3 +245,11 @@ export function InAnotherCallIndividual(): JSX.Element {
return <ConversationDetails {...props} hasActiveCall isGroup={false} />; return <ConversationDetails {...props} hasActiveCall isGroup={false} />;
} }
export function SignalConversation(): JSX.Element {
const props = createProps();
return (
<ConversationDetails {...props} isSignalConversation isGroup={false} />
);
}

View file

@ -84,6 +84,7 @@ export type StateProps = {
i18n: LocalizerType; i18n: LocalizerType;
isAdmin: boolean; isAdmin: boolean;
isGroup: boolean; isGroup: boolean;
isSignalConversation: boolean;
groupsInCommon: ReadonlyArray<ConversationType>; groupsInCommon: ReadonlyArray<ConversationType>;
maxGroupSize: number; maxGroupSize: number;
maxRecommendedGroupSize: number; maxRecommendedGroupSize: number;
@ -181,6 +182,7 @@ export function ConversationDetails({
i18n, i18n,
isAdmin, isAdmin,
isGroup, isGroup,
isSignalConversation,
leaveGroup, leaveGroup,
loadRecentMediaItems, loadRecentMediaItems,
memberships, memberships,
@ -397,6 +399,7 @@ export function ConversationDetails({
i18n={i18n} i18n={i18n}
isMe={conversation.isMe} isMe={conversation.isMe}
isGroup={isGroup} isGroup={isGroup}
isSignalConversation={isSignalConversation}
membersCount={conversation.membersCount ?? null} membersCount={conversation.membersCount ?? null}
startEditing={(isGroupTitle: boolean) => { startEditing={(isGroupTitle: boolean) => {
setModalState( setModalState(
@ -424,7 +427,7 @@ export function ConversationDetails({
{i18n('icu:ConversationDetails__HeaderButton--Message')} {i18n('icu:ConversationDetails__HeaderButton--Message')}
</Button> </Button>
)} )}
{!conversation.isMe && ( {!conversation.isMe && !isSignalConversation && (
<> <>
<ConversationDetailsCallButton <ConversationDetailsCallButton
hasActiveCall={hasActiveCall} hasActiveCall={hasActiveCall}
@ -477,152 +480,157 @@ export function ConversationDetails({
/> />
)} )}
<PanelSection> {!isSignalConversation && (
{!isGroup || canEditGroupInfo ? ( <PanelSection>
<PanelRow {!isGroup || canEditGroupInfo ? (
icon={ <PanelRow
<ConversationDetailsIcon icon={
ariaLabel={i18n( <ConversationDetailsIcon
'icu:ConversationDetails--disappearing-messages-label' ariaLabel={i18n(
)} 'icu:ConversationDetails--disappearing-messages-label'
icon={IconType.timer} )}
/> icon={IconType.timer}
} />
info={ }
isGroup info={
? i18n( isGroup
'icu:ConversationDetails--disappearing-messages-info--group' ? i18n(
) 'icu:ConversationDetails--disappearing-messages-info--group'
: i18n( )
'icu:ConversationDetails--disappearing-messages-info--direct' : i18n(
) 'icu:ConversationDetails--disappearing-messages-info--direct'
} )
label={i18n('icu:ConversationDetails--disappearing-messages-label')} }
right={ label={i18n(
<DisappearingTimerSelect 'icu:ConversationDetails--disappearing-messages-label'
i18n={i18n} )}
value={conversation.expireTimer || DurationInSeconds.ZERO} right={
onChange={value => <DisappearingTimerSelect
setDisappearingMessages(conversation.id, value)
}
/>
}
/>
) : null}
{canHaveNicknameAndNote(conversation) && (
<PanelRow
icon={
<ConversationDetailsIcon
ariaLabel={i18n('icu:ConversationDetails--nickname-label')}
icon={IconType.edit}
/>
}
label={i18n('icu:ConversationDetails--nickname-label')}
onClick={onOpenEditNicknameAndNoteModal}
actions={
(conversation.nicknameGivenName ||
conversation.nicknameFamilyName ||
conversation.note) && (
<ContextMenu
i18n={i18n} i18n={i18n}
portalToRoot value={conversation.expireTimer || DurationInSeconds.ZERO}
popperOptions={{ onChange={value =>
placement: 'bottom', setDisappearingMessages(conversation.id, value)
strategy: 'absolute', }
}} />
menuOptions={[ }
{ />
icon: 'ConversationDetails--nickname-actions--delete', ) : null}
label: i18n( {canHaveNicknameAndNote(conversation) && (
'icu:ConversationDetails--nickname-actions--delete' <PanelRow
), icon={
onClick: () => { <ConversationDetailsIcon
setModalState(ModalState.ConfirmDeleteNicknameAndNote); ariaLabel={i18n('icu:ConversationDetails--nickname-label')}
icon={IconType.edit}
/>
}
label={i18n('icu:ConversationDetails--nickname-label')}
onClick={onOpenEditNicknameAndNoteModal}
actions={
(conversation.nicknameGivenName ||
conversation.nicknameFamilyName ||
conversation.note) && (
<ContextMenu
i18n={i18n}
portalToRoot
popperOptions={{
placement: 'bottom',
strategy: 'absolute',
}}
menuOptions={[
{
icon: 'ConversationDetails--nickname-actions--delete',
label: i18n(
'icu:ConversationDetails--nickname-actions--delete'
),
onClick: () => {
setModalState(
ModalState.ConfirmDeleteNicknameAndNote
);
},
}, },
}, ]}
]} >
> {({ onClick }) => {
{({ onClick }) => { return (
return ( <button
<button type="button"
type="button" className="ConversationDetails--nickname-actions"
className="ConversationDetails--nickname-actions" onClick={onClick}
onClick={onClick} >
> <span className="ConversationDetails--nickname-actions-label">
<span className="ConversationDetails--nickname-actions-label"> {i18n('icu:ConversationDetails--nickname-actions')}
{i18n('icu:ConversationDetails--nickname-actions')} </span>
</span> </button>
</button> );
); }}
</ContextMenu>
)
}
/>
)}
{selectedNavTab === NavTab.Chats && (
<PanelRow
icon={
<ConversationDetailsIcon
ariaLabel={i18n('icu:showChatColorEditor')}
icon={IconType.color}
/>
}
label={i18n('icu:showChatColorEditor')}
onClick={() => {
pushPanelForConversation({
type: PanelType.ChatColorEditor,
});
}}
right={
<div
className={`ConversationDetails__chat-color ConversationDetails__chat-color--${conversation.conversationColor}`}
style={{
...getCustomColorStyle(conversation.customColor),
}} }}
</ContextMenu> />
) }
} />
/> )}
)} {isGroup && (
{selectedNavTab === NavTab.Chats && ( <PanelRow
<PanelRow icon={
icon={ <ConversationDetailsIcon
<ConversationDetailsIcon ariaLabel={i18n('icu:ConversationDetails--notifications')}
ariaLabel={i18n('icu:showChatColorEditor')} icon={IconType.notifications}
icon={IconType.color} />
/> }
} label={i18n('icu:ConversationDetails--notifications')}
label={i18n('icu:showChatColorEditor')} onClick={() =>
onClick={() => { pushPanelForConversation({
pushPanelForConversation({ type: PanelType.NotificationSettings,
type: PanelType.ChatColorEditor, })
}); }
}} right={
right={ conversation.muteExpiresAt
<div ? getMutedUntilText(conversation.muteExpiresAt, i18n)
className={`ConversationDetails__chat-color ConversationDetails__chat-color--${conversation.conversationColor}`} : undefined
style={{ }
...getCustomColorStyle(conversation.customColor), />
}} )}
/> {!isGroup && !conversation.isMe && (
} <PanelRow
/> onClick={() => toggleSafetyNumberModal(conversation.id)}
)} icon={
{isGroup && ( <ConversationDetailsIcon
<PanelRow ariaLabel={i18n('icu:ConversationDetails__viewSafetyNumber')}
icon={ icon={IconType.verify}
<ConversationDetailsIcon />
ariaLabel={i18n('icu:ConversationDetails--notifications')} }
icon={IconType.notifications} label={
/> <div className="ConversationDetails__safety-number">
} {i18n('icu:ConversationDetails__viewSafetyNumber')}
label={i18n('icu:ConversationDetails--notifications')} </div>
onClick={() => }
pushPanelForConversation({ />
type: PanelType.NotificationSettings, )}
}) </PanelSection>
} )}
right={
conversation.muteExpiresAt
? getMutedUntilText(conversation.muteExpiresAt, i18n)
: undefined
}
/>
)}
{!isGroup && !conversation.isMe && (
<PanelRow
onClick={() => toggleSafetyNumberModal(conversation.id)}
icon={
<ConversationDetailsIcon
ariaLabel={i18n('icu:ConversationDetails__viewSafetyNumber')}
icon={IconType.verify}
/>
}
label={
<div className="ConversationDetails__safety-number">
{i18n('icu:ConversationDetails__viewSafetyNumber')}
</div>
}
/>
)}
</PanelSection>
{isGroup && ( {isGroup && (
<ConversationDetailsMembershipList <ConversationDetailsMembershipList
canAddNewMembers={canAddNewMembers} canAddNewMembers={canAddNewMembers}
@ -705,7 +713,7 @@ export function ConversationDetails({
showLightbox={showLightbox} showLightbox={showLightbox}
/> />
{!isGroup && !conversation.isMe && ( {!isGroup && !conversation.isMe && !isSignalConversation && (
<ConversationDetailsGroups <ConversationDetailsGroups
contactId={conversation.id} contactId={conversation.id}
i18n={i18n} i18n={i18n}

View file

@ -44,6 +44,7 @@ function Wrapper(overrideProps: Partial<Props>) {
membersCount={0} membersCount={0}
isGroup isGroup
isMe={false} isMe={false}
isSignalConversation={false}
theme={theme} theme={theme}
toggleAboutContactModal={action('toggleAboutContactModal')} toggleAboutContactModal={action('toggleAboutContactModal')}
{...overrideProps} {...overrideProps}

View file

@ -25,6 +25,7 @@ export type Props = {
i18n: LocalizerType; i18n: LocalizerType;
isGroup: boolean; isGroup: boolean;
isMe: boolean; isMe: boolean;
isSignalConversation: boolean;
membersCount: number | null; membersCount: number | null;
startEditing: (isGroupTitle: boolean) => void; startEditing: (isGroupTitle: boolean) => void;
toggleAboutContactModal: (contactId: string) => void; toggleAboutContactModal: (contactId: string) => void;
@ -44,6 +45,7 @@ export function ConversationDetailsHeader({
i18n, i18n,
isGroup, isGroup,
isMe, isMe,
isSignalConversation,
membersCount, membersCount,
startEditing, startEditing,
toggleAboutContactModal, toggleAboutContactModal,
@ -194,6 +196,13 @@ export function ConversationDetailsHeader({
<span className="ContactModal__official-badge__large" /> <span className="ContactModal__official-badge__large" />
</div> </div>
); );
} else if (isSignalConversation) {
title = (
<div className="ConversationDetailsHeader__title">
<UserText text={conversation.title} />
<span className="ContactModal__official-badge__large" />
</div>
);
} else if (isGroup) { } else if (isGroup) {
title = ( title = (
<div className="ConversationDetailsHeader__title"> <div className="ConversationDetailsHeader__title">

View file

@ -396,6 +396,7 @@ export class ReleaseNotesFetcher {
await this.#scheduleForNextRun(); await this.#scheduleForNextRun();
this.setTimeoutForNextRun(); this.setTimeoutForNextRun();
window.SignalCI?.handleEvent('release_notes_fetcher_complete', {});
} catch (error) { } catch (error) {
const errorString = const errorString =
error instanceof HTTPError error instanceof HTTPError

View file

@ -67,6 +67,7 @@ import { useToastActions } from '../ducks/toast';
import { isShowingAnyModal } from '../selectors/globalModals'; import { isShowingAnyModal } from '../selectors/globalModals';
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered'; import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
import { isDirectConversation } from '../../util/whatTypeOfConversation'; import { isDirectConversation } from '../../util/whatTypeOfConversation';
import { isConversationMuted } from '../../util/isConversationMuted';
function renderSmartCompositionRecording( function renderSmartCompositionRecording(
recProps: SmartCompositionRecordingProps recProps: SmartCompositionRecordingProps
@ -232,6 +233,7 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
toggleSelectMode, toggleSelectMode,
scrollToMessage, scrollToMessage,
setMessageToEdit, setMessageToEdit,
setMuteExpiration,
showConversation, showConversation,
} = useConversationsActions(); } = useConversationsActions();
const { cancelRecording, completeRecording, startRecording, errorRecording } = const { cancelRecording, completeRecording, startRecording, errorRecording } =
@ -325,7 +327,6 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
(isConversationSMSOnly(conversation) || (isConversationSMSOnly(conversation) ||
isConversationEverUnregistered(conversation)) isConversationEverUnregistered(conversation))
} }
isSignalConversation={isSignalConversation(conversation)}
isFetchingUUID={conversation.isFetchingUUID ?? null} isFetchingUUID={conversation.isFetchingUUID ?? null}
isMissingMandatoryProfileSharing={isMissingRequiredProfileSharing( isMissingMandatoryProfileSharing={isMissingRequiredProfileSharing(
conversation conversation
@ -335,6 +336,10 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
blockConversation={blockConversation} blockConversation={blockConversation}
reportSpam={reportSpam} reportSpam={reportSpam}
deleteConversation={deleteConversation} deleteConversation={deleteConversation}
// Signal Conversation
isSignalConversation={isSignalConversation(conversation)}
isMuted={isConversationMuted(conversation)}
setMuteExpiration={setMuteExpiration}
// Groups // Groups
groupVersion={conversation.groupVersion ?? null} groupVersion={conversation.groupVersion ?? null}
isGroupV1AndDisabled={conversation.isGroupV1AndDisabled ?? null} isGroupV1AndDisabled={conversation.isGroupV1AndDisabled ?? null}

View file

@ -40,6 +40,7 @@ import { useCallingActions } from '../ducks/calling';
import { useSearchActions } from '../ducks/search'; import { useSearchActions } from '../ducks/search';
import { useGlobalModalActions } from '../ducks/globalModals'; import { useGlobalModalActions } from '../ducks/globalModals';
import { useLightboxActions } from '../ducks/lightbox'; import { useLightboxActions } from '../ducks/lightbox';
import { isSignalConversation } from '../../util/isSignalConversation';
export type SmartConversationDetailsProps = { export type SmartConversationDetailsProps = {
conversationId: string; conversationId: string;
@ -193,6 +194,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
i18n={i18n} i18n={i18n}
isAdmin={isAdmin} isAdmin={isAdmin}
isGroup={isGroup} isGroup={isGroup}
isSignalConversation={isSignalConversation(conversation)}
leaveGroup={leaveGroup} leaveGroup={leaveGroup}
loadRecentMediaItems={loadRecentMediaItems} loadRecentMediaItems={loadRecentMediaItems}
maxGroupSize={maxGroupSize} maxGroupSize={maxGroupSize}

View file

@ -129,6 +129,10 @@ export class App extends EventEmitter {
return this.#waitForEvent('receipts'); return this.#waitForEvent('receipts');
} }
public async waitForReleaseNotesFetcher(): Promise<void> {
return this.#waitForEvent('release_notes_fetcher_complete');
}
public async waitForStorageService(): Promise<StorageServiceInfoType> { public async waitForStorageService(): Promise<StorageServiceInfoType> {
return this.#waitForEvent('storageServiceComplete'); return this.#waitForEvent('storageServiceComplete');
} }

View file

@ -45,6 +45,7 @@ describe('release notes', function (this: Mocha.Suite) {
it('shows release notes with an image and body ranges', async () => { it('shows release notes with an image and body ranges', async () => {
const firstWindow = await app.getWindow(); const firstWindow = await app.getWindow();
await app.waitForReleaseNotesFetcher();
await firstWindow.evaluate('window.SignalCI.resetReleaseNotesFetcher()'); await firstWindow.evaluate('window.SignalCI.resetReleaseNotesFetcher()');
await app.close(); await app.close();

View file

@ -11,7 +11,7 @@ export function isSignalConversation(conversation: {
const { id, serviceId } = conversation; const { id, serviceId } = conversation;
if (serviceId) { if (serviceId) {
return serviceId === SIGNAL_ACI; return isSignalServiceId(serviceId);
} }
return window.ConversationController.isSignalConversationId(id); return window.ConversationController.isSignalConversationId(id);