signal-desktop/js/conversation_controller.js

199 lines
6.8 KiB
JavaScript
Raw Normal View History

/*global $, Whisper, Backbone, textsecure, extension*/
/*
* vim: ts=4:sw=4:expandtab
*/
// This script should only be included in background.html
(function () {
'use strict';
window.Whisper = window.Whisper || {};
var conversations = new Whisper.ConversationCollection();
var inboxCollection = new (Backbone.Collection.extend({
initialize: function() {
this.on('change:timestamp change:name change:number', this.sort);
this.listenTo(conversations, 'add change:active_at', this.addActive);
this.listenTo(conversations, 'reset', function() {
this.reset([]);
});
this.on('add remove change:unreadCount',
_.debounce(this.updateUnreadCount.bind(this), 1000)
);
this.startPruning();
this.collator = new Intl.Collator();
},
comparator: function(m1, m2) {
var timestamp1 = m1.get('timestamp');
var timestamp2 = m2.get('timestamp');
if (timestamp1 && !timestamp2) {
return -1;
}
if (timestamp2 && !timestamp1) {
return 1;
}
if (timestamp1 && timestamp2 && timestamp1 !== timestamp2) {
return timestamp2 - timestamp1;
}
var title1 = m1.getTitle().toLowerCase();
var title2 = m2.getTitle().toLowerCase();
return this.collator.compare(title1, title2);
},
addActive: function(model) {
if (model.get('active_at')) {
this.add(model);
} else {
this.remove(model);
}
},
updateUnreadCount: function() {
var newUnreadCount = _.reduce(
this.map(function(m) { return m.get('unreadCount'); }),
function(item, memo) {
return item + memo;
},
0
);
storage.put("unreadCount", newUnreadCount);
2017-05-04 05:29:06 +00:00
if (newUnreadCount > 0) {
window.setBadgeCount(newUnreadCount);
Beta versions support: SxS support, in-app env/instance display (#1606) * Script for beta config; unique data dir, in-app env/type display To release a beta build, increment the version and add -beta-N to the end, then go through all the standard release activities. The prepare-build npm script then updates key bits of the package.json to ensure that the beta build can be installed alongside a production build. This includes a new name ('Signal Beta') and a different location for application data. Note: Beta builds can be installed alongside production builds. As part of this, a couple new bits of data are shown across the app: - Environment (development or test, not shown if production) - App Instance (disabled in production; used for multiple accounts) These are shown in: - The window title - both environment and app instance. You can tell beta builds because the app name, preceding these data bits, is different. - The about window - both environment and app instance. You can tell beta builds from the version number. - The header added to the debug log - just environment. The version number will tell us if it's a beta build, and app instance isn't helpful. * Turn on single-window mode in non-production modes Because it's really frightening when you see 'unable to read from db' errors in the console. * aply.sh: More instructions for initial setup and testing * Gruntfile: Get consistent with use of package.json datas * Linux: manually update desktop keys, since macros not available
2017-10-30 20:57:13 +00:00
window.document.title = window.config.title + " (" + newUnreadCount + ")";
2017-05-04 05:29:06 +00:00
} else {
window.setBadgeCount(0);
Beta versions support: SxS support, in-app env/instance display (#1606) * Script for beta config; unique data dir, in-app env/type display To release a beta build, increment the version and add -beta-N to the end, then go through all the standard release activities. The prepare-build npm script then updates key bits of the package.json to ensure that the beta build can be installed alongside a production build. This includes a new name ('Signal Beta') and a different location for application data. Note: Beta builds can be installed alongside production builds. As part of this, a couple new bits of data are shown across the app: - Environment (development or test, not shown if production) - App Instance (disabled in production; used for multiple accounts) These are shown in: - The window title - both environment and app instance. You can tell beta builds because the app name, preceding these data bits, is different. - The about window - both environment and app instance. You can tell beta builds from the version number. - The header added to the debug log - just environment. The version number will tell us if it's a beta build, and app instance isn't helpful. * Turn on single-window mode in non-production modes Because it's really frightening when you see 'unable to read from db' errors in the console. * aply.sh: More instructions for initial setup and testing * Gruntfile: Get consistent with use of package.json datas * Linux: manually update desktop keys, since macros not available
2017-10-30 20:57:13 +00:00
window.document.title = window.config.title;
2017-05-04 05:29:06 +00:00
}
window.updateTrayIcon(newUnreadCount);
},
startPruning: function() {
var halfHour = 30 * 60 * 1000;
this.interval = setInterval(function() {
this.forEach(function(conversation) {
conversation.trigger('prune');
});
}.bind(this), halfHour);
}
}))();
window.getInboxCollection = function() {
return inboxCollection;
};
window.ConversationController = {
get: function(id) {
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: function(id) {
return conversations.get(id);
},
dangerouslyCreateAndAdd: function(attributes) {
return conversations.add(attributes);
},
getOrCreate: function(id, type) {
if (typeof id !== 'string') {
2018-04-11 19:44:52 +00:00
throw new TypeError("'id' must be a string");
}
if (type !== 'private' && type !== 'group') {
2018-04-11 19:44:52 +00:00
throw new TypeError(`'type' must be 'private' or 'group'; got: '${type}'`);
}
if (!this._initialFetchComplete) {
throw new Error('ConversationController.get() needs complete initial fetch');
}
var conversation = conversations.get(id);
if (conversation) {
return conversation;
2015-09-17 18:41:49 +00:00
}
conversation = conversations.add({
id: id,
type: type
});
conversation.initialPromise = new Promise(function(resolve, reject) {
if (!conversation.isValid()) {
var validationError = conversation.validationError || {};
console.log(
'Contact is not valid. Not saving, but adding to collection:',
conversation.idForLogging(),
validationError.stack
);
return resolve(conversation);
}
var deferred = conversation.save();
if (!deferred) {
console.log('Conversation save failed! ', id, type);
return reject(new Error('getOrCreate: Conversation save failed'));
}
deferred.then(function() {
resolve(conversation);
}, reject);
});
return conversation;
},
getOrCreateAndWait: function(id, type) {
return this._initialPromise.then(function() {
var conversation = this.getOrCreate(id, type);
if (conversation) {
return conversation.initialPromise.then(function() {
return conversation;
});
}
return Promise.reject(
new Error('getOrCreateAndWait: did not get conversation')
);
}.bind(this));
},
getAllGroupsInvolvingId: function(id) {
var groups = new Whisper.GroupCollection();
return groups.fetchGroups(id).then(function() {
return groups.map(function(group) {
return conversations.add(group);
});
});
},
loadPromise: function() {
return this._initialPromise;
},
reset: function() {
this._initialPromise = Promise.resolve();
conversations.reset([]);
},
load: function() {
console.log('ConversationController: starting initial fetch');
this._initialPromise = new Promise(function(resolve, reject) {
conversations.fetch().then(function() {
console.log('ConversationController: done with initial fetch');
this._initialFetchComplete = true;
resolve();
}.bind(this), function(error) {
console.log(
'ConversationController: initial fetch failed',
error && error.stack ? error.stack : error
);
reject(error);
});
}.bind(this));
return this._initialPromise;
}
};
})();