reactify group updates text bubble from redux store (#2083)

This commit is contained in:
Audric Ackermann 2021-12-15 11:32:07 +11:00 committed by GitHub
parent 5fb3237d1a
commit 58dc3e26ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 322 additions and 374 deletions

View file

@ -1,91 +0,0 @@
import React from 'react';
import {
PropsForGroupUpdate,
PropsForGroupUpdateAdd,
PropsForGroupUpdateKicked,
PropsForGroupUpdateRemove,
PropsForGroupUpdateType,
} from '../../state/ducks/conversations';
import _ from 'underscore';
import { NotificationBubble } from './message/message-item/notification-bubble/NotificationBubble';
import { ReadableMessage } from './message/message-item/ReadableMessage';
// This component is used to display group updates in the conversation view.
// This is a not a "notification" as the name suggests, but a message inside the conversation
type TypeWithContacts =
| PropsForGroupUpdateAdd
| PropsForGroupUpdateKicked
| PropsForGroupUpdateRemove;
function isTypeWithContact(change: PropsForGroupUpdateType): change is TypeWithContacts {
return (change as TypeWithContacts).contacts !== undefined;
}
function getPeople(change: TypeWithContacts) {
return change.contacts?.map(c => c.profileName || c.pubkey).join(', ');
}
// tslint:disable-next-line: cyclomatic-complexity
const ChangeItem = (change: PropsForGroupUpdateType): string => {
const people = isTypeWithContact(change) ? getPeople(change) : undefined;
switch (change.type) {
case 'name':
return window.i18n('titleIsNow', [change.newName || '']);
case 'add':
if (!change.contacts || !change.contacts.length || !people) {
throw new Error('Group update add is missing contacts');
}
const joinKey = change.contacts.length > 1 ? 'multipleJoinedTheGroup' : 'joinedTheGroup';
return window.i18n(joinKey, [people]);
case 'remove':
if (change.isMe) {
return window.i18n('youLeftTheGroup');
}
if (!change.contacts || !change.contacts.length || !people) {
throw new Error('Group update remove is missing contacts');
}
const leftKey = change.contacts.length > 1 ? 'multipleLeftTheGroup' : 'leftTheGroup';
return window.i18n(leftKey, [people]);
case 'kicked':
if (change.isMe) {
return window.i18n('youGotKickedFromGroup');
}
if (!change.contacts || !change.contacts.length || !people) {
throw new Error('Group update kicked is missing contacts');
}
const kickedKey =
change.contacts.length > 1 ? 'multipleKickedFromTheGroup' : 'kickedFromTheGroup';
return window.i18n(kickedKey, [people]);
case 'general':
return window.i18n('updatedTheGroup');
default:
throw new Error('Missing case error');
}
};
export const GroupNotification = (props: PropsForGroupUpdate) => {
const { changes, messageId, receivedAt, isUnread } = props;
const textChange = changes.map(ChangeItem)[0];
return (
<ReadableMessage
messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread}
key={`readable-message-${messageId}`}
>
<NotificationBubble notificationText={textChange} iconType="users" />
</ReadableMessage>
);
};

View file

@ -10,7 +10,7 @@ import {
PropsForGroupUpdate,
} from '../../state/ducks/conversations';
import { getSortedMessagesTypesOfSelectedConversation } from '../../state/selectors/conversations';
import { GroupNotification } from './GroupNotification';
import { GroupUpdateMessage } from './message/message-item/GroupUpdateMessage';
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
import { MessageDateBreak } from './message/message-item/DateBreak';
import { GroupInvitation } from './message/message-item/GroupInvitation';
@ -63,7 +63,7 @@ export const SessionMessagesList = (props: {
) : null;
if (messageProps.message?.messageType === 'group-notification') {
const msgProps = messageProps.message.props as PropsForGroupUpdate;
return [<GroupNotification key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
return [<GroupUpdateMessage key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
}
if (messageProps.message?.messageType === 'group-invitation') {

View file

@ -0,0 +1,89 @@
import React from 'react';
import {
PropsForGroupUpdate,
PropsForGroupUpdateType,
} from '../../../../state/ducks/conversations';
import _ from 'underscore';
import { NotificationBubble } from './notification-bubble/NotificationBubble';
import { ReadableMessage } from './ReadableMessage';
import { arrayContainsUsOnly } from '../../../../models/message';
import { useConversationsUsernameOrFull } from '../../../../hooks/useParamSelector';
// This component is used to display group updates in the conversation view.
const ChangeItemJoined = (added: Array<string>): string => {
if (!added.length) {
throw new Error('Group update add is missing contacts');
}
const names = useConversationsUsernameOrFull(added);
const joinKey = added.length > 1 ? 'multipleJoinedTheGroup' : 'joinedTheGroup';
return window.i18n(joinKey, [names.join(', ')]);
};
const ChangeItemKicked = (kicked: Array<string>): string => {
if (!kicked.length) {
throw new Error('Group update kicked is missing contacts');
}
const names = useConversationsUsernameOrFull(kicked);
if (arrayContainsUsOnly(kicked)) {
return window.i18n('youGotKickedFromGroup');
}
const kickedKey = kicked.length > 1 ? 'multipleKickedFromTheGroup' : 'kickedFromTheGroup';
return window.i18n(kickedKey, [names.join(', ')]);
};
const ChangeItemLeft = (left: Array<string>): string => {
if (!left.length) {
throw new Error('Group update remove is missing contacts');
}
const names = useConversationsUsernameOrFull(left);
if (arrayContainsUsOnly(left)) {
return window.i18n('youLeftTheGroup');
}
const leftKey = left.length > 1 ? 'multipleLeftTheGroup' : 'leftTheGroup';
return window.i18n(leftKey, [names.join(', ')]);
};
// tslint:disable-next-line: cyclomatic-complexity
const ChangeItem = (change: PropsForGroupUpdateType): string => {
switch (change.type) {
case 'name':
console.warn('name: ', change.newName);
return window.i18n('titleIsNow', [change.newName || '']);
case 'add':
return ChangeItemJoined(change.added);
case 'left':
return ChangeItemLeft(change.left);
case 'kicked':
return ChangeItemKicked(change.kicked);
case 'general':
return window.i18n('updatedTheGroup');
default:
throw new Error('Missing case error');
}
};
export const GroupUpdateMessage = (props: PropsForGroupUpdate) => {
const { change, messageId, receivedAt, isUnread } = props;
return (
<ReadableMessage
messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread}
key={`readable-message-${messageId}`}
>
<NotificationBubble notificationText={ChangeItem(change)} iconType="users" />
</ReadableMessage>
);
};

View file

@ -108,7 +108,9 @@ const Seed = (props: SeedProps) => {
<p className="session-modal__description">{i18n('recoveryPhraseSavePromptMain')}</p>
<SpacerXS />
<i className="session-modal__text-highlight">{recoveryPhrase}</i>
<i data-test-id="recovery-phrase-seed-modal" className="session-modal__text-highlight">
{recoveryPhrase}
</i>
</div>
<SpacerLG />
<div className="session-modal__button-group">

View file

@ -49,6 +49,21 @@ export function useConversationUsernameOrShorten(pubkey?: string) {
});
}
/**
* Returns either the nickname, profileName, or the shorten of the pubkeys given
*/
export function useConversationsUsernameOrFull(pubkeys: Array<string>) {
return useSelector((state: StateType) => {
return pubkeys.map(pubkey => {
if (pubkey === UserUtils.getOurPubKeyStrFromCache() || pubkey.toLowerCase() === 'you') {
return window.i18n('you');
}
const convo = state.conversations.conversationLookup[pubkey];
return convo?.profileName || convo?.name || pubkey;
});
});
}
export function useOurConversationUsername() {
return useConversationUsername(UserUtils.getOurPubKeyStrFromCache());
}

View file

@ -13,6 +13,7 @@ import {
fillMessageAttributesWithDefaults,
MessageAttributes,
MessageAttributesOptionals,
MessageGroupUpdate,
MessageModelType,
PropsForDataExtractionNotification,
} from './messageType';
@ -31,11 +32,10 @@ import {
PropsForGroupInvitation,
PropsForGroupUpdate,
PropsForGroupUpdateAdd,
PropsForGroupUpdateArray,
PropsForGroupUpdateGeneral,
PropsForGroupUpdateKicked,
PropsForGroupUpdateLeft,
PropsForGroupUpdateName,
PropsForGroupUpdateRemove,
PropsForMessageWithoutConvoProps,
} from '../state/ducks/conversations';
import { VisibleMessage } from '../session/messages/outgoing/visibleMessage/VisibleMessage';
@ -51,6 +51,23 @@ import { isUsFromCache } from '../session/utils/User';
import { perfEnd, perfStart } from '../session/utils/Performance';
import { AttachmentTypeWithPath } from '../types/Attachment';
import _ from 'lodash';
// tslint:disable: cyclomatic-complexity
/**
* @returns true if the array contains only a single item being 'You', 'you' or our device pubkey
*/
export function arrayContainsUsOnly(arrayToCheck: Array<string> | undefined) {
return (
arrayToCheck &&
arrayToCheck.length === 1 &&
(arrayToCheck[0] === UserUtils.getOurPubKeyStrFromCache() ||
arrayToCheck[0].toLowerCase() === 'you')
);
}
export function arrayContainsOneItemOnly(arrayToCheck: Array<string> | undefined) {
return arrayToCheck && arrayToCheck.length === 1;
}
export class MessageModel extends Backbone.Model<MessageAttributes> {
constructor(attributes: MessageAttributesOptionals & { skipTimerInit?: boolean }) {
@ -86,7 +103,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
perfStart(`getPropsMessage-${this.id}`);
const propsForDataExtractionNotification = this.getPropsForDataExtractionNotification();
const propsForGroupInvitation = this.getPropsForGroupInvitation();
const propsForGroupNotification = this.getPropsForGroupNotification();
const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage();
const propsForTimerNotification = this.getPropsForTimerNotification();
const callNotificationType = this.get('callNotificationType');
const messageProps: MessageModelPropsWithoutConvoProps = {
@ -98,8 +115,8 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (propsForGroupInvitation) {
messageProps.propsForGroupInvitation = propsForGroupInvitation;
}
if (propsForGroupNotification) {
messageProps.propsForGroupNotification = propsForGroupNotification;
if (propsForGroupUpdateMessage) {
messageProps.propsForGroupUpdateMessage = propsForGroupUpdateMessage;
}
if (propsForTimerNotification) {
messageProps.propsForTimerNotification = propsForTimerNotification;
@ -132,10 +149,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return !!(flags & expirationTimerFlag);
}
public isGroupUpdate() {
return Boolean(this.get('group_update'));
}
public isIncoming() {
return this.get('type') === 'incoming';
}
@ -158,118 +171,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
this.set(attributes);
}
// tslint:disable-next-line: cyclomatic-complexity
public getDescription() {
if (this.isGroupUpdate()) {
const groupUpdate = this.get('group_update');
const ourPrimary = window.textsecure.storage.get('primaryDevicePubKey');
if (
groupUpdate.left === 'You' ||
(Array.isArray(groupUpdate.left) &&
groupUpdate.left.length === 1 &&
groupUpdate.left[0] === ourPrimary)
) {
return window.i18n('youLeftTheGroup');
} else if (
(groupUpdate.left && Array.isArray(groupUpdate.left) && groupUpdate.left.length === 1) ||
typeof groupUpdate.left === 'string'
) {
return window.i18n('leftTheGroup', [
getConversationController().getContactProfileNameOrShortenedPubKey(groupUpdate.left),
]);
}
if (groupUpdate.kicked === 'You') {
return window.i18n('youGotKickedFromGroup');
}
const messages = [];
if (!groupUpdate.name && !groupUpdate.joined && !groupUpdate.kicked) {
messages.push(window.i18n('updatedTheGroup'));
}
if (groupUpdate.name) {
messages.push(window.i18n('titleIsNow', groupUpdate.name));
}
if (groupUpdate.joined && groupUpdate.joined.length) {
const names = groupUpdate.joined.map((pubKey: string) =>
getConversationController().getContactProfileNameOrFullPubKey(pubKey)
);
if (names.length > 1) {
messages.push(window.i18n('multipleJoinedTheGroup', names.join(', ')));
} else {
messages.push(window.i18n('joinedTheGroup', names[0]));
}
}
if (groupUpdate.kicked && groupUpdate.kicked.length) {
const names = _.map(
groupUpdate.kicked,
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (names.length > 1) {
messages.push(window.i18n('multipleKickedFromTheGroup', [names.join(', ')]));
} else {
messages.push(window.i18n('kickedFromTheGroup', [names[0]]));
}
}
return messages.join(' ');
}
if (this.isIncoming() && this.hasErrors()) {
return window.i18n('incomingError');
}
if (this.isGroupInvitation()) {
return `😎 ${window.i18n('openGroupInvitation')}`;
}
if (this.isDataExtractionNotification()) {
const dataExtraction = this.get(
'dataExtractionNotification'
) as DataExtractionNotificationMsg;
if (dataExtraction.type === SignalService.DataExtractionNotification.Type.SCREENSHOT) {
return window.i18n('tookAScreenshot', [
getConversationController().getContactProfileNameOrShortenedPubKey(dataExtraction.source),
]);
}
return window.i18n('savedTheFile', [
getConversationController().getContactProfileNameOrShortenedPubKey(dataExtraction.source),
]);
}
if (this.get('callNotificationType')) {
const displayName = getConversationController().getContactProfileNameOrShortenedPubKey(
this.get('conversationId')
);
const callNotificationType = this.get('callNotificationType');
if (callNotificationType === 'missed-call') {
return window.i18n('callMissed', [displayName]);
}
if (callNotificationType === 'started-call') {
return window.i18n('startedACall', [displayName]);
}
if (callNotificationType === 'answered-a-call') {
return window.i18n('answeredACall', [displayName]);
}
}
if (this.get('callNotificationType')) {
const displayName = getConversationController().getContactProfileNameOrShortenedPubKey(
this.get('conversationId')
);
const callNotificationType = this.get('callNotificationType');
if (callNotificationType === 'missed-call') {
return window.i18n('callMissed', [displayName]);
}
if (callNotificationType === 'started-call') {
return window.i18n('startedACall', [displayName]);
}
if (callNotificationType === 'answered-a-call') {
return window.i18n('answeredACall', [displayName]);
}
}
return this.get('body');
}
public isGroupInvitation() {
return !!this.get('groupInvitation');
}
@ -425,96 +326,56 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
}
// tslint:disable-next-line: cyclomatic-complexity
public getPropsForGroupNotification(): PropsForGroupUpdate | null {
if (!this.isGroupUpdate()) {
public getPropsForGroupUpdateMessage(): PropsForGroupUpdate | null {
const groupUpdate = this.getGroupUpdateAsArray();
if (!groupUpdate || _.isEmpty(groupUpdate)) {
return null;
}
const groupUpdate = this.get('group_update');
const changes: PropsForGroupUpdateArray = [];
if (!groupUpdate.name && !groupUpdate.left && !groupUpdate.joined && !groupUpdate.kicked) {
const change: PropsForGroupUpdateGeneral = {
type: 'general',
};
changes.push(change);
}
const sharedProps = {
messageId: this.id,
isUnread: this.isUnread(),
receivedAt: this.get('received_at'),
};
if (groupUpdate.joined) {
if (groupUpdate.joined?.length) {
const change: PropsForGroupUpdateAdd = {
type: 'add',
contacts: _.map(
Array.isArray(groupUpdate.joined) ? groupUpdate.joined : [groupUpdate.joined],
pubkey => this.findAndFormatContact(pubkey)
),
added: groupUpdate.joined,
};
changes.push(change);
return { change, ...sharedProps };
}
if (groupUpdate.kicked === 'You') {
if (groupUpdate.kicked?.length) {
const change: PropsForGroupUpdateKicked = {
type: 'kicked',
isMe: true,
kicked: groupUpdate.kicked,
};
changes.push(change);
} else if (groupUpdate.kicked) {
const change: PropsForGroupUpdateKicked = {
type: 'kicked',
isMe: false,
contacts: _.map(
Array.isArray(groupUpdate.kicked) ? groupUpdate.kicked : [groupUpdate.kicked],
pubkey => this.findAndFormatContact(pubkey)
),
};
changes.push(change);
return { change, ...sharedProps };
}
if (groupUpdate.left === 'You') {
const change: PropsForGroupUpdateRemove = {
type: 'remove',
isMe: true,
if (groupUpdate.left?.length) {
const change: PropsForGroupUpdateLeft = {
type: 'left',
left: groupUpdate.left,
};
changes.push(change);
} else if (groupUpdate.left) {
if (
Array.isArray(groupUpdate.left) &&
groupUpdate.left.length === 1 &&
groupUpdate.left[0] === UserUtils.getOurPubKeyStrFromCache()
) {
const change: PropsForGroupUpdateRemove = {
type: 'remove',
isMe: true,
};
changes.push(change);
} else if (
typeof groupUpdate.left === 'string' ||
(Array.isArray(groupUpdate.left) && groupUpdate.left.length === 1)
) {
const change: PropsForGroupUpdateRemove = {
type: 'remove',
isMe: false,
contacts: _.map(
Array.isArray(groupUpdate.left) ? groupUpdate.left : [groupUpdate.left],
pubkey => this.findAndFormatContact(pubkey)
),
};
changes.push(change);
}
return { change, ...sharedProps };
}
if (groupUpdate.name) {
const change: PropsForGroupUpdateName = {
type: 'name',
newName: groupUpdate.name as string,
newName: groupUpdate.name,
};
changes.push(change);
return { change, ...sharedProps };
}
return {
changes,
messageId: this.id,
isUnread: this.isUnread(),
receivedAt: this.get('received_at'),
// Just show a "Group Updated" message, not sure what was changed
const changeGeneral: PropsForGroupUpdateGeneral = {
type: 'general',
};
return { change: changeGeneral, ...sharedProps };
}
public getMessagePropStatus(): LastMessageStatusType {
@ -1265,6 +1126,151 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
updatesToDispatch.set(this.id, this.getMessageModelProps());
trotthledAllMessagesDispatch();
}
/**
* Before, group_update attributes could be just the string 'You' and not an array.
* Using this method to get the group update makes sure than the joined, kicked, or left are always an array of string, or undefined
*/
private getGroupUpdateAsArray() {
const groupUpdate = this.get('group_update');
if (!groupUpdate || _.isEmpty(groupUpdate)) {
return undefined;
}
const left: Array<string> | undefined = Array.isArray(groupUpdate.left)
? groupUpdate.left
: groupUpdate.left
? [groupUpdate.left]
: undefined;
const kicked: Array<string> | undefined = Array.isArray(groupUpdate.kicked)
? groupUpdate.kicked
: groupUpdate.kicked
? [groupUpdate.kicked]
: undefined;
const joined: Array<string> | undefined = Array.isArray(groupUpdate.joined)
? groupUpdate.joined
: groupUpdate.joined
? [groupUpdate.joined]
: undefined;
const forcedArrayUpdate: MessageGroupUpdate = {};
if (left) {
forcedArrayUpdate.left = left;
}
if (joined) {
forcedArrayUpdate.joined = joined;
}
if (kicked) {
forcedArrayUpdate.kicked = kicked;
}
if (groupUpdate.name) {
forcedArrayUpdate.name = groupUpdate.name;
}
return forcedArrayUpdate;
}
private getDescription() {
const groupUpdate = this.getGroupUpdateAsArray();
if (groupUpdate) {
if (arrayContainsUsOnly(groupUpdate.kicked)) {
return window.i18n('youGotKickedFromGroup');
}
if (arrayContainsUsOnly(groupUpdate.left)) {
return window.i18n('youLeftTheGroup');
}
if (groupUpdate.left && groupUpdate.left.length === 1) {
return window.i18n('leftTheGroup', [
getConversationController().getContactProfileNameOrShortenedPubKey(groupUpdate.left[0]),
]);
}
const messages = [];
if (!groupUpdate.name && !groupUpdate.joined && !groupUpdate.kicked && !groupUpdate.kicked) {
return window.i18n('updatedTheGroup'); // Group Updated
}
if (groupUpdate.name) {
return window.i18n('titleIsNow', [groupUpdate.name]);
}
if (groupUpdate.joined && groupUpdate.joined.length) {
const names = groupUpdate.joined.map((pubKey: string) =>
getConversationController().getContactProfileNameOrFullPubKey(pubKey)
);
if (names.length > 1) {
messages.push(window.i18n('multipleJoinedTheGroup', [names.join(', ')]));
} else {
messages.push(window.i18n('joinedTheGroup', names));
}
}
if (groupUpdate.kicked && groupUpdate.kicked.length) {
const names = _.map(
groupUpdate.kicked,
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (names.length > 1) {
messages.push(window.i18n('multipleKickedFromTheGroup', [names.join(', ')]));
} else {
messages.push(window.i18n('kickedFromTheGroup', names));
}
}
return messages.join(' ');
}
if (this.isIncoming() && this.hasErrors()) {
return window.i18n('incomingError');
}
if (this.isGroupInvitation()) {
return `😎 ${window.i18n('openGroupInvitation')}`;
}
if (this.isDataExtractionNotification()) {
const dataExtraction = this.get(
'dataExtractionNotification'
) as DataExtractionNotificationMsg;
if (dataExtraction.type === SignalService.DataExtractionNotification.Type.SCREENSHOT) {
return window.i18n('tookAScreenshot', [
getConversationController().getContactProfileNameOrShortenedPubKey(dataExtraction.source),
]);
}
return window.i18n('savedTheFile', [
getConversationController().getContactProfileNameOrShortenedPubKey(dataExtraction.source),
]);
}
if (this.get('callNotificationType')) {
const displayName = getConversationController().getContactProfileNameOrShortenedPubKey(
this.get('conversationId')
);
const callNotificationType = this.get('callNotificationType');
if (callNotificationType === 'missed-call') {
return window.i18n('callMissed', [displayName]);
}
if (callNotificationType === 'started-call') {
return window.i18n('startedACall', [displayName]);
}
if (callNotificationType === 'answered-a-call') {
return window.i18n('answeredACall', [displayName]);
}
}
if (this.get('callNotificationType')) {
const displayName = getConversationController().getContactProfileNameOrShortenedPubKey(
this.get('conversationId')
);
const callNotificationType = this.get('callNotificationType');
if (callNotificationType === 'missed-call') {
return window.i18n('callMissed', [displayName]);
}
if (callNotificationType === 'started-call') {
return window.i18n('startedACall', [displayName]);
}
if (callNotificationType === 'answered-a-call') {
return window.i18n('answeredACall', [displayName]);
}
}
return this.get('body');
}
}
const trotthledAllMessagesDispatch = _.throttle(() => {

View file

@ -24,7 +24,7 @@ export interface MessageAttributes {
expires_at?: number;
recipients: Array<string>;
type: MessageModelType;
group_update?: any;
group_update?: MessageGroupUpdate;
groupInvitation?: any;
attachments?: any;
conversationId: string;
@ -125,6 +125,13 @@ export type PropsForDataExtractionNotification = DataExtractionNotificationMsg &
isUnread: boolean;
};
export type MessageGroupUpdate = {
left?: Array<string>;
joined?: Array<string>;
kicked?: Array<string>;
name?: string;
};
export interface MessageAttributesOptionals {
id?: string;
source: string;
@ -141,7 +148,7 @@ export interface MessageAttributesOptionals {
expires_at?: number;
recipients?: Array<string>;
type: MessageModelType;
group_update?: any;
group_update?: MessageGroupUpdate;
groupInvitation?: any;
attachments?: any;
contact?: any;

View file

@ -3,8 +3,6 @@ import { queueAttachmentDownloads } from './attachments';
import { Quote } from './types';
import { PubKey } from '../session/types';
import _ from 'lodash';
import { SignalService } from '../protobuf';
import { UserUtils } from '../session/utils';
import { getConversationController } from '../session/conversations';
import { ConversationModel, ConversationTypeEnum } from '../models/conversation';
import { MessageModel } from '../models/message';
@ -13,71 +11,6 @@ import { MessageModelPropsWithoutConvoProps, messagesAdded } from '../state/duck
import { updateProfileOneAtATime } from './dataMessage';
import Long from 'long';
async function handleGroups(
conversation: ConversationModel,
group: any,
source: any
): Promise<any> {
const GROUP_TYPES = SignalService.GroupContext.Type;
let groupUpdate = null;
// conversation attributes
const attributes: any = {
type: 'group',
groupId: group.id,
...conversation.attributes,
};
const oldMembers = conversation.get('members');
if (group.type === GROUP_TYPES.UPDATE) {
attributes.name = group.name;
attributes.members = group.members;
groupUpdate = conversation.changedAttributes(_.pick(group, 'name', 'avatar')) || {};
const addedMembers = _.difference(attributes.members, oldMembers);
if (addedMembers.length > 0) {
groupUpdate.joined = addedMembers;
}
if (conversation.get('left')) {
// TODO: Maybe we shouldn't assume this message adds us:
// we could maybe still get this message by mistake
window?.log?.warn('re-added to a left group');
attributes.left = false;
}
if (attributes.isKickedFromGroup) {
// Assume somebody re-invited us since we received this update
attributes.isKickedFromGroup = false;
}
// Check if anyone got kicked:
const removedMembers = _.difference(oldMembers, attributes.members);
const ourDeviceWasRemoved = removedMembers.some(member => UserUtils.isUsFromCache(member));
if (ourDeviceWasRemoved) {
groupUpdate.kicked = 'You';
attributes.isKickedFromGroup = true;
} else if (removedMembers.length) {
groupUpdate.kicked = removedMembers;
}
} else if (group.type === GROUP_TYPES.QUIT) {
if (UserUtils.isUsFromCache(source)) {
attributes.left = true;
groupUpdate = { left: 'You' };
} else {
groupUpdate = { left: source };
}
attributes.members = _.without(oldMembers, source);
}
conversation.set(attributes);
return groupUpdate;
}
function contentTypeSupported(type: string): boolean {
const Chrome = window.Signal.Util.GoogleChrome;
return Chrome.isImageTypeSupported(type) || Chrome.isVideoTypeSupported(type);
@ -271,15 +204,6 @@ async function handleRegularMessage(
const now = Date.now();
// Medium groups might have `group` set even if with group chat messages...
if (dataMessage.group && !conversation.isMediumGroup()) {
// This is not necessarily a group update message, it could also be a regular group message
const groupUpdate = await handleGroups(conversation, dataMessage.group, source);
if (groupUpdate !== null) {
message.set({ group_update: groupUpdate });
}
}
if (dataMessage.openGroupInvitation) {
message.set({ groupInvitation: dataMessage.openGroupInvitation });
}

View file

@ -328,7 +328,7 @@ export async function leaveClosedGroup(groupId: string) {
const diffTimestamp = Date.now() - getLatestTimestampOffset();
const dbMessage = await convo.addSingleMessage({
group_update: { left: 'You' },
group_update: { left: [source] },
conversationId: groupId,
source,
type: 'outgoing',

View file

@ -30,7 +30,7 @@ export type MessageModelPropsWithoutConvoProps = {
propsForGroupInvitation?: PropsForGroupInvitation;
propsForTimerNotification?: PropsForExpirationTimer;
propsForDataExtractionNotification?: PropsForDataExtractionNotification;
propsForGroupNotification?: PropsForGroupUpdate;
propsForGroupUpdateMessage?: PropsForGroupUpdate;
propsForCallNotification?: PropsForCallNotification;
};
@ -90,19 +90,17 @@ export type PropsForGroupUpdateGeneral = {
export type PropsForGroupUpdateAdd = {
type: 'add';
contacts?: Array<FindAndFormatContactType>;
added: Array<string>;
};
export type PropsForGroupUpdateKicked = {
type: 'kicked';
isMe: boolean;
contacts?: Array<FindAndFormatContactType>;
kicked: Array<string>;
};
export type PropsForGroupUpdateRemove = {
type: 'remove';
isMe: boolean;
contacts?: Array<FindAndFormatContactType>;
export type PropsForGroupUpdateLeft = {
type: 'left';
left: Array<string>;
};
export type PropsForGroupUpdateName = {
@ -115,12 +113,10 @@ export type PropsForGroupUpdateType =
| PropsForGroupUpdateAdd
| PropsForGroupUpdateKicked
| PropsForGroupUpdateName
| PropsForGroupUpdateRemove;
export type PropsForGroupUpdateArray = Array<PropsForGroupUpdateType>;
| PropsForGroupUpdateLeft;
export type PropsForGroupUpdate = {
changes: PropsForGroupUpdateArray;
change: PropsForGroupUpdateType;
messageId: string;
receivedAt: number | undefined;
isUnread: boolean;

View file

@ -222,13 +222,13 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
};
}
if (msg.propsForGroupNotification) {
if (msg.propsForGroupUpdateMessage) {
return {
showUnreadIndicator: isFirstUnread,
showDateBreak,
message: {
messageType: 'group-notification',
props: { ...msg.propsForGroupNotification, messageId: msg.propsForMessage.id },
props: { ...msg.propsForGroupUpdateMessage, messageId: msg.propsForMessage.id },
},
};
}