2018-09-21 01:47:19 +00:00
|
|
|
/* global _, Whisper, Backbone, storage */
|
2015-09-17 06:13:17 +00:00
|
|
|
|
2018-07-07 00:48:14 +00:00
|
|
|
/* 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 || {};
|
|
|
|
|
2019-09-13 21:54:19 +00:00
|
|
|
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
|
|
|
|
|
2018-07-07 00:48:14 +00:00
|
|
|
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);
|
2018-07-07 00:48:14 +00:00
|
|
|
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)
|
|
|
|
);
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
addActive(model) {
|
2018-04-27 21:25:04 +00:00
|
|
|
if (model.get('active_at')) {
|
|
|
|
this.add(model);
|
|
|
|
} else {
|
|
|
|
this.remove(model);
|
|
|
|
}
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
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);
|
2018-07-07 00:48:14 +00:00
|
|
|
window.document.title = `${window.getTitle()} (${newUnreadCount})`;
|
2018-04-27 21:25:04 +00:00
|
|
|
} else {
|
|
|
|
window.setBadgeCount(0);
|
2018-06-02 00:55:35 +00:00
|
|
|
window.document.title = window.getTitle();
|
2018-04-27 21:25:04 +00:00
|
|
|
}
|
|
|
|
window.updateTrayIcon(newUnreadCount);
|
|
|
|
},
|
|
|
|
}))();
|
|
|
|
|
2018-07-07 00:48:14 +00:00
|
|
|
window.getInboxCollection = () => inboxCollection;
|
2019-03-25 17:22:33 +00:00
|
|
|
window.getConversations = () => conversations;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
|
|
|
window.ConversationController = {
|
2018-07-07 00:48:14 +00:00
|
|
|
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
|
2018-07-07 00:48:14 +00:00
|
|
|
getUnsafe(id) {
|
2018-04-27 21:25:04 +00:00
|
|
|
return conversations.get(id);
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
dangerouslyCreateAndAdd(attributes) {
|
2018-04-27 21:25:04 +00:00
|
|
|
return conversations.add(attributes);
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
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'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-07-07 00:48:14 +00:00
|
|
|
let conversation = conversations.get(id);
|
2018-04-27 21:25:04 +00:00
|
|
|
if (conversation) {
|
|
|
|
return conversation;
|
|
|
|
}
|
|
|
|
|
|
|
|
conversation = conversations.add({
|
2018-07-07 00:48:14 +00:00
|
|
|
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()) {
|
2018-07-07 00:48:14 +00:00
|
|
|
const validationError = conversation.validationError || {};
|
2018-07-21 19:00:08 +00:00
|
|
|
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;
|
2015-09-17 06:13:17 +00:00
|
|
|
}
|
2017-09-07 01:18:46 +00:00
|
|
|
|
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
|
|
|
}
|
2017-09-01 16:10:41 +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;
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
getOrCreateAndWait(id, type) {
|
|
|
|
return this._initialPromise.then(() => {
|
|
|
|
const conversation = this.getOrCreate(id, type);
|
2018-04-27 21:25:04 +00:00
|
|
|
|
2018-07-07 00:48:14 +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) {
|
2018-10-18 01:01:21 +00:00
|
|
|
// 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;
|
2018-10-18 01:01:21 +00:00
|
|
|
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));
|
2018-07-07 00:48:14 +00:00
|
|
|
},
|
|
|
|
loadPromise() {
|
2018-04-27 21:25:04 +00:00
|
|
|
return this._initialPromise;
|
|
|
|
},
|
2018-07-07 00:48:14 +00:00
|
|
|
reset() {
|
2018-04-27 21:25:04 +00:00
|
|
|
this._initialPromise = Promise.resolve();
|
2018-07-27 01:13:56 +00:00
|
|
|
this._initialFetchComplete = false;
|
2018-04-27 21:25:04 +00:00
|
|
|
conversations.reset([]);
|
|
|
|
},
|
2018-07-27 01:13:56 +00:00
|
|
|
async load() {
|
2018-07-21 19:00:08 +00:00
|
|
|
window.log.info('ConversationController: starting initial fetch');
|
2018-04-27 21:25:04 +00:00
|
|
|
|
2018-07-27 01:13:56 +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);
|
|
|
|
|
2018-07-27 01:13:56 +00:00
|
|
|
this._initialFetchComplete = true;
|
|
|
|
await Promise.all(
|
2019-09-13 21:54:19 +00:00
|
|
|
conversations.map(async conversation => {
|
2019-02-28 02:15:24 +00:00
|
|
|
if (!conversation.get('lastMessage')) {
|
2019-09-13 21:54:19 +00:00
|
|
|
await conversation.updateLastMessage();
|
2019-02-28 02:15:24 +00:00
|
|
|
}
|
|
|
|
|
2019-09-13 21:54:19 +00:00
|
|
|
// 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(
|
2019-09-13 21:54:19 +00:00
|
|
|
conversation.id,
|
2019-09-26 19:56:31 +00:00
|
|
|
conversation.attributes
|
2019-09-13 21:54:19 +00:00
|
|
|
);
|
|
|
|
}
|
2019-02-28 02:15:24 +00:00
|
|
|
})
|
2018-07-27 01:13:56 +00:00
|
|
|
);
|
|
|
|
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();
|
2017-08-30 16:35:04 +00:00
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
return this._initialPromise;
|
|
|
|
},
|
|
|
|
};
|
2015-09-17 06:13:17 +00:00
|
|
|
})();
|