2019-01-14 21:49:58 +00:00
|
|
|
/* global
|
|
|
|
ConversationController,
|
|
|
|
extension,
|
2019-03-25 17:22:33 +00:00
|
|
|
getConversations,
|
2019-01-14 21:49:58 +00:00
|
|
|
getInboxCollection,
|
|
|
|
i18n,
|
|
|
|
Whisper,
|
|
|
|
textsecure,
|
|
|
|
Signal
|
2019-04-17 21:17:00 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
*/
|
2018-03-02 20:54:15 +00:00
|
|
|
|
|
|
|
// eslint-disable-next-line func-names
|
2018-04-27 21:25:04 +00:00
|
|
|
(function() {
|
2018-03-02 20:54:15 +00:00
|
|
|
'use strict';
|
2014-11-16 23:30:40 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
window.Whisper = window.Whisper || {};
|
2014-11-16 23:30:40 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
Whisper.ConversationStack = Whisper.View.extend({
|
|
|
|
className: 'conversation-stack',
|
|
|
|
open(conversation) {
|
|
|
|
const id = `conversation-${conversation.cid}`;
|
|
|
|
if (id !== this.el.firstChild.id) {
|
2018-04-27 21:25:04 +00:00
|
|
|
this.$el
|
|
|
|
.first()
|
|
|
|
.find('video, audio')
|
|
|
|
.each(function pauseMedia() {
|
|
|
|
this.pause();
|
|
|
|
});
|
2018-03-02 20:54:15 +00:00
|
|
|
let $el = this.$(`#${id}`);
|
|
|
|
if ($el === null || $el.length === 0) {
|
|
|
|
const view = new Whisper.ConversationView({
|
|
|
|
model: conversation,
|
|
|
|
window: this.model.window,
|
|
|
|
});
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
|
$el = view.$el;
|
2015-12-07 20:36:30 +00:00
|
|
|
}
|
2018-03-02 20:54:15 +00:00
|
|
|
$el.prependTo(this.el);
|
|
|
|
}
|
2018-02-03 22:20:01 +00:00
|
|
|
conversation.trigger('opened');
|
2018-03-02 20:54:15 +00:00
|
|
|
},
|
|
|
|
});
|
2014-11-16 23:30:40 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
Whisper.AppLoadingScreen = Whisper.View.extend({
|
|
|
|
templateName: 'app-loading-screen',
|
|
|
|
className: 'app-loading-screen',
|
|
|
|
updateProgress(count) {
|
|
|
|
if (count > 0) {
|
|
|
|
const message = i18n('loadingMessages', count.toString());
|
|
|
|
this.$('.message').text(message);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
render_attributes: {
|
|
|
|
message: i18n('loading'),
|
|
|
|
},
|
|
|
|
});
|
2014-11-16 23:30:40 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
Whisper.InboxView = Whisper.View.extend({
|
|
|
|
templateName: 'two-column',
|
|
|
|
className: 'inbox index',
|
|
|
|
initialize(options = {}) {
|
|
|
|
this.ready = false;
|
|
|
|
this.render();
|
|
|
|
this.$el.attr('tabindex', '1');
|
2017-07-25 01:43:35 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
this.conversation_stack = new Whisper.ConversationStack({
|
|
|
|
el: this.$('.conversation-stack'),
|
|
|
|
model: { window: options.window },
|
|
|
|
});
|
2017-01-04 03:37:56 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
if (!options.initialLoadComplete) {
|
|
|
|
this.appLoadingScreen = new Whisper.AppLoadingScreen();
|
|
|
|
this.appLoadingScreen.render();
|
|
|
|
this.appLoadingScreen.$el.prependTo(this.el);
|
|
|
|
this.startConnectionListener();
|
|
|
|
}
|
2015-10-15 19:10:03 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
const inboxCollection = getInboxCollection();
|
2015-10-15 19:10:03 +00:00
|
|
|
|
2018-07-18 03:25:55 +00:00
|
|
|
this.listenTo(inboxCollection, 'messageError', () => {
|
2018-07-09 21:29:13 +00:00
|
|
|
if (this.networkStatusView) {
|
|
|
|
this.networkStatusView.render();
|
|
|
|
}
|
2018-03-02 20:54:15 +00:00
|
|
|
});
|
2016-03-21 03:02:38 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
this.networkStatusView = new Whisper.NetworkStatusView();
|
2018-04-27 21:25:04 +00:00
|
|
|
this.$el
|
|
|
|
.find('.network-status-container')
|
2018-03-02 20:54:15 +00:00
|
|
|
.append(this.networkStatusView.render().el);
|
2016-03-21 06:34:56 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
if (extension.expired()) {
|
|
|
|
const banner = new Whisper.ExpiredAlertBanner().render();
|
|
|
|
banner.$el.prependTo(this.$el);
|
|
|
|
this.$el.addClass('expired');
|
|
|
|
}
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
this.setupLeftPane();
|
2018-03-02 20:54:15 +00:00
|
|
|
},
|
|
|
|
render_attributes: {
|
|
|
|
welcomeToSignal: i18n('welcomeToSignal'),
|
|
|
|
selectAContact: i18n('selectAContact'),
|
|
|
|
},
|
|
|
|
events: {
|
|
|
|
click: 'onClick',
|
2019-01-14 21:49:58 +00:00
|
|
|
},
|
|
|
|
setupLeftPane() {
|
|
|
|
// Here we set up a full redux store with initial state for our LeftPane Root
|
2019-03-25 17:22:33 +00:00
|
|
|
const convoCollection = getConversations();
|
|
|
|
const conversations = convoCollection.map(
|
2019-01-14 21:49:58 +00:00
|
|
|
conversation => conversation.cachedProps
|
|
|
|
);
|
|
|
|
const initialState = {
|
|
|
|
conversations: {
|
|
|
|
conversationLookup: Signal.Util.makeLookup(conversations, 'id'),
|
|
|
|
},
|
|
|
|
user: {
|
|
|
|
regionCode: window.storage.get('regionCode'),
|
|
|
|
ourNumber: textsecure.storage.user.getNumber(),
|
|
|
|
i18n: window.i18n,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
this.store = Signal.State.createStore(initialState);
|
|
|
|
window.inboxStore = this.store;
|
|
|
|
this.leftPaneView = new Whisper.ReactWrapperView({
|
|
|
|
JSX: Signal.State.Roots.createLeftPane(this.store),
|
|
|
|
className: 'left-pane-wrapper',
|
|
|
|
});
|
|
|
|
|
|
|
|
// Enables our redux store to be updated by backbone events in the outside world
|
|
|
|
const {
|
|
|
|
conversationAdded,
|
|
|
|
conversationChanged,
|
|
|
|
conversationRemoved,
|
|
|
|
removeAllConversations,
|
|
|
|
messageExpired,
|
|
|
|
openConversationExternal,
|
|
|
|
} = Signal.State.bindActionCreators(
|
|
|
|
Signal.State.Ducks.conversations.actions,
|
|
|
|
this.store.dispatch
|
|
|
|
);
|
|
|
|
const { userChanged } = Signal.State.bindActionCreators(
|
|
|
|
Signal.State.Ducks.user.actions,
|
|
|
|
this.store.dispatch
|
|
|
|
);
|
|
|
|
|
|
|
|
this.openConversationAction = openConversationExternal;
|
|
|
|
|
2019-03-29 18:30:43 +00:00
|
|
|
// In the future this listener will be added by the conversation view itself. But
|
|
|
|
// because we currently have multiple converations open at once, we install just
|
|
|
|
// one global handler.
|
2019-04-17 21:17:00 +00:00
|
|
|
// $(document).on('keydown', event => {
|
|
|
|
// const { ctrlKey, key } = event;
|
|
|
|
|
|
|
|
// We can add Command-E as the Mac shortcut when we add it to our Electron menus:
|
|
|
|
// https://stackoverflow.com/questions/27380018/when-cmd-key-is-kept-pressed-keyup-is-not-triggered-for-any-other-key
|
|
|
|
// For now, it will stay as CTRL-E only
|
|
|
|
// if (key === 'e' && ctrlKey) {
|
|
|
|
// const state = this.store.getState();
|
|
|
|
// const selectedId = state.conversations.selectedConversation;
|
|
|
|
// const conversation = ConversationController.get(selectedId);
|
|
|
|
|
|
|
|
// if (conversation && !conversation.get('isArchived')) {
|
|
|
|
// conversation.setArchived(true);
|
|
|
|
// conversation.trigger('unload');
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// });
|
2019-03-29 18:30:43 +00:00
|
|
|
|
2019-03-25 17:22:33 +00:00
|
|
|
this.listenTo(convoCollection, 'remove', conversation => {
|
2019-01-14 21:49:58 +00:00
|
|
|
const { id } = conversation || {};
|
|
|
|
conversationRemoved(id);
|
|
|
|
});
|
2019-03-25 17:22:33 +00:00
|
|
|
this.listenTo(convoCollection, 'add', conversation => {
|
2019-01-14 21:49:58 +00:00
|
|
|
const { id, cachedProps } = conversation || {};
|
|
|
|
conversationAdded(id, cachedProps);
|
|
|
|
});
|
2019-03-25 17:22:33 +00:00
|
|
|
this.listenTo(convoCollection, 'change', conversation => {
|
2019-01-14 21:49:58 +00:00
|
|
|
const { id, cachedProps } = conversation || {};
|
|
|
|
conversationChanged(id, cachedProps);
|
|
|
|
});
|
2019-03-25 17:22:33 +00:00
|
|
|
this.listenTo(convoCollection, 'reset', removeAllConversations);
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
Whisper.events.on('messageExpired', messageExpired);
|
|
|
|
Whisper.events.on('userChanged', userChanged);
|
|
|
|
|
|
|
|
// Finally, add it to the DOM
|
|
|
|
this.$('.left-pane-placeholder').append(this.leftPaneView.el);
|
2018-03-02 20:54:15 +00:00
|
|
|
},
|
|
|
|
startConnectionListener() {
|
|
|
|
this.interval = setInterval(() => {
|
|
|
|
const status = window.getSocketStatus();
|
|
|
|
switch (status) {
|
|
|
|
case WebSocket.CONNECTING:
|
|
|
|
break;
|
|
|
|
case WebSocket.OPEN:
|
|
|
|
clearInterval(this.interval);
|
|
|
|
// if we've connected, we can wait for real empty event
|
|
|
|
this.interval = null;
|
|
|
|
break;
|
|
|
|
case WebSocket.CLOSING:
|
|
|
|
case WebSocket.CLOSED:
|
|
|
|
clearInterval(this.interval);
|
|
|
|
this.interval = null;
|
|
|
|
// if we failed to connect, we pretend we got an empty event
|
|
|
|
this.onEmpty();
|
|
|
|
break;
|
|
|
|
default:
|
2019-02-26 22:14:19 +00:00
|
|
|
// We also replicate empty here
|
|
|
|
this.onEmpty();
|
2018-03-02 20:54:15 +00:00
|
|
|
break;
|
2015-12-07 20:36:30 +00:00
|
|
|
}
|
2018-03-02 20:54:15 +00:00
|
|
|
}, 1000);
|
|
|
|
},
|
|
|
|
onEmpty() {
|
|
|
|
const view = this.appLoadingScreen;
|
|
|
|
if (view) {
|
|
|
|
this.appLoadingScreen = null;
|
|
|
|
view.remove();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onProgress(count) {
|
|
|
|
const view = this.appLoadingScreen;
|
|
|
|
if (view) {
|
|
|
|
view.updateProgress(count);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
focusConversation(e) {
|
|
|
|
if (e && this.$(e.target).closest('.placeholder').length) {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-16 23:30:40 +00:00
|
|
|
|
2018-03-02 20:54:15 +00:00
|
|
|
this.$('#header, .gutter').addClass('inactive');
|
|
|
|
this.$('.conversation-stack').removeClass('inactive');
|
|
|
|
},
|
|
|
|
focusHeader() {
|
|
|
|
this.$('.conversation-stack').addClass('inactive');
|
|
|
|
this.$('#header, .gutter').removeClass('inactive');
|
|
|
|
this.$('.conversation:first .menu').trigger('close');
|
|
|
|
},
|
|
|
|
reloadBackgroundPage() {
|
|
|
|
window.location.reload();
|
|
|
|
},
|
2019-01-14 21:49:58 +00:00
|
|
|
async openConversation(id, messageId) {
|
2019-03-12 00:20:16 +00:00
|
|
|
const conversation = await ConversationController.getOrCreateAndWait(
|
2019-01-14 21:49:58 +00:00
|
|
|
id,
|
|
|
|
'private'
|
|
|
|
);
|
|
|
|
|
|
|
|
if (this.openConversationAction) {
|
|
|
|
this.openConversationAction(id, messageId);
|
2018-03-02 20:54:15 +00:00
|
|
|
}
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
this.conversation_stack.open(conversation);
|
|
|
|
this.focusConversation();
|
2018-03-02 20:54:15 +00:00
|
|
|
},
|
|
|
|
closeRecording(e) {
|
|
|
|
if (e && this.$(e.target).closest('.capture-audio').length > 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.$('.conversation:first .recorder').trigger('close');
|
|
|
|
},
|
|
|
|
onClick(e) {
|
|
|
|
this.closeRecording(e);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
Whisper.ExpiredAlertBanner = Whisper.View.extend({
|
|
|
|
templateName: 'expired_alert',
|
|
|
|
className: 'expiredAlert clearfix',
|
|
|
|
render_attributes() {
|
|
|
|
return {
|
|
|
|
expiredWarning: i18n('expiredWarning'),
|
|
|
|
upgrade: i18n('upgrade'),
|
|
|
|
};
|
|
|
|
},
|
|
|
|
});
|
2018-04-27 21:25:04 +00:00
|
|
|
})();
|