signal-desktop/js/conversation_controller.js

222 lines
6.2 KiB
JavaScript
Raw Normal View History

2018-09-21 01:47:19 +00:00
/* global _, Whisper, Backbone, storage */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
2018-04-27 21:25:04 +00:00
(function() {
'use strict';
window.Whisper = window.Whisper || {};
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
const conversations = new Whisper.ConversationCollection();
const inboxCollection = new (Backbone.Collection.extend({
initialize() {
2018-04-27 21:25:04 +00:00
this.listenTo(conversations, 'add change:active_at', this.addActive);
this.listenTo(conversations, 'reset', () => this.reset([]));
2018-04-27 21:25:04 +00:00
this.on(
'add remove change:unreadCount',
_.debounce(this.updateUnreadCount.bind(this), 1000)
);
},
addActive(model) {
2018-04-27 21:25:04 +00:00
if (model.get('active_at')) {
this.add(model);
} else {
this.remove(model);
}
},
updateUnreadCount() {
const newUnreadCount = _.reduce(
this.map(m => m.get('unreadCount')),
(item, memo) => item + memo,
2018-04-27 21:25:04 +00:00
0
);
storage.put('unreadCount', newUnreadCount);
if (newUnreadCount > 0) {
window.setBadgeCount(newUnreadCount);
window.document.title = `${window.getTitle()} (${newUnreadCount})`;
2018-04-27 21:25:04 +00:00
} else {
window.setBadgeCount(0);
window.document.title = window.getTitle();
2018-04-27 21:25:04 +00:00
}
window.updateTrayIcon(newUnreadCount);
},
}))();
window.getInboxCollection = () => inboxCollection;
window.getConversations = () => conversations;
2018-04-27 21:25:04 +00:00
window.ConversationController = {
get(id) {
2018-04-27 21:25:04 +00:00
if (!this._initialFetchComplete) {
throw new Error(
'ConversationController.get() needs complete initial fetch'
);
}
return conversations.get(id);
},
// Needed for some model setup which happens during the initial fetch() call below
getUnsafe(id) {
2018-04-27 21:25:04 +00:00
return conversations.get(id);
},
dangerouslyCreateAndAdd(attributes) {
2018-04-27 21:25:04 +00:00
return conversations.add(attributes);
},
getOrCreate(id, type) {
2018-04-27 21:25:04 +00:00
if (typeof id !== 'string') {
throw new TypeError("'id' must be a string");
}
if (type !== 'private' && type !== 'group') {
throw new TypeError(
`'type' must be 'private' or 'group'; got: '${type}'`
);
}
if (!this._initialFetchComplete) {
throw new Error(
'ConversationController.get() needs complete initial fetch'
);
}
let conversation = conversations.get(id);
2018-04-27 21:25:04 +00:00
if (conversation) {
return conversation;
}
conversation = conversations.add({
id,
type,
2018-09-21 01:47:19 +00:00
version: 2,
2018-04-27 21:25:04 +00:00
});
2018-09-21 01:47:19 +00:00
const create = async () => {
2018-04-27 21:25:04 +00:00
if (!conversation.isValid()) {
const validationError = conversation.validationError || {};
window.log.error(
2018-04-27 21:25:04 +00:00
'Contact is not valid. Not saving, but adding to collection:',
conversation.idForLogging(),
validationError.stack
);
2018-09-21 01:47:19 +00:00
return conversation;
}
2018-09-21 01:47:19 +00:00
try {
await window.Signal.Data.saveConversation(conversation.attributes, {
Conversation: Whisper.Conversation,
});
} catch (error) {
window.log.error(
'Conversation save failed! ',
id,
type,
'Error:',
error && error.stack ? error.stack : error
);
throw error;
2018-04-27 21:25:04 +00:00
}
2018-09-21 01:47:19 +00:00
return conversation;
};
conversation.initialPromise = create();
2018-04-27 21:25:04 +00:00
return conversation;
},
getOrCreateAndWait(id, type) {
return this._initialPromise.then(() => {
const conversation = this.getOrCreate(id, type);
2018-04-27 21:25:04 +00:00
if (conversation) {
return conversation.initialPromise.then(() => conversation);
}
return Promise.reject(
new Error('getOrCreateAndWait: did not get conversation')
);
2018-04-27 21:25:04 +00:00
});
},
2018-11-07 19:20:43 +00:00
prepareForSend(id, options) {
// id is either a group id or an individual user's id
const conversation = this.get(id);
2018-11-07 19:20:43 +00:00
const sendOptions = conversation
? conversation.getSendOptions(options)
: null;
const wrap = conversation
? conversation.wrapSend.bind(conversation)
: promise => promise;
return { wrap, sendOptions };
},
2018-09-21 01:47:19 +00:00
async getAllGroupsInvolvingId(id) {
const groups = await window.Signal.Data.getAllGroupsInvolvingId(id, {
ConversationCollection: Whisper.ConversationCollection,
});
return groups.map(group => conversations.add(group));
},
loadPromise() {
2018-04-27 21:25:04 +00:00
return this._initialPromise;
},
reset() {
2018-04-27 21:25:04 +00:00
this._initialPromise = Promise.resolve();
this._initialFetchComplete = false;
2018-04-27 21:25:04 +00:00
conversations.reset([]);
},
async load() {
window.log.info('ConversationController: starting initial fetch');
2018-04-27 21:25:04 +00:00
if (conversations.length) {
throw new Error('ConversationController: Already loaded!');
}
const load = async () => {
try {
2018-09-21 01:47:19 +00:00
const collection = await window.Signal.Data.getAllConversations({
ConversationCollection: Whisper.ConversationCollection,
});
conversations.add(collection.models);
this._initialFetchComplete = true;
await Promise.all(
conversations.map(async conversation => {
if (!conversation.get('lastMessage')) {
await conversation.updateLastMessage();
}
// In case a too-large draft was saved to the database
const draft = conversation.get('draft');
if (draft && draft.length > MAX_MESSAGE_BODY_LENGTH) {
this.model.set({
draft: draft.slice(0, MAX_MESSAGE_BODY_LENGTH),
});
2019-09-26 19:56:31 +00:00
window.Signal.Data.updateConversation(
conversation.id,
2019-09-26 19:56:31 +00:00
conversation.attributes
);
}
})
);
window.log.info('ConversationController: done with initial fetch');
} catch (error) {
window.log.error(
'ConversationController: initial fetch failed',
error && error.stack ? error.stack : error
);
throw error;
}
};
this._initialPromise = load();
2018-04-27 21:25:04 +00:00
return this._initialPromise;
},
};
})();