mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-06-03 21:43:25 +01:00
Updated conversation hero UI & profile name warning
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
@@ -4664,10 +4664,22 @@
|
||||
"messageformat": "<clickable>Profile names</clickable> are not verified",
|
||||
"description": "Label for profile names in the name verification warning in conversation hero"
|
||||
},
|
||||
"icu:ConversationHero--name-not-verified": {
|
||||
"messageformat": "Name not verified",
|
||||
"description": "Label for profile names and group names in the name verification warning in conversation hero"
|
||||
},
|
||||
"icu:ConversationHero--signal-official-chat": {
|
||||
"messageformat": "This is the official and only chat from Signal",
|
||||
"description": "Text indicating that this is the official Signal conversation"
|
||||
},
|
||||
"icu:ConversationHero--signal-official-account": {
|
||||
"messageformat": "Official account",
|
||||
"description": "Text indicating that this is the official Signal conversation"
|
||||
},
|
||||
"icu:ConversationHero--signal-official-account--description": {
|
||||
"messageformat": "The only official chat from Signal. Keep up to date with news and release notes.",
|
||||
"description": "Description text at the top of the official Signal conversation"
|
||||
},
|
||||
"icu:ConversationHero--release-notes": {
|
||||
"messageformat": "Keep up to date with news and release notes.",
|
||||
"description": "Text explaining the purpose of the Signal official conversation"
|
||||
@@ -7033,6 +7045,10 @@
|
||||
"messageformat": "Safety Tips",
|
||||
"description": "Shown on the message request warning. Clicking this button will open a dialog with safety tips"
|
||||
},
|
||||
"icu:MessageRequestWarning__safety-tips-v2": {
|
||||
"messageformat": "Safety tips",
|
||||
"description": "Shown on the message request warning. Clicking this button will open a dialog with safety tips"
|
||||
},
|
||||
"icu:ContactSpoofing__same-name--link": {
|
||||
"messageformat": "Review requests carefully. Signal found another contact with the same name. <reviewRequestLink>Review request</reviewRequestLink>",
|
||||
"description": "Shown in the timeline warning when you have a message request from someone with the same name as someone else"
|
||||
@@ -10188,6 +10204,23 @@
|
||||
"messageformat": "Don't share personal information with people you don't know",
|
||||
"description": "Third list item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__warning--signal-cant-verify": {
|
||||
"messageformat": "Signal can’t verify names and photos",
|
||||
"description": "List item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__warning--signal-wont-contact": {
|
||||
"messageformat": "Signal will never contact you for your registration code, PIN, or recovery key",
|
||||
"description": "List item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__warning--be-cautious": {
|
||||
"messageformat": "Be cautious of accounts that impersonate others",
|
||||
"description": "Third list item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__warning--dont-share-info": {
|
||||
"messageformat": "Don't share personal information with people you don't know",
|
||||
"description": "List item in profile name warning modal for direct conversations"
|
||||
},
|
||||
|
||||
"icu:ProfileNameWarningModal__description--group": {
|
||||
"messageformat": "Group names are chosen by members of the group.",
|
||||
"description": "Description of how group names work in the profile name warning modal for group conversations"
|
||||
|
||||
@@ -1,263 +0,0 @@
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
@use '../mixins';
|
||||
@use '../variables';
|
||||
|
||||
.module-conversation-hero {
|
||||
padding-block: 32px 28px;
|
||||
padding-inline: 0;
|
||||
text-align: center;
|
||||
|
||||
&__avatar {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
@include mixins.button-reset();
|
||||
& {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__title span {
|
||||
@include mixins.font-title-1;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&__title__chevron {
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
// Align with the text
|
||||
position: relative;
|
||||
inset-block-start: 2px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-right-bold.svg',
|
||||
light-dark(variables.$color-gray-90, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__profile-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include mixins.font-title-1;
|
||||
margin-bottom: 2px;
|
||||
margin-top: 0;
|
||||
|
||||
color: light-dark(variables.$color-gray-90, variables.$color-gray-05);
|
||||
|
||||
.module-contact-name {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__with {
|
||||
@include mixins.font-body-2;
|
||||
margin-block: 0;
|
||||
margin-inline: auto;
|
||||
margin-bottom: 20px;
|
||||
max-width: 500px;
|
||||
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
}
|
||||
|
||||
&__note-to-self {
|
||||
@include mixins.font-body-2;
|
||||
|
||||
padding-block: 0;
|
||||
padding-inline: 16px;
|
||||
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
}
|
||||
|
||||
&__members-count__button {
|
||||
@include mixins.button-reset;
|
||||
& {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-color: variables.$color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__safety-tips-button {
|
||||
border-radius: 9999px;
|
||||
padding-block: 6px;
|
||||
padding-inline: 14px;
|
||||
margin-top: 5px;
|
||||
@include mixins.font-subtitle;
|
||||
}
|
||||
|
||||
&__review-carefully {
|
||||
@include mixins.font-body-2-bold;
|
||||
color: #a98b52;
|
||||
}
|
||||
|
||||
&__group-question-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 22px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group-questionmark-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__direct-question-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/person/person-questionmark-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__name-not-verified__button {
|
||||
@include mixins.button-reset;
|
||||
& {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-color: variables.$color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&--release-notes-notice {
|
||||
@include mixins.font-body-1;
|
||||
|
||||
user-select: none;
|
||||
|
||||
max-width: 255px;
|
||||
margin-inline: auto;
|
||||
margin-block-start: 10px;
|
||||
padding-block: 16px;
|
||||
padding-inline: 20px;
|
||||
|
||||
border-radius: 18px;
|
||||
background-color: light-dark(#eeefff, #3b3d50);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
color: light-dark(variables.$color-gray-75, variables.$color-gray-02);
|
||||
}
|
||||
|
||||
&__release-notes-notice-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__release-notes-notice-check-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-inline-end: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/official/official-compact.svg',
|
||||
light-dark(variables.$color-gray-75, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__release-notes-notice-bell-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
margin-inline-end: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/bell/bell-compact.svg',
|
||||
light-dark(variables.$color-gray-75, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__membership {
|
||||
@include mixins.font-body-2;
|
||||
user-select: none;
|
||||
|
||||
max-width: 255px;
|
||||
margin-inline: auto;
|
||||
margin-block-start: 10px;
|
||||
padding-block: 16px;
|
||||
padding-inline: 20px;
|
||||
|
||||
border-radius: 18px;
|
||||
border-style: solid;
|
||||
border-width: 2.5px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
border-color: light-dark(
|
||||
variables.$color-gray-04,
|
||||
variables.$color-gray-80
|
||||
);
|
||||
|
||||
color: light-dark(variables.$color-gray-90, variables.$color-gray-02);
|
||||
|
||||
&__chevron {
|
||||
display: inline-block;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__name {
|
||||
// Cancel bold
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__review-carefully-icon {
|
||||
display: inline-block;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/error/error-triangle-fill-compact-bold.svg',
|
||||
#a98b52
|
||||
);
|
||||
}
|
||||
|
||||
&__warning {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__members-count-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,6 @@ $is-storybook: false !default;
|
||||
@use 'components/ConversationDetails.scss';
|
||||
@use 'components/ConversationDetailsHeader.scss';
|
||||
@use 'components/ConversationHeader.scss';
|
||||
@use 'components/ConversationHero.scss';
|
||||
@use 'components/ConversationMergeNotification.scss';
|
||||
@use 'components/ConversationPanel.scss';
|
||||
@use 'components/ConversationView.scss';
|
||||
|
||||
@@ -27,7 +27,6 @@ function renderClickableButton(
|
||||
): React.JSX.Element {
|
||||
return (
|
||||
<button
|
||||
className="module-conversation-hero__members-count__button"
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -22,6 +22,8 @@ import type { ConversationType } from '../../state/ducks/conversations.preload.t
|
||||
import type { ContactNameColorType } from '../../types/Colors.std.ts';
|
||||
import type { FunStaticEmojiSize } from '../fun/FunEmoji.dom.tsx';
|
||||
import { UserText } from '../UserText.dom.tsx';
|
||||
import { AxoSymbol } from '../../axo/AxoSymbol.dom.tsx';
|
||||
import { tw } from '../../axo/tw.dom.tsx';
|
||||
|
||||
export type ContactNameData = {
|
||||
contactNameColor?: ContactNameColorType;
|
||||
@@ -58,7 +60,6 @@ export type PropsType = ContactNameData & {
|
||||
module?: string;
|
||||
preferFirstName?: boolean;
|
||||
onClick?: VoidFunction;
|
||||
largeVerifiedBadge?: boolean;
|
||||
};
|
||||
|
||||
export function ContactName({
|
||||
@@ -71,7 +72,6 @@ export function ContactName({
|
||||
preferFirstName,
|
||||
title,
|
||||
onClick,
|
||||
largeVerifiedBadge,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const getClassName = getClassNamesFor('module-contact-name', module);
|
||||
|
||||
@@ -98,15 +98,16 @@ export function ContactName({
|
||||
}}
|
||||
>
|
||||
<UserText text={text} />
|
||||
|
||||
{(isSignalConversation || isMe) && (
|
||||
<span
|
||||
className={
|
||||
largeVerifiedBadge
|
||||
? 'ContactModal__official-badge__large'
|
||||
: 'ContactModal__official-badge'
|
||||
}
|
||||
/>
|
||||
<>
|
||||
|
||||
<span className={tw('text-color-fill-primary')}>
|
||||
<AxoSymbol.InlineGlyph symbol="officialbadge-fill" label={null} />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
{contactLabel && (
|
||||
<>
|
||||
{' '}
|
||||
|
||||
@@ -48,9 +48,11 @@ export default {
|
||||
component: ConversationHero,
|
||||
args: {
|
||||
conversationType: 'direct',
|
||||
fromOrAddedByTrustedContact: true,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
i18n,
|
||||
isDirectConvoAndHasNickname: false,
|
||||
hasNickname: false,
|
||||
hasProfileName: true,
|
||||
isInSystemContacts: false,
|
||||
theme: ThemeType.light,
|
||||
sharedGroupNames: [],
|
||||
viewUserStories: action('viewUserStories'),
|
||||
@@ -65,8 +67,8 @@ export default {
|
||||
const Template: StoryFn<Props> = args => {
|
||||
const theme = useContext(StorybookThemeContext);
|
||||
const baseProps = {
|
||||
...args,
|
||||
...getDefaultConversation(),
|
||||
...args,
|
||||
};
|
||||
|
||||
const memberships = createMemberships({
|
||||
@@ -109,77 +111,35 @@ DirectOneOtherGroup.args = {
|
||||
sharedGroupNames: [casual.title],
|
||||
};
|
||||
|
||||
export const DirectNoGroupsName = Template.bind({});
|
||||
DirectNoGroupsName.args = {
|
||||
about: '👍 Free to chat',
|
||||
export const DirectNoGroups = Template.bind({});
|
||||
DirectNoGroups.args = {};
|
||||
|
||||
export const DirectWithNickname = Template.bind({});
|
||||
DirectWithNickname.args = { hasNickname: true };
|
||||
|
||||
export const DirectInSystemContacts = Template.bind({});
|
||||
DirectInSystemContacts.args = { hasNickname: true, isInSystemContacts: true };
|
||||
|
||||
export const DirectNoProfileName = Template.bind({});
|
||||
DirectNoProfileName.args = { title: '123-555-1234', hasProfileName: false };
|
||||
|
||||
export const DirectMessageRequest = Template.bind({});
|
||||
DirectMessageRequest.args = { acceptedMessageRequest: false };
|
||||
|
||||
export const DirectUnreadStories = Template.bind({});
|
||||
DirectUnreadStories.args = {
|
||||
hasStories: HasStories.Unread,
|
||||
};
|
||||
|
||||
export const DirectNoGroupsJustProfile = Template.bind({});
|
||||
DirectNoGroupsJustProfile.args = {
|
||||
phoneNumber: casual.phone,
|
||||
export const DirectReadStories = Template.bind({});
|
||||
DirectReadStories.args = {
|
||||
hasStories: HasStories.Read,
|
||||
};
|
||||
|
||||
export const SignalConversation = Template.bind({});
|
||||
SignalConversation.args = {
|
||||
avatarUrl: 'images/profile-avatar.svg',
|
||||
title: 'Signal',
|
||||
isSignalConversation: true,
|
||||
phoneNumber: casual.phone,
|
||||
};
|
||||
|
||||
export const DirectNoGroupsJustPhoneNumber = Template.bind({});
|
||||
DirectNoGroupsJustPhoneNumber.args = {
|
||||
phoneNumber: casual.phone,
|
||||
profileName: '',
|
||||
title: casual.phone,
|
||||
};
|
||||
|
||||
export const DirectNoGroupsNoData = Template.bind({});
|
||||
DirectNoGroupsNoData.args = {
|
||||
avatarUrl: undefined,
|
||||
phoneNumber: '',
|
||||
profileName: '',
|
||||
title: casual.phone,
|
||||
};
|
||||
|
||||
export const DirectNoGroupsNoDataNotAccepted = Template.bind({});
|
||||
DirectNoGroupsNoDataNotAccepted.args = {
|
||||
acceptedMessageRequest: false,
|
||||
avatarUrl: undefined,
|
||||
phoneNumber: '',
|
||||
profileName: '',
|
||||
title: '',
|
||||
};
|
||||
|
||||
export const DirectNoGroupsNotAcceptedWithAvatar = Template.bind({});
|
||||
DirectNoGroupsNotAcceptedWithAvatar.args = {
|
||||
acceptedMessageRequest: false,
|
||||
profileName: '',
|
||||
};
|
||||
|
||||
export const GroupLongGroupDescription = Template.bind({});
|
||||
GroupLongGroupDescription.args = {
|
||||
conversationType: 'group',
|
||||
groupDescription:
|
||||
"This is a group for all the rock climbers of NYC. We really like to climb rocks and these NYC people climb any rock. No rock is too small or too big to be climbed. We will ascend upon all rocks, and not just in NYC, in the whole world. We are just getting started, NYC is just the beginning, watch out rocks in the galaxy. Kuiper belt I'm looking at you. We will put on a space suit and climb all your rocks. No rock is near nor far for the rock climbers of NYC.",
|
||||
membersCount: casual.integer(1, 10),
|
||||
title: casual.title,
|
||||
};
|
||||
|
||||
export const GroupNoName = Template.bind({});
|
||||
GroupNoName.args = {
|
||||
conversationType: 'group',
|
||||
membersCount: 0,
|
||||
title: '',
|
||||
};
|
||||
|
||||
export const GroupNotAccepted = Template.bind({});
|
||||
GroupNotAccepted.args = {
|
||||
conversationType: 'group',
|
||||
groupDescription: casual.sentence,
|
||||
membersCount: casual.integer(20, 100),
|
||||
title: casual.title,
|
||||
acceptedMessageRequest: false,
|
||||
};
|
||||
|
||||
export const NoteToSelf = Template.bind({});
|
||||
@@ -187,37 +147,41 @@ NoteToSelf.args = {
|
||||
isMe: true,
|
||||
};
|
||||
|
||||
export const UnreadStories = Template.bind({});
|
||||
UnreadStories.args = {
|
||||
hasStories: HasStories.Unread,
|
||||
};
|
||||
|
||||
export const ReadStories = Template.bind({});
|
||||
ReadStories.args = {
|
||||
hasStories: HasStories.Read,
|
||||
};
|
||||
|
||||
export const DirectNotFromTrustedContact = Template.bind({});
|
||||
DirectNotFromTrustedContact.args = {
|
||||
conversationType: 'direct',
|
||||
title: casual.full_name,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
};
|
||||
|
||||
export const DirectWithNickname = Template.bind({});
|
||||
DirectWithNickname.args = {
|
||||
conversationType: 'direct',
|
||||
title: casual.full_name,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
isDirectConvoAndHasNickname: true,
|
||||
};
|
||||
|
||||
export const GroupNotFromTrustedContact = Template.bind({});
|
||||
GroupNotFromTrustedContact.args = {
|
||||
const groupArgs = {
|
||||
conversationType: 'group',
|
||||
title: casual.title,
|
||||
membersCount: casual.integer(5, 20),
|
||||
fromOrAddedByTrustedContact: false,
|
||||
membersCount: casual.integer(1, 10),
|
||||
title: 'Group title',
|
||||
} as const;
|
||||
|
||||
export const Group = Template.bind({});
|
||||
Group.args = {
|
||||
...groupArgs,
|
||||
title: 'This is the title that never ends',
|
||||
};
|
||||
export const GroupLongTitle = Template.bind({});
|
||||
GroupLongTitle.args = {
|
||||
...groupArgs,
|
||||
title: 'This is the title that never ends',
|
||||
};
|
||||
|
||||
export const GroupLongGroupDescription = Template.bind({});
|
||||
GroupLongGroupDescription.args = {
|
||||
...groupArgs,
|
||||
groupDescription:
|
||||
"This is anextremelylargewordinaverylargegroupdescriptionandagroup for all the rock climbers of NYC. We really like to climb rocks and these NYC people climb any rock. No rock is too small or too big to be climbed. We will ascend upon all rocks, and not just in NYC, in the whole world. We are just getting started, NYC is just the beginning, watch out rocks in the galaxy. Kuiper belt I'm looking at you. We will put on a space suit and climb all your rocks. No rock is near nor far for the rock climbers of NYC.",
|
||||
};
|
||||
|
||||
export const GroupMessageRequest = Template.bind({});
|
||||
GroupMessageRequest.args = {
|
||||
...groupArgs,
|
||||
groupDescription: casual.sentence,
|
||||
acceptedMessageRequest: false,
|
||||
};
|
||||
|
||||
export const GroupFromTrustedContact = Template.bind({});
|
||||
GroupFromTrustedContact.args = {
|
||||
...groupArgs,
|
||||
fromOrAddedByTrustedContact: true,
|
||||
};
|
||||
|
||||
export function GroupMemberNames(args: Props): React.JSX.Element {
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { type ReactNode, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { Props as AvatarProps } from '../Avatar.dom.tsx';
|
||||
import { Avatar, AvatarSize, AvatarBlur } from '../Avatar.dom.tsx';
|
||||
import { ContactName } from './ContactName.dom.tsx';
|
||||
import { About } from './About.dom.tsx';
|
||||
import { GroupDescription } from './GroupDescription.dom.tsx';
|
||||
import { SharedGroupNames } from '../SharedGroupNames.dom.tsx';
|
||||
import { GroupMembersNames } from '../GroupMembersNames.dom.tsx';
|
||||
@@ -15,10 +13,11 @@ import type { HasStories } from '../../types/Stories.std.ts';
|
||||
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories.preload.ts';
|
||||
import type { GroupV2Membership } from './conversation-details/ConversationDetailsMembershipList.dom.tsx';
|
||||
import { StoryViewModeType } from '../../types/Stories.std.ts';
|
||||
import { Button, ButtonVariant } from '../Button.dom.tsx';
|
||||
import { SafetyTipsModal } from '../SafetyTipsModal.dom.tsx';
|
||||
import { I18n } from '../I18n.dom.tsx';
|
||||
import type { ContactModalStateType } from '../../types/globalModals.std.ts';
|
||||
import { tw } from '../../axo/tw.dom.tsx';
|
||||
import { AxoSymbol } from '../../axo/AxoSymbol.dom.tsx';
|
||||
import { AxoButton } from '../../axo/AxoButton.dom.tsx';
|
||||
|
||||
export type Props = {
|
||||
about?: string;
|
||||
@@ -26,10 +25,12 @@ export type Props = {
|
||||
fromOrAddedByTrustedContact?: boolean;
|
||||
groupDescription?: string;
|
||||
hasAvatar?: boolean;
|
||||
hasNickname: boolean;
|
||||
hasProfileName: boolean;
|
||||
hasStories?: HasStories;
|
||||
id: string;
|
||||
i18n: LocalizerType;
|
||||
isDirectConvoAndHasNickname?: boolean;
|
||||
isInSystemContacts: boolean;
|
||||
isMe: boolean;
|
||||
invitesCount?: number;
|
||||
isSignalConversation?: boolean;
|
||||
@@ -37,7 +38,6 @@ export type Props = {
|
||||
memberships: ReadonlyArray<GroupV2Membership>;
|
||||
openConversationDetails?: () => unknown;
|
||||
pendingAvatarDownload?: boolean;
|
||||
phoneNumber?: string;
|
||||
sharedGroupNames?: ReadonlyArray<string>;
|
||||
startAvatarDownload: () => void;
|
||||
theme: ThemeType;
|
||||
@@ -46,193 +46,13 @@ export type Props = {
|
||||
toggleProfileNameWarningModal: (conversationType?: string) => unknown;
|
||||
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
||||
|
||||
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 (
|
||||
<div className="module-conversation-hero__note-to-self">
|
||||
{i18n('icu:noteToSelfHero')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const safetyTipsButton = !acceptedMessageRequest ? (
|
||||
<div>
|
||||
<Button
|
||||
className="module-conversation-hero__safety-tips-button"
|
||||
variant={ButtonVariant.SecondaryAffirmative}
|
||||
onClick={() => {
|
||||
onToggleSafetyTips(true);
|
||||
}}
|
||||
>
|
||||
{i18n('icu:MessageRequestWarning__safety-tips')}
|
||||
</Button>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const shouldShowReviewCarefully =
|
||||
!acceptedMessageRequest &&
|
||||
(conversationType === 'group' || (sharedGroupNames?.length ?? 0) <= 1);
|
||||
|
||||
const reviewCarefullyLabel = shouldShowReviewCarefully ? (
|
||||
<div className="module-conversation-hero__review-carefully">
|
||||
<i className="module-conversation-hero__membership__review-carefully-icon" />
|
||||
{i18n('icu:ConversationHero--review-carefully')}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const sharedGroupsLabel =
|
||||
conversationType === 'direct' ? (
|
||||
<div>
|
||||
<i className="module-conversation-hero__membership__chevron" />
|
||||
<SharedGroupNames
|
||||
i18n={i18n}
|
||||
nameClassName="module-conversation-hero__membership__name"
|
||||
sharedGroupNames={sharedGroupNames ?? []}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const nameNotVerifiedLabel =
|
||||
!fromOrAddedByTrustedContact && !isDirectConvoAndHasNickname ? (
|
||||
<div className="module-conversation-hero__name-not-verified">
|
||||
<i
|
||||
className={classNames({
|
||||
'module-conversation-hero__group-question-icon':
|
||||
conversationType === 'group',
|
||||
'module-conversation-hero__direct-question-icon':
|
||||
conversationType === 'direct',
|
||||
})}
|
||||
/>
|
||||
<I18n
|
||||
components={{
|
||||
clickable: (parts: ReactNode) => (
|
||||
<button
|
||||
className="module-conversation-hero__name-not-verified__button"
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
onClickProfileNameWarning();
|
||||
}}
|
||||
>
|
||||
{parts}
|
||||
</button>
|
||||
),
|
||||
}}
|
||||
i18n={i18n}
|
||||
id={
|
||||
conversationType === 'group'
|
||||
? 'icu:ConversationHero--group-names'
|
||||
: 'icu:ConversationHero--profile-names'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const membersCountLabel =
|
||||
conversationType === 'group' ? (
|
||||
<div className="module-conversation-hero__membership__members-count">
|
||||
<i className="module-conversation-hero__members-count-icon" />
|
||||
<GroupMembersNames
|
||||
i18n={i18n}
|
||||
nameClassName="module-conversation-hero__membership__name"
|
||||
memberships={memberships}
|
||||
invitesCount={invitesCount}
|
||||
onOtherMembersClick={openConversationDetails}
|
||||
/>
|
||||
</div>
|
||||
) : 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 (
|
||||
<div className="module-conversation-hero__membership">
|
||||
{reviewCarefullyLabel}
|
||||
{nameNotVerifiedLabel}
|
||||
{sharedGroupsLabel}
|
||||
{membersCountLabel}
|
||||
{safetyTipsButton}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function ReleaseNotesExtraInformation({
|
||||
i18n,
|
||||
}: {
|
||||
i18n: LocalizerType;
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<div className="module-conversation-hero--release-notes-notice">
|
||||
<div className="module-conversation-hero__release-notes-notice-content">
|
||||
<i className="module-conversation-hero__release-notes-notice-check-icon" />
|
||||
{i18n('icu:ConversationHero--signal-official-chat')}
|
||||
</div>
|
||||
<div className="module-conversation-hero__release-notes-notice-content">
|
||||
<i className="module-conversation-hero__release-notes-notice-bell-icon" />
|
||||
{i18n('icu:ConversationHero--release-notes')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
||||
? Omit<T, K>
|
||||
: 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 ? (
|
||||
<SafetyTipsModal
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setIsShowingSafetyTips(false);
|
||||
}}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const avatar = (
|
||||
<ConversationAvatar
|
||||
avatarPlaceholderGradient={avatarPlaceholderGradient}
|
||||
avatarUrl={avatarUrl}
|
||||
badge={badge}
|
||||
blur={avatarBlur}
|
||||
conversationType={conversationType}
|
||||
color={color}
|
||||
i18n={i18n}
|
||||
hasAvatar={hasAvatar}
|
||||
loading={pendingAvatarDownload && !avatarUrl}
|
||||
noteToSelf={isMe}
|
||||
onClick={avatarOnClick}
|
||||
profileName={profileName}
|
||||
storyRing={isMe ? undefined : hasStories}
|
||||
theme={theme}
|
||||
title={title}
|
||||
/>
|
||||
);
|
||||
|
||||
if (isMe) {
|
||||
titleElem = (
|
||||
<ContactName
|
||||
isMe={isMe}
|
||||
title={i18n('icu:noteToSelf')}
|
||||
largeVerifiedBadge={isMe}
|
||||
/>
|
||||
);
|
||||
} else if (isSignalConversation || conversationType !== 'direct') {
|
||||
titleElem = (
|
||||
<ContactName
|
||||
isSignalConversation={isSignalConversation}
|
||||
title={title}
|
||||
largeVerifiedBadge={isSignalConversation}
|
||||
/>
|
||||
);
|
||||
} else if (title) {
|
||||
titleElem = (
|
||||
<button
|
||||
type="button"
|
||||
className="module-conversation-hero__title"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
toggleAboutContactModal({ contactId: id });
|
||||
}}
|
||||
>
|
||||
<ContactName title={title} />
|
||||
<i className="module-conversation-hero__title__chevron" />
|
||||
</button>
|
||||
return (
|
||||
<Root>
|
||||
{avatar}
|
||||
<Title title={i18n('icu:noteToSelf')} isMe />
|
||||
<div
|
||||
className={tw(
|
||||
'mt-2 text-center type-body-medium text-label-secondary'
|
||||
)}
|
||||
>
|
||||
{i18n('icu:noteToSelfHero')}
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="module-conversation-hero">
|
||||
<Avatar
|
||||
avatarPlaceholderGradient={avatarPlaceholderGradient}
|
||||
avatarUrl={avatarUrl}
|
||||
badge={badge}
|
||||
blur={avatarBlur}
|
||||
className="module-conversation-hero__avatar"
|
||||
color={color}
|
||||
conversationType={conversationType}
|
||||
i18n={i18n}
|
||||
hasAvatar={hasAvatar}
|
||||
loading={pendingAvatarDownload && !avatarUrl}
|
||||
noteToSelf={isMe}
|
||||
onClick={avatarOnClick}
|
||||
profileName={profileName}
|
||||
size={AvatarSize.EIGHTY}
|
||||
// user may have stories, but we don't show that on Note to Self conversation
|
||||
storyRing={isMe ? undefined : hasStories}
|
||||
theme={theme}
|
||||
if (isSignalConversation) {
|
||||
return (
|
||||
<Root>
|
||||
{avatar}
|
||||
<Title title={title} isSignalConversation />
|
||||
<div
|
||||
className={tw(
|
||||
'my-2 rounded-3xl bg-color-fill-primary/12 px-2.5 py-1 type-body-medium font-medium text-color-fill-primary'
|
||||
)}
|
||||
>
|
||||
<AxoSymbol.InlineGlyph symbol="officialbadge" label={null} />
|
||||
{i18n('icu:ConversationHero--signal-official-account')}
|
||||
</div>
|
||||
<div className={tw('text-center type-body-medium text-label-primary')}>
|
||||
{i18n('icu:ConversationHero--signal-official-account--description')}
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
if (conversationType === 'direct') {
|
||||
const nameIsVerified = hasNickname || isInSystemContacts;
|
||||
return (
|
||||
<Root>
|
||||
{avatar}
|
||||
<Title
|
||||
title={title}
|
||||
onClick={() => toggleAboutContactModal({ contactId: id })}
|
||||
/>
|
||||
<h1 className="module-conversation-hero__profile-name">{titleElem}</h1>
|
||||
{about && !isMe && (
|
||||
<div className="module-about__container">
|
||||
<About text={about} />
|
||||
</div>
|
||||
)}
|
||||
{!isMe && groupDescription ? (
|
||||
<div className="module-conversation-hero__with">
|
||||
|
||||
{hasProfileName && !nameIsVerified ? (
|
||||
<NameNotVerifiedWarning
|
||||
conversationType={conversationType}
|
||||
onClick={() => toggleProfileNameWarningModal(conversationType)}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={tw(
|
||||
'mt-2.5 text-center type-body-medium text-label-primary'
|
||||
)}
|
||||
>
|
||||
<AxoSymbol.InlineGlyph symbol="group" label={null} />
|
||||
|
||||
<SharedGroupNames
|
||||
i18n={i18n}
|
||||
sharedGroupNames={sharedGroupNames ?? []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!acceptedMessageRequest ? (
|
||||
<SafetyTips
|
||||
onShowSafetyTips={() => setIsShowingSafetyTips(true)}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : null}
|
||||
{maybeSafetyTips}
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
if (conversationType === 'group') {
|
||||
const nameIsVerified = Boolean(fromOrAddedByTrustedContact);
|
||||
return (
|
||||
<Root>
|
||||
{avatar}
|
||||
<Title title={title} />
|
||||
{!nameIsVerified ? (
|
||||
<NameNotVerifiedWarning
|
||||
conversationType={conversationType}
|
||||
onClick={() => toggleProfileNameWarningModal(conversationType)}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{groupDescription ? (
|
||||
<div className={tw('mt-2 w-full text-center text-label-secondary')}>
|
||||
<GroupDescription
|
||||
i18n={i18n}
|
||||
title={title}
|
||||
@@ -354,38 +232,140 @@ export function ConversationHero({
|
||||
/>
|
||||
</div>
|
||||
) : 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 && <ReleaseNotesExtraInformation i18n={i18n} />}
|
||||
</div>
|
||||
|
||||
{isShowingSafetyTips && (
|
||||
<SafetyTipsModal
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setIsShowingSafetyTips(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
<div
|
||||
className={tw(
|
||||
'mt-2.5 w-full text-center type-body-medium text-label-primary'
|
||||
)}
|
||||
>
|
||||
<AxoSymbol.InlineGlyph symbol="group" label={null} />
|
||||
|
||||
<GroupMembersNames
|
||||
i18n={i18n}
|
||||
memberships={memberships}
|
||||
invitesCount={invitesCount}
|
||||
onOtherMembersClick={openConversationDetails}
|
||||
/>
|
||||
</div>
|
||||
{!acceptedMessageRequest ? (
|
||||
<SafetyTips
|
||||
onShowSafetyTips={() => setIsShowingSafetyTips(true)}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : null}
|
||||
{maybeSafetyTips}
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
type RootProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
const Root: React.FC<RootProps> = props => {
|
||||
return (
|
||||
<div
|
||||
data-testid="conversation-hero"
|
||||
className={tw(
|
||||
'flex w-3xs flex-col items-center rounded-4xl border-2 border-background-secondary p-5 pt-0'
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ConversationAvatar: React.FC<
|
||||
DistributiveOmit<AvatarProps, 'size'>
|
||||
> = props => {
|
||||
return (
|
||||
<Avatar
|
||||
{...props}
|
||||
size={AvatarSize.SEVENTY_TWO}
|
||||
className={tw('-mt-4.5')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type TitleProps = {
|
||||
isMe?: boolean;
|
||||
isSignalConversation?: boolean;
|
||||
title: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const Title: React.FC<TitleProps> = props => {
|
||||
const className = tw('mt-3 text-center text-[20px] font-medium');
|
||||
const { onClick, title, isMe, isSignalConversation } = props;
|
||||
const contactName = (
|
||||
<ContactName
|
||||
title={title}
|
||||
isMe={isMe}
|
||||
isSignalConversation={isSignalConversation}
|
||||
/>
|
||||
);
|
||||
|
||||
if (onClick) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={className}
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
{contactName}
|
||||
|
||||
<span className={tw('text-[18px] text-label-secondary')}>
|
||||
<AxoSymbol.InlineGlyph symbol="chevron-[end]" label={null} />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={className}>{contactName}</div>;
|
||||
};
|
||||
|
||||
const NameNotVerifiedWarning: React.FC<{
|
||||
conversationType: 'direct' | 'group';
|
||||
onClick: () => void;
|
||||
i18n: LocalizerType;
|
||||
}> = ({ conversationType, onClick, i18n }) => {
|
||||
return (
|
||||
<button
|
||||
className={tw(
|
||||
'mt-2 rounded-3xl bg-color-fill-destructive/12 px-2.5 py-1',
|
||||
// oxlint-disable-next-line better-tailwindcss/no-restricted-classes
|
||||
'type-body-medium font-medium text-[#C84118]'
|
||||
)}
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
{conversationType === 'direct' ? (
|
||||
<AxoSymbol.InlineGlyph symbol="person-question" label={null} />
|
||||
) : (
|
||||
// TODO: DESKTOP-10050
|
||||
<AxoSymbol.InlineGlyph symbol="person-question" label={null} />
|
||||
)}
|
||||
{i18n('icu:ConversationHero--name-not-verified')}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const SafetyTips: React.FC<{
|
||||
onShowSafetyTips: () => void;
|
||||
i18n: LocalizerType;
|
||||
}> = ({ i18n, onShowSafetyTips }) => {
|
||||
return (
|
||||
<div className={tw('mt-3')}>
|
||||
<AxoButton.Root variant="secondary" size="md" onClick={onShowSafetyTips}>
|
||||
{i18n('icu:MessageRequestWarning__safety-tips-v2')}
|
||||
</AxoButton.Root>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<Modal
|
||||
modalName="ProfileNameWarningModal"
|
||||
moduleClassName="ProfileNameWarningModal"
|
||||
hasXButton
|
||||
i18n={i18n}
|
||||
onClose={onClose}
|
||||
>
|
||||
<i className="ProfileNameWarningModal__header-icon" />
|
||||
<div className="ProfileNameWarningModal__description">
|
||||
{i18n(DESCRIPTION_KEYS[conversationType])}
|
||||
</div>
|
||||
<ul className="ProfileNameWarningModal__list">
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item1[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item2[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item3[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
<AxoDialog.Root open onOpenChange={onClose}>
|
||||
<AxoDialog.Content
|
||||
size="sm"
|
||||
escape="cancel-is-noop"
|
||||
disableMissingAriaDescriptionWarning
|
||||
>
|
||||
<AxoDialog.Header>
|
||||
<AxoDialog.Close aria-label={i18n('icu:close')} />
|
||||
</AxoDialog.Header>
|
||||
<AxoDialog.Body padding="normal">
|
||||
<div className={tw('flex justify-center')}>
|
||||
<div
|
||||
className={tw(
|
||||
// oxlint-disable-next-line better-tailwindcss/no-restricted-classes
|
||||
'rounded-3xl bg-color-fill-destructive/12 px-4 py-1.5 type-title-large font-regular text-[#C84118]'
|
||||
)}
|
||||
>
|
||||
{conversationType === 'direct' ? (
|
||||
<AxoSymbol.InlineGlyph symbol="person-question" label={null} />
|
||||
) : (
|
||||
<AxoSymbol.InlineGlyph symbol="person-question" label={null} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={tw('mt-5 mb-12 type-body-medium text-label-primary')}>
|
||||
{conversationType === 'direct' ? (
|
||||
<>
|
||||
{i18n('icu:ProfileNameWarningModal__description--direct')}
|
||||
<ul className={tw('list-disc ps-4 [&>li]:mt-3')}>
|
||||
<li>
|
||||
{i18n(
|
||||
'icu:ProfileNameWarningModal__warning--signal-cant-verify'
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{i18n(
|
||||
'icu:ProfileNameWarningModal__warning--signal-wont-contact'
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{i18n('icu:ProfileNameWarningModal__warning--be-cautious')}
|
||||
</li>
|
||||
<li>
|
||||
{i18n(
|
||||
'icu:ProfileNameWarningModal__warning--dont-share-info'
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{i18n('icu:ProfileNameWarningModal__description--group')}
|
||||
<ul className={tw('list-disc ps-4 [&>li]:mt-3')}>
|
||||
<li>
|
||||
{i18n('icu:ProfileNameWarningModal__list--item1--group')}
|
||||
</li>
|
||||
<li>
|
||||
{i18n('icu:ProfileNameWarningModal__list--item2--group')}
|
||||
</li>
|
||||
<li>
|
||||
{i18n('icu:ProfileNameWarningModal__list--item3--group')}
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</AxoDialog.Body>
|
||||
</AxoDialog.Content>
|
||||
</AxoDialog.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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']}
|
||||
|
||||
@@ -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 (
|
||||
<ConversationHero
|
||||
avatarPlaceholderGradient={avatarPlaceholderGradient}
|
||||
about={about}
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarUrl={avatarUrl}
|
||||
badge={badge}
|
||||
color={color}
|
||||
conversationType={type}
|
||||
fromOrAddedByTrustedContact={fromOrAddedByTrustedContact}
|
||||
groupDescription={groupDescription}
|
||||
hasAvatar={hasAvatar}
|
||||
hasStories={hasStories}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
isDirectConvoAndHasNickname={isDirectConvoAndHasNickname}
|
||||
isMe={isMe}
|
||||
invitesCount={invitesCount}
|
||||
isSignalConversation={isSignalConversationValue}
|
||||
membersCount={membersCount}
|
||||
memberships={memberships}
|
||||
openConversationDetails={openConversationDetails}
|
||||
pendingAvatarDownload={isPendingAvatarDownload(id)}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
startAvatarDownload={() => startAvatarDownload(id)}
|
||||
theme={theme}
|
||||
title={title}
|
||||
toggleAboutContactModal={toggleAboutContactModal}
|
||||
toggleProfileNameWarningModal={toggleProfileNameWarningModal}
|
||||
viewUserStories={viewUserStories}
|
||||
/>
|
||||
<div className={tw('mt-10 flex justify-center')}>
|
||||
<ConversationHero
|
||||
avatarPlaceholderGradient={avatarPlaceholderGradient}
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarUrl={avatarUrl}
|
||||
badge={badge}
|
||||
color={color}
|
||||
conversationType={type}
|
||||
fromOrAddedByTrustedContact={fromOrAddedByTrustedContact}
|
||||
groupDescription={groupDescription}
|
||||
hasAvatar={hasAvatar}
|
||||
hasNickname={Boolean(nicknameGivenName || nicknameFamilyName)}
|
||||
hasProfileName={Boolean(profileName)}
|
||||
hasStories={hasStories}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
isMe={isMe}
|
||||
invitesCount={invitesCount}
|
||||
isInSystemContacts={isInSystemContacts(conversation)}
|
||||
isSignalConversation={isSignalConversationValue}
|
||||
membersCount={membersCount}
|
||||
memberships={memberships}
|
||||
openConversationDetails={openConversationDetails}
|
||||
pendingAvatarDownload={isPendingAvatarDownload(id)}
|
||||
profileName={profileName}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
startAvatarDownload={() => startAvatarDownload(id)}
|
||||
theme={theme}
|
||||
title={title}
|
||||
toggleAboutContactModal={toggleAboutContactModal}
|
||||
toggleProfileNameWarningModal={toggleProfileNameWarningModal}
|
||||
viewUserStories={viewUserStories}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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');
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user