@@ -68,12 +58,13 @@ export class LeftPaneSettingSection extends React.Component
{
}
public renderRow(item: any): JSX.Element {
+ const {settingsCategory} = this.props;
return (
{
@@ -85,7 +76,7 @@ export class LeftPaneSettingSection extends React.Component
{
- {item.id === this.state.settingCategory && (
+ {item.id === settingsCategory && (
{
}
public setCategory(category: SessionSettingCategory) {
- this.setState({
- settingCategory: category,
- });
+ this.props.showSessionSettingsCategory(category);
}
}
diff --git a/ts/components/session/SessionInboxView.tsx b/ts/components/session/SessionInboxView.tsx
new file mode 100644
index 000000000..9b07574f8
--- /dev/null
+++ b/ts/components/session/SessionInboxView.tsx
@@ -0,0 +1,273 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import { getMessageQueue } from '../../session';
+import { createStore } from '../../state/createStore';
+import { StateType } from '../../state/reducer';
+import { SmartLeftPane } from '../../state/smart/LeftPane';
+import { SmartSessionConversation } from '../../state/smart/SessionConversation';
+import { SessionSettingCategory, SettingsView } from './settings/SessionSettings';
+
+// Workaround: A react component's required properties are filtering up through connect()
+// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
+const FilteredLeftPane = SmartLeftPane as any;
+const FilteredSessionConversation = SmartSessionConversation as any;
+
+
+type Props = {
+ focusedSection: number;
+};
+
+type State = {
+ isInitialLoadComplete: boolean;
+ settingsCategory?: SessionSettingCategory;
+};
+
+// tslint:disable: react-a11y-img-has-alt
+
+
+export class SessionInboxView extends React.Component {
+ private store: any;
+
+ constructor(props: any) {
+ super(props);
+ this.state = {
+ isInitialLoadComplete: false,
+ settingsCategory: undefined,
+ };
+
+ // Inbox
+ const inboxCollection = window.getInboxCollection();
+ this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind(
+ this
+ );
+ this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this);
+ this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this);
+ this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind(this);
+ this.showSessionViewConversation = this.showSessionViewConversation.bind(this);
+
+ void this.setupLeftPane();
+
+ // ConversationCollection
+ // this.listenTo(inboxCollection, 'messageError', () => {
+ // if (this.networkStatusView) {
+ // this.networkStatusView.render();
+ // }
+ // });
+
+ // this.networkStatusView = new Whisper.NetworkStatusView();
+ // this.$el
+ // .find('.network-status-container')
+ // .append(this.networkStatusView.render().el);
+
+ // extension.expired(expired => {
+ // if (expired) {
+ // const banner = new Whisper.ExpiredAlertBanner().render();
+ // banner.$el.prependTo(this.$el);
+ // this.$el.addClass('expired');
+ // }
+ // });
+ }
+
+
+ public render() {
+ if (!this.state.isInitialLoadComplete) {
+ return <>>;
+ }
+
+ const isSettingsView = this.state.settingsCategory !== undefined;
+ return (
+
+
+
+ {this.renderLeftPane()}
+
+ {isSettingsView ? this.renderSettings() : this.renderSessionConversation()}
+
+ ); }
+
+ private renderLeftPane() {
+ return (
+
+ );
+ }
+
+ private renderSettings() {
+ const isSecondaryDevice = !!window.textsecure.storage.get('isSecondaryDevice');
+ const category = this.state.settingsCategory || SessionSettingCategory.Appearance;
+
+ return (
+
+ );
+ }
+
+ private renderSessionConversation() {
+ return (
+
+
+
+ );
+}
+
+ 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];
+
+ if (!msg || !msg.message) {
+ return null;
+ }
+
+ return { msg: msg.message };
+ }
+
+ private async handleMessageSentSuccess(sentMessage: any, wrappedEnvelope: any) {
+ const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
+ if (!fetchedData) {
+ return;
+ }
+ const { msg } = fetchedData;
+
+ msg.handleMessageSentSuccess(sentMessage, wrappedEnvelope);
+ }
+
+ private async handleMessageSentFailure(sentMessage: any, error: any) {
+ const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
+ if (!fetchedData) {
+ return;
+ }
+ const { msg } = fetchedData;
+
+ await msg.handleMessageSentFailure(sentMessage, error);
+ }
+
+ private async setupLeftPane() {
+ // Here we set up a full redux store with initial state for our LeftPane Root
+ const convoCollection = window.getConversations();
+ const conversations = convoCollection.map(
+ (conversation: any) => conversation.cachedProps
+ );
+
+ const filledConversations = conversations.map(async (conv: any) => {
+ const messages = await window.getMessagesByKey(conv.id);
+ return { ...conv, messages };
+ });
+
+ const fullFilledConversations = await Promise.all(filledConversations);
+
+ const initialState = {
+ conversations: {
+ conversationLookup: window.Signal.Util.makeLookup(
+ fullFilledConversations,
+ 'id'
+ ),
+ },
+ user: {
+ regionCode: window.storage.get('regionCode'),
+ ourNumber:
+ window.storage.get('primaryDevicePubKey') ||
+ window.textsecure.storage.user.getNumber(),
+ isSecondaryDevice: !!window.storage.get('isSecondaryDevice'),
+ i18n: window.i18n,
+ },
+ section: {
+ focusedSection: 1,
+ },
+ };
+
+ this.store = createStore(initialState);
+ window.inboxStore = this.store;
+
+ // Enables our redux store to be updated by backbone events in the outside world
+ const {
+ conversationAdded,
+ conversationChanged,
+ conversationRemoved,
+ removeAllConversations,
+ messageExpired,
+ openConversationExternal,
+ } = bindActionCreators(
+ window.Signal.State.Ducks.conversations.actions,
+ this.store.dispatch
+ );
+ 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) {
+ this.setState({ settingsCategory: category });
+ }
+
+ private showSessionViewConversation() {
+ this.setState({ settingsCategory: undefined });
+ }
+}
diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx
index 545f43b22..d7b25f9bf 100644
--- a/ts/components/session/conversation/SessionConversation.tsx
+++ b/ts/components/session/conversation/SessionConversation.tsx
@@ -83,11 +83,11 @@ export class SessionConversation extends React.Component {
const { conversationKey } = this.props;
- const conversationModel = window.ConversationController.getOrThrow(
+ const conversationModel = window.ConversationController.get(
conversationKey
);
- const unreadCount = conversationModel.get('unreadCount');
+ const unreadCount = conversationModel?.get('unreadCount') || 0;
this.state = {
messageProgressVisible: false,
sendingProgress: 0,
@@ -151,8 +151,11 @@ export class SessionConversation extends React.Component {
// ~~~~~~~~~~~~~~~~ LIFECYCLES ~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- public async componentWillMount() {
- await this.loadInitialMessages();
+
+ public componentDidUpdate(prevProps: Props, prevState: State) {
+ if (this.props.conversationKey !== prevProps.conversationKey) {
+ void this.loadInitialMessages();
+ }
}
public componentWillUnmount() {
@@ -304,15 +307,17 @@ export class SessionConversation extends React.Component {
public async loadInitialMessages() {
const { conversationKey } = this.props;
- const conversationModel = window.ConversationController.getOrThrow(
+ const conversationModel = window.ConversationController.get(
conversationKey
);
+ if (!conversationModel) {
+ return;
+ }
const unreadCount = await conversationModel.getUnreadCount();
const messagesToFetch = Math.max(
Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT,
unreadCount
);
-
this.props.actions.fetchMessagesForConversation({
conversationKey,
count: messagesToFetch,
diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx
index 896cdd2cb..f2a153d8a 100644
--- a/ts/components/session/settings/SessionSettings.tsx
+++ b/ts/components/session/settings/SessionSettings.tsx
@@ -10,8 +10,6 @@ import {
import { BlockedNumberController, UserUtil } from '../../../util';
import { MultiDeviceProtocol } from '../../../session/protocols';
import { PubKey } from '../../../session/types';
-import { SessionToast, SessionToastType } from '../SessionToast';
-import { toast } from 'react-toastify';
import { ToastUtils } from '../../../session/utils';
export enum SessionSettingCategory {
diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts
index d2670b4d3..0380ab7e1 100644
--- a/ts/state/ducks/conversations.ts
+++ b/ts/state/ducks/conversations.ts
@@ -1,7 +1,6 @@
import { omit } from 'lodash';
import { trigger } from '../../shims/events';
-import { NoopActionType } from './noop';
// State
@@ -127,7 +126,6 @@ export const actions = {
conversationRemoved,
removeAllConversations,
messageExpired,
- openConversationInternal,
openConversationExternal,
};
@@ -183,20 +181,6 @@ function messageExpired(
};
}
-// Note: we need two actions here to simplify. Operations outside of the left pane can
-// trigger an 'openConversation' so we go through Whisper.events for all conversation
-// selection.
-function openConversationInternal(
- id: string,
- messageId?: string
-): NoopActionType {
- trigger('showConversation', id, messageId);
-
- return {
- type: 'NOOP',
- payload: null,
- };
-}
function openConversationExternal(
id: string,
messageId?: string
@@ -286,6 +270,9 @@ export function reducer(
if (action.type === 'SELECTED_CONVERSATION_CHANGED') {
const { payload } = action;
const { id } = payload;
+ if (state.selectedConversation !== id) {
+ window.owsDesktopApp.appView.openConversation(id, {});
+ }
return {
...state,
diff --git a/ts/state/ducks/noop.ts b/ts/state/ducks/noop.ts
deleted file mode 100644
index 8126c425e..000000000
--- a/ts/state/ducks/noop.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export type NoopActionType = {
- type: 'NOOP';
- payload: null;
-};
diff --git a/ts/state/roots/createLeftPane.tsx b/ts/state/roots/createLeftPane.tsx
deleted file mode 100644
index 608e13a0b..000000000
--- a/ts/state/roots/createLeftPane.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-
-import { Store } from 'redux';
-
-import { SmartLeftPane } from '../smart/LeftPane';
-
-// Workaround: A react component's required properties are filtering up through connect()
-// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
-const FilteredLeftPane = SmartLeftPane as any;
-
-export const createLeftPane = (store: Store) => (
-
-
-
-);
diff --git a/ts/state/roots/createSessionConversation.tsx b/ts/state/roots/createSessionConversation.tsx
deleted file mode 100644
index cd27dc370..000000000
--- a/ts/state/roots/createSessionConversation.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-
-import { Store } from 'redux';
-
-import { SmartSessionConversation } from '../smart/SessionConversation';
-
-// Workaround: A react component's required properties are filtering up through connect()
-// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
-const FilteredSessionConversation = SmartSessionConversation as any;
-
-export const createSessionConversation = (store: Store) => (
-
-
-
-);
diff --git a/ts/window.d.ts b/ts/window.d.ts
index 97b78d4c0..e06e985ef 100644
--- a/ts/window.d.ts
+++ b/ts/window.d.ts
@@ -16,6 +16,7 @@ import {} from 'styled-components/cssprop';
import { ConversationControllerType } from '../js/ConversationController';
import { any } from 'underscore';
+import { Store } from 'redux';
/*
We declare window stuff here instead of global.d.ts because we are importing other declarations.
If you import anything in global.d.ts, the type system won't work correctly.
@@ -95,7 +96,7 @@ declare global {
versionInfo: any;
getStoragePubKey: any;
pubkeyPattern: any;
- getConversations: any;
+ getConversations: () => ConversationCollection;
getGuid: any;
ContactBuffer: any;
GroupBuffer: any;
@@ -111,5 +112,8 @@ declare global {
) => Promise<{ pubKey: ArrayBufferLike; privKey: ArrayBufferLike }>;
setClockParams: any;
clientClockSynced: number | undefined;
+ getInboxCollection: any;
+ getMessagesByKey: any;
+ inboxStore: Store;
}
}