;
-const renderExtraInformation = ({
- acceptedMessageRequest,
- conversationType,
- fromOrAddedByTrustedContact,
- i18n,
- isDirectConvoAndHasNickname,
- isMe,
- invitesCount,
- memberships,
- onClickProfileNameWarning,
- onToggleSafetyTips,
- openConversationDetails,
- phoneNumber,
- sharedGroupNames,
-}: Pick<
- Props,
- | 'avatarPlaceholderGradient'
- | 'acceptedMessageRequest'
- | 'conversationType'
- | 'fromOrAddedByTrustedContact'
- | 'i18n'
- | 'isDirectConvoAndHasNickname'
- | 'isMe'
- | 'invitesCount'
- | 'membersCount'
- | 'memberships'
- | 'openConversationDetails'
- | 'phoneNumber'
- | 'sharedGroupNames'
-> & {
- onClickProfileNameWarning: () => void;
- onToggleSafetyTips: (showSafetyTips: boolean) => void;
-}) => {
- if (conversationType !== 'direct' && conversationType !== 'group') {
- return null;
- }
-
- if (isMe) {
- return (
-
- {i18n('icu:noteToSelfHero')}
-
- );
- }
-
- const safetyTipsButton = !acceptedMessageRequest ? (
-
-
-
- ) : null;
-
- const shouldShowReviewCarefully =
- !acceptedMessageRequest &&
- (conversationType === 'group' || (sharedGroupNames?.length ?? 0) <= 1);
-
- const reviewCarefullyLabel = shouldShowReviewCarefully ? (
-
-
- {i18n('icu:ConversationHero--review-carefully')}
-
- ) : null;
-
- const sharedGroupsLabel =
- conversationType === 'direct' ? (
-
-
-
-
- ) : null;
-
- const nameNotVerifiedLabel =
- !fromOrAddedByTrustedContact && !isDirectConvoAndHasNickname ? (
-
-
- (
-
- ),
- }}
- i18n={i18n}
- id={
- conversationType === 'group'
- ? 'icu:ConversationHero--group-names'
- : 'icu:ConversationHero--profile-names'
- }
- />
-
- ) : null;
-
- const membersCountLabel =
- conversationType === 'group' ? (
-
-
-
-
- ) : null;
-
- if (
- conversationType === 'direct' &&
- (sharedGroupNames?.length ?? 0) === 0 &&
- acceptedMessageRequest &&
- phoneNumber
- ) {
- return null;
- }
-
- // Check if we should show anything at all
- const shouldShowAnything =
- Boolean(reviewCarefullyLabel) ||
- Boolean(nameNotVerifiedLabel) ||
- Boolean(sharedGroupsLabel) ||
- Boolean(safetyTipsButton) ||
- Boolean(membersCountLabel);
-
- if (!shouldShowAnything) {
- return null;
- }
-
- return (
-
- {reviewCarefullyLabel}
- {nameNotVerifiedLabel}
- {sharedGroupsLabel}
- {membersCountLabel}
- {safetyTipsButton}
-
- );
-};
-
-function ReleaseNotesExtraInformation({
- i18n,
-}: {
- i18n: LocalizerType;
-}): React.JSX.Element {
- return (
-
-
-
- {i18n('icu:ConversationHero--signal-official-chat')}
-
-
-
- {i18n('icu:ConversationHero--release-notes')}
-
-
- );
-}
+type DistributiveOmit = T extends unknown
+ ? Omit
+ : never;
export function ConversationHero({
avatarPlaceholderGradient,
i18n,
- about,
acceptedMessageRequest,
avatarUrl,
badge,
@@ -241,26 +61,26 @@ export function ConversationHero({
fromOrAddedByTrustedContact,
groupDescription,
hasAvatar,
+ hasNickname,
+ hasProfileName,
hasStories,
id,
- isDirectConvoAndHasNickname,
+ isInSystemContacts,
isMe,
invitesCount,
openConversationDetails,
isSignalConversation,
- membersCount,
memberships,
pendingAvatarDownload,
- sharedGroupNames = [],
- phoneNumber,
profileName,
+ sharedGroupNames = [],
startAvatarDownload,
theme,
title,
viewUserStories,
toggleAboutContactModal,
toggleProfileNameWarningModal,
-}: Props): React.JSX.Element {
+}: Props): React.JSX.Element | null {
const [isShowingSafetyTips, setIsShowingSafetyTips] = useState(false);
let avatarBlur: AvatarBlur = AvatarBlur.NoBlur;
@@ -282,71 +102,129 @@ export function ConversationHero({
};
}
- let titleElem: React.JSX.Element | undefined;
+ const maybeSafetyTips = isShowingSafetyTips ? (
+ {
+ setIsShowingSafetyTips(false);
+ }}
+ />
+ ) : null;
+
+ const avatar = (
+
+ );
if (isMe) {
- titleElem = (
-
- );
- } else if (isSignalConversation || conversationType !== 'direct') {
- titleElem = (
-
- );
- } else if (title) {
- titleElem = (
-
+ return (
+
+ {avatar}
+
+
+ {i18n('icu:noteToSelfHero')}
+
+
);
}
- return (
- <>
-
-
+ {avatar}
+
+
+
+ {i18n('icu:ConversationHero--signal-official-account')}
+
+
+ {i18n('icu:ConversationHero--signal-official-account--description')}
+
+
+ );
+ }
+
+ if (conversationType === 'direct') {
+ const nameIsVerified = hasNickname || isInSystemContacts;
+ return (
+
+ {avatar}
+ toggleAboutContactModal({ contactId: id })}
/>
- {titleElem}
- {about && !isMe && (
-
- )}
- {!isMe && groupDescription ? (
-
+
+ {hasProfileName && !nameIsVerified ? (
+
toggleProfileNameWarningModal(conversationType)}
+ i18n={i18n}
+ />
+ ) : null}
+
+
+
+ {!acceptedMessageRequest ? (
+ setIsShowingSafetyTips(true)}
+ i18n={i18n}
+ />
+ ) : null}
+ {maybeSafetyTips}
+
+ );
+ }
+
+ if (conversationType === 'group') {
+ const nameIsVerified = Boolean(fromOrAddedByTrustedContact);
+ return (
+
+ {avatar}
+
+ {!nameIsVerified ? (
+ toggleProfileNameWarningModal(conversationType)}
+ i18n={i18n}
+ />
+ ) : null}
+
+ {groupDescription ? (
+
) : null}
- {!isSignalConversation &&
- renderExtraInformation({
- acceptedMessageRequest,
- conversationType,
- fromOrAddedByTrustedContact,
- i18n,
- isDirectConvoAndHasNickname,
- isMe,
- invitesCount,
- membersCount,
- memberships,
- onClickProfileNameWarning() {
- toggleProfileNameWarningModal(conversationType);
- },
- onToggleSafetyTips(showSafetyTips: boolean) {
- setIsShowingSafetyTips(showSafetyTips);
- },
- openConversationDetails,
- phoneNumber,
- sharedGroupNames: sharedGroupNames ?? [],
- })}
- {isSignalConversation && }
-
- {isShowingSafetyTips && (
- {
- setIsShowingSafetyTips(false);
- }}
- />
- )}
- >
- );
+
+ {!acceptedMessageRequest ? (
+ setIsShowingSafetyTips(true)}
+ i18n={i18n}
+ />
+ ) : null}
+ {maybeSafetyTips}
+
+ );
+ }
+ return null;
}
+
+type RootProps = {
+ children: ReactNode;
+};
+const Root: React.FC = props => {
+ return (
+
+ {props.children}
+
+ );
+};
+
+const ConversationAvatar: React.FC<
+ DistributiveOmit
+> = props => {
+ return (
+
+ );
+};
+
+type TitleProps = {
+ isMe?: boolean;
+ isSignalConversation?: boolean;
+ title: string;
+ onClick?: () => void;
+};
+
+const Title: React.FC = props => {
+ const className = tw('mt-3 text-center text-[20px] font-medium');
+ const { onClick, title, isMe, isSignalConversation } = props;
+ const contactName = (
+
+ );
+
+ if (onClick) {
+ return (
+
+ );
+ }
+
+ return {contactName}
;
+};
+
+const NameNotVerifiedWarning: React.FC<{
+ conversationType: 'direct' | 'group';
+ onClick: () => void;
+ i18n: LocalizerType;
+}> = ({ conversationType, onClick, i18n }) => {
+ return (
+
+ );
+};
+
+const SafetyTips: React.FC<{
+ onShowSafetyTips: () => void;
+ i18n: LocalizerType;
+}> = ({ i18n, onShowSafetyTips }) => {
+ return (
+
+
+ {i18n('icu:MessageRequestWarning__safety-tips-v2')}
+
+
+ );
+};
diff --git a/ts/components/conversation/ProfileNameWarningModal.dom.tsx b/ts/components/conversation/ProfileNameWarningModal.dom.tsx
index b9a3814647..28fdc7b131 100644
--- a/ts/components/conversation/ProfileNameWarningModal.dom.tsx
+++ b/ts/components/conversation/ProfileNameWarningModal.dom.tsx
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
-import { Modal } from '../Modal.dom.tsx';
import type { LocalizerType } from '../../types/Util.std.ts';
+import { AxoDialog } from '../../axo/AxoDialog.dom.tsx';
+import { AxoSymbol } from '../../axo/AxoSymbol.dom.tsx';
+import { tw } from '../../axo/tw.dom.tsx';
export type PropsType = Readonly<{
conversationType: 'group' | 'direct';
@@ -11,60 +13,80 @@ export type PropsType = Readonly<{
onClose: () => void;
}>;
-const DESCRIPTION_KEYS = {
- direct: 'icu:ProfileNameWarningModal__description--direct',
- group: 'icu:ProfileNameWarningModal__description--group',
-} as const;
-
-const LIST_ITEM_KEYS = {
- item1: {
- direct: 'icu:ProfileNameWarningModal__list--item1--direct',
- group: 'icu:ProfileNameWarningModal__list--item1--group',
- },
- item2: {
- direct: 'icu:ProfileNameWarningModal__list--item2--direct',
- group: 'icu:ProfileNameWarningModal__list--item2--group',
- },
- item3: {
- direct: 'icu:ProfileNameWarningModal__list--item3--direct',
- group: 'icu:ProfileNameWarningModal__list--item3--group',
- },
-} as const;
-
export function ProfileNameWarningModal({
conversationType,
i18n,
onClose,
}: PropsType): React.JSX.Element {
return (
-
-
-
- {i18n(DESCRIPTION_KEYS[conversationType])}
-
-
- -
-
- {i18n(LIST_ITEM_KEYS.item1[conversationType])}
-
-
- -
-
- {i18n(LIST_ITEM_KEYS.item2[conversationType])}
-
-
- -
-
- {i18n(LIST_ITEM_KEYS.item3[conversationType])}
-
-
-
-
+
+
+
+
+
+
+
+
+ {conversationType === 'direct' ? (
+
+ ) : (
+
+ )}
+
+
+
+ {conversationType === 'direct' ? (
+ <>
+ {i18n('icu:ProfileNameWarningModal__description--direct')}
+
li]:mt-3')}>
+ -
+ {i18n(
+ 'icu:ProfileNameWarningModal__warning--signal-cant-verify'
+ )}
+
+ -
+ {i18n(
+ 'icu:ProfileNameWarningModal__warning--signal-wont-contact'
+ )}
+
+ -
+ {i18n('icu:ProfileNameWarningModal__warning--be-cautious')}
+
+ -
+ {i18n(
+ 'icu:ProfileNameWarningModal__warning--dont-share-info'
+ )}
+
+
+ >
+ ) : (
+ <>
+ {i18n('icu:ProfileNameWarningModal__description--group')}
+
li]:mt-3')}>
+ -
+ {i18n('icu:ProfileNameWarningModal__list--item1--group')}
+
+ -
+ {i18n('icu:ProfileNameWarningModal__list--item2--group')}
+
+ -
+ {i18n('icu:ProfileNameWarningModal__list--item3--group')}
+
+
+ >
+ )}
+
+
+
+
);
}
diff --git a/ts/components/conversation/Timeline.dom.stories.tsx b/ts/components/conversation/Timeline.dom.stories.tsx
index 4f22e0f570..358bcdc969 100644
--- a/ts/components/conversation/Timeline.dom.stories.tsx
+++ b/ts/components/conversation/Timeline.dom.stories.tsx
@@ -411,9 +411,12 @@ const renderHeroRow = () => {
avatarUrl={getAvatarPath()}
badge={undefined}
conversationType="direct"
+ hasNickname={false}
+ hasProfileName
id={getDefaultConversation().id}
i18n={i18n}
isMe={false}
+ isInSystemContacts={false}
phoneNumber={getPhoneNumber()}
profileName={getProfileName()}
sharedGroupNames={['NYC Rock Climbers', 'Dinner Party']}
diff --git a/ts/state/smart/HeroRow.preload.tsx b/ts/state/smart/HeroRow.preload.tsx
index a65841936c..d136a9868b 100644
--- a/ts/state/smart/HeroRow.preload.tsx
+++ b/ts/state/smart/HeroRow.preload.tsx
@@ -23,6 +23,8 @@ import { useStoriesActions } from '../ducks/stories.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { getGroupMemberships } from '../../util/getGroupMemberships.dom.ts';
import { useNavActions } from '../ducks/nav.std.ts';
+import { tw } from '../../axo/tw.dom.tsx';
+import { isInSystemContacts } from '../../util/isInSystemContacts.std.ts';
type SmartHeroRowProps = Readonly<{
id: string;
@@ -83,7 +85,6 @@ export const SmartHeroRow = memo(function SmartHeroRow({
const { viewUserStories } = useStoriesActions();
const {
avatarPlaceholderGradient,
- about,
acceptedMessageRequest,
avatarUrl,
color,
@@ -93,50 +94,46 @@ export const SmartHeroRow = memo(function SmartHeroRow({
membersCount,
nicknameGivenName,
nicknameFamilyName,
- phoneNumber,
profileName,
title,
type,
} = conversation;
-
- const isDirectConvoAndHasNickname =
- type === 'direct' && Boolean(nicknameGivenName || nicknameFamilyName);
-
const invitesCount =
pendingMemberships.length + pendingApprovalMemberships.length;
-
return (
- startAvatarDownload(id)}
- theme={theme}
- title={title}
- toggleAboutContactModal={toggleAboutContactModal}
- toggleProfileNameWarningModal={toggleProfileNameWarningModal}
- viewUserStories={viewUserStories}
- />
+
+ startAvatarDownload(id)}
+ theme={theme}
+ title={title}
+ toggleAboutContactModal={toggleAboutContactModal}
+ toggleProfileNameWarningModal={toggleProfileNameWarningModal}
+ viewUserStories={viewUserStories}
+ />
+
);
});
diff --git a/ts/test-mock/messaging/edit_test.node.ts b/ts/test-mock/messaging/edit_test.node.ts
index 93a26bc226..9ddfb93249 100644
--- a/ts/test-mock/messaging/edit_test.node.ts
+++ b/ts/test-mock/messaging/edit_test.node.ts
@@ -201,7 +201,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for message');
await window
@@ -272,7 +272,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for message');
await window
@@ -352,7 +352,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for message');
await window.locator('.module-message__text >> "hello"').waitFor();
@@ -494,7 +494,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for latest message');
await window.locator('.module-message__text >> "v5"').waitFor();
@@ -540,7 +540,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for latest message');
await window.locator('.module-message__text >> "v2"').waitFor();
@@ -583,7 +583,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await page.locator('.module-conversation-hero').waitFor();
+ await page.getByTestId('conversation-hero').waitFor();
const { dataMessage: profileKeyMsg } = await friend.waitForMessage();
assert(profileKeyMsg.profileKey != null, 'Profile key message');
@@ -894,7 +894,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for latest message');
await window.locator('.module-message__text >> "v2"').waitFor();
@@ -966,7 +966,7 @@ describe('editing', function (this: Mocha.Suite) {
.locator('.module-conversation-list__item--contact-or-conversation')
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('checking for latest message');
await window.locator('.module-message__text >> "v5"').waitFor();
diff --git a/ts/test-mock/messaging/expire_timer_version_test.node.ts b/ts/test-mock/messaging/expire_timer_version_test.node.ts
index 0bde8ed3a6..e8c365ee66 100644
--- a/ts/test-mock/messaging/expire_timer_version_test.node.ts
+++ b/ts/test-mock/messaging/expire_timer_version_test.node.ts
@@ -273,7 +273,7 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
await expectSystemMessages(window, scenario.systemMessages);
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Send message to merged contact');
{
@@ -304,7 +304,7 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
const conversationStack = window.locator('.Inbox__conversation-stack');
diff --git a/ts/test-mock/pnp/merge_test.node.ts b/ts/test-mock/pnp/merge_test.node.ts
index cdbbe30c51..db98be9910 100644
--- a/ts/test-mock/pnp/merge_test.node.ts
+++ b/ts/test-mock/pnp/merge_test.node.ts
@@ -132,7 +132,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
.locator(`[data-testid="${pniContact.device.aci}"]`)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Send message to ACI');
{
@@ -148,7 +148,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
.first()
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Verify starting state');
{
@@ -175,7 +175,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
.locator(`[data-testid="${pniContact.device.aci}"]`)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
}
debug(
@@ -272,7 +272,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Send message to merged contact');
{
@@ -377,7 +377,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Unregistering ACI');
server.unregister(pniContact);
@@ -460,7 +460,8 @@ describe('pnp/merge', function (this: Mocha.Suite) {
debug('Wait for ACI conversation to go away');
await window
- .locator(`.module-conversation-hero >> "${pniContact.profileName}"`)
+ .getByTestId('conversation-hero')
+ .getByText(pniContact.profileName)
.waitFor({
state: 'hidden',
});
@@ -522,7 +523,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Verify that the message is in the ACI conversation');
{
@@ -638,7 +639,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Send message to merged contact');
{
diff --git a/ts/test-mock/pnp/phone_discovery_test.node.ts b/ts/test-mock/pnp/phone_discovery_test.node.ts
index 46914a5682..25ddbb5d0c 100644
--- a/ts/test-mock/pnp/phone_discovery_test.node.ts
+++ b/ts/test-mock/pnp/phone_discovery_test.node.ts
@@ -127,18 +127,11 @@ describe('pnp/phone discovery', function (this: Mocha.Suite) {
});
}
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
debug('Open ACI conversation');
await leftPane.locator(`[data-testid="${pniContact.device.aci}"]`).click();
- debug('Wait for PNI conversation to go away');
- await window
- .locator(`.module-conversation-hero >> ${pniContact.profileName}`)
- .waitFor({
- state: 'hidden',
- });
-
debug('Verify final state');
{
// Should have PNI message
diff --git a/ts/test-mock/pnp/pni_change_test.node.ts b/ts/test-mock/pnp/pni_change_test.node.ts
index d08573f282..95769a285e 100644
--- a/ts/test-mock/pnp/pni_change_test.node.ts
+++ b/ts/test-mock/pnp/pni_change_test.node.ts
@@ -93,7 +93,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
}
debug('Verify starting state');
@@ -198,7 +198,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
}
debug('Verify starting state');
@@ -307,7 +307,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
}
debug('Verify starting state');
@@ -446,7 +446,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
)
.click();
- await window.locator('.module-conversation-hero').waitFor();
+ await window.getByTestId('conversation-hero').waitFor();
}
debug('Verify starting state');
diff --git a/ts/test-mock/pnp/username_test.node.ts b/ts/test-mock/pnp/username_test.node.ts
index 3683219635..e36e0da8d3 100644
--- a/ts/test-mock/pnp/username_test.node.ts
+++ b/ts/test-mock/pnp/username_test.node.ts
@@ -381,7 +381,8 @@ describe('pnp/username', function (this: Mocha.Suite) {
debug('waiting for conversation to open');
await window
- .locator(`.module-conversation-hero >> "${CARL_USERNAME}"`)
+ .getByTestId('conversation-hero')
+ .getByText(CARL_USERNAME)
.waitFor();
debug('sending a message');