Updates to release note details pane

This commit is contained in:
trevor-signal
2026-04-29 13:41:05 -04:00
committed by GitHub
parent 3ab39b61b7
commit 7745835143
15 changed files with 70 additions and 129 deletions
+3
View File
@@ -98,6 +98,9 @@
&__main {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-block: 24px;
padding-inline: 10px;
@@ -1,55 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
@use '../mixins';
.BadgeSustainerInstructionsDialog {
user-select: none;
&__width-container {
max-width: 420px;
}
&__header {
@include mixins.font-title-2;
text-align: center;
}
&__subheader {
@include mixins.font-body-1;
font-weight: normal;
text-align: center;
}
&__instructions {
@include mixins.font-body-2;
padding: 0;
list-style-type: decimal;
list-style-position: inside;
&::before {
background-size: contain;
content: '';
display: block;
height: 160px;
margin-block: 24px;
margin-inline: auto;
width: 146px;
@include mixins.light-theme {
background-image: url('../images/mobile-settings-light.svg');
}
@include mixins.dark-theme {
background-image: url('../images/mobile-settings-dark.svg');
}
}
> li {
margin-top: 1em;
&:first-child {
margin-top: 0;
}
}
}
}
-1
View File
@@ -39,7 +39,6 @@ $is-storybook: false !default;
@use 'components/BackupMediaDownloadProgressSettings.scss';
@use 'components/BadgeCarouselIndex.scss';
@use 'components/BadgeDialog.scss';
@use 'components/BadgeSustainerInstructionsDialog.scss';
@use 'components/BetterAvatarBubble.scss';
@use 'components/Button.scss';
@use 'components/CallsTab.scss';
+1 -1
View File
@@ -134,7 +134,7 @@
--color-legacy-signal-conversation-bg: light-dark(#F6F7FF, #2F3240);
--color-legacy-official-chat-badge-bg: light-dark(--alpha(#4655FF / 12%), --alpha(#4952F8 / 40%));
--color-legacy-official-chat-badge-text: light-dark(#030FFC, #C2C5FE);
--color-legacy-signal-chat-message-bg: light-dark(#9294BC, #444664);
--color-legacy-signal-chat-message-bg: light-dark(#8889B4, #444664);
--color-legacy-warning-badge: light-dark(#C84118, #EB977D);
}
@@ -27,6 +27,7 @@ const defaultProps: ComponentProps<typeof BadgeDialog> = {
firstName: 'Alice',
i18n,
onClose: action('onClose'),
onDonate: action('onDonate'),
title: 'Alice Levine',
};
+9 -25
View File
@@ -13,44 +13,28 @@ import { Button, ButtonSize } from './Button.dom.tsx';
import { BadgeDescription } from './BadgeDescription.dom.tsx';
import { BadgeImage } from './BadgeImage.dom.tsx';
import { BadgeCarouselIndex } from './BadgeCarouselIndex.dom.tsx';
import { BadgeSustainerInstructionsDialog } from './BadgeSustainerInstructionsDialog.dom.tsx';
export type PropsType = Readonly<{
areWeASubscriber: boolean;
badges: ReadonlyArray<BadgeType>;
firstName?: string;
i18n: LocalizerType;
onClose: () => unknown;
onClose: () => void;
onDonate: () => void;
title: string;
}>;
export function BadgeDialog(props: PropsType): null | React.JSX.Element {
const { badges, i18n, onClose } = props;
const [isShowingInstructions, setIsShowingInstructions] = useState(false);
const { badges, onClose } = props;
const hasBadges = badges.length > 0;
useEffect(() => {
if (!hasBadges && !isShowingInstructions) {
if (!hasBadges) {
onClose();
}
}, [hasBadges, isShowingInstructions, onClose]);
}, [hasBadges, onClose]);
if (isShowingInstructions) {
return (
<BadgeSustainerInstructionsDialog
i18n={i18n}
onClose={() => setIsShowingInstructions(false)}
/>
);
}
return hasBadges ? (
<BadgeDialogWithBadges
{...props}
onShowInstructions={() => setIsShowingInstructions(true)}
/>
) : null;
return hasBadges ? <BadgeDialogWithBadges {...props} /> : null;
}
function BadgeDialogWithBadges({
@@ -59,9 +43,9 @@ function BadgeDialogWithBadges({
firstName,
i18n,
onClose,
onShowInstructions,
onDonate,
title,
}: PropsType & { onShowInstructions: () => unknown }): React.JSX.Element {
}: PropsType): React.JSX.Element {
const firstBadge = badges[0];
strictAssert(
firstBadge,
@@ -125,7 +109,7 @@ function BadgeDialogWithBadges({
currentBadge.category !== BadgeCategory.Donor &&
'BadgeDialog__instructions-button--hidden'
)}
onClick={onShowInstructions}
onClick={onDonate}
size={ButtonSize.Large}
>
{i18n('icu:BadgeDialog__become-a-sustainer-button')}
@@ -1,34 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactElement } from 'react';
import React from 'react';
import type { LocalizerType } from '../types/Util.std.ts';
import { Modal } from './Modal.dom.tsx';
export function BadgeSustainerInstructionsDialog({
i18n,
onClose,
}: Readonly<{ i18n: LocalizerType; onClose: () => unknown }>): ReactElement {
return (
<Modal
modalName="BadgeSustainerInstructionsDialog"
hasXButton
moduleClassName="BadgeSustainerInstructionsDialog"
i18n={i18n}
onClose={onClose}
>
<h1 className="BadgeSustainerInstructionsDialog__header">
{i18n('icu:BadgeSustainerInstructions__header')}
</h1>
<h2 className="BadgeSustainerInstructionsDialog__subheader">
{i18n('icu:BadgeSustainerInstructions__subheader')}
</h2>
<ol className="BadgeSustainerInstructionsDialog__instructions">
<li>{i18n('icu:BadgeSustainerInstructions__instructions__1')}</li>
<li>{i18n('icu:BadgeSustainerInstructions__instructions__2')}</li>
<li>{i18n('icu:BadgeSustainerInstructions__instructions__3')}</li>
</ol>
</Modal>
);
}
@@ -71,6 +71,7 @@ type PropsActionType = {
blockClientFromCall: (payload: RemoveClientType) => void;
blockConversation: (id: string) => void;
hideContactModal: () => void;
onNavigateToDonate: () => void;
onOpenEditNicknameAndNoteModal: () => void;
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
@@ -127,6 +128,7 @@ export function ContactModal({
isMuted,
isRemoteMuteVisible,
isRemoveFromCallVisible,
onNavigateToDonate,
onOpenEditNicknameAndNoteModal,
onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation,
@@ -682,6 +684,7 @@ export function ContactModal({
firstName={contact.firstName}
i18n={i18n}
onClose={() => setView(ContactModalView.Default)}
onDonate={onNavigateToDonate}
title={contact.title}
/>
);
@@ -9,10 +9,12 @@ export function OfficialChatInlineBadge(): JSX.Element {
return (
<span
className={tw(
'relative z-0 text-color-fill-primary',
// oxlint-disable-next-line better-tailwindcss/no-unknown-classes
'before:content[""] before:absolute before:inset-s-1/6 before:top-1/6 before:bg-label-primary-on-color',
'before:legacy-z-index-negative before:size-2/3 before:rounded-full'
'relative z-0 inline-block rounded-full text-color-fill-primary',
// Since the icon does not have a white checkmark built into the font, by making it a
// background of the parent element we can ensure that the background is clipped
// whenever the icon is (e.g. for overflow: ellipsis)
'bg-linear-0 from-label-primary-on-color to-label-primary-on-color',
'bg-size-[50%_50%] bg-center bg-no-repeat'
)}
>
<AxoSymbol.InlineGlyph symbol="officialbadge-fill" label={null} />
@@ -105,6 +105,7 @@ const createProps = (
memberColors,
maxGroupSize: 1001,
maxRecommendedGroupSize: 151,
onNavigateToDonate: action('onNavigateToDonate'),
pendingApprovalMemberships: times(8, () => ({
member: getDefaultConversation(),
})),
@@ -64,7 +64,6 @@ import {
InAnotherCallTooltip,
getTooltipContent,
} from '../InAnotherCallTooltip.dom.tsx';
import { BadgeSustainerInstructionsDialog } from '../../BadgeSustainerInstructionsDialog.dom.tsx';
import type { ContactModalStateType } from '../../../types/globalModals.std.ts';
import type { ShowToastAction } from '../../../state/ducks/toast.preload.ts';
import { ToastType } from '../../../types/Toast.dom.tsx';
@@ -72,7 +71,6 @@ import type { ContactNameColorType } from '../../../types/Colors.std.ts';
enum ModalState {
AddingGroupMembers,
BecomeSustainer,
ConfirmDeleteNicknameAndNote,
EditingGroupDescription,
EditingGroupTitle,
@@ -138,6 +136,7 @@ type ActionProps = {
onConversationDeleteMessages: () => void;
onConversationUnarchive: () => void;
onDeleteNicknameAndNote: () => void;
onNavigateToDonate: () => void;
onOpenEditNicknameAndNoteModal: () => void;
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
@@ -206,6 +205,7 @@ export function ConversationDetails({
onConversationDeleteMessages,
onConversationUnarchive,
onDeleteNicknameAndNote,
onNavigateToDonate,
onOpenEditNicknameAndNoteModal,
onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation,
@@ -270,11 +270,6 @@ export function ConversationDetails({
case ModalState.NothingOpen:
modalNode = undefined;
break;
case ModalState.BecomeSustainer:
modalNode = (
<BadgeSustainerInstructionsDialog i18n={i18n} onClose={onCloseModal} />
);
break;
case ModalState.EditingGroupDescription:
case ModalState.EditingGroupTitle:
modalNode = (
@@ -431,6 +426,7 @@ export function ConversationDetails({
isGroup={isGroup}
isSignalConversation={isSignalConversation}
membersCount={conversation.membersCount ?? null}
onNavigateToDonate={onNavigateToDonate}
pendingAvatarDownload={pendingAvatarDownload ?? false}
startAvatarDownload={startAvatarDownload}
startEditing={(isGroupTitle: boolean) => {
@@ -486,6 +482,15 @@ export function ConversationDetails({
<Button
icon={isMuted ? ButtonIconType.muted : ButtonIconType.unmuted}
onClick={() => {
if (isSignalConversation) {
if (isMuted) {
setMuteExpiration(conversation.id, 0);
} else {
setMuteExpiration(conversation.id, Number.MAX_SAFE_INTEGER);
}
return;
}
if (isMuted) {
setModalState(ModalState.UnmuteNotifications);
} else {
@@ -567,7 +572,7 @@ export function ConversationDetails({
/>
}
label={i18n('icu:BadgeDialog__become-a-sustainer-button')}
onClick={() => setModalState(ModalState.BecomeSustainer)}
onClick={onNavigateToDonate}
/>
</PanelSection>
</>
@@ -43,6 +43,7 @@ function Wrapper(overrideProps: Partial<Props>) {
isGroup
isMe={false}
isSignalConversation={false}
onNavigateToDonate={action('onNavigateToDonate')}
pendingAvatarDownload={false}
startAvatarDownload={action('startAvatarDownload')}
theme={theme}
@@ -31,6 +31,7 @@ export type Props = {
isMe: boolean;
isSignalConversation: boolean;
membersCount: number | null;
onNavigateToDonate: () => void;
pendingAvatarDownload: boolean;
startAvatarDownload: () => void;
startEditing: (isGroupTitle: boolean) => void;
@@ -53,6 +54,7 @@ export function ConversationDetailsHeader({
isMe,
isSignalConversation,
membersCount,
onNavigateToDonate,
pendingAvatarDownload,
startAvatarDownload,
startEditing,
@@ -155,6 +157,10 @@ export function ConversationDetailsHeader({
onClose={() => {
setActiveModal(undefined);
}}
onDonate={() => {
setActiveModal(undefined);
onNavigateToDonate();
}}
title={conversation.title}
/>
);
+14
View File
@@ -27,6 +27,8 @@ import { strictAssert } from '../../util/assert.std.ts';
import { CallMode } from '../../types/CallDisposition.std.ts';
import { isCallLinkAdmin } from '../../types/CallLink.std.ts';
import { isFeaturedEnabledSelector } from '../../util/isFeatureEnabled.dom.ts';
import { useNavActions } from '../ducks/nav.std.ts';
import { NavTab, SettingsPage } from '../../types/Nav.std.ts';
export const SmartContactModal = memo(function SmartContactModal() {
const i18n = useSelector(getIntl);
@@ -105,6 +107,7 @@ export const SmartContactModal = memo(function SmartContactModal() {
toggleGroupMemberLabelInfoModal,
toggleSafetyNumberModal,
} = useGlobalModalActions();
const { changeLocation } = useNavActions();
const {
blockClient: blockClientFromCall,
onOutgoingVideoCallInConversation,
@@ -119,6 +122,16 @@ export const SmartContactModal = memo(function SmartContactModal() {
toggleEditNicknameAndNoteModal({ conversationId: contactId });
}, [toggleEditNicknameAndNoteModal, contactId]);
const onNavigateToDonate = useCallback(() => {
hideContactModal();
changeLocation({
tab: NavTab.Settings,
details: {
page: SettingsPage.DonationsDonateFlow,
},
});
}, [hideContactModal, changeLocation]);
return (
<ContactModal
areWeAdmin={areWeAdmin}
@@ -142,6 +155,7 @@ export const SmartContactModal = memo(function SmartContactModal() {
isRemoteMuteVisible={isRemoteMuteVisible}
isRemoveFromCallVisible={isRemoveFromCallVisible}
activeCallDemuxId={activeCallDemuxId}
onNavigateToDonate={onNavigateToDonate}
onOpenEditNicknameAndNoteModal={handleOpenEditNicknameAndNoteModal}
onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation}
onOutgoingVideoCallInConversation={onOutgoingVideoCallInConversation}
+12 -1
View File
@@ -54,6 +54,7 @@ import { isFeaturedEnabledSelector } from '../../util/isFeatureEnabled.dom.ts';
import { getCanAddLabel } from '../../types/GroupMemberLabels.std.ts';
import { useToastActions } from '../ducks/toast.preload.ts';
import { useNavActions } from '../ducks/nav.std.ts';
import { NavTab, SettingsPage } from '../../types/Nav.std.ts';
const { sortBy } = lodash;
@@ -143,7 +144,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
updateGroupAttributes,
updateNicknameAndNote,
} = useConversationsActions();
const { pushPanelForConversation } = useNavActions();
const { pushPanelForConversation, changeLocation } = useNavActions();
const {
onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation,
@@ -232,6 +233,15 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
destroyMessages(conversationId);
}, [destroyMessages, conversationId]);
const onNavigateToDonate = useCallback(() => {
changeLocation({
tab: NavTab.Settings,
details: {
page: SettingsPage.DonationsDonateFlow,
},
});
}, [changeLocation]);
const [hasMedia, setHasMedia] = useState(false);
useEffect(() => {
@@ -286,6 +296,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
onConversationDeleteMessages={onConversationDeleteMessages}
onConversationUnarchive={onConversationUnarchive}
onDeleteNicknameAndNote={handleDeleteNicknameAndNote}
onNavigateToDonate={onNavigateToDonate}
onOpenEditNicknameAndNoteModal={handleOpenEditNicknameAndNoteModal}
onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation}
onOutgoingVideoCallInConversation={onOutgoingVideoCallInConversation}