link back backbone conversation events to redux store

pull/1381/head
Audric Ackermann 5 years ago
parent 918eeae275
commit 2f2eb2ad53
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -11,7 +11,6 @@ exports.createTemplate = (options, messages) => {
openReleaseNotes, openReleaseNotes,
openSupportPage, openSupportPage,
platform, platform,
setupAsStandalone,
setupWithImport, setupWithImport,
showAbout, showAbout,
showDebugLog, showDebugLog,
@ -153,12 +152,6 @@ exports.createTemplate = (options, messages) => {
const fileMenu = template[0]; const fileMenu = template[0];
// These are in reverse order, since we're prepending them one at a time // These are in reverse order, since we're prepending them one at a time
if (options.development) {
fileMenu.submenu.unshift({
label: messages.menuSetupAsStandalone.message,
click: setupAsStandalone,
});
}
fileMenu.submenu.unshift({ fileMenu.submenu.unshift({
type: 'separator', type: 'separator',
@ -177,13 +170,7 @@ exports.createTemplate = (options, messages) => {
}; };
function updateForMac(template, messages, options) { function updateForMac(template, messages, options) {
const { const { includeSetup, setupWithImport, showAbout, showWindow } = options;
includeSetup,
setupAsStandalone,
setupWithImport,
showAbout,
showWindow,
} = options;
// Remove About item and separator from Help menu, since it's on the first menu // Remove About item and separator from Help menu, since it's on the first menu
template[4].submenu.pop(); template[4].submenu.pop();
@ -205,13 +192,6 @@ function updateForMac(template, messages, options) {
], ],
}; };
if (options.development) {
fileMenu.submenu.push({
label: messages.menuSetupAsStandalone.message,
click: setupAsStandalone,
});
}
template.unshift(fileMenu); template.unshift(fileMenu);
} }

@ -165,7 +165,6 @@
let initialLoadComplete = false; let initialLoadComplete = false;
let newVersion = false; let newVersion = false;
window.owsDesktopApp = {};
window.document.title = window.getTitle(); window.document.title = window.getTitle();
// start a background worker for ecc // start a background worker for ecc
@ -477,13 +476,6 @@
} }
); );
Whisper.events.on('setupAsStandalone', () => {
const { appView } = window.owsDesktopApp;
if (appView) {
appView.openStandalone();
}
});
function manageExpiringData() { function manageExpiringData() {
window.Signal.Data.cleanSeenMessages(); window.Signal.Data.cleanSeenMessages();
window.Signal.Data.cleanLastHashes(); window.Signal.Data.cleanLastHashes();
@ -570,7 +562,6 @@
const appView = new Whisper.AppView({ const appView = new Whisper.AppView({
el: $('body'), el: $('body'),
}); });
window.owsDesktopApp.appView = appView;
Whisper.WallClockListener.init(Whisper.events); Whisper.WallClockListener.init(Whisper.events);
Whisper.ExpiringMessagesListener.init(Whisper.events); Whisper.ExpiringMessagesListener.init(Whisper.events);
@ -608,12 +599,6 @@
window.addEventListener('focus', () => Whisper.Notifications.clear()); window.addEventListener('focus', () => Whisper.Notifications.clear());
window.addEventListener('unload', () => Whisper.Notifications.fastClear()); window.addEventListener('unload', () => Whisper.Notifications.fastClear());
Whisper.events.on('showConversation', (id, messageId) => {
if (appView) {
appView.openConversation(id, messageId);
}
});
window.confirmationDialog = params => { window.confirmationDialog = params => {
const confirmDialog = new Whisper.SessionConfirmView({ const confirmDialog = new Whisper.SessionConfirmView({
el: $('body'), el: $('body'),
@ -634,9 +619,6 @@
confirmDialog.render(); confirmDialog.render();
}; };
window.showSeedDialog = window.owsDesktopApp.appView.showSeedDialog;
window.showPasswordDialog = window.owsDesktopApp.appView.showPasswordDialog;
window.showEditProfileDialog = async callback => { window.showEditProfileDialog = async callback => {
const ourNumber = window.storage.get('primaryDevicePubKey'); const ourNumber = window.storage.get('primaryDevicePubKey');
const conversation = await ConversationController.getOrCreateAndWait( const conversation = await ConversationController.getOrCreateAndWait(
@ -930,7 +912,9 @@
window.log.warn(`Could not connect to ${serverAddress}`); window.log.warn(`Could not connect to ${serverAddress}`);
return; return;
} }
appView.openConversation(conversationId, {}); window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(conversationId)
);
} }
); );
@ -943,7 +927,9 @@
Whisper.Notifications.on('click', (id, messageId) => { Whisper.Notifications.on('click', (id, messageId) => {
window.showWindow(); window.showWindow();
if (id) { if (id) {
appView.openConversation(id, messageId); window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(id, messageId)
);
} else { } else {
appView.openInbox({ appView.openInbox({
initialLoadComplete, initialLoadComplete,
@ -981,7 +967,9 @@
avatarPath, avatarPath,
isRss: conversation.isRss(), isRss: conversation.isRss(),
onStartConversation: () => { onStartConversation: () => {
Whisper.events.trigger('showConversation', userPubKey); window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(conversation.id)
);
}, },
}); });
} }
@ -1308,24 +1296,24 @@
} }
function onChangeTheme() { function onChangeTheme() {
const view = window.owsDesktopApp.appView; // const view = window.owsDesktopApp.appView;
if (view) { // if (view) {
view.applyTheme(); // view.applyTheme();
} // }
} }
function onEmpty() { function onEmpty() {
initialLoadComplete = true; initialLoadComplete = true;
window.readyForUpdates(); window.readyForUpdates();
let interval = setInterval(() => { // let interval = setInterval(() => {
const view = window.owsDesktopApp.appView; // const view = window.owsDesktopApp.appView;
if (view) { // if (view) {
clearInterval(interval); // clearInterval(interval);
interval = null; // interval = null;
view.onEmpty(); // view.onEmpty();
} // }
}, 500); // }, 500);
Whisper.Notifications.enable(); Whisper.Notifications.enable();
} }
@ -1345,10 +1333,10 @@
const { count } = ev; const { count } = ev;
window.log.info(`onProgress: Message count is ${count}`); window.log.info(`onProgress: Message count is ${count}`);
const view = window.owsDesktopApp.appView; // const view = window.owsDesktopApp.appView;
if (view) { // if (view) {
view.onProgress(count); // view.onProgress(count);
} // }
} }
function onConfiguration(ev) { function onConfiguration(ev) {
const { configuration } = ev; const { configuration } = ev;

@ -1,4 +1,4 @@
/* global Whisper, Backbone, textsecure, libsignal, log */ /* global Whisper, textsecure, libsignal, log */
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
@ -9,33 +9,7 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
const conversations = new Whisper.ConversationCollection(); const conversations = new Whisper.ConversationCollection();
const inboxCollection = new (Backbone.Collection.extend({
initialize() {
this.listenTo(conversations, 'add change:active_at', this.addActive);
this.listenTo(conversations, 'reset', () => this.reset([]));
this.listenTo(conversations, 'remove', this.remove);
this.startPruning();
},
addActive(model) {
if (model.get('active_at')) {
this.add(model);
model.updateLastMessage();
} else {
this.remove(model);
}
},
startPruning() {
const halfHour = 30 * 60 * 1000;
this.interval = setInterval(() => {
this.forEach(conversation => {
conversation.trigger('prune');
});
}, halfHour);
},
}))();
window.getInboxCollection = () => inboxCollection;
window.getConversations = () => conversations; window.getConversations = () => conversations;
window.getMessagesByKey = async key => { window.getMessagesByKey = async key => {
@ -154,6 +128,15 @@
window.SnodePool.getSnodesFor(id), window.SnodePool.getSnodesFor(id),
]); ]);
} }
if (window.inboxStore) {
conversation.on('change', this.updateReduxConvoChanged);
window.inboxStore.dispatch(
window.actionsCreators.conversationAdded(
conversation.id,
conversation.getProps()
)
);
}
}); });
return conversation; return conversation;
@ -203,7 +186,13 @@
await window.Signal.Data.removeConversation(id, { await window.Signal.Data.removeConversation(id, {
Conversation: Whisper.Conversation, Conversation: Whisper.Conversation,
}); });
conversation.off('change', this.updateReduxConvoChanged);
conversations.remove(conversation); conversations.remove(conversation);
if (window.inboxStore) {
window.inboxStore.dispatch(
window.actionsCreators.conversationRemoved(conversation.id)
);
}
}, },
getOrCreateAndWait(id, type) { getOrCreateAndWait(id, type) {
return this._initialPromise.then(() => { return this._initialPromise.then(() => {
@ -237,6 +226,22 @@
this._initialPromise = Promise.resolve(); this._initialPromise = Promise.resolve();
this._initialFetchComplete = false; this._initialFetchComplete = false;
conversations.reset([]); conversations.reset([]);
if (window.inboxStore) {
conversations.forEach(convo =>
convo.off('change', this.updateReduxConvoChanged)
);
window.inboxStore.dispatch(
window.actionsCreators.removeAllConversations()
);
}
},
updateReduxConvoChanged(convo) {
if (window.inboxStore) {
window.inboxStore.dispatch(
window.actionsCreators.conversationChanged(convo.id, convo.getProps())
);
}
}, },
async load() { async load() {
window.log.info('ConversationController: starting initial fetch'); window.log.info('ConversationController: starting initial fetch');
@ -265,6 +270,10 @@
conversation.updateProfileAvatar(), conversation.updateProfileAvatar(),
]); ]);
}); });
conversations.forEach(conversation => {
// register for change event on each conversation, and forward to redux
conversation.on('change', this.updateReduxConvoChanged);
});
await Promise.all(promises); await Promise.all(promises);
// Remove any unused images // Remove any unused images

@ -598,7 +598,6 @@
isExpired: this.hasExpired, isExpired: this.hasExpired,
expirationLength, expirationLength,
expirationTimestamp, expirationTimestamp,
multiSelectMode: conversation && conversation.selectedMessages.size > 0,
isPublic: !!this.get('isPublic'), isPublic: !!this.get('isPublic'),
isRss: !!this.get('isRss'), isRss: !!this.get('isRss'),
isKickedFromGroup: isKickedFromGroup:

@ -49,7 +49,9 @@ const { SessionModal } = require('../../ts/components/session/SessionModal');
const { const {
SessionSeedModal, SessionSeedModal,
} = require('../../ts/components/session/SessionSeedModal'); } = require('../../ts/components/session/SessionSeedModal');
const { SessionInboxView} = require('../../ts/components/session/SessionInboxView') const {
SessionInboxView,
} = require('../../ts/components/session/SessionInboxView');
const { const {
SessionPasswordModal, SessionPasswordModal,
} = require('../../ts/components/session/SessionPasswordModal'); } = require('../../ts/components/session/SessionPasswordModal');

@ -123,25 +123,18 @@
return Promise.resolve(); return Promise.resolve();
}, },
onEmpty() { onEmpty() {
const view = this.inboxView; // const view = this.inboxView;
this.initialLoadComplete = true; // this.initialLoadComplete = true;
if (view) { // if (view) {
view.onEmpty(); // view.onEmpty();
} // }
}, },
onProgress(count) { onProgress(count) {
const view = this.inboxView; // const view = this.inboxView;
if (view) { // if (view) {
view.onProgress(count); // view.onProgress(count);
} // }
},
openConversation(id, messageId) {
if (id) {
this.openInbox().then(() => {
this.inboxView.openConversation(id, messageId);
});
}
}, },
showEditProfileDialog(options) { showEditProfileDialog(options) {
const dialog = new Whisper.EditProfileDialogView(options); const dialog = new Whisper.EditProfileDialogView(options);

@ -1,15 +1,11 @@
/* /*
global global
$
ConversationController, ConversationController,
extension, extension,
ConversationController ConversationController
getConversations,
getInboxCollection, getInboxCollection,
i18n, i18n,
Whisper, Whisper,
textsecure,
Signal,
*/ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
@ -18,11 +14,6 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
Whisper.AppLoadingScreen = Whisper.View.extend({
templateName: 'app-loading-screen',
className: 'app-loading-screen',
});
Whisper.InboxViewWhisper = Whisper.View.extend({ Whisper.InboxViewWhisper = Whisper.View.extend({
templateName: 'two-column', templateName: 'two-column',
className: 'inbox index', className: 'inbox index',
@ -67,35 +58,6 @@
this.setupLeftPane(); this.setupLeftPane();
}, },
startConnectionListener() {
this.interval = setInterval(() => {
const status = window.getSocketStatus();
switch (status) {
case WebSocket.CONNECTING:
break;
case WebSocket.OPEN:
clearInterval(this.interval);
// Default to connected, but lokinet is slow so we pretend empty event
this.onEmpty();
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:
// We also replicate empty here
this.onEmpty();
break;
}
}, 1000);
},
onEmpty() { onEmpty() {
const view = this.appLoadingScreen; const view = this.appLoadingScreen;
if (view) { if (view) {

@ -2,27 +2,27 @@
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function() { (function() {
'use strict'; 'use strict';
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
Whisper.InboxView = Whisper.View.extend({ Whisper.InboxView = Whisper.View.extend({
initialize() { initialize() {
this.render(); this.render();
}, },
render() { render() {
this.dialogView = new Whisper.ReactWrapperView({ this.dialogView = new Whisper.ReactWrapperView({
className: 'inbox index', className: 'inbox index',
Component: window.Signal.Components.SessionInboxView, Component: window.Signal.Components.SessionInboxView,
}); });
this.$el.append(this.dialogView.el); this.$el.append(this.dialogView.el);
return this; return this;
}, },
close() { close() {
this.remove(); this.remove();
}, },
}); });
})(); })();

@ -513,12 +513,6 @@ function setupWithImport() {
} }
} }
function setupAsStandalone() {
if (mainWindow) {
mainWindow.webContents.send('set-up-as-standalone');
}
}
let passwordWindow; let passwordWindow;
function showPasswordWindow() { function showPasswordWindow() {
if (passwordWindow) { if (passwordWindow) {
@ -868,7 +862,6 @@ function setupMenu(options) {
openSupportPage, openSupportPage,
platform, platform,
setupWithImport, setupWithImport,
setupAsStandalone,
}); });
const template = createTemplate(menuOptions, locale.messages); const template = createTemplate(menuOptions, locale.messages);
const menu = Menu.buildFromTemplate(template); const menu = Menu.buildFromTemplate(template);

@ -240,10 +240,6 @@ ipc.on('set-up-with-import', () => {
Whisper.events.trigger('setupWithImport'); Whisper.events.trigger('setupWithImport');
}); });
ipc.on('set-up-as-standalone', () => {
Whisper.events.trigger('setupAsStandalone');
});
ipc.on('get-theme-setting', () => { ipc.on('get-theme-setting', () => {
const theme = window.Events.getThemeSetting(); const theme = window.Events.getThemeSetting();
ipc.send('get-success-theme-setting', theme); ipc.send('get-success-theme-setting', theme);

@ -1,4 +1,4 @@
.inbox { .inbox {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }

@ -27,49 +27,20 @@
} }
} }
.conversation-item { .conversation-item__options-pane {
display: flex; position: absolute;
flex-direction: column;
flex-grow: 1;
height: 100%; height: 100%;
outline: none; right: 0vw;
.selection-mode {
.messages-container > *:not(.message-selected) {
animation: toShadow $session-transition-duration;
opacity: 0.25;
}
.conversation-header {
.conversation-header {
&--items-wrapper {
.session-icon {
opacity: 0;
}
user-select: none;
pointer-events: none;
opacity: 0.25;
}
}
}
}
&__options-pane {
position: absolute;
height: 100%;
right: 0vw;
transition: transform 1.5 * $session-transition-duration ease-in-out; transition: transform 1.5 * $session-transition-duration ease-in-out;
transform: translateX(100%); transform: translateX(100%);
will-change: transform; will-change: transform;
width: 25vw; width: 25vw;
&.show { &.show {
transform: none; transform: none;
transition: transform $session-transition-duration ease-in-out; transition: transform $session-transition-duration ease-in-out;
z-index: 2; z-index: 2;
}
} }
} }
@ -116,6 +87,28 @@
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-width: calc(100vw - 380px);
.selection-mode {
.messages-container > *:not(.message-selected) {
animation: toShadow $session-transition-duration;
opacity: 0.25;
}
.conversation-header {
.conversation-header {
&--items-wrapper {
.session-icon {
opacity: 0;
}
user-select: none;
pointer-events: none;
opacity: 0.25;
}
}
}
}
} }
.conversation-content { .conversation-content {
@ -173,7 +166,6 @@
flex-grow: 1; flex-grow: 1;
flex-direction: column-reverse; flex-direction: column-reverse;
position: relative; position: relative;
overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
min-width: 370px; min-width: 370px;
scrollbar-width: 4px; scrollbar-width: 4px;

@ -108,9 +108,11 @@ $session-compose-margin: 20px;
&__sections-container { &__sections-container {
height: 100vh; height: 100vh;
flex-shrink: 0; flex-shrink: 0;
width: 80px;
overflow-x: hidden;
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
overflow: auto;
@include themify($themes) { @include themify($themes) {
border-right: themed('sessionBorder'); border-right: themed('sessionBorder');
} }

@ -51,7 +51,6 @@ describe('SignalMenu', () => {
openSupportPage: null, openSupportPage: null,
platform, platform,
includeSetup, includeSetup,
setupAsStandalone: null,
setupWithImport: null, setupWithImport: null,
showAbout: null, showAbout: null,
showDebugLog: null, showDebugLog: null,

@ -168,7 +168,11 @@ export class LeftPane extends React.Component<Props> {
} }
private renderSettingSection() { private renderSettingSection() {
const { isSecondaryDevice, showSessionSettingsCategory, settingsCategory } = this.props; const {
isSecondaryDevice,
showSessionSettingsCategory,
settingsCategory,
} = this.props;
const category = settingsCategory || SessionSettingCategory.Appearance; const category = settingsCategory || SessionSettingCategory.Appearance;

@ -21,7 +21,7 @@ export type PropsData = {
type PropsHousekeeping = { type PropsHousekeeping = {
i18n: LocalizerType; i18n: LocalizerType;
openConversation: (id: string, messageId?: string) => void; openConversationExternal: (id: string, messageId?: string) => void;
}; };
type Props = PropsData & PropsHousekeeping; type Props = PropsData & PropsHousekeeping;
@ -34,7 +34,7 @@ export class SearchResults extends React.Component<Props> {
hideMessagesHeader, hideMessagesHeader,
i18n, i18n,
messages, messages,
openConversation, openConversationExternal,
searchTerm, searchTerm,
} = this.props; } = this.props;
@ -59,14 +59,14 @@ export class SearchResults extends React.Component<Props> {
<ConversationListItemWithDetails <ConversationListItemWithDetails
key={conversation.phoneNumber} key={conversation.phoneNumber}
{...conversation} {...conversation}
onClick={openConversation} onClick={openConversationExternal}
i18n={i18n} i18n={i18n}
/> />
))} ))}
</div> </div>
) : null} ) : null}
{haveContacts {haveContacts
? this.renderContacts(i18n('contactsHeader'), contacts, true) ? this.renderContacts(i18n('contactsHeader'), contacts)
: null} : null}
{haveMessages ? ( {haveMessages ? (
@ -80,7 +80,7 @@ export class SearchResults extends React.Component<Props> {
<MessageSearchResult <MessageSearchResult
key={message.id} key={message.id}
{...message} {...message}
onClick={openConversation} onClick={openConversationExternal}
i18n={i18n} i18n={i18n}
/> />
))} ))}
@ -91,10 +91,9 @@ export class SearchResults extends React.Component<Props> {
} }
private renderContacts( private renderContacts(
header: string, header: string,
items: Array<ConversationListItemPropsType>, items: Array<ConversationListItemPropsType>
contacts?: boolean
) { ) {
const { i18n, openConversation } = this.props; const { i18n, openConversationExternal } = this.props;
return ( return (
<div className="module-search-results__contacts"> <div className="module-search-results__contacts">
@ -103,7 +102,7 @@ export class SearchResults extends React.Component<Props> {
<ConversationListItemWithDetails <ConversationListItemWithDetails
key={contact.phoneNumber} key={contact.phoneNumber}
{...contact} {...contact}
onClick={openConversation} onClick={openConversationExternal}
i18n={i18n} i18n={i18n}
/> />
))} ))}

@ -127,7 +127,13 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
if (error) { if (error) {
ToastUtils.pushToastError('addContact', error); ToastUtils.pushToastError('addContact', error);
} else { } else {
window.Whisper.events.trigger('showConversation', sessionID); // tslint:disable-next-line: no-floating-promises
window.ConversationController.getOrCreateAndWait(
sessionID,
'private'
).then(() => {
this.props.openConversationExternal(sessionID);
});
} }
} }

@ -144,7 +144,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
<SearchResults <SearchResults
{...searchResults} {...searchResults}
contacts={contacts} contacts={contacts}
openConversation={openConversationExternal} openConversationExternal={openConversationExternal}
i18n={window.i18n} i18n={window.i18n}
/> />
); );
@ -378,7 +378,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
this.setState({ valuePasted: value }); this.setState({ valuePasted: value });
} }
private handleMessageButtonClick() { private async handleMessageButtonClick() {
const { openConversationExternal } = this.props; const { openConversationExternal } = this.props;
if (!this.state.valuePasted && !this.props.searchTerm) { if (!this.state.valuePasted && !this.props.searchTerm) {
@ -394,6 +394,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
const error = validateNumber(pubkey); const error = validateNumber(pubkey);
if (!error) { if (!error) {
await window.ConversationController.getOrCreateAndWait(pubkey, 'private');
openConversationExternal(pubkey); openConversationExternal(pubkey);
} else { } else {
ToastUtils.pushToastError('invalidPubKey', error); ToastUtils.pushToastError('invalidPubKey', error);

@ -58,7 +58,7 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
} }
public renderRow(item: any): JSX.Element { public renderRow(item: any): JSX.Element {
const {settingsCategory} = this.props; const { settingsCategory } = this.props;
return ( return (
<div <div
key={item.id} key={item.id}

@ -6,14 +6,16 @@ import { createStore } from '../../state/createStore';
import { StateType } from '../../state/reducer'; import { StateType } from '../../state/reducer';
import { SmartLeftPane } from '../../state/smart/LeftPane'; import { SmartLeftPane } from '../../state/smart/LeftPane';
import { SmartSessionConversation } from '../../state/smart/SessionConversation'; import { SmartSessionConversation } from '../../state/smart/SessionConversation';
import { SessionSettingCategory, SettingsView } from './settings/SessionSettings'; import {
SessionSettingCategory,
SettingsView,
} from './settings/SessionSettings';
// Workaround: A react component's required properties are filtering up through connect() // Workaround: A react component's required properties are filtering up through connect()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
const FilteredLeftPane = SmartLeftPane as any; const FilteredLeftPane = SmartLeftPane as any;
const FilteredSessionConversation = SmartSessionConversation as any; const FilteredSessionConversation = SmartSessionConversation as any;
type Props = { type Props = {
focusedSection: number; focusedSection: number;
}; };
@ -21,38 +23,44 @@ type Props = {
type State = { type State = {
isInitialLoadComplete: boolean; isInitialLoadComplete: boolean;
settingsCategory?: SessionSettingCategory; settingsCategory?: SessionSettingCategory;
networkError: boolean;
}; };
// tslint:disable: react-a11y-img-has-alt // tslint:disable: react-a11y-img-has-alt
export class SessionInboxView extends React.Component<Props, State> { export class SessionInboxView extends React.Component<Props, State> {
private store: any; private store: any;
private interval: NodeJS.Timeout | null = null;
constructor(props: any) {
super(props); constructor(props: any) {
this.state = { super(props);
isInitialLoadComplete: false, this.state = {
settingsCategory: undefined, isInitialLoadComplete: false,
}; settingsCategory: undefined,
networkError: false,
// Inbox };
const inboxCollection = window.getInboxCollection();
this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind( const conversationModels = window.getConversations();
this this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind(
); this
this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this); );
this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this); this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this);
this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind(this); this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this);
this.showSessionViewConversation = this.showSessionViewConversation.bind(this); this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind(
this
void this.setupLeftPane(); );
this.showSessionViewConversation = this.showSessionViewConversation.bind(
// ConversationCollection this
);
void this.setupLeftPane();
// ConversationCollection
conversationModels;
// this.listenTo(inboxCollection, 'messageError', () => { // this.listenTo(inboxCollection, 'messageError', () => {
// if (this.networkStatusView) { // if (this.networkStatusView) {
// this.networkStatusView.render(); // this.networkStatusView.render();
// } // }
// }); // });
// this.networkStatusView = new Whisper.NetworkStatusView(); // this.networkStatusView = new Whisper.NetworkStatusView();
@ -67,24 +75,26 @@ export class SessionInboxView extends React.Component<Props, State> {
// this.$el.addClass('expired'); // this.$el.addClass('expired');
// } // }
// }); // });
} }
public render() { public render() {
if (!this.state.isInitialLoadComplete) { if (!this.state.isInitialLoadComplete) {
return <></>; return <></>;
} }
const isSettingsView = this.state.settingsCategory !== undefined; const isSettingsView = this.state.settingsCategory !== undefined;
return ( return (
<Provider store={this.store}> <Provider store={this.store}>
<div className="gutter"> <div className="gutter">
<div className="network-status-container"/> <div className="network-status-container" />
{this.renderLeftPane()} {this.renderLeftPane()}
</div> </div>
{isSettingsView ? this.renderSettings() : this.renderSessionConversation()} {isSettingsView
</Provider> ? this.renderSettings()
); } : this.renderSessionConversation()}
</Provider>
);
}
private renderLeftPane() { private renderLeftPane() {
return ( return (
@ -97,14 +107,14 @@ export class SessionInboxView extends React.Component<Props, State> {
} }
private renderSettings() { private renderSettings() {
const isSecondaryDevice = !!window.textsecure.storage.get('isSecondaryDevice'); const isSecondaryDevice = !!window.textsecure.storage.get(
const category = this.state.settingsCategory || SessionSettingCategory.Appearance; 'isSecondaryDevice'
);
const category =
this.state.settingsCategory || SessionSettingCategory.Appearance;
return ( return (
<SettingsView <SettingsView isSecondaryDevice={isSecondaryDevice} category={category} />
isSecondaryDevice={isSecondaryDevice}
category={category}
/>
); );
} }
@ -114,154 +124,134 @@ export class SessionInboxView extends React.Component<Props, State> {
<FilteredSessionConversation /> <FilteredSessionConversation />
</div> </div>
); );
} }
private async fetchHandleMessageSentData(m: any) {
// nobody is listening to this freshly fetched message .trigger calls
const tmpMsg = await window.Signal.Data.getMessageById(m.identifier, {
Message: window.Whisper.Message,
});
if (!tmpMsg) {
return null;
}
// find the corresponding conversation of this message
const conv = window.ConversationController.get(
tmpMsg.get('conversationId')
);
if (!conv) {
return null;
}
// then, find in this conversation the very same message
// const msg = conv.messageCollection.models.find(
// convMsg => convMsg.id === tmpMsg.id
// );
const msg = window.MessageController._get()[m.identifier];
private async fetchHandleMessageSentData(m: any) { if (!msg || !msg.message) {
// nobody is listening to this freshly fetched message .trigger calls return null;
const tmpMsg = await window.Signal.Data.getMessageById(m.identifier, { }
Message: window.Whisper.Message,
}); return { msg: msg.message };
}
if (!tmpMsg) {
return null; private async handleMessageSentSuccess(
} sentMessage: any,
wrappedEnvelope: any
// find the corresponding conversation of this message ) {
const conv = window.ConversationController.get( const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
tmpMsg.get('conversationId') if (!fetchedData) {
); return;
}
if (!conv) { const { msg } = fetchedData;
return null;
} msg.handleMessageSentSuccess(sentMessage, wrappedEnvelope);
}
// then, find in this conversation the very same message
// const msg = conv.messageCollection.models.find( private async handleMessageSentFailure(sentMessage: any, error: any) {
// convMsg => convMsg.id === tmpMsg.id const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
// ); if (!fetchedData) {
const msg = window.MessageController._get()[m.identifier]; return;
}
if (!msg || !msg.message) { const { msg } = fetchedData;
return null;
} await msg.handleMessageSentFailure(sentMessage, error);
}
return { msg: msg.message };
} private async setupLeftPane() {
// Here we set up a full redux store with initial state for our LeftPane Root
private async handleMessageSentSuccess(sentMessage: any, wrappedEnvelope: any) { const convoCollection = window.getConversations();
const fetchedData = await this.fetchHandleMessageSentData(sentMessage); const conversations = convoCollection.map(
if (!fetchedData) { (conversation: any) => conversation.cachedProps
return; );
}
const { msg } = fetchedData; const filledConversations = conversations.map(async (conv: any) => {
const messages = await window.getMessagesByKey(conv.id);
msg.handleMessageSentSuccess(sentMessage, wrappedEnvelope); return { ...conv, messages };
} });
private async handleMessageSentFailure(sentMessage: any, error: any) { const fullFilledConversations = await Promise.all(filledConversations);
const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
if (!fetchedData) { const initialState = {
return; conversations: {
} conversationLookup: window.Signal.Util.makeLookup(
const { msg } = fetchedData; fullFilledConversations,
'id'
await msg.handleMessageSentFailure(sentMessage, error); ),
} },
user: {
private async setupLeftPane() { regionCode: window.storage.get('regionCode'),
// Here we set up a full redux store with initial state for our LeftPane Root ourNumber:
const convoCollection = window.getConversations(); window.storage.get('primaryDevicePubKey') ||
const conversations = convoCollection.map( window.textsecure.storage.user.getNumber(),
(conversation: any) => conversation.cachedProps isSecondaryDevice: !!window.storage.get('isSecondaryDevice'),
); i18n: window.i18n,
},
const filledConversations = conversations.map(async (conv: any) => { section: {
const messages = await window.getMessagesByKey(conv.id); focusedSection: 1,
return { ...conv, messages }; },
}); };
const fullFilledConversations = await Promise.all(filledConversations); this.store = createStore(initialState);
window.inboxStore = this.store;
const initialState = {
conversations: { // Enables our redux store to be updated by backbone events in the outside world
conversationLookup: window.Signal.Util.makeLookup( const { messageExpired } = bindActionCreators(
fullFilledConversations, window.Signal.State.Ducks.conversations.actions,
'id' this.store.dispatch
), );
}, window.actionsCreators = window.Signal.State.Ducks.conversations.actions;
user: { const { userChanged } = bindActionCreators(
regionCode: window.storage.get('regionCode'), window.Signal.State.Ducks.user.actions,
ourNumber: this.store.dispatch
window.storage.get('primaryDevicePubKey') || );
window.textsecure.storage.user.getNumber(), const { messageChanged } = bindActionCreators(
isSecondaryDevice: !!window.storage.get('isSecondaryDevice'), window.Signal.State.Ducks.messages.actions,
i18n: window.i18n, this.store.dispatch
}, );
section: {
focusedSection: 1, this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind(
}, this
}; );
this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this);
this.store = createStore(initialState); this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this);
window.inboxStore = this.store;
getMessageQueue().events.addListener(
// Enables our redux store to be updated by backbone events in the outside world 'success',
const { this.handleMessageSentSuccess
conversationAdded, );
conversationChanged,
conversationRemoved, getMessageQueue().events.addListener('fail', this.handleMessageSentFailure);
removeAllConversations,
messageExpired, window.Whisper.events.on('messageExpired', messageExpired);
openConversationExternal, window.Whisper.events.on('messageChanged', messageChanged);
} = bindActionCreators( window.Whisper.events.on('userChanged', userChanged);
window.Signal.State.Ducks.conversations.actions,
this.store.dispatch this.setState({ isInitialLoadComplete: true });
); }
const { userChanged } = bindActionCreators(
window.Signal.State.Ducks.user.actions,
this.store.dispatch
);
const { messageChanged } = bindActionCreators(
window.Signal.State.Ducks.messages.actions,
this.store.dispatch
);
// this.openConversationAction = openConversationExternal;
this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind(
this
);
this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this);
this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this);
// this.listenTo(convoCollection, 'remove', conversation => {
// const { id } = conversation || {};
// conversationRemoved(id);
// });
// this.listenTo(convoCollection, 'add', conversation => {
// const { id, cachedProps } = conversation || {};
// conversationAdded(id, cachedProps);
// });
// this.listenTo(convoCollection, 'change', conversation => {
// const { id, cachedProps } = conversation || {};
// conversationChanged(id, cachedProps);
// });
// this.listenTo(convoCollection, 'reset', removeAllConversations);
getMessageQueue()
.events.addListener('success', this.handleMessageSentSuccess);
getMessageQueue()
.events.addListener('fail', this.handleMessageSentFailure);
window.Whisper.events.on('messageExpired', messageExpired);
window.Whisper.events.on('messageChanged', messageChanged);
window.Whisper.events.on('userChanged', userChanged);
// Finally, add it to the DOM
// this.$('.left-pane-placeholder').append(this.leftPaneView.el);
this.setState({ isInitialLoadComplete: true });
}
private showSessionSettingsCategory(category: SessionSettingCategory) { private showSessionSettingsCategory(category: SessionSettingCategory) {
this.setState({ settingsCategory: category }); this.setState({ settingsCategory: category });
@ -270,4 +260,34 @@ export class SessionInboxView extends React.Component<Props, State> {
private showSessionViewConversation() { private showSessionViewConversation() {
this.setState({ settingsCategory: undefined }); this.setState({ settingsCategory: undefined });
} }
// private startConnectionListener() {
// this.interval = global.setInterval(() => {
// const status = window.getSocketStatus();
// switch (status) {
// case WebSocket.CONNECTING:
// break;
// case WebSocket.OPEN:
// if (this.interval) {
// clearInterval(this.interval);
// }
// // Default to connected, but lokinet is slow so we pretend empty event
// // this.onEmpty();
// this.interval = null;
// break;
// case WebSocket.CLOSING:
// case WebSocket.CLOSED:
// if (this.interval) {
// clearInterval(this.interval);
// }
// this.interval = null;
// // if we failed to connect, we pretend we got an empty event
// // this.onEmpty();
// break;
// default:
// // We also replicate empty here
// // this.onEmpty();
// }
// }, 1000);
// }
} }

@ -151,7 +151,6 @@ export class SessionConversation extends React.Component<Props, State> {
// ~~~~~~~~~~~~~~~~ LIFECYCLES ~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~ LIFECYCLES ~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public componentDidUpdate(prevProps: Props, prevState: State) { public componentDidUpdate(prevProps: Props, prevState: State) {
if (this.props.conversationKey !== prevProps.conversationKey) { if (this.props.conversationKey !== prevProps.conversationKey) {
void this.loadInitialMessages(); void this.loadInitialMessages();

@ -128,7 +128,9 @@ export async function createMediumGroup(
convo.updateGroupAdmins(admins); convo.updateGroupAdmins(admins);
window.owsDesktopApp.appView.openConversation(groupId, {}); window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(groupId)
);
// Subscribe to this group id // Subscribe to this group id
window.SwarmPolling.addGroupId(new PubKey(groupId)); window.SwarmPolling.addGroupId(new PubKey(groupId));
@ -177,7 +179,9 @@ export async function createLegacyGroup(
await sendGroupUpdate(convo, diff, groupDetails, dbMessage.id); await sendGroupUpdate(convo, diff, groupDetails, dbMessage.id);
window.textsecure.messaging.sendGroupSyncMessage([convo]); window.textsecure.messaging.sendGroupSyncMessage([convo]);
window.owsDesktopApp.appView.openConversation(groupId, {}); window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(groupId)
);
} }
export async function leaveMediumGroup(groupId: string) { export async function leaveMediumGroup(groupId: string) {

@ -241,7 +241,6 @@ export function reducer(
selectedConversation = undefined; selectedConversation = undefined;
} }
} }
return { return {
...state, ...state,
selectedConversation, selectedConversation,
@ -255,7 +254,6 @@ export function reducer(
const { payload } = action; const { payload } = action;
const { id } = payload; const { id } = payload;
const { conversationLookup } = state; const { conversationLookup } = state;
return { return {
...state, ...state,
conversationLookup: omit(conversationLookup, [id]), conversationLookup: omit(conversationLookup, [id]),
@ -270,15 +268,10 @@ export function reducer(
if (action.type === 'SELECTED_CONVERSATION_CHANGED') { if (action.type === 'SELECTED_CONVERSATION_CHANGED') {
const { payload } = action; const { payload } = action;
const { id } = payload; const { id } = payload;
if (state.selectedConversation !== id) {
window.owsDesktopApp.appView.openConversation(id, {});
}
return { return {
...state, ...state,
selectedConversation: id, selectedConversation: id,
}; };
} }
return state; return state;
} }

@ -75,7 +75,6 @@ export const actions = {
search, search,
clearSearch, clearSearch,
updateSearchTerm, updateSearchTerm,
startNewConversation,
}; };
function search( function search(
@ -143,22 +142,6 @@ function updateSearchTerm(query: string): UpdateSearchTermActionType {
}, },
}; };
} }
function startNewConversation(
query: string,
options: { regionCode: string }
): ClearSearchActionType {
const { regionCode } = options;
const normalized = normalize(query, { regionCode });
if (!normalized) {
throw new Error('Attempted to start new conversation with invalid number');
}
trigger('showConversation', normalized);
return {
type: 'SEARCH_CLEAR',
payload: null,
};
}
// Helper functions for search // Helper functions for search

@ -40,10 +40,8 @@ function getConversationTitle(
if (conversation.type === 'group') { if (conversation.type === 'group') {
const { i18n } = options; const { i18n } = options;
return i18n('unknown'); return i18n('unknown');
} }
return format(conversation.phoneNumber, options); return format(conversation.phoneNumber, options);
} }
@ -65,7 +63,6 @@ export const _getConversationComparator = (
if (leftTimestamp && rightTimestamp && leftTimestamp !== rightTimestamp) { if (leftTimestamp && rightTimestamp && leftTimestamp !== rightTimestamp) {
return rightTimestamp - leftTimestamp; return rightTimestamp - leftTimestamp;
} }
const leftTitle = getConversationTitle(left, { const leftTitle = getConversationTitle(left, {
i18n, i18n,
ourRegionCode, ourRegionCode,

4
ts/window.d.ts vendored

@ -102,7 +102,6 @@ declare global {
GroupBuffer: any; GroupBuffer: any;
SwarmPolling: SwarmPolling; SwarmPolling: SwarmPolling;
MediaRecorder: any; MediaRecorder: any;
owsDesktopApp: any;
loadImage: any; loadImage: any;
dataURLToBlobSync: any; dataURLToBlobSync: any;
autoOrientImage: any; autoOrientImage: any;
@ -112,8 +111,9 @@ declare global {
) => Promise<{ pubKey: ArrayBufferLike; privKey: ArrayBufferLike }>; ) => Promise<{ pubKey: ArrayBufferLike; privKey: ArrayBufferLike }>;
setClockParams: any; setClockParams: any;
clientClockSynced: number | undefined; clientClockSynced: number | undefined;
getInboxCollection: any;
getMessagesByKey: any; getMessagesByKey: any;
inboxStore: Store; inboxStore: Store;
getSocketStatus: any;
actionsCreators: any;
} }
} }

Loading…
Cancel
Save