AboutContactModal improvements

This commit is contained in:
Fedor Indutny 2024-02-16 14:16:13 -08:00 committed by GitHub
parent a569c04e2e
commit c772f2abc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 110 additions and 68 deletions

View file

@ -1394,6 +1394,10 @@
"messageformat": "About", "messageformat": "About",
"description": "Title of About modal" "description": "Title of About modal"
}, },
"icu:AboutContactModal__title--myself": {
"messageformat": "You",
"description": "Title of About modal when viewing your own information"
},
"icu:AboutContactModal__verified": { "icu:AboutContactModal__verified": {
"messageformat": "Verified", "messageformat": "Verified",
"description": "Text of a button on About modal leading to a safety number modal" "description": "Text of a button on About modal leading to a safety number modal"

View file

@ -27,6 +27,7 @@
&__title { &__title {
@include font-title-2; @include font-title-2;
font-weight: 500;
margin: 0; margin: 0;
margin-block-end: 4px; margin-block-end: 4px;

View file

@ -23,8 +23,6 @@ import { ConfirmationDialog } from './ConfirmationDialog';
import { FormattingWarningModal } from './FormattingWarningModal'; import { FormattingWarningModal } from './FormattingWarningModal';
import { SendEditWarningModal } from './SendEditWarningModal'; import { SendEditWarningModal } from './SendEditWarningModal';
import { SignalConnectionsModal } from './SignalConnectionsModal'; import { SignalConnectionsModal } from './SignalConnectionsModal';
import { AboutContactModal } from './conversation/AboutContactModal';
import type { ExternalPropsType as AboutContactModalPropsType } from './conversation/AboutContactModal';
import { WhatsNewModal } from './WhatsNewModal'; import { WhatsNewModal } from './WhatsNewModal';
// NOTE: All types should be required for this component so that the smart // NOTE: All types should be required for this component so that the smart
@ -76,8 +74,8 @@ export type PropsType = {
isSignalConnectionsVisible: boolean; isSignalConnectionsVisible: boolean;
toggleSignalConnectionsModal: () => unknown; toggleSignalConnectionsModal: () => unknown;
// AboutContactModal // AboutContactModal
aboutContactModalProps: AboutContactModalPropsType | undefined; isAboutContactModalVisible: boolean;
toggleAboutContactModal: () => unknown; renderAboutContactModal: () => JSX.Element | null;
// StickerPackPreviewModal // StickerPackPreviewModal
stickerPackPreviewId: string | undefined; stickerPackPreviewId: string | undefined;
renderStickerPreviewModal: () => JSX.Element | null; renderStickerPreviewModal: () => JSX.Element | null;
@ -145,8 +143,8 @@ export function GlobalModalContainer({
isSignalConnectionsVisible, isSignalConnectionsVisible,
toggleSignalConnectionsModal, toggleSignalConnectionsModal,
// AboutContactModal // AboutContactModal
aboutContactModalProps, isAboutContactModalVisible,
toggleAboutContactModal, renderAboutContactModal,
// StickerPackPreviewModal // StickerPackPreviewModal
stickerPackPreviewId, stickerPackPreviewId,
renderStickerPreviewModal, renderStickerPreviewModal,
@ -260,14 +258,8 @@ export function GlobalModalContainer({
return renderSafetyNumber(); return renderSafetyNumber();
} }
if (aboutContactModalProps) { if (isAboutContactModalVisible) {
return ( return renderAboutContactModal();
<AboutContactModal
i18n={i18n}
onClose={toggleAboutContactModal}
{...aboutContactModalProps}
/>
);
} }
if (contactModalState) { if (contactModalState) {

View file

@ -44,6 +44,11 @@ const systemContact = getDefaultConversation({
phoneNumber: '+1 555 123-4567', phoneNumber: '+1 555 123-4567',
hasMessages: true, hasMessages: true,
}); });
const me = getDefaultConversation({
isMe: true,
acceptedMessageRequest: true,
hasMessages: true,
});
export default { export default {
title: 'Components/Conversation/AboutContactModal', title: 'Components/Conversation/AboutContactModal',
@ -67,6 +72,10 @@ export function Defaults(args: PropsType): JSX.Element {
return <AboutContactModal {...args} />; return <AboutContactModal {...args} />;
} }
export function Me(args: PropsType): JSX.Element {
return <AboutContactModal {...args} conversation={me} />;
}
export function Verified(args: PropsType): JSX.Element { export function Verified(args: PropsType): JSX.Element {
return <AboutContactModal {...args} conversation={verifiedConversation} />; return <AboutContactModal {...args} conversation={verifiedConversation} />;
} }

View file

@ -15,10 +15,6 @@ import { About } from './About';
export type PropsType = Readonly<{ export type PropsType = Readonly<{
i18n: LocalizerType; i18n: LocalizerType;
onClose: () => void; onClose: () => void;
}> &
ExternalPropsType;
export type ExternalPropsType = Readonly<{
conversation: ConversationType; conversation: ConversationType;
isSignalConnection: boolean; isSignalConnection: boolean;
toggleSignalConnectionsModal: () => void; toggleSignalConnectionsModal: () => void;
@ -37,6 +33,8 @@ export function AboutContactModal({
unblurAvatar, unblurAvatar,
onClose, onClose,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
const { isMe } = conversation;
useEffect(() => { useEffect(() => {
// Kick off the expensive hydration of the current sharedGroupNames // Kick off the expensive hydration of the current sharedGroupNames
updateSharedGroups(conversation.id); updateSharedGroups(conversation.id);
@ -70,7 +68,9 @@ export function AboutContactModal({
let statusRow: JSX.Element | undefined; let statusRow: JSX.Element | undefined;
if (conversation.isBlocked) { if (isMe) {
// No status for ourselves
} else if (conversation.isBlocked) {
statusRow = ( statusRow = (
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--blocked" /> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--blocked" />
@ -127,7 +127,9 @@ export function AboutContactModal({
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<h3 className="AboutContactModal__title"> <h3 className="AboutContactModal__title">
{i18n('icu:AboutContactModal__title')} {isMe
? i18n('icu:AboutContactModal__title--myself')
: i18n('icu:AboutContactModal__title')}
</h3> </h3>
</div> </div>
@ -136,7 +138,7 @@ export function AboutContactModal({
<UserText text={conversation.title} /> <UserText text={conversation.title} />
</div> </div>
{conversation.isVerified ? ( {!isMe && conversation.isVerified ? (
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--verified" /> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--verified" />
<button <button
@ -149,7 +151,7 @@ export function AboutContactModal({
</div> </div>
) : null} ) : null}
{conversation.about ? ( {!isMe && conversation.about ? (
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--about" /> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--about" />
<About <About
@ -159,7 +161,7 @@ export function AboutContactModal({
</div> </div>
) : null} ) : null}
{isSignalConnection ? ( {!isMe && isSignalConnection ? (
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--connections" /> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--connections" />
<button <button
@ -172,7 +174,7 @@ export function AboutContactModal({
</div> </div>
) : null} ) : null}
{isInSystemContacts(conversation) ? ( {!isMe && isInSystemContacts(conversation) ? (
<div className="AboutContactModal__row"> <div className="AboutContactModal__row">
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--person" /> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--person" />
{i18n('icu:AboutContactModal__system-contact', { {i18n('icu:AboutContactModal__system-contact', {
@ -188,15 +190,17 @@ export function AboutContactModal({
</div> </div>
) : null} ) : null}
<div className="AboutContactModal__row"> {!isMe && (
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--group" /> <div className="AboutContactModal__row">
<div> <i className="AboutContactModal__row__icon AboutContactModal__row__icon--group" />
<SharedGroupNames <div>
i18n={i18n} <SharedGroupNames
sharedGroupNames={conversation.sharedGroupNames || []} i18n={i18n}
/> sharedGroupNames={conversation.sharedGroupNames || []}
/>
</div>
</div> </div>
</div> )}
{statusRow} {statusRow}
</Modal> </Modal>

View file

@ -77,13 +77,10 @@ type MigrateToGV2PropsType = ReadonlyDeep<{
hasMigrated: boolean; hasMigrated: boolean;
invitedMemberIds: Array<string>; invitedMemberIds: Array<string>;
}>; }>;
export type AboutContactModalPropsType = ReadonlyDeep<{
contactId: string;
}>;
export type GlobalModalsStateType = ReadonlyDeep<{ export type GlobalModalsStateType = ReadonlyDeep<{
addUserToAnotherGroupModalContactId?: string; addUserToAnotherGroupModalContactId?: string;
aboutContactModalProps?: AboutContactModalPropsType; aboutContactModalContactId?: string;
authArtCreatorData?: AuthorizeArtCreatorDataType; authArtCreatorData?: AuthorizeArtCreatorDataType;
contactModalState?: ContactModalStateType; contactModalState?: ContactModalStateType;
deleteMessagesProps?: DeleteMessagesPropsType; deleteMessagesProps?: DeleteMessagesPropsType;
@ -237,7 +234,7 @@ type ToggleAddUserToAnotherGroupModalActionType = ReadonlyDeep<{
type ToggleAboutContactModalActionType = ReadonlyDeep<{ type ToggleAboutContactModalActionType = ReadonlyDeep<{
type: typeof TOGGLE_ABOUT_MODAL; type: typeof TOGGLE_ABOUT_MODAL;
payload: AboutContactModalPropsType | undefined; payload: string | undefined;
}>; }>;
type ToggleSignalConnectionsModalActionType = ReadonlyDeep<{ type ToggleSignalConnectionsModalActionType = ReadonlyDeep<{
@ -644,7 +641,7 @@ function toggleAboutContactModal(
): ToggleAboutContactModalActionType { ): ToggleAboutContactModalActionType {
return { return {
type: TOGGLE_ABOUT_MODAL, type: TOGGLE_ABOUT_MODAL,
payload: contactId ? { contactId } : undefined, payload: contactId,
}; };
} }
@ -915,7 +912,7 @@ export function reducer(
if (action.type === TOGGLE_ABOUT_MODAL) { if (action.type === TOGGLE_ABOUT_MODAL) {
return { return {
...state, ...state,
aboutContactModalProps: action.payload, aboutContactModalContactId: action.payload,
}; };
} }
@ -965,6 +962,14 @@ export function reducer(
} }
if (action.type === SHOW_CONTACT_MODAL) { if (action.type === SHOW_CONTACT_MODAL) {
const ourId = window.ConversationController.getOurConversationIdOrThrow();
if (action.payload.contactId === ourId) {
return {
...state,
aboutContactModalContactId: ourId,
};
}
return { return {
...state, ...state,
contactModalState: action.payload, contactModalState: action.payload,

View file

@ -0,0 +1,47 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { useSelector } from 'react-redux';
import { AboutContactModal } from '../../components/conversation/AboutContactModal';
import { isSignalConnection } from '../../util/getSignalConnections';
import { getIntl } from '../selectors/user';
import { getGlobalModalsState } from '../selectors/globalModals';
import { getConversationSelector } from '../selectors/conversations';
import { useConversationsActions } from '../ducks/conversations';
import { useGlobalModalActions } from '../ducks/globalModals';
export function SmartAboutContactModal(): JSX.Element | null {
const i18n = useSelector(getIntl);
const globalModals = useSelector(getGlobalModalsState);
const { aboutContactModalContactId: contactId } = globalModals;
const getConversation = useSelector(getConversationSelector);
const { updateSharedGroups, unblurAvatar } = useConversationsActions();
const {
toggleAboutContactModal,
toggleSignalConnectionsModal,
toggleSafetyNumberModal,
} = useGlobalModalActions();
if (!contactId) {
return null;
}
const conversation = getConversation(contactId);
return (
<AboutContactModal
i18n={i18n}
conversation={conversation}
updateSharedGroups={updateSharedGroups}
unblurAvatar={unblurAvatar}
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
toggleSafetyNumberModal={toggleSafetyNumberModal}
isSignalConnection={isSignalConnection(conversation)}
onClose={toggleAboutContactModal}
/>
);
}

View file

@ -6,10 +6,9 @@ import { useSelector } from 'react-redux';
import type { GlobalModalsStateType } from '../ducks/globalModals'; import type { GlobalModalsStateType } from '../ducks/globalModals';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import { isSignalConnection } from '../../util/getSignalConnections';
import type { ExternalPropsType as AboutContactModalPropsType } from '../../components/conversation/AboutContactModal';
import { ErrorModal } from '../../components/ErrorModal'; import { ErrorModal } from '../../components/ErrorModal';
import { GlobalModalContainer } from '../../components/GlobalModalContainer'; import { GlobalModalContainer } from '../../components/GlobalModalContainer';
import { SmartAboutContactModal } from './AboutContactModal';
import { SmartAddUserToAnotherGroupModal } from './AddUserToAnotherGroupModal'; import { SmartAddUserToAnotherGroupModal } from './AddUserToAnotherGroupModal';
import { SmartContactModal } from './ContactModal'; import { SmartContactModal } from './ContactModal';
import { SmartEditHistoryMessagesModal } from './EditHistoryMessagesModal'; import { SmartEditHistoryMessagesModal } from './EditHistoryMessagesModal';
@ -21,13 +20,9 @@ import { SmartSendAnywayDialog } from './SendAnywayDialog';
import { SmartShortcutGuideModal } from './ShortcutGuideModal'; import { SmartShortcutGuideModal } from './ShortcutGuideModal';
import { SmartStickerPreviewModal } from './StickerPreviewModal'; import { SmartStickerPreviewModal } from './StickerPreviewModal';
import { SmartStoriesSettingsModal } from './StoriesSettingsModal'; import { SmartStoriesSettingsModal } from './StoriesSettingsModal';
import { import { getConversationsStoppingSend } from '../selectors/conversations';
getConversationSelector,
getConversationsStoppingSend,
} from '../selectors/conversations';
import { getIntl, getTheme } from '../selectors/user'; import { getIntl, getTheme } from '../selectors/user';
import { useGlobalModalActions } from '../ducks/globalModals'; import { useGlobalModalActions } from '../ducks/globalModals';
import { useConversationsActions } from '../ducks/conversations';
import { SmartDeleteMessagesModal } from './DeleteMessagesModal'; import { SmartDeleteMessagesModal } from './DeleteMessagesModal';
function renderEditHistoryMessagesModal(): JSX.Element { function renderEditHistoryMessagesModal(): JSX.Element {
@ -66,16 +61,19 @@ function renderShortcutGuideModal(): JSX.Element {
return <SmartShortcutGuideModal />; return <SmartShortcutGuideModal />;
} }
function renderAboutContactModal(): JSX.Element {
return <SmartAboutContactModal />;
}
export function SmartGlobalModalContainer(): JSX.Element { export function SmartGlobalModalContainer(): JSX.Element {
const conversationsStoppingSend = useSelector(getConversationsStoppingSend); const conversationsStoppingSend = useSelector(getConversationsStoppingSend);
const getConversation = useSelector(getConversationSelector);
const i18n = useSelector(getIntl); const i18n = useSelector(getIntl);
const theme = useSelector(getTheme); const theme = useSelector(getTheme);
const hasSafetyNumberChangeModal = conversationsStoppingSend.length > 0; const hasSafetyNumberChangeModal = conversationsStoppingSend.length > 0;
const { const {
aboutContactModalProps: aboutContactModalRawProps, aboutContactModalContactId,
addUserToAnotherGroupModalContactId, addUserToAnotherGroupModalContactId,
authArtCreatorData, authArtCreatorData,
contactModalState, contactModalState,
@ -108,27 +106,9 @@ export function SmartGlobalModalContainer(): JSX.Element {
hideWhatsNewModal, hideWhatsNewModal,
showFormattingWarningModal, showFormattingWarningModal,
showSendEditWarningModal, showSendEditWarningModal,
toggleAboutContactModal,
toggleSignalConnectionsModal, toggleSignalConnectionsModal,
toggleSafetyNumberModal,
} = useGlobalModalActions(); } = useGlobalModalActions();
const { updateSharedGroups, unblurAvatar } = useConversationsActions();
let aboutContactModalProps: AboutContactModalPropsType | undefined;
if (aboutContactModalRawProps) {
const conversation = getConversation(aboutContactModalRawProps.contactId);
aboutContactModalProps = {
conversation,
isSignalConnection: isSignalConnection(conversation),
toggleSignalConnectionsModal,
toggleSafetyNumberModal,
updateSharedGroups,
unblurAvatar,
};
}
const renderAddUserToAnotherGroup = useCallback(() => { const renderAddUserToAnotherGroup = useCallback(() => {
return ( return (
<SmartAddUserToAnotherGroupModal <SmartAddUserToAnotherGroupModal
@ -166,7 +146,6 @@ export function SmartGlobalModalContainer(): JSX.Element {
return ( return (
<GlobalModalContainer <GlobalModalContainer
aboutContactModalProps={aboutContactModalProps}
addUserToAnotherGroupModalContactId={addUserToAnotherGroupModalContactId} addUserToAnotherGroupModalContactId={addUserToAnotherGroupModalContactId}
contactModalState={contactModalState} contactModalState={contactModalState}
editHistoryMessages={editHistoryMessages} editHistoryMessages={editHistoryMessages}
@ -178,11 +157,13 @@ export function SmartGlobalModalContainer(): JSX.Element {
hideUserNotFoundModal={hideUserNotFoundModal} hideUserNotFoundModal={hideUserNotFoundModal}
hideWhatsNewModal={hideWhatsNewModal} hideWhatsNewModal={hideWhatsNewModal}
i18n={i18n} i18n={i18n}
isAboutContactModalVisible={aboutContactModalContactId != null}
isProfileEditorVisible={isProfileEditorVisible} isProfileEditorVisible={isProfileEditorVisible}
isShortcutGuideModalVisible={isShortcutGuideModalVisible} isShortcutGuideModalVisible={isShortcutGuideModalVisible}
isSignalConnectionsVisible={isSignalConnectionsVisible} isSignalConnectionsVisible={isSignalConnectionsVisible}
isStoriesSettingsVisible={isStoriesSettingsVisible} isStoriesSettingsVisible={isStoriesSettingsVisible}
isWhatsNewVisible={isWhatsNewVisible} isWhatsNewVisible={isWhatsNewVisible}
renderAboutContactModal={renderAboutContactModal}
renderAddUserToAnotherGroup={renderAddUserToAnotherGroup} renderAddUserToAnotherGroup={renderAddUserToAnotherGroup}
renderContactModal={renderContactModal} renderContactModal={renderContactModal}
renderEditHistoryMessagesModal={renderEditHistoryMessagesModal} renderEditHistoryMessagesModal={renderEditHistoryMessagesModal}
@ -203,7 +184,6 @@ export function SmartGlobalModalContainer(): JSX.Element {
showSendEditWarningModal={showSendEditWarningModal} showSendEditWarningModal={showSendEditWarningModal}
stickerPackPreviewId={stickerPackPreviewId} stickerPackPreviewId={stickerPackPreviewId}
theme={theme} theme={theme}
toggleAboutContactModal={toggleAboutContactModal}
toggleSignalConnectionsModal={toggleSignalConnectionsModal} toggleSignalConnectionsModal={toggleSignalConnectionsModal}
userNotFoundModalState={userNotFoundModalState} userNotFoundModalState={userNotFoundModalState}
usernameOnboardingState={usernameOnboardingState} usernameOnboardingState={usernameOnboardingState}