Add edit nickname to contact modal for group members

This commit is contained in:
Jamie Kyle 2024-03-27 17:32:47 -07:00 committed by GitHub
parent cf02337d6d
commit 2fbbcdf358
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 53 additions and 1 deletions

View file

@ -4846,6 +4846,10 @@
"messageformat": "Include muted chats in badge count", "messageformat": "Include muted chats in badge count",
"description": "Description for counting muted conversations in badge setting" "description": "Description for counting muted conversations in badge setting"
}, },
"icu:ContactModal--nickname": {
"messageformat": "Nickname",
"description": "Label for the nickname field in the Group Contact Details modal"
},
"icu:ContactModal--message": { "icu:ContactModal--message": {
"messageformat": "Message", "messageformat": "Message",
"description": "(Deleted 2024/03/07) Button text for send message button in Group Contact Details modal" "description": "(Deleted 2024/03/07) Button text for send message button in Group Contact Details modal"

View file

@ -115,6 +115,19 @@
width: 20px; width: 20px;
} }
&__nickname__bubble-icon {
height: 20px;
width: 20px;
@include light-theme {
@include color-svg('../images/icons/v3/edit/edit.svg', $color-gray-75);
}
@include dark-theme {
@include color-svg('../images/icons/v3/edit/edit.svg', $color-gray-15);
}
}
&__send-message__bubble-icon { &__send-message__bubble-icon {
height: 20px; height: 20px;
width: 20px; width: 20px;

View file

@ -14,6 +14,7 @@ import type {
import { Input } from './Input'; import { Input } from './Input';
import { AutoSizeTextArea } from './AutoSizeTextArea'; import { AutoSizeTextArea } from './AutoSizeTextArea';
import { Button, ButtonVariant } from './Button'; import { Button, ButtonVariant } from './Button';
import { strictAssert } from '../util/assert';
const formSchema = z.object({ const formSchema = z.object({
nickname: z nickname: z
@ -43,6 +44,11 @@ export function EditNicknameAndNoteModal({
onSave, onSave,
onClose, onClose,
}: EditNicknameAndNoteModalProps): JSX.Element { }: EditNicknameAndNoteModalProps): JSX.Element {
strictAssert(
conversation.type === 'direct',
'Expected a direct conversation'
);
const [givenName, setGivenName] = useState( const [givenName, setGivenName] = useState(
conversation.nicknameGivenName ?? '' conversation.nicknameGivenName ?? ''
); );

View file

@ -25,6 +25,7 @@ import { UserText } from '../UserText';
import { Button, ButtonIconType, ButtonVariant } from '../Button'; import { Button, ButtonIconType, ButtonVariant } from '../Button';
import { isInSystemContacts } from '../../util/isInSystemContacts'; import { isInSystemContacts } from '../../util/isInSystemContacts';
import { InContactsIcon } from '../InContactsIcon'; import { InContactsIcon } from '../InContactsIcon';
import { areNicknamesEnabled } from '../../util/nicknames';
export type PropsDataType = { export type PropsDataType = {
areWeASubscriber: boolean; areWeASubscriber: boolean;
@ -43,6 +44,7 @@ export type PropsDataType = {
type PropsActionType = { type PropsActionType = {
blockConversation: (id: string) => void; blockConversation: (id: string) => void;
hideContactModal: () => void; hideContactModal: () => void;
onOpenEditNicknameAndNoteModal: () => void;
onOutgoingAudioCallInConversation: (conversationId: string) => unknown; onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
onOutgoingVideoCallInConversation: (conversationId: string) => unknown; onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
removeMemberFromGroup: (conversationId: string, contactId: string) => void; removeMemberFromGroup: (conversationId: string, contactId: string) => void;
@ -83,6 +85,7 @@ export function ContactModal({
i18n, i18n,
isAdmin, isAdmin,
isMember, isMember,
onOpenEditNicknameAndNoteModal,
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
removeMemberFromGroup, removeMemberFromGroup,
@ -303,6 +306,19 @@ export function ContactModal({
)} )}
<div className="ContactModal__divider" /> <div className="ContactModal__divider" />
<div className="ContactModal__button-container"> <div className="ContactModal__button-container">
{areNicknamesEnabled() && !contact.isMe && (
<button
type="button"
className="ContactModal__button ContactModal__block"
onClick={onOpenEditNicknameAndNoteModal}
>
<div className="ContactModal__bubble-icon">
<div className="ContactModal__nickname__bubble-icon" />
</div>
<span>{i18n('icu:ContactModal--nickname')}</span>
</button>
)}
{!contact.isMe && ( {!contact.isMe && (
<button <button
type="button" type="button"

View file

@ -4698,6 +4698,11 @@ function updateNicknameAndNote(
'updateNicknameAndNote: Conversation not found' 'updateNicknameAndNote: Conversation not found'
); );
strictAssert(
isDirectConversation(conversationModel.attributes),
'updateNicknameAndNote: Conversation is not a group'
);
conversationModel.set({ conversationModel.set({
nicknameGivenName: nickname?.givenName, nicknameGivenName: nickname?.givenName,
nicknameFamilyName: nickname?.familyName, nicknameFamilyName: nickname?.familyName,

View file

@ -1,7 +1,7 @@
// Copyright 2020 Signal Messenger, LLC // Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React, { memo, useMemo } from 'react'; import React, { memo, useCallback, useMemo } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { ContactModal } from '../../components/conversation/ContactModal'; import { ContactModal } from '../../components/conversation/ContactModal';
import { getAreWeASubscriber } from '../selectors/items'; import { getAreWeASubscriber } from '../selectors/items';
@ -15,6 +15,7 @@ import { useConversationsActions } from '../ducks/conversations';
import { useGlobalModalActions } from '../ducks/globalModals'; import { useGlobalModalActions } from '../ducks/globalModals';
import { useCallingActions } from '../ducks/calling'; import { useCallingActions } from '../ducks/calling';
import { getContactModalState } from '../selectors/globalModals'; import { getContactModalState } from '../selectors/globalModals';
import { strictAssert } from '../../util/assert';
export const SmartContactModal = memo(function SmartContactModal() { export const SmartContactModal = memo(function SmartContactModal() {
const i18n = useSelector(getIntl); const i18n = useSelector(getIntl);
@ -56,12 +57,18 @@ export const SmartContactModal = memo(function SmartContactModal() {
toggleAddUserToAnotherGroupModal, toggleAddUserToAnotherGroupModal,
toggleSafetyNumberModal, toggleSafetyNumberModal,
hideContactModal, hideContactModal,
toggleEditNicknameAndNoteModal,
} = useGlobalModalActions(); } = useGlobalModalActions();
const { const {
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,
} = useCallingActions(); } = useCallingActions();
const handleOpenEditNicknameAndNoteModal = useCallback(() => {
strictAssert(contactId != null, 'Expected conversationId to be set');
toggleEditNicknameAndNoteModal({ conversationId: contactId });
}, [toggleEditNicknameAndNoteModal, contactId]);
return ( return (
<ContactModal <ContactModal
areWeAdmin={areWeAdmin} areWeAdmin={areWeAdmin}
@ -76,6 +83,7 @@ export const SmartContactModal = memo(function SmartContactModal() {
i18n={i18n} i18n={i18n}
isAdmin={isAdmin} isAdmin={isAdmin}
isMember={isMember} isMember={isMember}
onOpenEditNicknameAndNoteModal={handleOpenEditNicknameAndNoteModal}
onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation} onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation}
onOutgoingVideoCallInConversation={onOutgoingVideoCallInConversation} onOutgoingVideoCallInConversation={onOutgoingVideoCallInConversation}
removeMemberFromGroup={removeMemberFromGroup} removeMemberFromGroup={removeMemberFromGroup}