First-class profile name rendering
This commit is contained in:
parent
632cd0e87e
commit
d07b8e82b2
63 changed files with 1044 additions and 454 deletions
|
@ -715,6 +715,10 @@
|
|||
"message": "Typing animation for this conversation",
|
||||
"description": "Used as the 'title' attibute for the typing animation"
|
||||
},
|
||||
"contactInAddressBook": {
|
||||
"message": "This person is in your contacts.",
|
||||
"description": "Description of icon denoting that contact is from your address book"
|
||||
},
|
||||
"contactAvatarAlt": {
|
||||
"message": "Avatar for contact $name$",
|
||||
"description": "Used in the alt tag for the image avatar of a contact",
|
||||
|
|
|
@ -467,6 +467,7 @@
|
|||
uuid: this.get('uuid'),
|
||||
e164: this.get('e164'),
|
||||
|
||||
isAccepted: this.getAccepted(),
|
||||
isArchived: this.get('isArchived'),
|
||||
isBlocked: this.isBlocked(),
|
||||
isVerified: this.isVerified(),
|
||||
|
@ -477,7 +478,7 @@
|
|||
isMe: this.isMe(),
|
||||
typingContact: typingContact ? typingContact.format() : null,
|
||||
lastUpdated: this.get('timestamp'),
|
||||
name: this.getName(),
|
||||
name: this.get('name'),
|
||||
profileName: this.getProfileName(),
|
||||
timestamp,
|
||||
inboxPosition,
|
||||
|
@ -589,10 +590,9 @@
|
|||
const receiptSpecs = readMessages.map(m => ({
|
||||
senderE164: m.get('source'),
|
||||
senderUuid: m.get('sourceUuid'),
|
||||
senderId: ConversationController.get({
|
||||
senderId: ConversationController.ensureContactIds({
|
||||
e164: m.get('source'),
|
||||
uuid: m.get('sourceUuid'),
|
||||
lowTrust: true,
|
||||
}),
|
||||
timestamp: m.get('sent_at'),
|
||||
hasErrors: m.hasErrors(),
|
||||
|
@ -2651,16 +2651,14 @@
|
|||
});
|
||||
},
|
||||
|
||||
getName() {
|
||||
if (this.isPrivate()) {
|
||||
return this.get('name');
|
||||
}
|
||||
return this.get('name') || i18n('unknownGroup');
|
||||
},
|
||||
|
||||
getTitle() {
|
||||
if (this.isPrivate()) {
|
||||
return this.get('name') || this.getNumber() || i18n('unknownContact');
|
||||
return (
|
||||
this.get('name') ||
|
||||
this.getProfileName() ||
|
||||
this.getNumber() ||
|
||||
i18n('unknownContact')
|
||||
);
|
||||
}
|
||||
return this.get('name') || i18n('unknownGroup');
|
||||
},
|
||||
|
@ -2675,24 +2673,6 @@
|
|||
return null;
|
||||
},
|
||||
|
||||
getDisplayName() {
|
||||
if (!this.isPrivate()) {
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
const name = this.get('name');
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
const profileName = this.get('profileName');
|
||||
if (profileName) {
|
||||
return `${this.getNumber()} ~${profileName}`;
|
||||
}
|
||||
|
||||
return this.getNumber();
|
||||
},
|
||||
|
||||
getNumber() {
|
||||
if (!this.isPrivate()) {
|
||||
return '';
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
status: this.getMessagePropStatus(),
|
||||
contact: this.getPropsForEmbeddedContact(),
|
||||
canReply: this.canReply(),
|
||||
authorTitle: contact.title,
|
||||
authorColor,
|
||||
authorName: contact.name,
|
||||
authorProfileName: contact.profileName,
|
||||
|
@ -781,7 +782,8 @@
|
|||
ourRegionCode: regionCode,
|
||||
});
|
||||
const authorProfileName = contact ? contact.getProfileName() : null;
|
||||
const authorName = contact ? contact.getName() : null;
|
||||
const authorName = contact ? contact.get('name') : null;
|
||||
const authorTitle = contact ? contact.getTitle() : null;
|
||||
const isFromMe = contact ? contact.isMe() : false;
|
||||
const firstAttachment = quote.attachments && quote.attachments[0];
|
||||
|
||||
|
@ -795,6 +797,7 @@
|
|||
authorId: author,
|
||||
authorPhoneNumber,
|
||||
authorProfileName,
|
||||
authorTitle,
|
||||
authorName,
|
||||
authorColor,
|
||||
referencedMessageNotFound,
|
||||
|
@ -891,7 +894,7 @@
|
|||
if (fromContact.isMe()) {
|
||||
messages.push(i18n('youUpdatedTheGroup'));
|
||||
} else {
|
||||
messages.push(i18n('updatedTheGroup', fromContact.getDisplayName()));
|
||||
messages.push(i18n('updatedTheGroup', fromContact.getTitle()));
|
||||
}
|
||||
|
||||
if (groupUpdate.joined && groupUpdate.joined.length) {
|
||||
|
@ -906,9 +909,7 @@
|
|||
messages.push(
|
||||
i18n(
|
||||
'multipleJoinedTheGroup',
|
||||
_.map(joinedWithoutMe, contact =>
|
||||
contact.getDisplayName()
|
||||
).join(', ')
|
||||
_.map(joinedWithoutMe, contact => contact.getTitle()).join(', ')
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -924,7 +925,7 @@
|
|||
messages.push(i18n('youJoinedTheGroup'));
|
||||
} else {
|
||||
messages.push(
|
||||
i18n('joinedTheGroup', joinedContacts[0].getDisplayName())
|
||||
i18n('joinedTheGroup', joinedContacts[0].getTitle())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1018,7 +1019,7 @@
|
|||
if (!conversation) {
|
||||
return number;
|
||||
}
|
||||
return conversation.getDisplayName();
|
||||
return conversation.getTitle();
|
||||
},
|
||||
onDestroy() {
|
||||
this.cleanup();
|
||||
|
@ -2346,11 +2347,11 @@
|
|||
|
||||
if (
|
||||
// Avatar added
|
||||
!existingAvatar ||
|
||||
(!existingAvatar && avatarAttachment) ||
|
||||
// Avatar changed
|
||||
(existingAvatar && existingAvatar.hash !== hash) ||
|
||||
// Avatar removed
|
||||
avatarAttachment === null
|
||||
(existingAvatar && !avatarAttachment)
|
||||
) {
|
||||
// Removes existing avatar from disk
|
||||
if (existingAvatar && existingAvatar.path) {
|
||||
|
@ -2396,10 +2397,11 @@
|
|||
conversation.set({ addedBy: message.getContactId() });
|
||||
}
|
||||
} else if (dataMessage.group.type === GROUP_TYPES.QUIT) {
|
||||
const sender = ConversationController.ensureContactIds({
|
||||
const senderId = ConversationController.ensureContactIds({
|
||||
e164: source,
|
||||
uuid: sourceUuid,
|
||||
});
|
||||
const sender = ConversationController.get(senderId);
|
||||
const inGroup = Boolean(
|
||||
sender &&
|
||||
(conversation.get('members') || []).includes(sender.id)
|
||||
|
|
|
@ -26,21 +26,12 @@
|
|||
this.contactView = null;
|
||||
}
|
||||
|
||||
const isMe = this.model.isMe();
|
||||
|
||||
this.contactView = new Whisper.ReactWrapperView({
|
||||
className: 'contact-wrapper',
|
||||
Component: window.Signal.Components.ContactListItem,
|
||||
props: {
|
||||
isMe,
|
||||
color: this.model.getColor(),
|
||||
avatarPath: this.model.getAvatarPath(),
|
||||
phoneNumber: this.model.getNumber(),
|
||||
name: this.model.getName(),
|
||||
profileName: this.model.getProfileName(),
|
||||
verified: this.model.isVerified(),
|
||||
...this.model.cachedProps,
|
||||
onClick: this.showIdentity.bind(this),
|
||||
disabled: this.loading,
|
||||
},
|
||||
});
|
||||
this.$el.append(this.contactView.el);
|
||||
|
|
|
@ -360,18 +360,8 @@
|
|||
: null;
|
||||
|
||||
return {
|
||||
id: this.model.id,
|
||||
name: this.model.getName(),
|
||||
phoneNumber: this.model.getNumber(),
|
||||
profileName: this.model.getProfileName(),
|
||||
color: this.model.getColor(),
|
||||
avatarPath: this.model.getAvatarPath(),
|
||||
...this.model.cachedProps,
|
||||
|
||||
isAccepted: this.model.getAccepted(),
|
||||
isVerified: this.model.isVerified(),
|
||||
isMe: this.model.isMe(),
|
||||
isGroup: !this.model.isPrivate(),
|
||||
isArchived: this.model.get('isArchived'),
|
||||
leftGroup: this.model.get('left'),
|
||||
|
||||
expirationSettingName,
|
||||
|
|
60
patches/react-tooltip-lite+1.12.0.patch
Normal file
60
patches/react-tooltip-lite+1.12.0.patch
Normal file
|
@ -0,0 +1,60 @@
|
|||
diff --git a/node_modules/react-tooltip-lite/dist/index.js b/node_modules/react-tooltip-lite/dist/index.js
|
||||
index 32ce07d..6461913 100644
|
||||
--- a/node_modules/react-tooltip-lite/dist/index.js
|
||||
+++ b/node_modules/react-tooltip-lite/dist/index.js
|
||||
@@ -80,7 +80,7 @@ function (_React$Component) {
|
||||
|
||||
_this.state = {
|
||||
showTip: false,
|
||||
- hasHover: false,
|
||||
+ hasHover: 0,
|
||||
ignoreShow: false,
|
||||
hasBeenShown: false
|
||||
};
|
||||
@@ -232,7 +232,7 @@ function (_React$Component) {
|
||||
var _this3 = this;
|
||||
|
||||
this.setState({
|
||||
- hasHover: false
|
||||
+ hasHover: 0
|
||||
});
|
||||
|
||||
if (this.state.showTip) {
|
||||
@@ -250,7 +250,7 @@ function (_React$Component) {
|
||||
value: function startHover() {
|
||||
if (!this.state.ignoreShow) {
|
||||
this.setState({
|
||||
- hasHover: true
|
||||
+ hasHover: (this.state.hasHover || 0) + 1,
|
||||
});
|
||||
clearTimeout(this.hoverTimeout);
|
||||
this.hoverTimeout = setTimeout(this.checkHover, this.props.hoverDelay);
|
||||
@@ -260,7 +260,7 @@ function (_React$Component) {
|
||||
key: "endHover",
|
||||
value: function endHover() {
|
||||
this.setState({
|
||||
- hasHover: false
|
||||
+ hasHover: Math.max((this.state.hasHover || 0) - 1, 0),
|
||||
});
|
||||
clearTimeout(this.hoverTimeout);
|
||||
this.hoverTimeout = setTimeout(this.checkHover, this.props.mouseOutDelay || this.props.hoverDelay);
|
||||
@@ -268,7 +268,7 @@ function (_React$Component) {
|
||||
}, {
|
||||
key: "checkHover",
|
||||
value: function checkHover() {
|
||||
- this.state.hasHover ? this.showTip() : this.hideTip();
|
||||
+ this.state.hasHover > 0 ? this.showTip() : this.hideTip();
|
||||
}
|
||||
}, {
|
||||
key: "render",
|
||||
@@ -330,7 +330,9 @@ function (_React$Component) {
|
||||
props[eventToggle] = this.toggleTip; // only use hover if they don't have a toggle event
|
||||
} else if (useHover && !isControlledByProps) {
|
||||
props.onMouseEnter = this.startHover;
|
||||
- props.onMouseLeave = tipContentHover || mouseOutDelay ? this.endHover : this.hideTip;
|
||||
+ props.onMouseLeave = this.endHover;
|
||||
+ props.onFocus = this.startHover;
|
||||
+ props.onBlur = this.endHover;
|
||||
props.onTouchStart = this.targetTouchStart;
|
||||
props.onTouchEnd = this.targetTouchEnd;
|
||||
|
|
@ -152,6 +152,18 @@
|
|||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dark-mouse-mode() {
|
||||
.dark-theme.mouse-mode & {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@mixin ios-mouse-mode() {
|
||||
.ios-theme.mouse-mode & {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dark-ios-keyboard-mode() {
|
||||
.dark-theme.ios-theme.keyboard-mode & {
|
||||
@content;
|
||||
|
|
|
@ -36,12 +36,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Module: Contact Name
|
||||
|
||||
.module-contact-name__profile-name {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// Module: Message
|
||||
|
||||
// Note: this does the same thing as module-timeline__message-container but
|
||||
|
@ -2389,6 +2383,10 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
|||
}
|
||||
}
|
||||
|
||||
.module-safety-number__bold-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.module-message-calling {
|
||||
&--audio {
|
||||
text-align: center;
|
||||
|
@ -2612,18 +2610,40 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
|||
|
||||
cursor: inherit;
|
||||
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
|
||||
@include mouse-mode {
|
||||
&:hover {
|
||||
background-color: $color-gray-02;
|
||||
}
|
||||
}
|
||||
@include keyboard-mode {
|
||||
&:focus {
|
||||
background-color: $color-gray-02;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-15;
|
||||
}
|
||||
@include dark-mouse-mode {
|
||||
&:hover {
|
||||
background-color: $color-gray-80;
|
||||
}
|
||||
}
|
||||
@include dark-keyboard-mode {
|
||||
&:focus {
|
||||
background-color: $color-gray-80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-contact-list-item--with-click-handler {
|
||||
|
@ -2665,6 +2685,61 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
|||
}
|
||||
}
|
||||
|
||||
// Module: In Contacts Icon
|
||||
|
||||
.module-in-contacts-icon__icon {
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
|
||||
margin-bottom: 2px;
|
||||
vertical-align: middle;
|
||||
|
||||
@include light-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$color-gray-60
|
||||
);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$color-gray-25
|
||||
);
|
||||
}
|
||||
|
||||
@include keyboard-mode {
|
||||
&:focus {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$ultramarine-ui-light
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-in-contacts-icon__tooltip {
|
||||
.react-tooltip-lite {
|
||||
color: $color-white;
|
||||
background-color: $ultramarine-ui-light;
|
||||
}
|
||||
|
||||
.react-tooltip-lite-arrow {
|
||||
border-color: $ultramarine-ui-light;
|
||||
}
|
||||
|
||||
@include dark-theme {
|
||||
.react-tooltip-lite {
|
||||
color: $color-white;
|
||||
background-color: $ultramarine-ui-light;
|
||||
}
|
||||
|
||||
.react-tooltip-lite-arrow {
|
||||
border-color: $ultramarine-ui-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Module: Conversation Header
|
||||
|
||||
.module-conversation-header {
|
||||
|
@ -2771,6 +2846,37 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
|||
}
|
||||
}
|
||||
|
||||
.module-conversation-header__contacts-icon {
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
|
||||
margin-bottom: 3px;
|
||||
vertical-align: middle;
|
||||
|
||||
@include light-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$color-gray-60
|
||||
);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$color-gray-25
|
||||
);
|
||||
}
|
||||
|
||||
@include keyboard-mode {
|
||||
&:focus {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/profile-circle-outline-24.svg',
|
||||
$ultramarine-ui-light
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-conversation-header__title__profile-name {
|
||||
@include font-body-1-bold-italic;
|
||||
}
|
||||
|
@ -4380,7 +4486,10 @@ button.module-image__border-overlay:focus {
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
|
||||
// This allows click-through to the overlay button behind it
|
||||
pointer-events: none;
|
||||
|
||||
color: $color-white;
|
||||
|
||||
|
|
|
@ -228,11 +228,10 @@ export class ConversationController {
|
|||
}
|
||||
/**
|
||||
* Given a UUID and/or an E164, resolves to a string representing the local
|
||||
* database id of the given contact. It may create new contacts, and it may merge
|
||||
* contacts.
|
||||
* database id of the given contact. In high trust mode, it may create new contacts,
|
||||
* and it may merge contacts.
|
||||
*
|
||||
* lowTrust = uuid/e164 pairing came from source like GroupV1 member list
|
||||
* highTrust = uuid/e164 pairing came from source like CDS
|
||||
* highTrust = uuid/e164 pairing came from CDS, the server, or your own device
|
||||
*/
|
||||
// tslint:disable-next-line cyclomatic-complexity max-func-body-length
|
||||
ensureContactIds({
|
||||
|
|
|
@ -10,6 +10,7 @@ export type Props = {
|
|||
|
||||
conversationType: 'group' | 'direct';
|
||||
noteToSelf?: boolean;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
|
@ -63,17 +64,13 @@ export class Avatar extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public renderImage() {
|
||||
const { avatarPath, i18n, name, phoneNumber, profileName } = this.props;
|
||||
const { avatarPath, i18n, title } = this.props;
|
||||
const { imageBroken } = this.state;
|
||||
|
||||
if (!avatarPath || imageBroken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = `${name || phoneNumber}${
|
||||
!name && profileName ? ` ~${profileName}` : ''
|
||||
}`;
|
||||
|
||||
return (
|
||||
<img
|
||||
onError={this.handleImageErrorBound}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { Avatar, Props as AvatarProps } from './Avatar';
|
||||
import { useRestoreFocus } from '../util/hooks';
|
||||
|
@ -22,14 +21,16 @@ export const AvatarPopup = (props: Props) => {
|
|||
const focusRef = React.useRef<HTMLButtonElement>(null);
|
||||
const {
|
||||
i18n,
|
||||
name,
|
||||
profileName,
|
||||
phoneNumber,
|
||||
title,
|
||||
onViewPreferences,
|
||||
onViewArchive,
|
||||
style,
|
||||
} = props;
|
||||
|
||||
const hasProfileName = !isEmpty(profileName);
|
||||
const shouldShowNumber = Boolean(name || profileName);
|
||||
|
||||
// Note: mechanisms to dismiss this view are all in its host, MainHeader
|
||||
|
||||
|
@ -41,10 +42,8 @@ export const AvatarPopup = (props: Props) => {
|
|||
<div className="module-avatar-popup__profile">
|
||||
<Avatar {...props} size={52} />
|
||||
<div className="module-avatar-popup__profile__text">
|
||||
<div className="module-avatar-popup__profile__name">
|
||||
{hasProfileName ? profileName : phoneNumber}
|
||||
</div>
|
||||
{hasProfileName ? (
|
||||
<div className="module-avatar-popup__profile__name">{title}</div>
|
||||
{shouldShowNumber ? (
|
||||
<div className="module-avatar-popup__profile__number">
|
||||
{phoneNumber}
|
||||
</div>
|
||||
|
|
|
@ -14,11 +14,13 @@ import { action } from '@storybook/addon-actions';
|
|||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
avatarPath: undefined,
|
||||
callId: 0,
|
||||
contactColor: 'ultramarine' as ColorType,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
|
|
|
@ -15,11 +15,13 @@ import { action } from '@storybook/addon-actions';
|
|||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
avatarPath: undefined,
|
||||
callId: 0,
|
||||
contactColor: 'ultramarine' as ColorType,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
|
|
|
@ -275,22 +275,24 @@ export class CallScreen extends React.Component<PropsType, StateType> {
|
|||
const { i18n } = this.props;
|
||||
const {
|
||||
avatarPath,
|
||||
contactColor,
|
||||
color,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = callDetails;
|
||||
return (
|
||||
<div className="module-ongoing-call__remote-video-disabled">
|
||||
<Avatar
|
||||
avatarPath={avatarPath}
|
||||
color={contactColor || 'ultramarine'}
|
||||
color={color || 'ultramarine'}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={112}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -114,18 +114,19 @@ export const CompositionArea = ({
|
|||
showPickerHint,
|
||||
clearShowPickerHint,
|
||||
// Message Requests
|
||||
messageRequestsEnabled,
|
||||
acceptedMessageRequest,
|
||||
conversationType,
|
||||
isBlocked,
|
||||
messageRequestsEnabled,
|
||||
name,
|
||||
onAccept,
|
||||
onBlock,
|
||||
onBlockAndDelete,
|
||||
onUnblock,
|
||||
onDelete,
|
||||
profileName,
|
||||
onUnblock,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
}: Props) => {
|
||||
const [disabled, setDisabled] = React.useState(false);
|
||||
const [showMic, setShowMic] = React.useState(!startingText);
|
||||
|
@ -333,6 +334,7 @@ export const CompositionArea = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
#### It's me!
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
isMe
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
verified
|
||||
profileName="🔥Flames🔥"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### With name and profile
|
||||
|
||||
Note the proper spacing between these two.
|
||||
|
||||
```jsx
|
||||
<div>
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
name="Another ❄️ Yes"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="❄️Ice❄️"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### With name and profile, verified
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
verified
|
||||
avatarPath={util.gifObjectUrl}
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### With name and profile, no avatar
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
name="Someone 🔥 Somewhere"
|
||||
color="teal"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Profile, no name, no avatar
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Verified, profile, no name, no avatar
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
verified
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### No name, no profile, no avatar
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Verified, no name, no profile, no avatar
|
||||
|
||||
```jsx
|
||||
<ContactListItem
|
||||
i18n={util.i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
verified
|
||||
onClick={() => console.log('onClick')}
|
||||
/>
|
||||
```
|
133
ts/components/ContactListItem.stories.tsx
Normal file
133
ts/components/ContactListItem.stories.tsx
Normal file
|
@ -0,0 +1,133 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { gifUrl } from '../storybook/Fixtures';
|
||||
|
||||
// @ts-ignore
|
||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||
// @ts-ignore
|
||||
import enMessages from '../../\_locales/en/messages.json';
|
||||
|
||||
import { ContactListItem } from './ContactListItem';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
const onClick = action('onClick');
|
||||
|
||||
storiesOf('Components/ContactListItem', module)
|
||||
.add("It's me!", () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
isMe
|
||||
title="Someone 🔥 Somewhere"
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
isVerified
|
||||
profileName="🔥Flames🔥"
|
||||
avatarPath={gifUrl}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('With name and profile (note vertical spacing)', () => {
|
||||
return (
|
||||
<div>
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
title="Someone 🔥 Somewhere"
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
avatarPath={gifUrl}
|
||||
onClick={onClick}
|
||||
/>
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
title="Another ❄️ Yes"
|
||||
name="Another ❄️ Yes"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="❄️Ice❄️"
|
||||
avatarPath={gifUrl}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('With name and profile, verified', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
title="Someone 🔥 Somewhere"
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
isVerified
|
||||
avatarPath={gifUrl}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('With name and profile, no avatar', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
title="Someone 🔥 Somewhere"
|
||||
name="Someone 🔥 Somewhere"
|
||||
color="teal"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('Profile, no name, no avatar', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
title="🔥Flames🔥"
|
||||
profileName="🔥Flames🔥"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('Verified, profile, no name, no avatar', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
title="🔥Flames🔥"
|
||||
profileName="🔥Flames🔥"
|
||||
isVerified
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('No name, no profile, no avatar', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
phoneNumber="(202) 555-0011"
|
||||
title="(202) 555-0011"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('Verified, no name, no profile, no avatar', () => {
|
||||
return (
|
||||
<ContactListItem
|
||||
i18n={i18n}
|
||||
title="(202) 555-0011"
|
||||
phoneNumber="(202) 555-0011"
|
||||
isVerified
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('No name, no profile, no number', () => {
|
||||
return (
|
||||
<ContactListItem i18n={i18n} title="Unknown contact" onClick={onClick} />
|
||||
);
|
||||
});
|
|
@ -3,15 +3,17 @@ import classNames from 'classnames';
|
|||
|
||||
import { Avatar } from './Avatar';
|
||||
import { Emojify } from './conversation/Emojify';
|
||||
import { InContactsIcon } from './InContactsIcon';
|
||||
|
||||
import { ColorType, LocalizerType } from '../types/Util';
|
||||
|
||||
interface Props {
|
||||
phoneNumber: string;
|
||||
title: string;
|
||||
phoneNumber?: string;
|
||||
isMe?: boolean;
|
||||
name?: string;
|
||||
color: ColorType;
|
||||
verified: boolean;
|
||||
color?: ColorType;
|
||||
isVerified?: boolean;
|
||||
profileName?: string;
|
||||
avatarPath?: string;
|
||||
i18n: LocalizerType;
|
||||
|
@ -27,6 +29,7 @@ export class ContactListItem extends React.Component<Props> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -38,6 +41,7 @@ export class ContactListItem extends React.Component<Props> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={52}
|
||||
/>
|
||||
);
|
||||
|
@ -51,21 +55,15 @@ export class ContactListItem extends React.Component<Props> {
|
|||
isMe,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
verified,
|
||||
title,
|
||||
isVerified,
|
||||
} = this.props;
|
||||
|
||||
const title = name ? name : phoneNumber;
|
||||
const displayName = isMe ? i18n('you') : title;
|
||||
const shouldShowIcon = Boolean(name);
|
||||
|
||||
const profileElement =
|
||||
!isMe && profileName && !name ? (
|
||||
<span className="module-contact-list-item__text__profile-name">
|
||||
~<Emojify text={profileName} />
|
||||
</span>
|
||||
) : null;
|
||||
|
||||
const showNumber = isMe || name;
|
||||
const showVerified = !isMe && verified;
|
||||
const showNumber = Boolean(isMe || name || profileName);
|
||||
const showVerified = !isMe && isVerified;
|
||||
|
||||
return (
|
||||
<button
|
||||
|
@ -78,7 +76,13 @@ export class ContactListItem extends React.Component<Props> {
|
|||
{this.renderAvatar()}
|
||||
<div className="module-contact-list-item__text">
|
||||
<div className="module-contact-list-item__text__name">
|
||||
<Emojify text={displayName} /> {profileElement}
|
||||
<Emojify text={displayName} />
|
||||
{shouldShowIcon ? (
|
||||
<span>
|
||||
{' '}
|
||||
<InContactsIcon i18n={i18n} />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="module-contact-list-item__text__additional-data">
|
||||
{showVerified ? (
|
||||
|
|
|
@ -12,9 +12,10 @@ import { ColorType, LocalizerType } from '../types/Util';
|
|||
|
||||
export type PropsData = {
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
color?: ColorType;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
type: 'group' | 'direct';
|
||||
avatarPath?: string;
|
||||
|
@ -54,6 +55,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -67,6 +69,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={52}
|
||||
/>
|
||||
{this.renderUnread()}
|
||||
|
@ -97,6 +100,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -116,6 +120,8 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
phoneNumber={phoneNumber}
|
||||
name={name}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
16
ts/components/InContactsIcon.stories.tsx
Normal file
16
ts/components/InContactsIcon.stories.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
// @ts-ignore
|
||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||
// @ts-ignore
|
||||
import enMessages from '../../\_locales/en/messages.json';
|
||||
|
||||
import { InContactsIcon } from './InContactsIcon';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
storiesOf('Components/InContactsIcon', module).add('Default', () => {
|
||||
return <InContactsIcon i18n={i18n} />;
|
||||
});
|
31
ts/components/InContactsIcon.tsx
Normal file
31
ts/components/InContactsIcon.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import Tooltip from 'react-tooltip-lite';
|
||||
|
||||
import { LocalizerType } from '../types/Util';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export const InContactsIcon = (props: PropsType): JSX.Element => {
|
||||
const { i18n } = props;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
tagName="span"
|
||||
direction="bottom"
|
||||
className="module-in-contacts-icon__tooltip"
|
||||
arrowSize={8}
|
||||
content={i18n('contactInAddressBook')}
|
||||
distance={13}
|
||||
hoverDelay={0}
|
||||
>
|
||||
<span
|
||||
tabIndex={0}
|
||||
role="img"
|
||||
aria-label={i18n('contactInAddressBook')}
|
||||
className="module-in-contacts-icon__icon"
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
|
@ -16,14 +16,16 @@ const i18n = setupI18n('en', enMessages);
|
|||
const defaultProps = {
|
||||
acceptCall: action('accept-call'),
|
||||
callDetails: {
|
||||
avatarPath: undefined,
|
||||
callId: 0,
|
||||
contactColor: 'ultramarine' as ColorType,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
avatarPath: undefined,
|
||||
contactColor: 'ultramarine' as ColorType,
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
title: 'Rick Sanchez',
|
||||
},
|
||||
declineCall: action('decline-call'),
|
||||
i18n,
|
||||
|
@ -72,7 +74,7 @@ const permutations = [
|
|||
|
||||
storiesOf('Components/IncomingCallBar', module)
|
||||
.add('Knobs Playground', () => {
|
||||
const contactColor = select('contactColor', colors, 'ultramarine');
|
||||
const color = select('color', colors, 'ultramarine');
|
||||
const isVideoCall = boolean('isVideoCall', false);
|
||||
const name = text(
|
||||
'name',
|
||||
|
@ -84,7 +86,7 @@ storiesOf('Components/IncomingCallBar', module)
|
|||
{...defaultProps}
|
||||
callDetails={{
|
||||
...defaultProps.callDetails,
|
||||
contactColor,
|
||||
color,
|
||||
isVideoCall,
|
||||
name,
|
||||
}}
|
||||
|
|
|
@ -62,7 +62,8 @@ export const IncomingCallBar = ({
|
|||
const {
|
||||
avatarPath,
|
||||
callId,
|
||||
contactColor,
|
||||
color,
|
||||
title,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
|
@ -74,22 +75,25 @@ export const IncomingCallBar = ({
|
|||
<div className="module-incoming-call__contact--avatar">
|
||||
<Avatar
|
||||
avatarPath={avatarPath}
|
||||
color={contactColor || 'ultramarine'}
|
||||
color={color || 'ultramarine'}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={52}
|
||||
/>
|
||||
</div>
|
||||
<div className="module-incoming-call__contact--name">
|
||||
<div className="module-incoming-call__contact--name-header">
|
||||
<ContactName
|
||||
phoneNumber={phoneNumber}
|
||||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -22,12 +22,13 @@ export interface PropsType {
|
|||
regionCode: string;
|
||||
|
||||
// For display
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
isMe: boolean;
|
||||
name?: string;
|
||||
color?: ColorType;
|
||||
verified: boolean;
|
||||
isVerified?: boolean;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
avatarPath?: string;
|
||||
|
||||
i18n: LocalizerType;
|
||||
|
@ -295,6 +296,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
searchConversationId,
|
||||
searchConversationName,
|
||||
searchTerm,
|
||||
|
@ -319,6 +321,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={28}
|
||||
innerRef={ref}
|
||||
onClick={this.showAvatarPopup}
|
||||
|
@ -338,6 +341,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
avatarPath={avatarPath}
|
||||
size={28}
|
||||
onViewPreferences={() => {
|
||||
|
|
|
@ -19,7 +19,8 @@ export type PropsDataType = {
|
|||
snippet: string;
|
||||
|
||||
from: {
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
title: string;
|
||||
isMe?: boolean;
|
||||
name?: string;
|
||||
color?: ColorType;
|
||||
|
@ -29,7 +30,8 @@ export type PropsDataType = {
|
|||
|
||||
to: {
|
||||
groupName?: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
title: string;
|
||||
isMe?: boolean;
|
||||
name?: string;
|
||||
profileName?: string;
|
||||
|
@ -70,7 +72,9 @@ export class MessageSearchResult extends React.PureComponent<PropsType> {
|
|||
phoneNumber={from.phoneNumber}
|
||||
name={from.name}
|
||||
profileName={from.profileName}
|
||||
title={from.title}
|
||||
module="module-message-search-result__header__name"
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -88,6 +92,8 @@ export class MessageSearchResult extends React.PureComponent<PropsType> {
|
|||
phoneNumber={to.phoneNumber}
|
||||
name={to.name}
|
||||
profileName={to.profileName}
|
||||
title={to.title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -115,6 +121,7 @@ export class MessageSearchResult extends React.PureComponent<PropsType> {
|
|||
noteToSelf={isNoteToSelf}
|
||||
phoneNumber={from.phoneNumber}
|
||||
profileName={from.profileName}
|
||||
title={from.title}
|
||||
size={52}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -12,19 +12,44 @@ import { storiesOf } from '@storybook/react';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const contact = {
|
||||
const contactWithAllData = {
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: '-*Smartest Dude*-',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '(305) 123-4567',
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithJustProfile = {
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: '-*Smartest Dude*-',
|
||||
name: undefined,
|
||||
phoneNumber: '(305) 123-4567',
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithJustNumber = {
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: undefined,
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
name: undefined,
|
||||
phoneNumber: '(305) 123-4567',
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithNothing = {
|
||||
id: 'some-guid',
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: undefined,
|
||||
name: undefined,
|
||||
phoneNumber: undefined,
|
||||
} as ConversationType;
|
||||
|
||||
storiesOf('Components/SafetyNumberChangeDialog', module)
|
||||
.add('Single Contact Dialog', () => {
|
||||
return (
|
||||
<SafetyNumberChangeDialog
|
||||
contacts={[contact]}
|
||||
contacts={[contactWithAllData]}
|
||||
i18n={i18n}
|
||||
onCancel={action('cancel')}
|
||||
onConfirm={action('confirm')}
|
||||
|
@ -38,7 +63,12 @@ storiesOf('Components/SafetyNumberChangeDialog', module)
|
|||
.add('Multi Contact Dialog', () => {
|
||||
return (
|
||||
<SafetyNumberChangeDialog
|
||||
contacts={[contact, contact, contact, contact]}
|
||||
contacts={[
|
||||
contactWithAllData,
|
||||
contactWithJustProfile,
|
||||
contactWithJustNumber,
|
||||
contactWithNothing,
|
||||
]}
|
||||
i18n={i18n}
|
||||
onCancel={action('cancel')}
|
||||
onConfirm={action('confirm')}
|
||||
|
@ -53,16 +83,16 @@ storiesOf('Components/SafetyNumberChangeDialog', module)
|
|||
return (
|
||||
<SafetyNumberChangeDialog
|
||||
contacts={[
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contact,
|
||||
contactWithAllData,
|
||||
contactWithJustProfile,
|
||||
contactWithJustNumber,
|
||||
contactWithNothing,
|
||||
contactWithAllData,
|
||||
contactWithAllData,
|
||||
contactWithAllData,
|
||||
contactWithAllData,
|
||||
contactWithAllData,
|
||||
contactWithAllData,
|
||||
]}
|
||||
i18n={i18n}
|
||||
onCancel={action('cancel')}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { Avatar } from './Avatar';
|
||||
import { ConfirmationModal } from './ConfirmationModal';
|
||||
import { InContactsIcon } from './InContactsIcon';
|
||||
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
|
||||
|
@ -45,8 +48,14 @@ const SafetyDialogContents = ({
|
|||
{i18n('changedVerificationWarning')}
|
||||
</div>
|
||||
<ul className="module-sfn-dialog__contacts">
|
||||
{contacts.map((contact: ConversationType) => (
|
||||
<li className="module-sfn-dialog__contact" key={contact.phoneNumber}>
|
||||
{contacts.map((contact: ConversationType) => {
|
||||
const shouldShowNumber = Boolean(contact.name || contact.profileName);
|
||||
|
||||
return (
|
||||
<li
|
||||
className="module-sfn-dialog__contact"
|
||||
key={contact.phoneNumber}
|
||||
>
|
||||
<Avatar
|
||||
avatarPath={contact.avatarPath}
|
||||
color={contact.color}
|
||||
|
@ -55,24 +64,24 @@ const SafetyDialogContents = ({
|
|||
name={contact.name}
|
||||
phoneNumber={contact.phoneNumber}
|
||||
profileName={contact.profileName}
|
||||
title={contact.title}
|
||||
size={52}
|
||||
/>
|
||||
<div className="module-sfn-dialog__contact--wrapper">
|
||||
{contact.name && (
|
||||
<>
|
||||
<div className="module-sfn-dialog__contact--name">
|
||||
{contact.name}
|
||||
{contact.title}
|
||||
{contact.name ? (
|
||||
<span>
|
||||
{' '}
|
||||
<InContactsIcon i18n={i18n} />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{shouldShowNumber ? (
|
||||
<div className="module-sfn-dialog__contact--number">
|
||||
{contact.phoneNumber}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!contact.name && (
|
||||
<div className="module-sfn-dialog__contact--name">
|
||||
{contact.phoneNumber}
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
className="module-sfn-dialog__contact--view"
|
||||
|
@ -84,7 +93,8 @@ const SafetyDialogContents = ({
|
|||
{i18n('view')}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div className="module-sfn-dialog__actions">
|
||||
<button
|
||||
|
|
|
@ -13,11 +13,39 @@ import { storiesOf } from '@storybook/react';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
contact: {
|
||||
title: 'Summer Smith',
|
||||
const contactWithAllData = {
|
||||
name: 'Summer Smith',
|
||||
phoneNumber: '(305) 123-4567',
|
||||
isVerified: true,
|
||||
} as ConversationType,
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithJustProfile = {
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: '-*Smartest Dude*-',
|
||||
name: undefined,
|
||||
phoneNumber: '(305) 123-4567',
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithJustNumber = {
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: undefined,
|
||||
name: undefined,
|
||||
phoneNumber: '(305) 123-4567',
|
||||
} as ConversationType;
|
||||
|
||||
const contactWithNothing = {
|
||||
id: 'some-guid',
|
||||
avatarPath: undefined,
|
||||
color: 'signal-blue',
|
||||
profileName: undefined,
|
||||
name: undefined,
|
||||
phoneNumber: undefined,
|
||||
} as ConversationType;
|
||||
|
||||
const defaultProps = {
|
||||
contact: contactWithAllData,
|
||||
generateSafetyNumber: action('generate-safety-number'),
|
||||
i18n,
|
||||
safetyNumber: 'XXX',
|
||||
|
@ -35,9 +63,9 @@ const permutations = [
|
|||
title: 'Safety Number (not verified)',
|
||||
props: {
|
||||
contact: {
|
||||
title: 'Morty Smith',
|
||||
isVerified: false,
|
||||
} as ConversationType,
|
||||
...contactWithAllData,
|
||||
verified: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -58,6 +86,24 @@ const permutations = [
|
|||
onClose: action('close'),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Just Profile',
|
||||
props: {
|
||||
contact: contactWithJustProfile,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Just Number',
|
||||
props: {
|
||||
contact: contactWithJustNumber,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'No display info',
|
||||
props: {
|
||||
contact: contactWithNothing,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
storiesOf('Components/SafetyNumberViewer', module)
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { getPlaceholder } from '../util/safetyNumber';
|
||||
import { Intl } from './Intl';
|
||||
|
||||
type SafetyNumberViewerProps = {
|
||||
contact?: ConversationType;
|
||||
|
@ -32,11 +33,20 @@ export const SafetyNumberViewer = ({
|
|||
generateSafetyNumber(contact);
|
||||
}, [safetyNumber]);
|
||||
|
||||
const name = contact.title;
|
||||
const showNumber = Boolean(contact.name || contact.profileName);
|
||||
const numberFragment = showNumber ? ` · ${contact.phoneNumber}` : '';
|
||||
const name = `${contact.title}${numberFragment}`;
|
||||
const boldName = (key?: number) => (
|
||||
<span className="module-safety-number__bold-name" key={key}>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
|
||||
const isVerified = contact.isVerified;
|
||||
const verifiedStatus = isVerified
|
||||
? i18n('isVerified', [name])
|
||||
: i18n('isNotVerified', [name]);
|
||||
const verifiedStatusKey = isVerified ? 'isVerified' : 'isNotVerified';
|
||||
const safetyNumberChangedKey = safetyNumberChanged
|
||||
? 'changedRightAfterVerify'
|
||||
: 'yourSafetyNumberWith';
|
||||
const verifyButtonText = isVerified ? i18n('unverify') : i18n('verify');
|
||||
|
||||
return (
|
||||
|
@ -49,21 +59,23 @@ export const SafetyNumberViewer = ({
|
|||
</div>
|
||||
)}
|
||||
<div className="module-safety-number__verification-label">
|
||||
{safetyNumberChanged
|
||||
? i18n('changedRightAfterVerify', [name, name])
|
||||
: i18n('yourSafetyNumberWith', [name])}
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={safetyNumberChangedKey}
|
||||
components={[boldName(1), boldName(2)]}
|
||||
/>
|
||||
</div>
|
||||
<div className="module-safety-number__number">
|
||||
{safetyNumber || getPlaceholder()}
|
||||
</div>
|
||||
{i18n('verifyHelp', [name])}
|
||||
<Intl i18n={i18n} id="verifyHelp" components={[boldName()]} />
|
||||
<div className="module-safety-number__verification-status">
|
||||
{isVerified ? (
|
||||
<span className="module-safety-number__icon--verified" />
|
||||
) : (
|
||||
<span className="module-safety-number__icon--shield" />
|
||||
)}
|
||||
{verifiedStatus}
|
||||
<Intl i18n={i18n} id={verifiedStatusKey} components={[boldName()]} />
|
||||
</div>
|
||||
<div className="module-safety-number__verify-container">
|
||||
<button
|
||||
|
|
|
@ -45,12 +45,14 @@ messageLookup.set('1-guid-guid-guid-guid-guid', {
|
|||
|
||||
from: {
|
||||
phoneNumber: '(202) 555-0020',
|
||||
title: '(202) 555-0020',
|
||||
isMe: true,
|
||||
color: 'blue',
|
||||
avatarPath: gifUrl,
|
||||
},
|
||||
to: {
|
||||
phoneNumber: '(202) 555-0015',
|
||||
title: 'Mr. Fire 🔥',
|
||||
name: 'Mr. Fire 🔥',
|
||||
},
|
||||
});
|
||||
|
@ -63,10 +65,12 @@ messageLookup.set('2-guid-guid-guid-guid-guid', {
|
|||
from: {
|
||||
phoneNumber: '(202) 555-0016',
|
||||
name: 'Jon ❄️',
|
||||
title: 'Jon ❄️',
|
||||
color: 'green',
|
||||
},
|
||||
to: {
|
||||
phoneNumber: '(202) 555-0020',
|
||||
title: '(202) 555-0020',
|
||||
isMe: true,
|
||||
},
|
||||
});
|
||||
|
@ -79,12 +83,14 @@ messageLookup.set('3-guid-guid-guid-guid-guid', {
|
|||
from: {
|
||||
phoneNumber: '(202) 555-0011',
|
||||
name: 'Someone',
|
||||
title: 'Someone',
|
||||
color: 'green',
|
||||
avatarPath: pngUrl,
|
||||
},
|
||||
to: {
|
||||
phoneNumber: '(202) 555-0016',
|
||||
name: "Y'all 🌆",
|
||||
title: "Y'all 🌆",
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -95,6 +101,7 @@ messageLookup.set('4-guid-guid-guid-guid-guid', {
|
|||
snippet: 'Well, <<left>>everyone<<right>>, happy new year!',
|
||||
from: {
|
||||
phoneNumber: '(202) 555-0020',
|
||||
title: '(202) 555-0020',
|
||||
isMe: true,
|
||||
color: 'light_green',
|
||||
avatarPath: gifUrl,
|
||||
|
@ -102,6 +109,7 @@ messageLookup.set('4-guid-guid-guid-guid-guid', {
|
|||
to: {
|
||||
phoneNumber: '(202) 555-0016',
|
||||
name: "Y'all 🌆",
|
||||
title: "Y'all 🌆",
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -142,6 +150,7 @@ const conversations = [
|
|||
id: '+12025550011',
|
||||
phoneNumber: '(202) 555-0011',
|
||||
name: 'Everyone 🌆',
|
||||
title: 'Everyone 🌆',
|
||||
type: GROUP,
|
||||
color: 'signal-blue' as 'signal-blue',
|
||||
avatarPath: landscapeGreenUrl,
|
||||
|
@ -161,6 +170,7 @@ const conversations = [
|
|||
id: '+12025550012',
|
||||
phoneNumber: '(202) 555-0012',
|
||||
name: 'Everyone Else 🔥',
|
||||
title: 'Everyone Else 🔥',
|
||||
color: 'pink' as 'pink',
|
||||
type: DIRECT,
|
||||
avatarPath: landscapePurpleUrl,
|
||||
|
@ -183,6 +193,7 @@ const contacts = [
|
|||
id: '+12025550013',
|
||||
phoneNumber: '(202) 555-0013',
|
||||
name: 'The one Everyone',
|
||||
title: 'The one Everyone',
|
||||
color: 'blue' as 'blue',
|
||||
type: DIRECT,
|
||||
avatarPath: gifUrl,
|
||||
|
@ -198,6 +209,7 @@ const contacts = [
|
|||
id: '+12025550014',
|
||||
phoneNumber: '(202) 555-0014',
|
||||
name: 'No likey everyone',
|
||||
title: 'No likey everyone',
|
||||
type: DIRECT,
|
||||
color: 'red' as 'red',
|
||||
isMe: false,
|
||||
|
|
|
@ -20,7 +20,7 @@ export class StartNewConversation extends React.PureComponent<Props> {
|
|||
color="grey"
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
phoneNumber={phoneNumber}
|
||||
title={phoneNumber}
|
||||
size={52}
|
||||
/>
|
||||
<div className="module-start-new-conversation__content">
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#### Number, name and profile
|
||||
|
||||
```jsx
|
||||
<ContactName
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
/>
|
||||
```
|
||||
|
||||
#### Number and profile, no name
|
||||
|
||||
```jsx
|
||||
<ContactName phoneNumber="(202) 555-0011" profileName="🔥Flames🔥" />
|
||||
```
|
||||
|
||||
#### No name, no profile
|
||||
|
||||
```jsx
|
||||
<ContactName phoneNumber="(202) 555-0011" />
|
||||
```
|
47
ts/components/conversation/ContactName.stories.tsx
Normal file
47
ts/components/conversation/ContactName.stories.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
// @ts-ignore
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
// @ts-ignore
|
||||
import enMessages from '../../../\_locales/en/messages.json';
|
||||
|
||||
import { ContactName } from './ContactName';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
storiesOf('Components/Conversation/ContactName', module)
|
||||
.add('Number, name and profile', () => {
|
||||
return (
|
||||
<ContactName
|
||||
title="Someone 🔥 Somewhere"
|
||||
name="Someone 🔥 Somewhere"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('Number and profile, no name', () => {
|
||||
return (
|
||||
<ContactName
|
||||
title="🔥Flames🔥"
|
||||
phoneNumber="(202) 555-0011"
|
||||
profileName="🔥Flames🔥"
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('No name, no profile', () => {
|
||||
return (
|
||||
<ContactName
|
||||
title="(202) 555-0011"
|
||||
phoneNumber="(202) 555-0011"
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add('No data provided', () => {
|
||||
return <ContactName title="unknownContact" i18n={i18n} />;
|
||||
});
|
|
@ -1,32 +1,25 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Emojify } from './Emojify';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
phoneNumber?: string;
|
||||
name?: string;
|
||||
profileName?: string;
|
||||
module?: string;
|
||||
i18n: LocalizerType;
|
||||
}
|
||||
|
||||
export class ContactName extends React.Component<Props> {
|
||||
public render() {
|
||||
const { phoneNumber, name, profileName, module } = this.props;
|
||||
const { module, title } = this.props;
|
||||
const prefix = module ? module : 'module-contact-name';
|
||||
|
||||
const title = name ? name : phoneNumber;
|
||||
const shouldShowProfile = Boolean(profileName && !name);
|
||||
const profileElement = shouldShowProfile ? (
|
||||
<span className={`${prefix}__profile-name`}>
|
||||
~<Emojify text={profileName || ''} />
|
||||
</span>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<span className={prefix} dir="auto">
|
||||
<Emojify text={title || ''} />
|
||||
{shouldShowProfile ? ' ' : null}
|
||||
{profileElement}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import enMessages from '../../../\_locales/en/messages.json';
|
|||
|
||||
import {
|
||||
ConversationHeader,
|
||||
Props,
|
||||
PropsActions,
|
||||
PropsHousekeeping,
|
||||
PropsActionsType,
|
||||
PropsHousekeepingType,
|
||||
PropsType,
|
||||
} from './ConversationHeader';
|
||||
|
||||
import { gifUrl } from '../../storybook/Fixtures';
|
||||
|
@ -25,11 +25,11 @@ type ConversationHeaderStory = {
|
|||
description: string;
|
||||
items: Array<{
|
||||
title: string;
|
||||
props: Props;
|
||||
props: PropsType;
|
||||
}>;
|
||||
};
|
||||
|
||||
const actionProps: PropsActions = {
|
||||
const actionProps: PropsActionsType = {
|
||||
onSetDisappearingMessages: action('onSetDisappearingMessages'),
|
||||
onDeleteMessages: action('onDeleteMessages'),
|
||||
onResetSession: action('onResetSession'),
|
||||
|
@ -50,7 +50,7 @@ const actionProps: PropsActions = {
|
|||
onMoveToInbox: action('onMoveToInbox'),
|
||||
};
|
||||
|
||||
const housekeepingProps: PropsHousekeeping = {
|
||||
const housekeepingProps: PropsHousekeepingType = {
|
||||
i18n,
|
||||
};
|
||||
|
||||
|
@ -66,8 +66,10 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
color: 'red',
|
||||
isVerified: true,
|
||||
avatarPath: gifUrl,
|
||||
title: 'Someone 🔥 Somewhere',
|
||||
name: 'Someone 🔥 Somewhere',
|
||||
phoneNumber: '(202) 555-0001',
|
||||
type: 'direct',
|
||||
id: '1',
|
||||
profileName: '🔥Flames🔥',
|
||||
isAccepted: true,
|
||||
|
@ -80,8 +82,25 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
props: {
|
||||
color: 'blue',
|
||||
isVerified: false,
|
||||
title: 'Someone 🔥 Somewhere',
|
||||
name: 'Someone 🔥 Somewhere',
|
||||
phoneNumber: '(202) 555-0002',
|
||||
type: 'direct',
|
||||
id: '2',
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
...housekeepingProps,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'With name, not verified, descenders',
|
||||
props: {
|
||||
color: 'blue',
|
||||
isVerified: false,
|
||||
title: 'Joyrey 🔥 Leppey',
|
||||
name: 'Joyrey 🔥 Leppey',
|
||||
phoneNumber: '(202) 555-0002',
|
||||
type: 'direct',
|
||||
id: '2',
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
|
@ -94,7 +113,9 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
color: 'teal',
|
||||
isVerified: false,
|
||||
phoneNumber: '(202) 555-0003',
|
||||
type: 'direct',
|
||||
id: '3',
|
||||
title: '🔥Flames🔥',
|
||||
profileName: '🔥Flames🔥',
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
|
@ -104,7 +125,9 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
{
|
||||
title: 'No name, no profile, no color',
|
||||
props: {
|
||||
title: '(202) 555-0011',
|
||||
phoneNumber: '(202) 555-0011',
|
||||
type: 'direct',
|
||||
id: '11',
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
|
@ -117,6 +140,8 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
showBackButton: true,
|
||||
color: 'deep_orange',
|
||||
phoneNumber: '(202) 555-0004',
|
||||
title: '(202) 555-0004',
|
||||
type: 'direct',
|
||||
id: '4',
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
|
@ -127,7 +152,9 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
title: 'Disappearing messages set',
|
||||
props: {
|
||||
color: 'indigo',
|
||||
title: '(202) 555-0005',
|
||||
phoneNumber: '(202) 555-0005',
|
||||
type: 'direct',
|
||||
id: '5',
|
||||
expirationSettingName: '10 seconds',
|
||||
timerOptions: [
|
||||
|
@ -156,10 +183,11 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
title: 'Basic',
|
||||
props: {
|
||||
color: 'signal-blue',
|
||||
title: 'Typescript support group',
|
||||
name: 'Typescript support group',
|
||||
phoneNumber: '',
|
||||
id: '1',
|
||||
isGroup: true,
|
||||
type: 'group',
|
||||
expirationSettingName: '10 seconds',
|
||||
timerOptions: [
|
||||
{
|
||||
|
@ -180,10 +208,11 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
title: 'In a group you left - no disappearing messages',
|
||||
props: {
|
||||
color: 'signal-blue',
|
||||
title: 'Typescript support group',
|
||||
name: 'Typescript support group',
|
||||
phoneNumber: '',
|
||||
id: '2',
|
||||
isGroup: true,
|
||||
type: 'group',
|
||||
leftGroup: true,
|
||||
expirationSettingName: '10 seconds',
|
||||
timerOptions: [
|
||||
|
@ -211,8 +240,10 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
title: 'In chat with yourself',
|
||||
props: {
|
||||
color: 'blue',
|
||||
title: '(202) 555-0007',
|
||||
phoneNumber: '(202) 555-0007',
|
||||
id: '7',
|
||||
type: 'direct',
|
||||
isMe: true,
|
||||
isAccepted: true,
|
||||
...actionProps,
|
||||
|
@ -229,8 +260,10 @@ const stories: Array<ConversationHeaderStory> = [
|
|||
title: '1:1 conversation',
|
||||
props: {
|
||||
color: 'blue',
|
||||
title: '(202) 555-0007',
|
||||
phoneNumber: '(202) 555-0007',
|
||||
id: '7',
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
isAccepted: false,
|
||||
...actionProps,
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Emojify } from './Emojify';
|
||||
import { Avatar } from '../Avatar';
|
||||
import { ColorType, LocalizerType } from '../../types/Util';
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
|
@ -11,24 +7,31 @@ import {
|
|||
SubMenu,
|
||||
} from 'react-contextmenu';
|
||||
|
||||
import { Emojify } from './Emojify';
|
||||
import { Avatar } from '../Avatar';
|
||||
import { InContactsIcon } from '../InContactsIcon';
|
||||
|
||||
import { ColorType, LocalizerType } from '../../types/Util';
|
||||
|
||||
interface TimerOption {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface PropsData {
|
||||
export interface PropsDataType {
|
||||
id: string;
|
||||
name?: string;
|
||||
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
color?: ColorType;
|
||||
avatarPath?: string;
|
||||
type: 'direct' | 'group';
|
||||
title: string;
|
||||
|
||||
isAccepted?: boolean;
|
||||
isVerified?: boolean;
|
||||
isMe?: boolean;
|
||||
isGroup?: boolean;
|
||||
isArchived?: boolean;
|
||||
leftGroup?: boolean;
|
||||
|
||||
|
@ -37,7 +40,7 @@ export interface PropsData {
|
|||
timerOptions?: Array<TimerOption>;
|
||||
}
|
||||
|
||||
export interface PropsActions {
|
||||
export interface PropsActionsType {
|
||||
onSetDisappearingMessages: (seconds: number) => void;
|
||||
onDeleteMessages: () => void;
|
||||
onResetSession: () => void;
|
||||
|
@ -54,17 +57,19 @@ export interface PropsActions {
|
|||
onMoveToInbox: () => void;
|
||||
}
|
||||
|
||||
export interface PropsHousekeeping {
|
||||
export interface PropsHousekeepingType {
|
||||
i18n: LocalizerType;
|
||||
}
|
||||
|
||||
export type Props = PropsData & PropsActions & PropsHousekeeping;
|
||||
export type PropsType = PropsDataType &
|
||||
PropsActionsType &
|
||||
PropsHousekeepingType;
|
||||
|
||||
export class ConversationHeader extends React.Component<Props> {
|
||||
export class ConversationHeader extends React.Component<PropsType> {
|
||||
public showMenuBound: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
public menuTriggerRef: React.RefObject<any>;
|
||||
|
||||
public constructor(props: Props) {
|
||||
public constructor(props: PropsType) {
|
||||
super(props);
|
||||
|
||||
this.menuTriggerRef = React.createRef();
|
||||
|
@ -96,6 +101,8 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
const {
|
||||
name,
|
||||
phoneNumber,
|
||||
title,
|
||||
type,
|
||||
i18n,
|
||||
isMe,
|
||||
profileName,
|
||||
|
@ -110,19 +117,22 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
const shouldShowIcon = Boolean(name && type === 'direct');
|
||||
const shouldShowNumber = Boolean(phoneNumber && (name || profileName));
|
||||
|
||||
return (
|
||||
<div className="module-conversation-header__title">
|
||||
{name ? <Emojify text={name} /> : null}
|
||||
{name && phoneNumber ? ' · ' : null}
|
||||
{phoneNumber ? phoneNumber : null}{' '}
|
||||
{profileName && !name ? (
|
||||
<span className="module-conversation-header__title__profile-name">
|
||||
~<Emojify text={profileName} />
|
||||
<Emojify text={title} />
|
||||
{shouldShowIcon ? (
|
||||
<span>
|
||||
{' '}
|
||||
<InContactsIcon i18n={i18n} />
|
||||
</span>
|
||||
) : null}
|
||||
{isVerified ? ' · ' : null}
|
||||
{shouldShowNumber ? ` · ${phoneNumber}` : null}
|
||||
{isVerified ? (
|
||||
<span>
|
||||
{' · '}
|
||||
<span className="module-conversation-header__title__verified-icon" />
|
||||
{i18n('verified')}
|
||||
</span>
|
||||
|
@ -136,23 +146,23 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
avatarPath,
|
||||
color,
|
||||
i18n,
|
||||
isGroup,
|
||||
type,
|
||||
isMe,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = this.props;
|
||||
|
||||
const conversationType = isGroup ? 'group' : 'direct';
|
||||
|
||||
return (
|
||||
<span className="module-conversation-header__avatar">
|
||||
<Avatar
|
||||
avatarPath={avatarPath}
|
||||
color={color}
|
||||
conversationType={conversationType}
|
||||
conversationType={type}
|
||||
i18n={i18n}
|
||||
noteToSelf={isMe}
|
||||
title={title}
|
||||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
|
@ -226,7 +236,7 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
if (!window.CALLING) {
|
||||
return null;
|
||||
}
|
||||
if (this.props.isGroup || this.props.isMe) {
|
||||
if (this.props.type === 'group' || this.props.isMe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -250,7 +260,7 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
if (!window.CALLING) {
|
||||
return null;
|
||||
}
|
||||
if (this.props.isGroup || this.props.isMe) {
|
||||
if (this.props.type === 'group' || this.props.isMe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -275,7 +285,7 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
i18n,
|
||||
isAccepted,
|
||||
isMe,
|
||||
isGroup,
|
||||
type,
|
||||
isArchived,
|
||||
leftGroup,
|
||||
onDeleteMessages,
|
||||
|
@ -290,6 +300,7 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
} = this.props;
|
||||
|
||||
const disappearingTitle = i18n('disappearingMessages') as any;
|
||||
const isGroup = type === 'group';
|
||||
|
||||
return (
|
||||
<ContextMenu id={triggerId}>
|
||||
|
|
|
@ -10,8 +10,9 @@ import enMessages from '../../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const getTitle = () => text('name', 'Cayce Bollard');
|
||||
const getName = () => text('name', 'Cayce Bollard');
|
||||
const getProfileName = () => text('profileName', 'Cayce Bollard');
|
||||
const getProfileName = () => text('profileName', 'Cayce Bollard (profile)');
|
||||
const getAvatarPath = () =>
|
||||
text('avatarPath', '/fixtures/kitten-4-112-112.jpg');
|
||||
const getPhoneNumber = () => text('phoneNumber', '+1 (646) 327-2700');
|
||||
|
@ -22,6 +23,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={getTitle()}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={getName()}
|
||||
profileName={getProfileName()}
|
||||
|
@ -37,6 +39,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={getTitle()}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={getName()}
|
||||
profileName={getProfileName()}
|
||||
|
@ -52,6 +55,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={getTitle()}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={getName()}
|
||||
profileName={getProfileName()}
|
||||
|
@ -62,13 +66,30 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
</div>
|
||||
);
|
||||
})
|
||||
.add('Direct (No Other Groups)', () => {
|
||||
.add('Direct (No Groups, Name)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={getTitle()}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={getName()}
|
||||
profileName={text('profileName', '')}
|
||||
phoneNumber={getPhoneNumber()}
|
||||
conversationType="direct"
|
||||
groups={[]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('Direct (No Groups, Just Profile)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'Cayce Bollard (profile)')}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={text('name', '')}
|
||||
profileName={getProfileName()}
|
||||
phoneNumber={getPhoneNumber()}
|
||||
conversationType="direct"
|
||||
|
@ -77,13 +98,45 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
</div>
|
||||
);
|
||||
})
|
||||
.add('Direct (No Groups, Just Phone Number)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', '+1 (646) 327-2700')}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={text('name', '')}
|
||||
profileName={text('profileName', '')}
|
||||
phoneNumber={getPhoneNumber()}
|
||||
conversationType="direct"
|
||||
groups={[]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('Direct (No Groups, No Data)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'Unknown contact')}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={text('name', '')}
|
||||
profileName={text('profileName', '')}
|
||||
phoneNumber={text('phoneNumber', '')}
|
||||
conversationType="direct"
|
||||
groups={[]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('Group (many members)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
name={text('groupName', 'NYC Rock Climbers')}
|
||||
phoneNumber={text('phoneNumber', '+1 (646) 327-2700')}
|
||||
conversationType="group"
|
||||
membersCount={numberKnob('membersCount', 22)}
|
||||
/>
|
||||
|
@ -95,8 +148,8 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
name={text('groupName', 'NYC Rock Climbers')}
|
||||
phoneNumber={text('phoneNumber', '+1 (646) 327-2700')}
|
||||
conversationType="group"
|
||||
membersCount={1}
|
||||
/>
|
||||
|
@ -108,8 +161,21 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
name={text('groupName', 'NYC Rock Climbers')}
|
||||
phoneNumber={text('phoneNumber', '+1 (646) 327-2700')}
|
||||
conversationType="group"
|
||||
membersCount={0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('Group (No name)', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<ConversationHero
|
||||
i18n={i18n}
|
||||
title={text('title', 'Unknown group')}
|
||||
name={text('groupName', '')}
|
||||
conversationType="group"
|
||||
membersCount={0}
|
||||
/>
|
||||
|
@ -122,6 +188,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<ConversationHero
|
||||
i18n={i18n}
|
||||
isMe={true}
|
||||
title={getTitle()}
|
||||
conversationType="direct"
|
||||
phoneNumber={getPhoneNumber()}
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,7 @@ export type Props = {
|
|||
isMe?: boolean;
|
||||
groups?: Array<string>;
|
||||
membersCount?: number;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
onHeightChange?: () => unknown;
|
||||
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
||||
|
||||
|
@ -60,6 +60,7 @@ export const ConversationHero = ({
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
onHeightChange,
|
||||
}: Props) => {
|
||||
const firstRenderRef = React.useRef(true);
|
||||
|
@ -86,6 +87,12 @@ export const ConversationHero = ({
|
|||
...groups.map(g => `g-${g}`),
|
||||
]);
|
||||
|
||||
const displayName =
|
||||
name || (conversationType === 'group' ? i18n('unknownGroup') : undefined);
|
||||
const phoneNumberOnly = Boolean(
|
||||
!name && !profileName && conversationType === 'direct'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="module-conversation-hero">
|
||||
<Avatar
|
||||
|
@ -96,6 +103,7 @@ export const ConversationHero = ({
|
|||
conversationType={conversationType}
|
||||
name={name}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={112}
|
||||
className="module-conversation-hero__avatar"
|
||||
/>
|
||||
|
@ -104,9 +112,11 @@ export const ConversationHero = ({
|
|||
i18n('noteToSelf')
|
||||
) : (
|
||||
<ContactName
|
||||
name={name}
|
||||
title={title}
|
||||
name={displayName}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
i18n={i18n}
|
||||
/>
|
||||
)}
|
||||
</h1>
|
||||
|
@ -116,6 +126,8 @@ export const ConversationHero = ({
|
|||
? i18n('ConversationHero--members-1')
|
||||
: membersCount !== undefined
|
||||
? i18n('ConversationHero--members', [`${membersCount}`])
|
||||
: phoneNumberOnly
|
||||
? null
|
||||
: phoneNumber}
|
||||
</div>
|
||||
) : null}
|
||||
|
|
|
@ -20,6 +20,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -28,12 +29,14 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
{
|
||||
phoneNumber: '(202) 555-1002',
|
||||
name: 'Ms. Earth',
|
||||
title: 'Ms. Earth',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -44,6 +47,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -53,10 +57,12 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
{
|
||||
title: 'Ms. Earth',
|
||||
phoneNumber: '(202) 555-1002',
|
||||
name: 'Ms. Earth',
|
||||
},
|
||||
|
@ -74,6 +80,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -82,13 +89,16 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: '(202) 555-1000',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
{
|
||||
title: 'Ms. Earth',
|
||||
phoneNumber: '(202) 555-1002',
|
||||
name: 'Ms. Earth',
|
||||
},
|
||||
|
@ -99,6 +109,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -107,14 +118,17 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: '(202) 555-1000',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
},
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
{
|
||||
title: 'Ms. Earth',
|
||||
phoneNumber: '(202) 555-1002',
|
||||
name: 'Ms. Earth',
|
||||
},
|
||||
|
@ -125,6 +139,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -133,6 +148,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
},
|
||||
|
@ -143,6 +159,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -152,6 +169,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
},
|
||||
|
@ -162,6 +180,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -170,6 +189,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
isMe: true,
|
||||
|
@ -181,6 +201,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -189,11 +210,13 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'add',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
isMe: true,
|
||||
},
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
|
@ -209,6 +232,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -217,14 +241,17 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'remove',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
},
|
||||
{
|
||||
title: 'Mrs. Ice',
|
||||
phoneNumber: '(202) 555-1001',
|
||||
profileName: 'Mrs. Ice',
|
||||
},
|
||||
{
|
||||
title: 'Ms. Earth',
|
||||
phoneNumber: '(202) 555-1002',
|
||||
name: 'Ms. Earth',
|
||||
},
|
||||
|
@ -235,6 +262,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -243,6 +271,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'remove',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Mr. Fire',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
},
|
||||
|
@ -253,6 +282,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -262,6 +292,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
type: 'remove',
|
||||
contacts: [
|
||||
{
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -278,6 +309,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -291,6 +323,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -310,6 +343,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
@ -323,6 +357,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
},
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
isMe: true,
|
||||
|
@ -342,6 +377,7 @@ const stories: Array<GroupNotificationStory> = [
|
|||
[
|
||||
{
|
||||
from: {
|
||||
title: 'Alice',
|
||||
name: 'Alice',
|
||||
phoneNumber: '(202) 555-1000',
|
||||
},
|
||||
|
|
|
@ -8,9 +8,10 @@ import { LocalizerType } from '../../types/Util';
|
|||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
|
||||
interface Contact {
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
name?: string;
|
||||
title: string;
|
||||
isMe?: boolean;
|
||||
}
|
||||
|
||||
|
@ -48,9 +49,11 @@ export class GroupNotification extends React.Component<Props> {
|
|||
className="module-group-notification__contact"
|
||||
>
|
||||
<ContactName
|
||||
title={contact.title}
|
||||
phoneNumber={contact.phoneNumber}
|
||||
profileName={contact.profileName}
|
||||
name={contact.name}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
@ -128,9 +131,11 @@ export class GroupNotification extends React.Component<Props> {
|
|||
|
||||
const fromContact = (
|
||||
<ContactName
|
||||
title={from.title}
|
||||
phoneNumber={from.phoneNumber}
|
||||
profileName={from.profileName}
|
||||
name={from.name}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ const baseDataProps: Pick<
|
|||
| 'conversationType'
|
||||
| 'previews'
|
||||
| 'timestamp'
|
||||
| 'authorPhoneNumber'
|
||||
| 'authorTitle'
|
||||
> = {
|
||||
id: 'asdf',
|
||||
canReply: true,
|
||||
|
@ -38,7 +38,7 @@ const baseDataProps: Pick<
|
|||
conversationType: 'direct',
|
||||
previews: [],
|
||||
timestamp: Date.now(),
|
||||
authorPhoneNumber: '(202) 555-2001',
|
||||
authorTitle: '(202) 555-2001',
|
||||
};
|
||||
|
||||
type MessageStory = [
|
||||
|
|
|
@ -73,10 +73,10 @@ export type PropsData = {
|
|||
timestamp: number;
|
||||
status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error';
|
||||
contact?: ContactType;
|
||||
authorTitle: string;
|
||||
authorName?: string;
|
||||
authorProfileName?: string;
|
||||
/** Note: this should be formatted for display */
|
||||
authorPhoneNumber: string;
|
||||
authorPhoneNumber?: string;
|
||||
authorColor?: ColorType;
|
||||
conversationType: 'group' | 'direct';
|
||||
attachments?: Array<AttachmentType>;
|
||||
|
@ -86,8 +86,9 @@ export type PropsData = {
|
|||
isFromMe: boolean;
|
||||
sentAt: number;
|
||||
authorId: string;
|
||||
authorPhoneNumber: string;
|
||||
authorPhoneNumber?: string;
|
||||
authorProfileName?: string;
|
||||
authorTitle: string;
|
||||
authorName?: string;
|
||||
authorColor?: ColorType;
|
||||
referencedMessageNotFound: boolean;
|
||||
|
@ -483,12 +484,14 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
|
||||
public renderAuthor() {
|
||||
const {
|
||||
authorTitle,
|
||||
authorName,
|
||||
authorPhoneNumber,
|
||||
authorProfileName,
|
||||
collapseMetadata,
|
||||
conversationType,
|
||||
direction,
|
||||
i18n,
|
||||
isSticker,
|
||||
isTapToView,
|
||||
isTapToViewExpired,
|
||||
|
@ -498,9 +501,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
const title = authorName ? authorName : authorPhoneNumber;
|
||||
|
||||
if (direction !== 'incoming' || conversationType !== 'group' || !title) {
|
||||
if (
|
||||
direction !== 'incoming' ||
|
||||
conversationType !== 'group' ||
|
||||
!authorTitle
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -515,10 +520,12 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return (
|
||||
<div className={moduleName}>
|
||||
<ContactName
|
||||
title={authorTitle}
|
||||
phoneNumber={authorPhoneNumber}
|
||||
name={authorName}
|
||||
profileName={authorProfileName}
|
||||
module={moduleName}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -847,6 +854,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
authorProfileName={quote.authorProfileName}
|
||||
authorName={quote.authorName}
|
||||
authorColor={quoteColor}
|
||||
authorTitle={quote.authorTitle}
|
||||
referencedMessageNotFound={referencedMessageNotFound}
|
||||
isFromMe={quote.isFromMe}
|
||||
withContentAbove={withContentAbove}
|
||||
|
@ -917,6 +925,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
authorName,
|
||||
authorPhoneNumber,
|
||||
authorProfileName,
|
||||
authorTitle,
|
||||
collapseMetadata,
|
||||
authorColor,
|
||||
conversationType,
|
||||
|
@ -942,6 +951,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
name={authorName}
|
||||
phoneNumber={authorPhoneNumber}
|
||||
profileName={authorProfileName}
|
||||
title={authorTitle}
|
||||
size={28}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,9 @@ import { ColorType, LocalizerType } from '../../types/Util';
|
|||
|
||||
interface Contact {
|
||||
status: string;
|
||||
phoneNumber: string;
|
||||
|
||||
title: string;
|
||||
phoneNumber?: string;
|
||||
name?: string;
|
||||
profileName?: string;
|
||||
avatarPath?: string;
|
||||
|
@ -49,7 +51,14 @@ export class MessageDetail extends React.Component<Props> {
|
|||
|
||||
public renderAvatar(contact: Contact) {
|
||||
const { i18n } = this.props;
|
||||
const { avatarPath, color, phoneNumber, name, profileName } = contact;
|
||||
const {
|
||||
avatarPath,
|
||||
color,
|
||||
phoneNumber,
|
||||
name,
|
||||
profileName,
|
||||
title,
|
||||
} = contact;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
|
@ -60,6 +69,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={52}
|
||||
/>
|
||||
);
|
||||
|
@ -123,6 +133,8 @@ export class MessageDetail extends React.Component<Props> {
|
|||
phoneNumber={contact.phoneNumber}
|
||||
name={contact.name}
|
||||
profileName={contact.profileName}
|
||||
title={contact.title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
{errors.map((error, index) => (
|
||||
|
|
|
@ -17,7 +17,9 @@ const i18n = setupI18n('en', enMessages);
|
|||
const getBaseProps = (isGroup = false): MessageRequestActionsProps => ({
|
||||
i18n,
|
||||
conversationType: isGroup ? 'group' : 'direct',
|
||||
profileName: isGroup ? undefined : text('profileName', 'Cayce Bollard'),
|
||||
title: isGroup
|
||||
? text('title', 'NYC Rock Climbers')
|
||||
: text('title', 'Cayce Bollard'),
|
||||
name: isGroup
|
||||
? text('name', 'NYC Rock Climbers')
|
||||
: text('name', 'Cayce Bollard'),
|
||||
|
|
|
@ -12,17 +12,19 @@ import { LocalizerType } from '../../types/Util';
|
|||
export type Props = {
|
||||
i18n: LocalizerType;
|
||||
onAccept(): unknown;
|
||||
} & Omit<ContactNameProps, 'module'> &
|
||||
} & Omit<ContactNameProps, 'module' | 'i18n'> &
|
||||
Omit<
|
||||
MessageRequestActionsConfirmationProps,
|
||||
'i18n' | 'state' | 'onChangeState'
|
||||
>;
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
export const MessageRequestActions = ({
|
||||
i18n,
|
||||
name,
|
||||
profileName,
|
||||
phoneNumber,
|
||||
title,
|
||||
conversationType,
|
||||
isBlocked,
|
||||
onBlock,
|
||||
|
@ -45,6 +47,7 @@ export const MessageRequestActions = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
conversationType={conversationType}
|
||||
state={mrState}
|
||||
onChangeState={setMrState}
|
||||
|
@ -66,6 +69,8 @@ export const MessageRequestActions = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</strong>,
|
||||
]}
|
||||
|
|
|
@ -21,7 +21,7 @@ export type Props = {
|
|||
onDelete(): unknown;
|
||||
state: MessageRequestState;
|
||||
onChangeState(state: MessageRequestState): unknown;
|
||||
} & Omit<ContactNameProps, 'module'>;
|
||||
} & Omit<ContactNameProps, 'module' | 'i18n'>;
|
||||
|
||||
// tslint:disable-next-line: max-func-body-length
|
||||
export const MessageRequestActionsConfirmation = ({
|
||||
|
@ -29,6 +29,7 @@ export const MessageRequestActionsConfirmation = ({
|
|||
name,
|
||||
profileName,
|
||||
phoneNumber,
|
||||
title,
|
||||
conversationType,
|
||||
onBlock,
|
||||
onBlockAndDelete,
|
||||
|
@ -55,6 +56,8 @@ export const MessageRequestActionsConfirmation = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
|
@ -95,6 +98,8 @@ export const MessageRequestActionsConfirmation = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
|
@ -135,6 +140,8 @@ export const MessageRequestActionsConfirmation = ({
|
|||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
i18n={i18n}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -12,7 +12,8 @@ import { ContactName } from './ContactName';
|
|||
|
||||
interface Props {
|
||||
attachment?: QuotedAttachmentType;
|
||||
authorPhoneNumber: string;
|
||||
authorTitle: string;
|
||||
authorPhoneNumber?: string;
|
||||
authorProfileName?: string;
|
||||
authorName?: string;
|
||||
authorColor?: ColorType;
|
||||
|
@ -307,6 +308,7 @@ export class Quote extends React.Component<Props, State> {
|
|||
const {
|
||||
authorProfileName,
|
||||
authorPhoneNumber,
|
||||
authorTitle,
|
||||
authorName,
|
||||
i18n,
|
||||
isFromMe,
|
||||
|
@ -327,6 +329,8 @@ export class Quote extends React.Component<Props, State> {
|
|||
phoneNumber={authorPhoneNumber}
|
||||
name={authorName}
|
||||
profileName={authorProfileName}
|
||||
title={authorTitle}
|
||||
i18n={i18n}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -16,6 +16,7 @@ export type Reaction = {
|
|||
avatarPath?: string;
|
||||
name?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
isMe?: boolean;
|
||||
phoneNumber?: string;
|
||||
};
|
||||
|
@ -156,6 +157,7 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
|
|||
name={from.name}
|
||||
profileName={from.profileName}
|
||||
phoneNumber={from.phoneNumber}
|
||||
title={from.title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
|
@ -168,6 +170,8 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
|
|||
name={from.name}
|
||||
profileName={from.profileName}
|
||||
phoneNumber={from.phoneNumber}
|
||||
title={from.title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
<SafetyNumberNotification
|
||||
i18n={util.i18n}
|
||||
isGroup={true}
|
||||
contact={{ phoneNumber: '(202) 500-1000', profileName: 'Mr. Fire' }}
|
||||
contact={{
|
||||
phoneNumber: '(202) 500-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
title: 'Mr. Fire',
|
||||
}}
|
||||
onVerify={() => console.log('onVerify')}
|
||||
/>
|
||||
</util.ConversationContext>
|
||||
|
@ -18,7 +22,11 @@
|
|||
<SafetyNumberNotification
|
||||
i18n={util.i18n}
|
||||
isGroup={false}
|
||||
contact={{ phoneNumber: '(202) 500-1000', profileName: 'Mr. Fire' }}
|
||||
contact={{
|
||||
phoneNumber: '(202) 500-1000',
|
||||
profileName: 'Mr. Fire',
|
||||
title: 'Mr. Fire',
|
||||
}}
|
||||
onVerify={() => console.log('onVerify')}
|
||||
/>
|
||||
</util.ConversationContext>
|
||||
|
|
|
@ -6,8 +6,9 @@ import { LocalizerType } from '../../types/Util';
|
|||
|
||||
interface ContactType {
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
|
@ -48,7 +49,9 @@ export class SafetyNumberNotification extends React.Component<Props> {
|
|||
name={contact.name}
|
||||
profileName={contact.profileName}
|
||||
phoneNumber={contact.phoneNumber}
|
||||
title={contact.title}
|
||||
module="module-safety-number-notification__contact"
|
||||
i18n={i18n}
|
||||
/>
|
||||
</span>,
|
||||
]}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
type="fromOther"
|
||||
phoneNumber="(202) 555-1000"
|
||||
profileName="Mr. Fire"
|
||||
title="Mr. Fire"
|
||||
timespan="1 hour"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
|
@ -13,6 +14,7 @@
|
|||
type="fromOther"
|
||||
phoneNumber="(202) 555-1000"
|
||||
profileName="Mr. Fire"
|
||||
title="Mr. Fire"
|
||||
disabled={true}
|
||||
timespan="Off"
|
||||
i18n={util.i18n}
|
||||
|
@ -27,12 +29,14 @@
|
|||
<TimerNotification
|
||||
type="fromMe"
|
||||
phoneNumber="(202) 555-1000"
|
||||
title="(202) 555-1000"
|
||||
timespan="1 hour"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<TimerNotification
|
||||
type="fromMe"
|
||||
phoneNumber="(202) 555-1000"
|
||||
title="(202) 555-1000"
|
||||
disabled={true}
|
||||
timespan="Off"
|
||||
i18n={util.i18n}
|
||||
|
@ -47,12 +51,14 @@
|
|||
<TimerNotification
|
||||
type="fromSync"
|
||||
phoneNumber="(202) 555-1000"
|
||||
title="(202) 555-1000"
|
||||
timespan="1 hour"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<TimerNotification
|
||||
type="fromSync"
|
||||
phoneNumber="(202) 555-1000"
|
||||
title="(202) 555-1000"
|
||||
disabled={true}
|
||||
timespan="Off"
|
||||
i18n={util.i18n}
|
||||
|
|
|
@ -7,8 +7,9 @@ import { LocalizerType } from '../../types/Util';
|
|||
|
||||
export type PropsData = {
|
||||
type: 'fromOther' | 'fromMe' | 'fromSync';
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
disabled: boolean;
|
||||
timespan: string;
|
||||
|
@ -27,6 +28,7 @@ export class TimerNotification extends React.Component<Props> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
timespan,
|
||||
type,
|
||||
disabled,
|
||||
|
@ -46,7 +48,9 @@ export class TimerNotification extends React.Component<Props> {
|
|||
key="external-1"
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
name={name}
|
||||
i18n={i18n}
|
||||
/>,
|
||||
timespan,
|
||||
]}
|
||||
|
|
|
@ -10,8 +10,9 @@ interface Props {
|
|||
avatarPath?: string;
|
||||
color: ColorType;
|
||||
name?: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
conversationType: 'group' | 'direct';
|
||||
i18n: LocalizerType;
|
||||
}
|
||||
|
@ -24,6 +25,7 @@ export class TypingBubble extends React.PureComponent<Props> {
|
|||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
conversationType,
|
||||
i18n,
|
||||
} = this.props;
|
||||
|
@ -42,6 +44,7 @@ export class TypingBubble extends React.PureComponent<Props> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={28}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -7,8 +7,9 @@ import { LocalizerType } from '../../types/Util';
|
|||
|
||||
interface ContactType {
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
isMe: boolean;
|
||||
}
|
||||
|
@ -63,7 +64,9 @@ export class UnsupportedMessage extends React.Component<Props> {
|
|||
name={contact.name}
|
||||
profileName={contact.profileName}
|
||||
phoneNumber={contact.phoneNumber}
|
||||
title={contact.title}
|
||||
module="module-unsupported-message__contact"
|
||||
i18n={i18n}
|
||||
/>
|
||||
</span>,
|
||||
]}
|
||||
|
|
|
@ -8,9 +8,10 @@ import { LocalizerType } from '../../types/Util';
|
|||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
|
||||
interface Contact {
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
name?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export type PropsData = {
|
||||
|
@ -56,7 +57,9 @@ export class VerificationNotification extends React.Component<Props> {
|
|||
name={contact.name}
|
||||
profileName={contact.profileName}
|
||||
phoneNumber={contact.phoneNumber}
|
||||
title={contact.title}
|
||||
module="module-verification-notification__contact"
|
||||
i18n={i18n}
|
||||
/>,
|
||||
]}
|
||||
i18n={i18n}
|
||||
|
|
|
@ -24,7 +24,7 @@ export function renderAvatar({
|
|||
|
||||
const avatarPath = avatar && avatar.avatar && avatar.avatar.path;
|
||||
const pending = avatar && avatar.avatar && avatar.avatar.pending;
|
||||
const name = getName(contact) || '';
|
||||
const title = getName(contact) || '';
|
||||
const spinnerSvgSize = size < 50 ? 'small' : 'normal';
|
||||
const spinnerSize = size < 50 ? '24px' : undefined;
|
||||
|
||||
|
@ -46,7 +46,7 @@ export function renderAvatar({
|
|||
color="grey"
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
title={title}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -409,14 +409,11 @@ export class CallingClass {
|
|||
call: Call
|
||||
): CallDetailsType {
|
||||
return {
|
||||
avatarPath: conversation.getAvatarPath(),
|
||||
...conversation.cachedProps,
|
||||
|
||||
callId: call.callId,
|
||||
contactColor: conversation.getColor(),
|
||||
isIncoming: call.isIncoming,
|
||||
isVideoCall: call.isVideoCall,
|
||||
name: conversation.getName(),
|
||||
phoneNumber: conversation.getNumber(),
|
||||
profileName: conversation.getProfileName(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,14 +16,16 @@ import {
|
|||
export type CallId = any;
|
||||
|
||||
export type CallDetailsType = {
|
||||
avatarPath?: string;
|
||||
callId: CallId;
|
||||
contactColor?: ColorType;
|
||||
isIncoming: boolean;
|
||||
isVideoCall: boolean;
|
||||
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
name?: string;
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type CallingStateType = {
|
||||
|
@ -221,10 +223,10 @@ async function showCallNotification(callDetails: CallDetailsType) {
|
|||
if (!canNotify) {
|
||||
return;
|
||||
}
|
||||
const { name, phoneNumber, profileName, isVideoCall } = callDetails;
|
||||
const { title, isVideoCall } = callDetails;
|
||||
notify({
|
||||
platform: window.platform,
|
||||
title: `${name || phoneNumber} ${profileName || ''}`,
|
||||
title,
|
||||
icon: isVideoCall
|
||||
? 'images/icons/v2/video-solid-24.svg'
|
||||
: 'images/icons/v2/phone-right-solid-24.svg',
|
||||
|
|
|
@ -25,7 +25,7 @@ export type DBConversationType = {
|
|||
export type ConversationType = {
|
||||
id: string;
|
||||
uuid?: string;
|
||||
e164: string;
|
||||
e164?: string;
|
||||
name?: string;
|
||||
profileName?: string;
|
||||
avatarPath?: string;
|
||||
|
@ -34,13 +34,13 @@ export type ConversationType = {
|
|||
isBlocked?: boolean;
|
||||
isVerified?: boolean;
|
||||
activeAt?: number;
|
||||
timestamp: number;
|
||||
inboxPosition: number;
|
||||
timestamp?: number;
|
||||
inboxPosition?: number;
|
||||
lastMessage?: {
|
||||
status: 'error' | 'sending' | 'sent' | 'delivered' | 'read';
|
||||
text: string;
|
||||
};
|
||||
phoneNumber: string;
|
||||
phoneNumber?: string;
|
||||
membersCount?: number;
|
||||
type: 'direct' | 'group';
|
||||
isMe: boolean;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import memoizee from 'memoizee';
|
||||
import { fromPairs, isNumber } from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import { format } from '../../types/PhoneNumber';
|
||||
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import { StateType } from '../reducer';
|
||||
import {
|
||||
ConversationLookupType,
|
||||
|
@ -81,29 +79,11 @@ export const getMessagesByConversation = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
function getConversationTitle(
|
||||
conversation: ConversationType,
|
||||
options: { i18n: LocalizerType; ourRegionCode: string }
|
||||
): string {
|
||||
if (conversation.name) {
|
||||
return conversation.name;
|
||||
}
|
||||
|
||||
if (conversation.type === 'group') {
|
||||
const { i18n } = options;
|
||||
|
||||
return i18n('unknownGroup');
|
||||
}
|
||||
|
||||
return format(conversation.phoneNumber, options);
|
||||
}
|
||||
|
||||
const collator = new Intl.Collator();
|
||||
|
||||
export const _getConversationComparator = (
|
||||
i18n: LocalizerType,
|
||||
ourRegionCode: string
|
||||
) => {
|
||||
// Note: we will probably want to put i18n and regionCode back when we are formatting
|
||||
// phone numbers and contacts from scratch here again.
|
||||
export const _getConversationComparator = () => {
|
||||
return (left: ConversationType, right: ConversationType): number => {
|
||||
const leftTimestamp = left.timestamp;
|
||||
const rightTimestamp = right.timestamp;
|
||||
|
@ -132,16 +112,7 @@ export const _getConversationComparator = (
|
|||
return 1;
|
||||
}
|
||||
|
||||
const leftTitle = getConversationTitle(left, {
|
||||
i18n,
|
||||
ourRegionCode,
|
||||
});
|
||||
const rightTitle = getConversationTitle(right, {
|
||||
i18n,
|
||||
ourRegionCode,
|
||||
});
|
||||
|
||||
return collator.compare(leftTitle, rightTitle);
|
||||
return collator.compare(left.title, right.title);
|
||||
};
|
||||
};
|
||||
export const getConversationComparator = createSelector(
|
||||
|
|
|
@ -9,8 +9,6 @@ import {
|
|||
describe('state/selectors/conversations', () => {
|
||||
describe('#getLeftPaneList', () => {
|
||||
it('sorts conversations based on timestamp then by intl-friendly title', () => {
|
||||
const i18n = (key: string) => key;
|
||||
const regionCode = 'US';
|
||||
const data: ConversationLookupType = {
|
||||
id1: {
|
||||
id: 'id1',
|
||||
|
@ -133,7 +131,7 @@ describe('state/selectors/conversations', () => {
|
|||
acceptedMessageRequest: true,
|
||||
},
|
||||
};
|
||||
const comparator = _getConversationComparator(i18n, regionCode);
|
||||
const comparator = _getConversationComparator();
|
||||
const { conversations } = _getLeftPaneLists(data, comparator);
|
||||
|
||||
assert.strictEqual(conversations[0].name, 'First!');
|
||||
|
|
|
@ -353,7 +353,7 @@
|
|||
"rule": "jQuery-append(",
|
||||
"path": "js/views/contact_list_view.js",
|
||||
"line": " this.$el.append(this.contactView.el);",
|
||||
"lineNumber": 46,
|
||||
"lineNumber": 37,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2019-07-31T00:19:18.696Z",
|
||||
"reasonDetail": "Known DOM elements"
|
||||
|
@ -11546,7 +11546,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/MainHeader.tsx",
|
||||
"line": " this.inputRef = React.createRef();",
|
||||
"lineNumber": 69,
|
||||
"lineNumber": 70,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-02-14T20:02:37.507Z",
|
||||
"reasonDetail": "Used only to set focus"
|
||||
|
@ -11555,7 +11555,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/SafetyNumberChangeDialog.js",
|
||||
"line": " const cancelButtonRef = React.createRef();",
|
||||
"lineNumber": 14,
|
||||
"lineNumber": 15,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-06-23T06:48:06.829Z",
|
||||
"reasonDetail": "Used to focus cancel button when dialog opens"
|
||||
|
@ -11573,7 +11573,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/ConversationHeader.js",
|
||||
"line": " this.menuTriggerRef = react_1.default.createRef();",
|
||||
"lineNumber": 14,
|
||||
"lineNumber": 15,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2019-07-31T00:19:18.696Z",
|
||||
"reasonDetail": "Used to reference popup menu"
|
||||
|
@ -11582,7 +11582,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/ConversationHeader.tsx",
|
||||
"line": " this.menuTriggerRef = React.createRef();",
|
||||
"lineNumber": 70,
|
||||
"lineNumber": 75,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-20T20:10:43.540Z",
|
||||
"reasonDetail": "Used to reference popup menu"
|
||||
|
@ -11626,7 +11626,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/Message.tsx",
|
||||
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
|
||||
"lineNumber": 184,
|
||||
"lineNumber": 185,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-21T16:56:07.875Z"
|
||||
},
|
||||
|
@ -11634,7 +11634,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/Message.tsx",
|
||||
"line": " > = React.createRef();",
|
||||
"lineNumber": 188,
|
||||
"lineNumber": 189,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-21T16:56:07.875Z"
|
||||
},
|
||||
|
|
|
@ -42,6 +42,13 @@ export async function generateSecurityNumberBlock(
|
|||
throw new Error('Could not load their key');
|
||||
}
|
||||
|
||||
if (!contact.e164) {
|
||||
window.log.error(
|
||||
'generateSecurityNumberBlock: Attempted to generate security number for contact with no e164'
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const securityNumber = await generateSecurityNumber(
|
||||
ourNumber,
|
||||
ourKey,
|
||||
|
|
Loading…
Reference in a new issue