cleanup models with unused events
also, sort message from DB and on redux by sent_at or received_at when not a public group
This commit is contained in:
parent
6edcb88788
commit
ea2c4437a3
|
@ -2472,6 +2472,8 @@ async function getUnreadCountByConversation(conversationId) {
|
|||
}
|
||||
|
||||
// Note: Sorting here is necessary for getting the last message (with limit 1)
|
||||
// be sure to update the sorting order to sort messages on reduxz too (sortMessages
|
||||
|
||||
async function getMessagesByConversation(
|
||||
conversationId,
|
||||
{ limit = 100, receivedAt = Number.MAX_VALUE, type = '%' } = {}
|
||||
|
@ -2482,7 +2484,7 @@ async function getMessagesByConversation(
|
|||
conversationId = $conversationId AND
|
||||
received_at < $received_at AND
|
||||
type LIKE $type
|
||||
ORDER BY serverTimestamp DESC, serverId DESC, sent_at DESC
|
||||
ORDER BY serverTimestamp DESC, serverId DESC, sent_at DESC, received_at DESC
|
||||
LIMIT $limit;
|
||||
`,
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface LokiAppDotNetServerInterface {
|
|||
}
|
||||
|
||||
export interface LokiPublicChannelAPI {
|
||||
banUser(source: string): Promise<boolean>;
|
||||
getModerators: () => Promise<Array<string>>;
|
||||
serverAPI: any;
|
||||
deleteMessages(arg0: any[]);
|
||||
|
|
|
@ -88,11 +88,6 @@
|
|||
|
||||
const force = true;
|
||||
await message.setToExpire(force);
|
||||
|
||||
const conversation = message.getConversation();
|
||||
if (conversation) {
|
||||
conversation.trigger('expiration-change', message);
|
||||
}
|
||||
}
|
||||
|
||||
this.remove(receipt);
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
ReadReceiptMessage,
|
||||
TypingMessage,
|
||||
} from '../session/messages/outgoing';
|
||||
import { ClosedGroupChatMessage } from '../session/messages/outgoing/content/data/group';
|
||||
import { ClosedGroupChatMessage } from '../session/messages/outgoing/content/data/group/ClosedGroupChatMessage';
|
||||
import { OpenGroup, PubKey } from '../session/types';
|
||||
import { ToastUtils, UserUtils } from '../session/utils';
|
||||
import { BlockedNumberController } from '../util';
|
||||
|
@ -20,7 +20,7 @@ import { leaveClosedGroup } from '../session/group';
|
|||
import { SignalService } from '../protobuf';
|
||||
import { MessageCollection, MessageModel } from './message';
|
||||
import * as Data from '../../js/modules/data';
|
||||
import { MessageAttributesOptionals } from './messageType';
|
||||
import { MessageAttributesOptionals, MessageModelType } from './messageType';
|
||||
import autoBind from 'auto-bind';
|
||||
|
||||
export interface OurLokiProfile {
|
||||
|
@ -160,15 +160,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
this.bouncyUpdateLastMessage.bind(this),
|
||||
1000
|
||||
);
|
||||
// this.listenTo(
|
||||
// this.messageCollection,
|
||||
// 'add remove destroy',
|
||||
// debouncedUpdateLastMessage
|
||||
// );
|
||||
// Listening for out-of-band data updates
|
||||
this.on('delivered', this.updateAndMerge);
|
||||
this.on('read', this.updateAndMerge);
|
||||
this.on('expiration-change', this.updateAndMerge);
|
||||
this.on('expired', this.onExpired);
|
||||
|
||||
this.on('ourAvatarChanged', avatar =>
|
||||
|
@ -370,21 +362,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
}
|
||||
}
|
||||
|
||||
public async updateAndMerge(message: any) {
|
||||
await this.updateLastMessage();
|
||||
|
||||
const mergeMessage = () => {
|
||||
const existing = this.messageCollection.get(message.id);
|
||||
if (!existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
existing.merge(message.attributes);
|
||||
};
|
||||
|
||||
mergeMessage();
|
||||
}
|
||||
|
||||
public async onExpired(message: any) {
|
||||
await this.updateLastMessage();
|
||||
|
||||
|
@ -439,16 +416,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
await model.setServerTimestamp(serverTimestamp);
|
||||
return undefined;
|
||||
}
|
||||
public addSingleMessage(
|
||||
message: MessageAttributesOptionals,
|
||||
setToExpire = true
|
||||
) {
|
||||
const model = this.messageCollection.add(message, { merge: true });
|
||||
if (setToExpire) {
|
||||
void model.setToExpire();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
public format() {
|
||||
return this.cachedProps;
|
||||
}
|
||||
|
@ -673,17 +641,23 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
conversationId: this.id,
|
||||
});
|
||||
}
|
||||
public async sendMessageJob(message: any) {
|
||||
public async sendMessageJob(message: MessageModel) {
|
||||
try {
|
||||
const uploads = await message.uploadData();
|
||||
const { id } = message;
|
||||
const expireTimer = this.get('expireTimer');
|
||||
const destination = this.id;
|
||||
|
||||
const sentAt = message.get('sent_at');
|
||||
|
||||
if (!sentAt) {
|
||||
throw new Error('sendMessageJob() sent_at must be set.');
|
||||
}
|
||||
|
||||
const chatMessage = new ChatMessage({
|
||||
body: uploads.body,
|
||||
identifier: id,
|
||||
timestamp: message.get('sent_at'),
|
||||
timestamp: sentAt,
|
||||
attachments: uploads.attachments,
|
||||
expireTimer,
|
||||
preview: uploads.preview,
|
||||
|
@ -696,7 +670,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
|
||||
const openGroupParams = {
|
||||
body: uploads.body,
|
||||
timestamp: message.get('sent_at'),
|
||||
timestamp: sentAt,
|
||||
group: openGroup,
|
||||
attachments: uploads.attachments,
|
||||
preview: uploads.preview,
|
||||
|
@ -716,7 +690,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const groupInvitation = message.get('groupInvitation');
|
||||
const groupInvitMessage = new GroupInvitationMessage({
|
||||
identifier: id,
|
||||
timestamp: message.get('sent_at'),
|
||||
timestamp: sentAt,
|
||||
serverName: groupInvitation.name,
|
||||
channelId: groupInvitation.channelId,
|
||||
serverAddress: groupInvitation.address,
|
||||
|
@ -767,6 +741,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
this.clearTypingTimers();
|
||||
|
||||
const destination = this.id;
|
||||
const isPrivate = this.isPrivate();
|
||||
const expireTimer = this.get('expireTimer');
|
||||
const recipients = this.getRecipients();
|
||||
|
||||
|
@ -808,28 +783,16 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const attributes: MessageAttributesOptionals = {
|
||||
...messageWithSchema,
|
||||
groupInvitation,
|
||||
id: window.getGuid(),
|
||||
conversationId: this.id,
|
||||
destination: isPrivate ? destination : undefined,
|
||||
};
|
||||
|
||||
const model = this.addSingleMessage(attributes);
|
||||
MessageController.getInstance().register(model.id, model);
|
||||
const model = await this.addSingleMessage(attributes);
|
||||
|
||||
const id = await model.commit();
|
||||
model.set({ id });
|
||||
|
||||
if (this.isPrivate()) {
|
||||
model.set({ destination });
|
||||
}
|
||||
if (this.isPublic()) {
|
||||
await model.setServerTimestamp(new Date().getTime());
|
||||
}
|
||||
|
||||
window.Whisper.events.trigger('messageAdded', {
|
||||
conversationKey: this.id,
|
||||
messageModel: model,
|
||||
});
|
||||
|
||||
this.set({
|
||||
lastMessage: model.getNotificationText(),
|
||||
lastMessageStatus: 'sending',
|
||||
|
@ -912,7 +875,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
public async updateExpirationTimer(
|
||||
providedExpireTimer: any,
|
||||
providedSource?: string,
|
||||
receivedAt?: number,
|
||||
receivedAt?: number, // is set if it comes from outside
|
||||
options: any = {}
|
||||
) {
|
||||
let expireTimer = providedExpireTimer;
|
||||
|
@ -936,6 +899,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
source,
|
||||
});
|
||||
|
||||
const isOutgoing = Boolean(receivedAt);
|
||||
|
||||
source = source || UserUtils.getOurPubKeyStrFromCache();
|
||||
|
||||
// When we add a disappearing messages notification to the conversation, we want it
|
||||
|
@ -943,9 +908,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const timestamp = (receivedAt || Date.now()) - 1;
|
||||
|
||||
this.set({ expireTimer });
|
||||
await this.commit();
|
||||
|
||||
const message = new MessageModel({
|
||||
const messageAttributes = {
|
||||
// Even though this isn't reflected to the user, we want to place the last seen
|
||||
// indicator above it. We set it to 'unread' to trigger that placement.
|
||||
unread: true,
|
||||
|
@ -961,23 +925,14 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
fromGroupUpdate: options.fromGroupUpdate,
|
||||
},
|
||||
expireTimer: 0,
|
||||
type: 'incoming',
|
||||
});
|
||||
type: isOutgoing ? 'outgoing' : ('incoming' as MessageModelType),
|
||||
destination: this.id,
|
||||
recipients: isOutgoing ? this.getRecipients() : undefined,
|
||||
};
|
||||
|
||||
message.set({ destination: this.id });
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
message.set({ recipients: this.getRecipients() });
|
||||
}
|
||||
|
||||
const id = await message.commit();
|
||||
|
||||
message.set({ id });
|
||||
window.Whisper.events.trigger('messageAdded', {
|
||||
conversationKey: this.id,
|
||||
messageModel: message,
|
||||
});
|
||||
const message = await this.addSingleMessage(messageAttributes);
|
||||
|
||||
// tell the UI this conversation was updated
|
||||
await this.commit();
|
||||
|
||||
// if change was made remotely, don't send it to the number/group
|
||||
|
@ -991,7 +946,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
}
|
||||
|
||||
const expireUpdate = {
|
||||
identifier: id,
|
||||
identifier: message.id,
|
||||
timestamp,
|
||||
expireTimer,
|
||||
profileKey,
|
||||
|
@ -1008,13 +963,16 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
return message.sendSyncMessageOnly(expirationTimerMessage);
|
||||
}
|
||||
|
||||
if (this.get('type') === 'private') {
|
||||
if (this.isPrivate()) {
|
||||
const expirationTimerMessage = new ExpirationTimerUpdateMessage(
|
||||
expireUpdate
|
||||
);
|
||||
const pubkey = new PubKey(this.get('id'));
|
||||
await getMessageQueue().sendToPubKey(pubkey, expirationTimerMessage);
|
||||
} else {
|
||||
window.log.warn(
|
||||
'TODO: Expiration update for closed groups are to be updated'
|
||||
);
|
||||
const expireUpdateForGroup = {
|
||||
...expireUpdate,
|
||||
groupId: this.get('id'),
|
||||
|
@ -1023,24 +981,12 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const expirationTimerMessage = new ExpirationTimerUpdateMessage(
|
||||
expireUpdateForGroup
|
||||
);
|
||||
// special case when we are the only member of a closed group
|
||||
const ourNumber = UserUtils.getOurPubKeyStrFromCache();
|
||||
|
||||
if (
|
||||
this.get('members').length === 1 &&
|
||||
this.get('members')[0] === ourNumber
|
||||
) {
|
||||
return message.sendSyncMessageOnly(expirationTimerMessage);
|
||||
}
|
||||
await getMessageQueue().sendToGroup(expirationTimerMessage);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public isSearchable() {
|
||||
return !this.get('left');
|
||||
}
|
||||
|
||||
public async commit() {
|
||||
await window.Signal.Data.updateConversation(this.id, this.attributes, {
|
||||
Conversation: ConversationModel,
|
||||
|
@ -1048,27 +994,33 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
this.trigger('change', this);
|
||||
}
|
||||
|
||||
public async addMessage(messageAttributes: MessageAttributesOptionals) {
|
||||
public async addSingleMessage(
|
||||
messageAttributes: MessageAttributesOptionals,
|
||||
setToExpire = true
|
||||
) {
|
||||
const model = new MessageModel(messageAttributes);
|
||||
|
||||
const messageId = await model.commit();
|
||||
model.set({ id: messageId });
|
||||
|
||||
if (setToExpire) {
|
||||
await model.setToExpire();
|
||||
}
|
||||
MessageController.getInstance().register(messageId, model);
|
||||
|
||||
window.Whisper.events.trigger('messageAdded', {
|
||||
conversationKey: this.id,
|
||||
messageModel: model,
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public async leaveGroup() {
|
||||
if (this.get('type') !== ConversationType.GROUP) {
|
||||
window.log.error('Cannot leave a non-group conversation');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isMediumGroup()) {
|
||||
await leaveClosedGroup(this.id);
|
||||
} else {
|
||||
window.log.error('Cannot leave a non-medium group conversation');
|
||||
throw new Error(
|
||||
'Legacy group are not supported anymore. You need to create this group again.'
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
DataMessage,
|
||||
OpenGroupMessage,
|
||||
} from '../../ts/session/messages/outgoing';
|
||||
import { ClosedGroupChatMessage } from '../../ts/session/messages/outgoing/content/data/group';
|
||||
import { ClosedGroupChatMessage } from '../../ts/session/messages/outgoing/content/data/group/ClosedGroupChatMessage';
|
||||
import { EncryptionType, PubKey } from '../../ts/session/types';
|
||||
import { ToastUtils, UserUtils } from '../../ts/session/utils';
|
||||
import {
|
||||
|
@ -41,9 +41,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
);
|
||||
}
|
||||
|
||||
this.on('destroy', this.onDestroy);
|
||||
this.on('change:expirationStartTimestamp', this.setToExpire);
|
||||
this.on('change:expireTimer', this.setToExpire);
|
||||
// this.on('expired', this.onExpired);
|
||||
void this.setToExpire();
|
||||
autoBind(this);
|
||||
|
@ -674,7 +671,9 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
? [this.get('source')]
|
||||
: _.union(
|
||||
this.get('sent_to') || [],
|
||||
this.get('recipients') || this.getConversation().getRecipients()
|
||||
this.get('recipients') ||
|
||||
this.getConversation()?.getRecipients() ||
|
||||
[]
|
||||
);
|
||||
|
||||
// This will make the error message for outgoing key errors a bit nicer
|
||||
|
@ -750,8 +749,20 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
resolve: async () => {
|
||||
const source = this.get('source');
|
||||
const conversation = this.getConversation();
|
||||
if (!conversation) {
|
||||
window.log.info(
|
||||
'cannot ban user, the corresponding conversation was not found.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const channelAPI = await conversation.getPublicSendData();
|
||||
if (!channelAPI) {
|
||||
window.log.info(
|
||||
'cannot ban user, the corresponding channelAPI was not found.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const success = await channelAPI.banUser(source);
|
||||
|
||||
if (success) {
|
||||
|
@ -805,7 +816,8 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
|
||||
const conversation = this.getConversation();
|
||||
const openGroup =
|
||||
conversation && conversation.isPublic() && conversation.toOpenGroup();
|
||||
(conversation && conversation.isPublic() && conversation.toOpenGroup()) ||
|
||||
undefined;
|
||||
|
||||
const { AttachmentUtils } = Utils;
|
||||
const [attachments, preview, quote] = await Promise.all([
|
||||
|
@ -836,9 +848,12 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
await this.commit();
|
||||
try {
|
||||
const conversation = this.getConversation();
|
||||
const intendedRecipients = this.get('recipients') || [];
|
||||
const successfulRecipients = this.get('sent_to') || [];
|
||||
const currentRecipients = conversation.getRecipients();
|
||||
if (!conversation) {
|
||||
window.log.info(
|
||||
'cannot retry send message, the corresponding conversation was not found.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (conversation.isPublic()) {
|
||||
const openGroup = {
|
||||
|
@ -858,19 +873,8 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
return getMessageQueue().sendToGroup(openGroupMessage);
|
||||
}
|
||||
|
||||
let recipients = _.intersection(intendedRecipients, currentRecipients);
|
||||
recipients = recipients.filter(
|
||||
key => !successfulRecipients.includes(key)
|
||||
);
|
||||
|
||||
if (!recipients.length) {
|
||||
window.log.warn('retrySend: Nobody to send to!');
|
||||
|
||||
return this.commit();
|
||||
}
|
||||
|
||||
const { body, attachments, preview, quote } = await this.uploadData();
|
||||
const ourNumber = window.storage.get('primaryDevicePubKey');
|
||||
const ourNumber = UserUtils.getOurPubKeyStrFromCache();
|
||||
const ourConversation = ConversationController.getInstance().get(
|
||||
ourNumber
|
||||
);
|
||||
|
@ -893,86 +897,33 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
const chatMessage = new ChatMessage(chatParams);
|
||||
|
||||
// Special-case the self-send case - we send only a sync message
|
||||
if (recipients.length === 1) {
|
||||
const isOurDevice = UserUtils.isUsFromCache(recipients[0]);
|
||||
if (isOurDevice) {
|
||||
return this.sendSyncMessageOnly(chatMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation.isPrivate()) {
|
||||
const [number] = recipients;
|
||||
const recipientPubKey = new PubKey(number);
|
||||
|
||||
return getMessageQueue().sendToPubKey(recipientPubKey, chatMessage);
|
||||
}
|
||||
|
||||
// TODO should we handle medium groups message here too?
|
||||
// Not sure there is the concept of retrySend for those
|
||||
const closedGroupChatMessage = new ClosedGroupChatMessage({
|
||||
identifier: this.id,
|
||||
chatMessage,
|
||||
groupId: this.get('conversationId'),
|
||||
});
|
||||
// Because this is a partial group send, we send the message with the groupId field set, but individually
|
||||
// to each recipient listed
|
||||
return Promise.all(
|
||||
recipients.map(async r => {
|
||||
const recipientPubKey = new PubKey(r);
|
||||
return getMessageQueue().sendToPubKey(
|
||||
recipientPubKey,
|
||||
closedGroupChatMessage
|
||||
);
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
await this.saveErrors(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the user ran into an error with a specific user, wants to send to them
|
||||
public async resend(number: string) {
|
||||
const error = this.removeOutgoingErrors(number);
|
||||
if (!error) {
|
||||
window.log.warn('resend: requested number was not present in errors');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const { body, attachments, preview, quote } = await this.uploadData();
|
||||
|
||||
const chatMessage = new ChatMessage({
|
||||
identifier: this.id,
|
||||
body,
|
||||
timestamp: this.get('sent_at') || Date.now(),
|
||||
expireTimer: this.get('expireTimer'),
|
||||
attachments,
|
||||
preview,
|
||||
quote,
|
||||
});
|
||||
|
||||
// Special-case the self-send case - we send only a sync message
|
||||
if (UserUtils.isUsFromCache(number)) {
|
||||
if (conversation.isMe()) {
|
||||
return this.sendSyncMessageOnly(chatMessage);
|
||||
}
|
||||
|
||||
const conversation = this.getConversation();
|
||||
const recipientPubKey = new PubKey(number);
|
||||
|
||||
if (conversation.isPrivate()) {
|
||||
return getMessageQueue().sendToPubKey(recipientPubKey, chatMessage);
|
||||
return getMessageQueue().sendToPubKey(
|
||||
PubKey.cast(conversation.id),
|
||||
chatMessage
|
||||
);
|
||||
}
|
||||
|
||||
// Here, the convo is neither an open group, a private convo or ourself. It can only be a medium group.
|
||||
// For a medium group, retry send only means trigger a send again to all recipients
|
||||
// as they are all polling from the same group swarm pubkey
|
||||
if (!conversation.isMediumGroup()) {
|
||||
throw new Error(
|
||||
'We should only end up with a medium group here. Anything else is an error'
|
||||
);
|
||||
}
|
||||
|
||||
const closedGroupChatMessage = new ClosedGroupChatMessage({
|
||||
identifier: this.id,
|
||||
chatMessage,
|
||||
groupId: this.get('conversationId'),
|
||||
});
|
||||
// resend tries to send the message to that specific user only in the context of a closed group
|
||||
return getMessageQueue().sendToPubKey(
|
||||
recipientPubKey,
|
||||
closedGroupChatMessage
|
||||
);
|
||||
|
||||
return getMessageQueue().sendToGroup(closedGroupChatMessage);
|
||||
} catch (e) {
|
||||
await this.saveErrors(e);
|
||||
return null;
|
||||
|
@ -1085,9 +1036,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
|
||||
await this.commit();
|
||||
|
||||
this.getConversation().updateLastMessage();
|
||||
|
||||
this.trigger('sent', this);
|
||||
this.getConversation()?.updateLastMessage();
|
||||
}
|
||||
|
||||
public async handleMessageSentFailure(sentMessage: any, error: any) {
|
||||
|
@ -1113,8 +1062,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
});
|
||||
await this.commit();
|
||||
|
||||
this.getConversation().updateLastMessage();
|
||||
this.trigger('done');
|
||||
this.getConversation()?.updateLastMessage();
|
||||
}
|
||||
|
||||
public getConversation() {
|
||||
|
|
|
@ -13,6 +13,8 @@ export type MessageDeliveryStatus =
|
|||
| 'error';
|
||||
|
||||
export interface MessageAttributes {
|
||||
// the id of the message
|
||||
// this can have several uses:
|
||||
id: string;
|
||||
source: string;
|
||||
quote?: any;
|
||||
|
|
|
@ -51,7 +51,7 @@ export class ConversationController {
|
|||
);
|
||||
}
|
||||
// Needed for some model setup which happens during the initial fetch() call below
|
||||
public getUnsafe(id: string) {
|
||||
public getUnsafe(id: string): ConversationModel | undefined {
|
||||
return this.conversations.get(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ export async function addUpdateMessage(
|
|||
|
||||
const unread = type === 'incoming';
|
||||
|
||||
const message = await convo.addMessage({
|
||||
const message = await convo.addSingleMessage({
|
||||
conversationId: convo.get('id'),
|
||||
type,
|
||||
sent_at: now,
|
||||
|
@ -340,7 +340,7 @@ export async function leaveClosedGroup(groupId: string) {
|
|||
convo.set({ groupAdmins: admins });
|
||||
await convo.commit();
|
||||
|
||||
const dbMessage = await convo.addMessage({
|
||||
const dbMessage = await convo.addSingleMessage({
|
||||
group_update: { left: 'You' },
|
||||
conversationId: groupId,
|
||||
type: 'outgoing',
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './ClosedGroupChatMessage';
|
||||
export * from './ClosedGroupEncryptionPairMessage';
|
||||
export * from './ClosedGroupNewMessage';
|
||||
export * from './ClosedGroupAddedMembersMessage';
|
||||
|
|
|
@ -56,7 +56,6 @@ export interface ConversationType {
|
|||
index?: number;
|
||||
|
||||
activeAt?: number;
|
||||
timestamp: number;
|
||||
lastMessage?: {
|
||||
status: 'error' | 'sending' | 'sent' | 'delivered' | 'read';
|
||||
text: string;
|
||||
|
@ -443,15 +442,26 @@ function sortMessages(
|
|||
isPublic: boolean
|
||||
): Array<MessageTypeInConvo> {
|
||||
// we order by serverTimestamp for public convos
|
||||
// be sure to update the sorting order to fetch messages from the DB too at getMessagesByConversation
|
||||
if (isPublic) {
|
||||
return messages.sort(
|
||||
(a: any, b: any) =>
|
||||
b.attributes.serverTimestamp - a.attributes.serverTimestamp
|
||||
);
|
||||
}
|
||||
return messages.sort(
|
||||
(a: any, b: any) => b.attributes.timestamp - a.attributes.timestamp
|
||||
if (messages.some(n => !n.attributes.sent_at && !n.attributes.received_at)) {
|
||||
throw new Error('Found some messages without any timestamp set');
|
||||
}
|
||||
|
||||
// for non public convos, we order by sent_at or received_at timestamp.
|
||||
// we assume that a message has either a sent_at or a received_at field set.
|
||||
const messagesSorted = messages.sort(
|
||||
(a: any, b: any) =>
|
||||
(b.attributes.sent_at || b.attributes.received_at) -
|
||||
(a.attributes.sent_at || a.attributes.received_at)
|
||||
);
|
||||
|
||||
return messagesSorted;
|
||||
}
|
||||
|
||||
function handleMessageAdded(
|
||||
|
@ -488,12 +498,13 @@ function handleMessageChanged(
|
|||
state: ConversationsStateType,
|
||||
action: MessageChangedActionType
|
||||
) {
|
||||
const { payload } = action;
|
||||
const messageInStoreIndex = state?.messages?.findIndex(
|
||||
m => m.id === action.payload.id
|
||||
m => m.id === payload.id
|
||||
);
|
||||
if (messageInStoreIndex >= 0) {
|
||||
const changedMessage = _.pick(
|
||||
action.payload as any,
|
||||
payload as any,
|
||||
toPickFromMessageModel
|
||||
) as MessageTypeInConvo;
|
||||
// we cannot edit the array directly, so slice the first part, insert our edited message, and slice the second part
|
||||
|
@ -503,7 +514,10 @@ function handleMessageChanged(
|
|||
...state.messages.slice(messageInStoreIndex + 1),
|
||||
];
|
||||
|
||||
const convo = state.conversationLookup[payload.get('conversationId')];
|
||||
const isPublic = convo?.isPublic || false;
|
||||
// reorder the messages depending on the timestamp (we might have an updated serverTimestamp now)
|
||||
const sortedMessage = sortMessages(editedMessages, isPublic);
|
||||
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(
|
||||
editedMessages
|
||||
);
|
||||
|
|
|
@ -142,7 +142,7 @@ export const _getLeftPaneLists = (
|
|||
}
|
||||
|
||||
// Show loading icon while fetching messages
|
||||
if (conversation.isPublic && !conversation.timestamp) {
|
||||
if (conversation.isPublic && !conversation.activeAt) {
|
||||
conversation.lastMessage = {
|
||||
status: 'sending',
|
||||
text: '',
|
||||
|
|
|
@ -13,9 +13,8 @@ describe('state/selectors/conversations', () => {
|
|||
const data: ConversationLookupType = {
|
||||
id1: {
|
||||
id: 'id1',
|
||||
activeAt: Date.now(),
|
||||
activeAt: 0,
|
||||
name: 'No timestamp',
|
||||
timestamp: 0,
|
||||
phoneNumber: 'notused',
|
||||
|
||||
type: 'direct',
|
||||
|
@ -30,9 +29,8 @@ describe('state/selectors/conversations', () => {
|
|||
},
|
||||
id2: {
|
||||
id: 'id2',
|
||||
activeAt: Date.now(),
|
||||
activeAt: 20,
|
||||
name: 'B',
|
||||
timestamp: 20,
|
||||
phoneNumber: 'notused',
|
||||
|
||||
type: 'direct',
|
||||
|
@ -47,9 +45,8 @@ describe('state/selectors/conversations', () => {
|
|||
},
|
||||
id3: {
|
||||
id: 'id3',
|
||||
activeAt: Date.now(),
|
||||
activeAt: 20,
|
||||
name: 'C',
|
||||
timestamp: 20,
|
||||
phoneNumber: 'notused',
|
||||
|
||||
type: 'direct',
|
||||
|
@ -64,9 +61,8 @@ describe('state/selectors/conversations', () => {
|
|||
},
|
||||
id4: {
|
||||
id: 'id4',
|
||||
activeAt: Date.now(),
|
||||
activeAt: 20,
|
||||
name: 'Á',
|
||||
timestamp: 20,
|
||||
phoneNumber: 'notused',
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -80,9 +76,8 @@ describe('state/selectors/conversations', () => {
|
|||
},
|
||||
id5: {
|
||||
id: 'id5',
|
||||
activeAt: Date.now(),
|
||||
activeAt: 30,
|
||||
name: 'First!',
|
||||
timestamp: 30,
|
||||
phoneNumber: 'notused',
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
import { v4 as uuid } from 'uuid';
|
||||
import { OpenGroup } from '../../../session/types';
|
||||
import { generateFakePubKey, generateFakePubKeys } from './pubkey';
|
||||
import { ClosedGroupChatMessage } from '../../../session/messages/outgoing/content/data/group';
|
||||
import { ClosedGroupChatMessage } from '../../../session/messages/outgoing/content/data/group/ClosedGroupChatMessage';
|
||||
import { ConversationAttributes } from '../../../models/conversation';
|
||||
|
||||
export function generateChatMessage(identifier?: string): ChatMessage {
|
||||
|
|
Loading…
Reference in New Issue