Reliability fixes for conversation formatting and message send
This commit is contained in:
parent
fa2d300714
commit
8eea20ea91
11 changed files with 114 additions and 87 deletions
|
@ -41,7 +41,6 @@ describe('Message', () => {
|
||||||
const fakeDataMessage = new ArrayBuffer(0);
|
const fakeDataMessage = new ArrayBuffer(0);
|
||||||
const result = {
|
const result = {
|
||||||
dataMessage: fakeDataMessage,
|
dataMessage: fakeDataMessage,
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
};
|
};
|
||||||
const promise = Promise.resolve(result);
|
const promise = Promise.resolve(result);
|
||||||
await message.send(promise);
|
await message.send(promise);
|
||||||
|
@ -52,11 +51,7 @@ describe('Message', () => {
|
||||||
it('updates the `sent` attribute', async () => {
|
it('updates the `sent` attribute', async () => {
|
||||||
const message = createMessage({ type: 'outgoing', source, sent: false });
|
const message = createMessage({ type: 'outgoing', source, sent: false });
|
||||||
|
|
||||||
await message.send(
|
await message.send(Promise.resolve({}));
|
||||||
Promise.resolve({
|
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.isTrue(message.get('sent'));
|
assert.isTrue(message.get('sent'));
|
||||||
});
|
});
|
||||||
|
@ -69,11 +64,7 @@ describe('Message', () => {
|
||||||
callCount += 1;
|
callCount += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
await message.send(
|
await message.send(Promise.resolve({}));
|
||||||
Promise.resolve({
|
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(callCount, 1);
|
assert.strictEqual(callCount, 1);
|
||||||
});
|
});
|
||||||
|
@ -86,11 +77,7 @@ describe('Message', () => {
|
||||||
calls.push(args);
|
calls.push(args);
|
||||||
});
|
});
|
||||||
|
|
||||||
await message.send(
|
await message.send(Promise.resolve({}));
|
||||||
Promise.resolve({
|
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.lengthOf(calls, 1);
|
assert.lengthOf(calls, 1);
|
||||||
assert.strictEqual(calls[0][0], message);
|
assert.strictEqual(calls[0][0], message);
|
||||||
|
@ -125,7 +112,6 @@ describe('Message', () => {
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
errors: [new Error('baz qux')],
|
errors: [new Error('baz qux')],
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
};
|
};
|
||||||
const promise = Promise.reject(result);
|
const promise = Promise.reject(result);
|
||||||
await message.send(promise);
|
await message.send(promise);
|
||||||
|
|
|
@ -655,7 +655,14 @@ export class ConversationController {
|
||||||
const groups = await getAllGroupsInvolvingId(conversationId, {
|
const groups = await getAllGroupsInvolvingId(conversationId, {
|
||||||
ConversationCollection: window.Whisper.ConversationCollection,
|
ConversationCollection: window.Whisper.ConversationCollection,
|
||||||
});
|
});
|
||||||
return groups.map(group => this._conversations.add(group));
|
return groups.map(group => {
|
||||||
|
const existing = this.get(group.id);
|
||||||
|
if (existing) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._conversations.add(group);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadPromise(): Promise<void> {
|
async loadPromise(): Promise<void> {
|
||||||
|
|
|
@ -24,9 +24,11 @@ story.addDecorator(storyFn => (
|
||||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
...overrideProps,
|
...overrideProps,
|
||||||
i18n,
|
i18n,
|
||||||
isAccepted: boolean(
|
acceptedMessageRequest: boolean(
|
||||||
'isAccepted',
|
'acceptedMessageRequest',
|
||||||
overrideProps.isAccepted !== undefined ? overrideProps.isAccepted : true
|
overrideProps.acceptedMessageRequest !== undefined
|
||||||
|
? overrideProps.acceptedMessageRequest
|
||||||
|
: true
|
||||||
),
|
),
|
||||||
isMe: boolean('isMe', overrideProps.isMe || false),
|
isMe: boolean('isMe', overrideProps.isMe || false),
|
||||||
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
|
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
|
||||||
|
@ -103,7 +105,7 @@ story.add('Typing Status', () => {
|
||||||
|
|
||||||
story.add('Message Request', () => {
|
story.add('Message Request', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
isAccepted: false,
|
acceptedMessageRequest: false,
|
||||||
lastMessage: {
|
lastMessage: {
|
||||||
text: 'A Message',
|
text: 'A Message',
|
||||||
status: 'delivered',
|
status: 'delivered',
|
||||||
|
|
|
@ -39,7 +39,7 @@ export type PropsData = {
|
||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
|
|
||||||
isAccepted?: boolean;
|
acceptedMessageRequest?: boolean;
|
||||||
draftPreview?: string;
|
draftPreview?: string;
|
||||||
shouldShowDraft?: boolean;
|
shouldShowDraft?: boolean;
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
||||||
const {
|
const {
|
||||||
draftPreview,
|
draftPreview,
|
||||||
i18n,
|
i18n,
|
||||||
isAccepted,
|
acceptedMessageRequest,
|
||||||
lastMessage,
|
lastMessage,
|
||||||
muteExpiresAt,
|
muteExpiresAt,
|
||||||
shouldShowDraft,
|
shouldShowDraft,
|
||||||
|
@ -209,7 +209,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
||||||
{muteExpiresAt && Date.now() < muteExpiresAt && (
|
{muteExpiresAt && Date.now() < muteExpiresAt && (
|
||||||
<span className="module-conversation-list-item__muted" />
|
<span className="module-conversation-list-item__muted" />
|
||||||
)}
|
)}
|
||||||
{!isAccepted ? (
|
{!acceptedMessageRequest ? (
|
||||||
<span className="module-conversation-list-item__message-request">
|
<span className="module-conversation-list-item__message-request">
|
||||||
{i18n('ConversationListItem--message-request')}
|
{i18n('ConversationListItem--message-request')}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -70,7 +70,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '1',
|
id: '1',
|
||||||
profileName: '🔥Flames🔥',
|
profileName: '🔥Flames🔥',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -85,7 +85,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
phoneNumber: '(202) 555-0002',
|
phoneNumber: '(202) 555-0002',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '2',
|
id: '2',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -100,7 +100,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
phoneNumber: '(202) 555-0002',
|
phoneNumber: '(202) 555-0002',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '2',
|
id: '2',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -115,7 +115,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
id: '3',
|
id: '3',
|
||||||
title: '🔥Flames🔥',
|
title: '🔥Flames🔥',
|
||||||
profileName: '🔥Flames🔥',
|
profileName: '🔥Flames🔥',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -127,7 +127,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
phoneNumber: '(202) 555-0011',
|
phoneNumber: '(202) 555-0011',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '11',
|
id: '11',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -141,7 +141,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
title: '(202) 555-0004',
|
title: '(202) 555-0004',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '4',
|
id: '4',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -165,7 +165,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
value: 10,
|
value: 10,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -179,7 +179,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
id: '6',
|
id: '6',
|
||||||
muteExpirationLabel: '10/18/3000, 11:11 AM',
|
muteExpirationLabel: '10/18/3000, 11:11 AM',
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -211,7 +211,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
value: 10,
|
value: 10,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -237,7 +237,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
value: 10,
|
value: 10,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -257,7 +257,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
id: '7',
|
id: '7',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
isMe: true,
|
isMe: true,
|
||||||
isAccepted: true,
|
acceptedMessageRequest: true,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
@ -277,7 +277,7 @@ const stories: Array<ConversationHeaderStory> = [
|
||||||
id: '7',
|
id: '7',
|
||||||
type: 'direct',
|
type: 'direct',
|
||||||
isMe: false,
|
isMe: false,
|
||||||
isAccepted: false,
|
acceptedMessageRequest: false,
|
||||||
...actionProps,
|
...actionProps,
|
||||||
...housekeepingProps,
|
...housekeepingProps,
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,7 +31,7 @@ export interface PropsDataType {
|
||||||
type: 'direct' | 'group';
|
type: 'direct' | 'group';
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
isAccepted?: boolean;
|
acceptedMessageRequest?: boolean;
|
||||||
isVerified?: boolean;
|
isVerified?: boolean;
|
||||||
isMe?: boolean;
|
isMe?: boolean;
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
|
@ -305,7 +305,7 @@ export class ConversationHeader extends React.Component<PropsType> {
|
||||||
const {
|
const {
|
||||||
disableTimerChanges,
|
disableTimerChanges,
|
||||||
i18n,
|
i18n,
|
||||||
isAccepted,
|
acceptedMessageRequest,
|
||||||
isMe,
|
isMe,
|
||||||
isPinned,
|
isPinned,
|
||||||
type,
|
type,
|
||||||
|
@ -388,7 +388,7 @@ export class ConversationHeader extends React.Component<PropsType> {
|
||||||
{i18n('showSafetyNumber')}
|
{i18n('showSafetyNumber')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : null}
|
) : null}
|
||||||
{!isGroup && isAccepted ? (
|
{!isGroup && acceptedMessageRequest ? (
|
||||||
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
|
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
|
||||||
) : null}
|
) : null}
|
||||||
{isArchived ? (
|
{isArchived ? (
|
||||||
|
|
2
ts/model-types.d.ts
vendored
2
ts/model-types.d.ts
vendored
|
@ -147,7 +147,7 @@ export type ConversationAttributesType = {
|
||||||
draftTimestamp: number | null;
|
draftTimestamp: number | null;
|
||||||
inbox_position: number;
|
inbox_position: number;
|
||||||
isPinned: boolean;
|
isPinned: boolean;
|
||||||
lastMessageDeletedForEveryone: unknown;
|
lastMessageDeletedForEveryone: boolean;
|
||||||
lastMessageStatus: LastMessageStatus | null;
|
lastMessageStatus: LastMessageStatus | null;
|
||||||
messageCount: number;
|
messageCount: number;
|
||||||
messageCountBeforeMessageRequests: number;
|
messageCountBeforeMessageRequests: number;
|
||||||
|
|
|
@ -80,6 +80,8 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
|
|
||||||
cachedProps?: ConversationType | null;
|
cachedProps?: ConversationType | null;
|
||||||
|
|
||||||
|
oldCachedProps?: ConversationType | null;
|
||||||
|
|
||||||
contactTypingTimers?: Record<
|
contactTypingTimers?: Record<
|
||||||
string,
|
string,
|
||||||
{ senderId: string; timer: NodeJS.Timer }
|
{ senderId: string; timer: NodeJS.Timer }
|
||||||
|
@ -235,6 +237,9 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
// We clear our cached props whenever we change so that the next call to format() will
|
// We clear our cached props whenever we change so that the next call to format() will
|
||||||
// result in refresh via a getProps() call. See format() below.
|
// result in refresh via a getProps() call. See format() below.
|
||||||
this.on('change', () => {
|
this.on('change', () => {
|
||||||
|
if (this.cachedProps) {
|
||||||
|
this.oldCachedProps = this.cachedProps;
|
||||||
|
}
|
||||||
this.cachedProps = null;
|
this.cachedProps = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1020,12 +1025,38 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
}
|
}
|
||||||
|
|
||||||
format(): ConversationType {
|
format(): ConversationType {
|
||||||
this.cachedProps = this.cachedProps || this.getProps();
|
if (this.cachedProps) {
|
||||||
|
return this.cachedProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldFormat = this.format;
|
||||||
|
// We don't want to crash or have an infinite loop if we loop back into this function
|
||||||
|
// again. We'll log a warning and returned old cached props or throw an error.
|
||||||
|
this.format = () => {
|
||||||
|
const { stack } = new Error('for stack');
|
||||||
|
window.log.warn(
|
||||||
|
`Conversation.format()/${this.idForLogging()} reentrant call! ${stack}`
|
||||||
|
);
|
||||||
|
if (!this.oldCachedProps) {
|
||||||
|
throw new Error(
|
||||||
|
`Conversation.format()/${this.idForLogging()} reentrant call, no old cached props!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.oldCachedProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.cachedProps = this.getProps();
|
||||||
|
|
||||||
|
this.format = oldFormat;
|
||||||
|
|
||||||
return this.cachedProps;
|
return this.cachedProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
getProps(): ConversationType {
|
// Note: this should never be called directly. Use conversation.format() instead, which
|
||||||
|
// maintains a cache, and protects against reentrant calls.
|
||||||
|
// Note: When writing code inside this function, do not call .format() on a conversation
|
||||||
|
// unless you are sure that it's not this very same conversation.
|
||||||
|
private getProps(): ConversationType {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const color = this.getColor()!;
|
const color = this.getColor()!;
|
||||||
|
|
||||||
|
@ -1060,7 +1091,7 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
|
|
||||||
// TODO: DESKTOP-720
|
// TODO: DESKTOP-720
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
const result = {
|
const result: ConversationType = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
uuid: this.get('uuid'),
|
uuid: this.get('uuid'),
|
||||||
e164: this.get('e164'),
|
e164: this.get('e164'),
|
||||||
|
@ -1077,7 +1108,6 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
firstName: this.get('profileName')!,
|
firstName: this.get('profileName')!,
|
||||||
groupVersion,
|
groupVersion,
|
||||||
inboxPosition,
|
inboxPosition,
|
||||||
isAccepted: this.getAccepted(),
|
|
||||||
isArchived: this.get('isArchived')!,
|
isArchived: this.get('isArchived')!,
|
||||||
isBlocked: this.isBlocked(),
|
isBlocked: this.isBlocked(),
|
||||||
isMe: this.isMe(),
|
isMe: this.isMe(),
|
||||||
|
@ -1104,9 +1134,17 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
timestamp,
|
timestamp,
|
||||||
title: this.getTitle()!,
|
title: this.getTitle()!,
|
||||||
type: (this.isPrivate() ? 'direct' : 'group') as ConversationTypeType,
|
type: (this.isPrivate() ? 'direct' : 'group') as ConversationTypeType,
|
||||||
typingContact: typingContact ? typingContact.format() : null,
|
|
||||||
unreadCount: this.get('unreadCount')! || 0,
|
unreadCount: this.get('unreadCount')! || 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (typingContact) {
|
||||||
|
// We don't want to call .format() on our own conversation
|
||||||
|
if (typingContact.id === this.id) {
|
||||||
|
result.typingContact = result;
|
||||||
|
} else {
|
||||||
|
result.typingContact = typingContact.format();
|
||||||
|
}
|
||||||
|
}
|
||||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -2710,8 +2748,7 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
if (result) {
|
if (result) {
|
||||||
await this.handleMessageSendResult(
|
await this.handleMessageSendResult(
|
||||||
result.failoverIdentifiers,
|
result.failoverIdentifiers,
|
||||||
result.unidentifiedDeliveries,
|
result.unidentifiedDeliveries
|
||||||
result.discoveredIdentifierPairs
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -2721,8 +2758,7 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
if (result) {
|
if (result) {
|
||||||
await this.handleMessageSendResult(
|
await this.handleMessageSendResult(
|
||||||
result.failoverIdentifiers,
|
result.failoverIdentifiers,
|
||||||
result.unidentifiedDeliveries,
|
result.unidentifiedDeliveries
|
||||||
result.discoveredIdentifierPairs
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw result;
|
throw result;
|
||||||
|
@ -2732,23 +2768,8 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
|
|
||||||
async handleMessageSendResult(
|
async handleMessageSendResult(
|
||||||
failoverIdentifiers: Array<string> | undefined,
|
failoverIdentifiers: Array<string> | undefined,
|
||||||
unidentifiedDeliveries: Array<string> | undefined,
|
unidentifiedDeliveries: Array<string> | undefined
|
||||||
discoveredIdentifierPairs:
|
|
||||||
| Array<{
|
|
||||||
uuid: string | null;
|
|
||||||
e164: string | null;
|
|
||||||
}>
|
|
||||||
| undefined
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
(discoveredIdentifierPairs || []).forEach(item => {
|
|
||||||
const { uuid, e164 } = item;
|
|
||||||
window.ConversationController.ensureContactIds({
|
|
||||||
uuid,
|
|
||||||
e164,
|
|
||||||
highTrust: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
(failoverIdentifiers || []).map(async identifier => {
|
(failoverIdentifiers || []).map(async identifier => {
|
||||||
const conversation = window.ConversationController.get(identifier);
|
const conversation = window.ConversationController.get(identifier);
|
||||||
|
@ -4246,15 +4267,37 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
||||||
this._byGroupId = Object.create(null);
|
this._byGroupId = Object.create(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
add(...models: Array<WhatIsThis>) {
|
add(data: WhatIsThis | Array<WhatIsThis>) {
|
||||||
const result = window.Backbone.Collection.prototype.add.apply(
|
let hydratedData;
|
||||||
this,
|
|
||||||
models as WhatIsThis
|
// First, we need to ensure that the data we're working with is Conversation models
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
hydratedData = [];
|
||||||
|
for (let i = 0, max = data.length; i < max; i += 1) {
|
||||||
|
const item = data[i];
|
||||||
|
|
||||||
|
// We create a new model if it's not already a model
|
||||||
|
if (!item.get) {
|
||||||
|
hydratedData.push(new Whisper.Conversation(item));
|
||||||
|
} else {
|
||||||
|
hydratedData.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!data.get) {
|
||||||
|
hydratedData = new Whisper.Conversation(data);
|
||||||
|
} else {
|
||||||
|
hydratedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we update our lookups first to prevent infinite loops on the 'add' event
|
||||||
|
this.generateLookups(
|
||||||
|
Array.isArray(hydratedData) ? hydratedData : [hydratedData]
|
||||||
);
|
);
|
||||||
|
|
||||||
this.generateLookups(Array.isArray(result) ? result.slice(0) : [result]);
|
// Lastly, we fire off the add events related to this change
|
||||||
|
window.Backbone.Collection.prototype.add.call(this, hydratedData);
|
||||||
|
|
||||||
return result;
|
return hydratedData;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -54,6 +54,7 @@ export type ConversationType = {
|
||||||
lastMessage?: {
|
lastMessage?: {
|
||||||
status: LastMessageStatus;
|
status: LastMessageStatus;
|
||||||
text: string;
|
text: string;
|
||||||
|
deletedForEveryone?: boolean;
|
||||||
};
|
};
|
||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
membersCount?: number;
|
membersCount?: number;
|
||||||
|
@ -76,6 +77,7 @@ export type ConversationType = {
|
||||||
draftText?: string | null;
|
draftText?: string | null;
|
||||||
draftPreview?: string;
|
draftPreview?: string;
|
||||||
|
|
||||||
|
sharedGroupNames?: Array<string>;
|
||||||
groupVersion?: 1 | 2;
|
groupVersion?: 1 | 2;
|
||||||
isMissingMandatoryProfileSharing?: boolean;
|
isMissingMandatoryProfileSharing?: boolean;
|
||||||
messageRequestsEnabled?: boolean;
|
messageRequestsEnabled?: boolean;
|
||||||
|
|
|
@ -53,11 +53,6 @@ export default class OutgoingMessage {
|
||||||
|
|
||||||
unidentifiedDeliveries: Array<unknown>;
|
unidentifiedDeliveries: Array<unknown>;
|
||||||
|
|
||||||
discoveredIdentifierPairs: Array<{
|
|
||||||
e164: string;
|
|
||||||
uuid: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
sendMetadata?: SendMetadataType;
|
sendMetadata?: SendMetadataType;
|
||||||
|
|
||||||
senderCertificate?: ArrayBuffer;
|
senderCertificate?: ArrayBuffer;
|
||||||
|
@ -93,7 +88,6 @@ export default class OutgoingMessage {
|
||||||
this.successfulIdentifiers = [];
|
this.successfulIdentifiers = [];
|
||||||
this.failoverIdentifiers = [];
|
this.failoverIdentifiers = [];
|
||||||
this.unidentifiedDeliveries = [];
|
this.unidentifiedDeliveries = [];
|
||||||
this.discoveredIdentifierPairs = [];
|
|
||||||
|
|
||||||
const { sendMetadata, senderCertificate, online } = options;
|
const { sendMetadata, senderCertificate, online } = options;
|
||||||
this.sendMetadata = sendMetadata;
|
this.sendMetadata = sendMetadata;
|
||||||
|
@ -109,7 +103,6 @@ export default class OutgoingMessage {
|
||||||
failoverIdentifiers: this.failoverIdentifiers,
|
failoverIdentifiers: this.failoverIdentifiers,
|
||||||
errors: this.errors,
|
errors: this.errors,
|
||||||
unidentifiedDeliveries: this.unidentifiedDeliveries,
|
unidentifiedDeliveries: this.unidentifiedDeliveries,
|
||||||
discoveredIdentifierPairs: this.discoveredIdentifierPairs,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -621,9 +614,10 @@ export default class OutgoingMessage {
|
||||||
]);
|
]);
|
||||||
const uuid = lookup[identifier];
|
const uuid = lookup[identifier];
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
this.discoveredIdentifierPairs.push({
|
window.ConversationController.ensureContactIds({
|
||||||
uuid,
|
uuid,
|
||||||
e164: identifier,
|
e164: identifier,
|
||||||
|
highTrust: true,
|
||||||
});
|
});
|
||||||
identifier = uuid;
|
identifier = uuid;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -78,10 +78,6 @@ export type CallbackResultType = {
|
||||||
errors?: Array<CustomError>;
|
errors?: Array<CustomError>;
|
||||||
unidentifiedDeliveries?: Array<any>;
|
unidentifiedDeliveries?: Array<any>;
|
||||||
dataMessage?: ArrayBuffer;
|
dataMessage?: ArrayBuffer;
|
||||||
discoveredIdentifierPairs: Array<{
|
|
||||||
e164: string;
|
|
||||||
uuid: string | null;
|
|
||||||
}>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type PreviewType = {
|
type PreviewType = {
|
||||||
|
@ -1383,7 +1379,6 @@ export default class MessageSender {
|
||||||
if (identifiers.length === 0) {
|
if (identifiers.length === 0) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
dataMessage: proto.toArrayBuffer(),
|
dataMessage: proto.toArrayBuffer(),
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
errors: [],
|
errors: [],
|
||||||
failoverIdentifiers: [],
|
failoverIdentifiers: [],
|
||||||
successfulIdentifiers: [],
|
successfulIdentifiers: [],
|
||||||
|
@ -1657,7 +1652,6 @@ export default class MessageSender {
|
||||||
errors: [],
|
errors: [],
|
||||||
unidentifiedDeliveries: [],
|
unidentifiedDeliveries: [],
|
||||||
dataMessage: await this.getMessageProtoObj(attrs),
|
dataMessage: await this.getMessageProtoObj(attrs),
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1731,7 +1725,6 @@ export default class MessageSender {
|
||||||
errors: [],
|
errors: [],
|
||||||
unidentifiedDeliveries: [],
|
unidentifiedDeliveries: [],
|
||||||
dataMessage: await this.getMessageProtoObj(attrs),
|
dataMessage: await this.getMessageProtoObj(attrs),
|
||||||
discoveredIdentifierPairs: [],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue