Modern profile sharing in 1:1 and GroupV1 groups
This commit is contained in:
parent
60f2422e2a
commit
04b7a29229
22 changed files with 371 additions and 115 deletions
|
@ -8,6 +8,7 @@ import {
|
|||
LastMessageStatus,
|
||||
ConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
import { PropsData } from '../components/conversation/Message';
|
||||
import { CallbackResultType } from '../textsecure/SendMessage';
|
||||
import { BodyRangesType } from '../types/Util';
|
||||
import { PropsDataType as GroupsV2Props } from '../components/conversation/GroupV2Change';
|
||||
|
@ -59,7 +60,9 @@ const { getTextWithMentions, GoogleChrome } = window.Signal.Util;
|
|||
|
||||
const { addStickerPackReference, getMessageBySender } = window.Signal.Data;
|
||||
const { bytesFromString } = window.Signal.Crypto;
|
||||
const PLACEHOLDER_CONTACT = {
|
||||
const PLACEHOLDER_CONTACT: Pick<ConversationType, 'title' | 'type' | 'id'> = {
|
||||
id: 'placeholder-contact',
|
||||
type: 'direct',
|
||||
title: window.i18n('unknownContact'),
|
||||
};
|
||||
|
||||
|
@ -694,26 +697,26 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
.map(attachment => this.getPropsForAttachment(attachment));
|
||||
}
|
||||
|
||||
getPropsForMessage(): WhatIsThis {
|
||||
// Note: interactionMode is mixed in via selectors/conversations._messageSelector
|
||||
getPropsForMessage(): Omit<PropsData, 'interactionMode'> {
|
||||
const sourceId = this.getContactId();
|
||||
const contact = this.findAndFormatContact(sourceId);
|
||||
const contactModel = this.findContact(sourceId);
|
||||
|
||||
const authorColor = contactModel ? contactModel.getColor() : null;
|
||||
const authorAvatarPath = contactModel ? contactModel.getAvatarPath() : null;
|
||||
const authorColor = contactModel ? contactModel.getColor() : undefined;
|
||||
const authorAvatarPath = contactModel
|
||||
? contactModel.getAvatarPath()
|
||||
: undefined;
|
||||
|
||||
const expirationLength = this.get('expireTimer') * 1000;
|
||||
const expireTimerStart = this.get('expirationStartTimestamp');
|
||||
const expirationTimestamp =
|
||||
expirationLength && expireTimerStart
|
||||
? expireTimerStart + expirationLength
|
||||
: null;
|
||||
: undefined;
|
||||
|
||||
const conversation = this.getConversation();
|
||||
const isGroup = conversation && !conversation.isPrivate();
|
||||
const conversationAccepted = Boolean(
|
||||
conversation && conversation.getAccepted()
|
||||
);
|
||||
const sticker = this.get('sticker');
|
||||
|
||||
const isTapToView = this.isTapToView();
|
||||
|
@ -739,7 +742,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
textPending: this.get('bodyPending'),
|
||||
id: this.id,
|
||||
conversationId: this.get('conversationId'),
|
||||
conversationAccepted,
|
||||
isSticker: Boolean(sticker),
|
||||
direction: this.isIncoming() ? 'incoming' : 'outgoing',
|
||||
timestamp: this.get('sent_at'),
|
||||
|
@ -747,6 +749,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
contact: this.getPropsForEmbeddedContact(),
|
||||
canReply: this.canReply(),
|
||||
canDeleteForEveryone: this.canDeleteForEveryone(),
|
||||
canDownload: this.canDownload(),
|
||||
authorTitle: contact.title,
|
||||
authorColor,
|
||||
authorName: contact.name,
|
||||
|
@ -801,7 +804,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
// Dependencies of prop-generation functions
|
||||
findAndFormatContact(
|
||||
identifier?: string
|
||||
): Partial<ConversationType> & Pick<ConversationType, 'title'> {
|
||||
): Partial<ConversationType> &
|
||||
Pick<ConversationType, 'title' | 'id' | 'type'> {
|
||||
if (!identifier) {
|
||||
return PLACEHOLDER_CONTACT;
|
||||
}
|
||||
|
@ -824,6 +828,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
});
|
||||
|
||||
return {
|
||||
id: 'phone-only',
|
||||
type: 'direct',
|
||||
title: phoneNumber,
|
||||
phoneNumber,
|
||||
};
|
||||
|
@ -839,9 +845,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
createNonBreakingLastSeparator(text: string): string | null {
|
||||
createNonBreakingLastSeparator(text: string): string | undefined {
|
||||
if (!text) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const nbsp = '\xa0';
|
||||
|
@ -859,7 +865,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return this.get('type') === 'incoming';
|
||||
}
|
||||
|
||||
getMessagePropStatus(): LastMessageStatus | null {
|
||||
getMessagePropStatus(): LastMessageStatus | undefined {
|
||||
const sent = this.get('sent');
|
||||
const sentTo = this.get('sent_to') || [];
|
||||
|
||||
|
@ -870,7 +876,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return 'error';
|
||||
}
|
||||
if (!this.isOutgoing()) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const readBy = this.get('read_by') || [];
|
||||
|
@ -2010,34 +2016,50 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return true;
|
||||
}
|
||||
|
||||
canDownload(): boolean {
|
||||
const conversation = this.getConversation();
|
||||
const isAccepted = Boolean(conversation && conversation.getAccepted());
|
||||
|
||||
if (this.isOutgoing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isAccepted;
|
||||
}
|
||||
|
||||
canReply(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const isAccepted = this.getConversation()!.getAccepted();
|
||||
const conversation = this.getConversation();
|
||||
const errors = this.get('errors');
|
||||
const isOutgoing = this.get('type') === 'outgoing';
|
||||
const numDelivered = this.get('delivered');
|
||||
|
||||
// Case 1: We cannot reply if we have accepted the message request
|
||||
if (!isAccepted) {
|
||||
// Case 1: If mandatory profile sharing is enabled, and we haven't shared yet, then
|
||||
// we can't reply.
|
||||
if (conversation?.isMissingRequiredProfileSharing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Case 2: We cannot reply if this message is deleted for everyone
|
||||
// Case 2: We cannot reply if we have accepted the message request
|
||||
if (!conversation?.getAccepted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Case 3: We cannot reply if this message is deleted for everyone
|
||||
if (this.get('deletedForEveryone')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Case 3: We can reply if this is outgoing and delievered to at least one recipient
|
||||
// Case 4: We can reply if this is outgoing and delievered to at least one recipient
|
||||
if (isOutgoing && numDelivered > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case 4: We can reply if there are no errors
|
||||
// Case 5: We can reply if there are no errors
|
||||
if (!errors || (errors && errors.length === 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case 5: default
|
||||
// Case 6: default
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue