Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve open group loading speed #1459

Merged
merged 13 commits into from
Jan 29, 2021
Merged
1 change: 0 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ module.exports = grunt => {
'libtextsecure/account_manager.js',
'libtextsecure/http-resources.js',
'libtextsecure/message_receiver.js',
'libtextsecure/contacts_parser.js',
'libtextsecure/task_with_timeout.js',
],
dest: 'js/libtextsecure.js',
Expand Down
19 changes: 2 additions & 17 deletions js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@
accountManager = new textsecure.AccountManager(USERNAME, PASSWORD);
accountManager.addEventListener('registration', () => {
const user = {
regionCode: window.storage.get('regionCode'),
ourNumber: textsecure.storage.user.getNumber(),
ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'),
};
Expand Down Expand Up @@ -618,6 +617,7 @@
displayName: newName,
avatar: newAvatarPath,
});
conversation.commit();
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
Expand Down Expand Up @@ -1071,25 +1071,10 @@
}
function onConfiguration(ev) {
const { configuration } = ev;
const {
readReceipts,
typingIndicators,
unidentifiedDeliveryIndicators,
linkPreviews,
} = configuration;
const { readReceipts, typingIndicators, linkPreviews } = configuration;

storage.put('read-receipt-setting', readReceipts);

if (
unidentifiedDeliveryIndicators === true ||
unidentifiedDeliveryIndicators === false
) {
storage.put(
'unidentifiedDeliveryIndicators',
unidentifiedDeliveryIndicators
);
}

if (typingIndicators === true || typingIndicators === false) {
storage.put('typing-indicators-setting', typingIndicators);
}
Expand Down
1 change: 0 additions & 1 deletion js/models/conversations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export interface ConversationModel
isPublic: () => boolean;
isClosedGroup: () => boolean;
isBlocked: () => boolean;
isClosable: () => boolean;
isAdmin: (id: string) => boolean;
throttledBumpTyping: () => void;

Expand Down
132 changes: 20 additions & 112 deletions js/models/conversations.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,7 @@

window.Whisper = window.Whisper || {};

const SEALED_SENDER = {
UNKNOWN: 0,
ENABLED: 1,
DISABLED: 2,
UNRESTRICTED: 3,
};

const { Conversation, Contact, Message, PhoneNumber } = window.Signal.Types;
const { Contact, Conversation, Message } = window.Signal.Types;
const {
upgradeMessageSchema,
loadAttachmentData,
Expand Down Expand Up @@ -58,43 +51,28 @@
return `group(${this.id})`;
},

getContactCollection() {
const collection = new Backbone.Collection();
const collator = new Intl.Collator();
collection.comparator = (left, right) => {
const leftLower = left.getTitle().toLowerCase();
const rightLower = right.getTitle().toLowerCase();
return collator.compare(leftLower, rightLower);
};
return collection;
},

initialize() {
this.ourNumber = textsecure.storage.user.getNumber();

// This may be overridden by ConversationController.getOrCreate, and signify
// our first save to the database. Or first fetch from the database.
this.initialPromise = Promise.resolve();

this.contactCollection = this.getContactCollection();
this.messageCollection = new Whisper.MessageCollection([], {
conversation: this,
});

this.throttledBumpTyping = _.throttle(this.bumpTyping, 300);
const debouncedUpdateLastMessage = _.debounce(
this.updateLastMessage.bind(this),
200
this.updateLastMessage = _.throttle(
this.bouncyUpdateLastMessage.bind(this),
1000
);
this.listenTo(
this.messageCollection,
'add remove destroy',
debouncedUpdateLastMessage
);
this.on('newmessage', this.onNewMessage);

// this.listenTo(
// this.messageCollection,
// 'add remove destroy',
// debouncedUpdateLastMessage
// );
// Listening for out-of-band data updates
this.on('updateMessage', this.updateAndMerge);
this.on('delivered', this.updateAndMerge);
this.on('read', this.updateAndMerge);
this.on('expiration-change', this.updateAndMerge);
Expand All @@ -108,13 +86,6 @@
if (this.isPublic) {
this.set('profileSharing', true);
}

const sealedSender = this.get('sealedSender');
if (sealedSender === undefined) {
this.set({ sealedSender: SEALED_SENDER.UNKNOWN });
}
this.unset('unidentifiedDelivery');
this.unset('unidentifiedDeliveryUnrestricted');
this.unset('hasFetchedProfile');
this.unset('tokens');

Expand All @@ -137,9 +108,7 @@
isClosedGroup() {
return this.get('type') === Message.GROUP && !this.isPublic();
},
isClosable() {
return this.get('closable');
},

isBlocked() {
if (!this.id || this.isMe()) {
return false;
Expand Down Expand Up @@ -313,7 +282,6 @@
existing.merge(message.attributes);
};

await this.inProgressFetch;
mergeMessage();
},

Expand All @@ -335,11 +303,6 @@
existing.trigger('expired');
};

// If a fetch is in progress, then we need to wait until that's complete to
// do this removal. Otherwise we could remove from messageCollection, then
// the async database fetch could include the removed message.

await this.inProgressFetch;
removeMessage();
},

Expand Down Expand Up @@ -372,26 +335,6 @@
await model.setServerTimestamp(serverTimestamp);
return undefined;
},

async onNewMessage(message) {
await this.updateLastMessage();

// Clear typing indicator for a given contact if we receive a message from them
const identifier = message.get
? `${message.get('source')}.${message.get('sourceDevice')}`
: `${message.source}.${message.sourceDevice}`;
this.clearContactTypingTimer(identifier);

const model = this.addSingleMessage(message);
getMessageController().register(model.id, model);

window.Whisper.events.trigger('messageAdded', {
conversationKey: this.id,
messageModel: model,
});

this.commit();
},
addSingleMessage(message, setToExpire = true) {
const model = this.messageCollection.add(message, { merge: true });
if (setToExpire) {
Expand All @@ -406,8 +349,6 @@
return this.get('groupAdmins') || this.get('moderators');
},
getProps() {
const { format } = PhoneNumber;
const regionCode = storage.get('regionCode');
const typingKeys = Object.keys(this.contactTypingTimers || {});

const groupAdmins = this.getGroupAdmins();
Expand All @@ -422,7 +363,6 @@
type: this.isPrivate() ? 'direct' : 'group',
isMe: this.isMe(),
isPublic: this.isPublic(),
isClosable: this.isClosable(),
isTyping: typingKeys.length > 0,
lastUpdated: this.get('timestamp'),
name: this.getName(),
Expand All @@ -432,10 +372,7 @@
unreadCount: this.get('unreadCount') || 0,
mentionedUs: this.get('mentionedUs') || false,
isBlocked: this.isBlocked(),
primaryDevice: this.id,
phoneNumber: format(this.id, {
ourRegionCode: regionCode,
}),
phoneNumber: this.id,
lastMessage: {
status: this.get('lastMessageStatus'),
text: this.get('lastMessage'),
Expand Down Expand Up @@ -844,21 +781,10 @@

// We're offline!
if (!textsecure.messaging) {
let errors;
if (this.contactCollection.length) {
errors = this.contactCollection.map(contact => {
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.number = contact.id;
return error;
});
} else {
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.number = this.id;
errors = [error];
}
await message.saveErrors(errors);
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
error.number = this.id;
await message.saveErrors([error]);
return null;
}

Expand All @@ -885,16 +811,18 @@
);
await serverAPI.setAvatar(url, profileKey);
},
async updateLastMessage() {
async bouncyUpdateLastMessage() {
if (!this.id) {
return;
}

if (!this.get('active_at')) {
window.log.info('Skipping update last message as active_at is falsy');
return;
}
const messages = await window.Signal.Data.getMessagesByConversation(
this.id,
{ limit: 1, MessageCollection: Whisper.MessageCollection }
);

const lastMessageModel = messages.at(0);
const lastMessageJSON = lastMessageModel
? lastMessageModel.toJSON()
Expand All @@ -910,12 +838,10 @@
? lastMessageModel.getNotificationText()
: null,
});

// Because we're no longer using Backbone-integrated saves, we need to manually
// clear the changed fields here so our hasChanged() check below is useful.
this.changed = {};
this.set(lastMessageUpdate);

if (this.hasChanged()) {
await this.commit();
}
Expand Down Expand Up @@ -1355,13 +1281,9 @@
async setProfileKey(profileKey) {
// profileKey is a string so we can compare it directly
if (this.get('profileKey') !== profileKey) {
window.log.info(
`Setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
);
this.set({
profileKey,
accessKey: null,
sealedSender: SEALED_SENDER.UNKNOWN,
});

await this.deriveAccessKeyIfNeeded();
Expand Down Expand Up @@ -1415,20 +1337,6 @@
hasMember(number) {
return _.contains(this.get('members'), number);
},
fetchContacts() {
if (this.isPrivate()) {
this.contactCollection.reset([this]);
return Promise.resolve();
}
const members = this.get('members') || [];
const promises = members.map(number =>
window.getConversationController().getOrCreateAndWait(number, 'private')
);

return Promise.all(promises).then(contacts => {
this.contactCollection.reset(contacts);
});
},
// returns true if this is a closed/medium or open group
isGroup() {
return this.get('type') === 'group';
Expand Down
1 change: 0 additions & 1 deletion js/models/messages.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export interface MessageRegularProps {
status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error' | 'pow';
// What if changed this over to a single contact like quote, and put the events on it?
contact?: Contact & {
hasSignalAccount: boolean;
onSendMessage?: () => void;
onClick?: () => void;
};
Expand Down
Loading