
diff --git a/src/components/views/right_panel/HeaderButtons.js b/src/components/views/right_panel/HeaderButtons.js
index f0479eb8be..3f5f58121d 100644
--- a/src/components/views/right_panel/HeaderButtons.js
+++ b/src/components/views/right_panel/HeaderButtons.js
@@ -78,7 +78,6 @@ export default class HeaderButtons extends React.Component {
// till show_right_panel, just without the fromHeader flag
// as that would hide the right panel again
dis.dispatch(Object.assign({}, payload, {fromHeader: false}));
-
}
this.setState({
phase: payload.phase,
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index 12dc2117a0..226adb910f 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -39,7 +39,6 @@ import Unread from '../../../Unread';
import { findReadReceiptFromUserId } from '../../../utils/Receipt';
import withMatrixClient from '../../../wrappers/withMatrixClient';
import AccessibleButton from '../elements/AccessibleButton';
-import RoomViewStore from '../../../stores/RoomViewStore';
import SdkConfig from '../../../SdkConfig';
import MultiInviter from "../../../utils/MultiInviter";
import SettingsStore from "../../../settings/SettingsStore";
@@ -50,6 +49,7 @@ module.exports = withMatrixClient(React.createClass({
propTypes: {
matrixClient: PropTypes.object.isRequired,
member: PropTypes.object.isRequired,
+ roomId: PropTypes.string,
},
getInitialState: function() {
@@ -713,7 +713,7 @@ module.exports = withMatrixClient(React.createClass({
}
if (!member || !member.membership || member.membership === 'leave') {
- const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId();
+ const roomId = member && member.roomId ? member.roomId : this.props.roomId;
const onInviteUserButton = async() => {
try {
// We use a MultiInviter to re-use the invite logic, even though
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index d4b607a93a..e15ca047ac 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -22,7 +22,6 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
import dis from '../../../dispatcher';
-import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../matrix-to';
@@ -63,7 +62,7 @@ export default class MessageComposer extends React.Component {
isRichTextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
},
showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
- isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
+ isQuoting: Boolean(this.props.roomViewStore.getQuotingEvent()),
tombstone: this._getRoomTombstone(),
};
}
@@ -75,7 +74,7 @@ export default class MessageComposer extends React.Component {
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
MatrixClientPeg.get().on("event", this.onEvent);
MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
- this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
+ this._roomStoreToken = this.props.roomViewStore.addListener(this._onRoomViewStoreUpdate);
this._waitForOwnMember();
}
@@ -124,14 +123,14 @@ export default class MessageComposer extends React.Component {
}
_onRoomViewStoreUpdate() {
- const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
+ const isQuoting = Boolean(this.props.roomViewStore.getQuotingEvent());
if (this.state.isQuoting === isQuoting) return;
this.setState({ isQuoting });
}
onUploadClick(ev) {
if (MatrixClientPeg.get().isGuest()) {
- dis.dispatch({action: 'require_registration'});
+ this.props.roomViewStore.getDispatcher().dispatch({action: 'require_registration'});
return;
}
@@ -165,7 +164,7 @@ export default class MessageComposer extends React.Component {
}
}
- const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
+ const isQuoting = Boolean(this.props.roomViewStore.getQuotingEvent());
let replyToWarning = null;
if (isQuoting) {
replyToWarning =
{
@@ -229,7 +228,7 @@ export default class MessageComposer extends React.Component {
if (!call) {
return;
}
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'hangup',
// hangup the call for this room, which may not be the room in props
// (e.g. conferences which will hangup the 1:1 room instead)
@@ -238,7 +237,7 @@ export default class MessageComposer extends React.Component {
}
onCallClick(ev) {
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'place_call',
type: ev.shiftKey ? "screensharing" : "video",
room_id: this.props.room.roomId,
@@ -246,7 +245,7 @@ export default class MessageComposer extends React.Component {
}
onVoiceCallClick(ev) {
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'place_call',
type: "voice",
room_id: this.props.room.roomId,
@@ -282,7 +281,7 @@ export default class MessageComposer extends React.Component {
ev.preventDefault();
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'view_room',
highlighted: true,
room_id: replacementRoomId,
@@ -421,8 +420,10 @@ export default class MessageComposer extends React.Component {
controls.push(
this.messageComposerInput = c}
key="controls_input"
+ isGrid={this.props.isGrid}
onResize={this.props.onResize}
room={this.props.room}
placeholder={placeholderText}
@@ -529,5 +530,6 @@ MessageComposer.propTypes = {
uploadAllowed: PropTypes.func.isRequired,
// string representing the current room app drawer state
- showApps: PropTypes.bool
+ showApps: PropTypes.bool,
+ roomViewStore: PropTypes.object.isRequired,
};
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index 14d394ab41..4e7b4d3bbf 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -41,8 +41,6 @@ import sdk from '../../../index';
import { _t, _td } from '../../../languageHandler';
import Analytics from '../../../Analytics';
-import dis from '../../../dispatcher';
-
import * as RichText from '../../../RichText';
import * as HtmlUtils from '../../../HtmlUtils';
import Autocomplete from './Autocomplete';
@@ -58,7 +56,6 @@ import {asciiRegexp, unicodeRegexp, shortnameToUnicode, emojioneList, asciiList,
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import {makeUserPermalink} from "../../../matrix-to";
import ReplyPreview from "./ReplyPreview";
-import RoomViewStore from '../../../stores/RoomViewStore';
import ReplyThread from "../elements/ReplyThread";
import {ContentHelpers} from 'matrix-js-sdk';
@@ -121,7 +118,7 @@ function onSendMessageFailed(err, room) {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('MessageComposer got send failure: ' + err.name + '('+err+')');
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'message_send_failed',
});
}
@@ -135,6 +132,18 @@ function rangeEquals(a: Range, b: Range): boolean {
&& a.isBackward === b.isBackward);
}
+class NoopHistoryManager {
+ getItem() {}
+ save() {}
+
+ get currentIndex() { return 0; }
+ set currentIndex(_) {}
+
+ get history() { return []; }
+ set history(_) {}
+}
+
+
/*
* The textInput part of the MessageComposer
*/
@@ -150,6 +159,7 @@ export default class MessageComposerInput extends React.Component {
onFilesPasted: PropTypes.func,
onInputStateChanged: PropTypes.func,
+ roomViewStore: PropTypes.object.isRequired,
};
client: MatrixClient;
@@ -344,12 +354,16 @@ export default class MessageComposerInput extends React.Component {
}
componentWillMount() {
- this.dispatcherRef = dis.register(this.onAction);
- this.historyManager = new ComposerHistoryManager(this.props.room.roomId, 'mx_slate_composer_history_');
+ this.dispatcherRef = this.props.roomViewStore.getDispatcher().register(this.onAction);
+ if (this.props.isGrid) {
+ this.historyManager = new NoopHistoryManager();
+ } else {
+ this.historyManager = new ComposerHistoryManager(this.props.room.roomId, 'mx_slate_composer_history_');
+ }
}
componentWillUnmount() {
- dis.unregister(this.dispatcherRef);
+ this.props.roomViewStore.getDispatcher().unregister(this.dispatcherRef);
}
_collectEditor = (e) => {
@@ -1120,7 +1134,7 @@ export default class MessageComposerInput extends React.Component {
return true;
}
- const replyingToEv = RoomViewStore.getQuotingEvent();
+ const replyingToEv = this.props.roomViewStore.getQuotingEvent();
const mustSendHTML = Boolean(replyingToEv);
if (this.state.isRichTextEnabled) {
@@ -1208,14 +1222,14 @@ export default class MessageComposerInput extends React.Component {
// Clear reply_to_event as we put the message into the queue
// if the send fails, retry will handle resending.
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'reply_to_event',
event: null,
});
}
this.client.sendMessage(this.props.room.roomId, content).then((res) => {
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'message_sent',
});
}).catch((e) => {
@@ -1589,7 +1603,7 @@ export default class MessageComposerInput extends React.Component {
return (
-
+
this.autocomplete = e}
room={this.props.room}
diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js
index 46e2826634..04ff9d0778 100644
--- a/src/components/views/rooms/ReplyPreview.js
+++ b/src/components/views/rooms/ReplyPreview.js
@@ -18,7 +18,6 @@ import React from 'react';
import dis from '../../../dispatcher';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
-import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore from "../../../settings/SettingsStore";
function cancelQuoting() {
@@ -38,7 +37,7 @@ export default class ReplyPreview extends React.Component {
this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
- this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
+ this._roomStoreToken = this.props.roomViewStore.addListener(this._onRoomViewStoreUpdate);
this._onRoomViewStoreUpdate();
}
@@ -50,7 +49,7 @@ export default class ReplyPreview extends React.Component {
}
_onRoomViewStoreUpdate() {
- const event = RoomViewStore.getQuotingEvent();
+ const event = this.props.roomViewStore.getQuotingEvent();
if (this.state.event !== event) {
this.setState({ event });
}
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 4292fa6a4d..91ca73dd59 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -24,6 +24,7 @@ import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from "../../../Modal";
import RateLimitedFunc from '../../../ratelimitedfunc';
+import dis from '../../../dispatcher';
import * as linkify from 'linkifyjs';
import linkifyElement from 'linkifyjs/element';
@@ -152,6 +153,14 @@ module.exports = React.createClass({
});
},
+ onToggleRightPanelClick: function(ev) {
+ if (this.props.collapsedRhs) {
+ dis.dispatch({action: "show_right_panel"});
+ } else {
+ dis.dispatch({action: "hide_right_panel"});
+ }
+ },
+
_hasUnreadPins: function() {
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
if (!currentPinEvent) return false;
@@ -409,6 +418,17 @@ module.exports = React.createClass({
;
}
+ let toggleRightPanelButton;
+ if (this.props.isGrid) {
+ toggleRightPanelButton =
+
+
+ ;
+ }
+
return (
@@ -419,7 +439,8 @@ module.exports = React.createClass({
{ saveButton }
{ cancelButton }
{ rightRow }
-
+ { !this.props.isGrid ? : undefined }
+ { toggleRightPanelButton }
);
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index bce4d15f16..95073b7be8 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -29,7 +29,6 @@ import * as RoomNotifs from '../../../RoomNotifs';
import * as FormattingUtils from '../../../utils/FormattingUtils';
import AccessibleButton from '../elements/AccessibleButton';
import ActiveRoomObserver from '../../../ActiveRoomObserver';
-import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore from "../../../settings/SettingsStore";
module.exports = React.createClass({
@@ -62,7 +61,7 @@ module.exports = React.createClass({
roomName: this.props.room.name,
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
notificationCount: this.props.room.getUnreadNotificationCount(),
- selected: this.props.room.roomId === RoomViewStore.getRoomId(),
+ selected: this.props.room.roomId === ActiveRoomObserver.getActiveRoomId(),
});
},
@@ -117,9 +116,9 @@ module.exports = React.createClass({
}
},
- _onActiveRoomChange: function() {
+ _onActiveRoomChange: function(activeRoomId) {
this.setState({
- selected: this.props.room.roomId === RoomViewStore.getRoomId(),
+ selected: this.props.room.roomId === activeRoomId,
});
},
diff --git a/src/dispatcher.js b/src/dispatcher.js
index 48c8dc86e9..4dc6e1e37d 100644
--- a/src/dispatcher.js
+++ b/src/dispatcher.js
@@ -17,42 +17,10 @@ limitations under the License.
'use strict';
-const flux = require("flux");
-
-class MatrixDispatcher extends flux.Dispatcher {
- /**
- * @param {Object|function} payload Required. The payload to dispatch.
- * If an Object, must contain at least an 'action' key.
- * If a function, must have the signature (dispatch) => {...}.
- * @param {boolean=} sync Optional. Pass true to dispatch
- * synchronously. This is useful for anything triggering
- * an operation that the browser requires user interaction
- * for.
- */
- dispatch(payload, sync) {
- // Allow for asynchronous dispatching by accepting payloads that have the
- // type `function (dispatch) {...}`
- if (typeof payload === 'function') {
- payload((action) => {
- this.dispatch(action, sync);
- });
- return;
- }
-
- if (sync) {
- super.dispatch(payload);
- } else {
- // Unless the caller explicitly asked for us to dispatch synchronously,
- // we always set a timeout to do this: The flux dispatcher complains
- // if you dispatch from within a dispatch, so rather than action
- // handlers having to worry about not calling anything that might
- // then dispatch, we just do dispatches asynchronously.
- setTimeout(super.dispatch.bind(this, payload), 0);
- }
- }
-}
+import MatrixDispatcher from "./matrix-dispatcher";
if (global.mxDispatcher === undefined) {
global.mxDispatcher = new MatrixDispatcher();
}
+
module.exports = global.mxDispatcher;
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index ef1b2e9162..2b976cf4d2 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1408,5 +1408,8 @@
"Go to Settings": "Go to Settings",
"Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
- "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"
+ "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
+ "View as Grid": "View as Grid",
+ "Allow up to 6 rooms in a community to be shown simultaneously in a grid via the context menu": "Allow up to 6 rooms in a community to be shown simultaneously in a grid via the context menu",
+ "No room in this tile yet.": "No room in this tile yet."
}
diff --git a/src/matrix-dispatcher.js b/src/matrix-dispatcher.js
new file mode 100644
index 0000000000..fb81ed837f
--- /dev/null
+++ b/src/matrix-dispatcher.js
@@ -0,0 +1,53 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+'use strict';
+
+const flux = require("flux");
+
+export default class MatrixDispatcher extends flux.Dispatcher {
+ /**
+ * @param {Object|function} payload Required. The payload to dispatch.
+ * If an Object, must contain at least an 'action' key.
+ * If a function, must have the signature (dispatch) => {...}.
+ * @param {boolean=} sync Optional. Pass true to dispatch
+ * synchronously. This is useful for anything triggering
+ * an operation that the browser requires user interaction
+ * for.
+ */
+ dispatch(payload, sync) {
+ // Allow for asynchronous dispatching by accepting payloads that have the
+ // type `function (dispatch) {...}`
+ if (typeof payload === 'function') {
+ payload((action) => {
+ this.dispatch(action, sync);
+ });
+ return;
+ }
+
+ if (sync) {
+ super.dispatch(payload);
+ } else {
+ // Unless the caller explicitly asked for us to dispatch synchronously,
+ // we always set a timeout to do this: The flux dispatcher complains
+ // if you dispatch from within a dispatch, so rather than action
+ // handlers having to worry about not calling anything that might
+ // then dispatch, we just do dispatches asynchronously.
+ setTimeout(super.dispatch.bind(this, payload), 0);
+ }
+ }
+}
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index 14f4bdc6dd..8edec434bf 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -102,6 +102,12 @@ export const SETTINGS = {
supportedLevels: LEVELS_FEATURE,
default: false,
},
+ "feature_gridview": {
+ isFeature: true,
+ displayName: _td("Allow up to 6 rooms in a community to be shown simultaneously in a grid via the context menu"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ },
"MessageComposerInput.dontSuggestEmoji": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td('Disable Emoji suggestions while typing'),
diff --git a/src/stores/OpenRoomsStore.js b/src/stores/OpenRoomsStore.js
new file mode 100644
index 0000000000..21f02fe28d
--- /dev/null
+++ b/src/stores/OpenRoomsStore.js
@@ -0,0 +1,277 @@
+/*
+Copyright 2018 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import MatrixDispatcher from '../matrix-dispatcher';
+import dis from '../dispatcher';
+import {RoomViewStore} from './RoomViewStore';
+import GroupStore from './GroupStore';
+import {Store} from 'flux/utils';
+import MatrixClientPeg from '../MatrixClientPeg';
+
+
+function matchesRoom(payload, roomStore) {
+ if (!roomStore) {
+ return false;
+ }
+ if (payload.room_alias) {
+ return payload.room_alias === roomStore.getRoomAlias();
+ }
+ return payload.room_id === roomStore.getRoomId();
+}
+
+/**
+ * A class for keeping track of the RoomViewStores of the rooms shown on the screen.
+ * Routes the dispatcher actions to the store of currently active room.
+ */
+class OpenRoomsStore extends Store {
+ constructor() {
+ super(dis);
+
+ // Initialise state
+ this._state = {
+ rooms: [],
+ currentIndex: null,
+ group_id: null,
+ };
+
+ this._forwardingEvent = null;
+ }
+
+ getRoomStores() {
+ return this._state.rooms.map((r) => r.store);
+ }
+
+ getActiveRoomStore() {
+ const openRoom = this._getActiveOpenRoom();
+ if (openRoom) {
+ return openRoom.store;
+ }
+ }
+
+ getRoomStoreAt(index) {
+ if (index >= 0 && index < this._state.rooms.length) {
+ return this._state.rooms[index].store;
+ }
+ }
+
+ _getActiveOpenRoom() {
+ const index = this._state.currentIndex;
+ if (index !== null && index < this._state.rooms.length) {
+ return this._state.rooms[index];
+ }
+ }
+
+ _setState(newState) {
+ this._state = Object.assign(this._state, newState);
+ this.__emitChange();
+ }
+
+ _hasRoom(payload) {
+ return this._roomIndex(payload) !== -1;
+ }
+
+ _roomIndex(payload) {
+ return this._state.rooms.findIndex((r) => matchesRoom(payload, r.store));
+ }
+
+ _cleanupOpenRooms() {
+ this._state.rooms.forEach((room) => {
+ room.dispatcher.unregister(room.dispatcherRef);
+ room.dispatcher.unregister(room.store.getDispatchToken());
+ });
+ this._setState({
+ rooms: [],
+ group_id: null,
+ currentIndex: null,
+ });
+ }
+
+ _createOpenRoom(roomId, roomAlias) {
+ const dispatcher = new MatrixDispatcher();
+ // forward all actions coming from the room dispatcher
+ // to the global one
+ const dispatcherRef = dispatcher.register((payload) => {
+ // block a view_room action for the same room because it will switch to
+ // single room mode in MatrixChat
+ if (payload.action === 'view_room' && roomId === payload.room_id) {
+ return;
+ }
+ payload.grid_src_room_id = roomId;
+ payload.grid_src_room_alias = roomAlias;
+ this.getDispatcher().dispatch(payload);
+ });
+ const openRoom = {
+ store: new RoomViewStore(dispatcher),
+ dispatcher,
+ dispatcherRef,
+ };
+
+ dispatcher.dispatch({
+ action: 'view_room',
+ room_id: roomId,
+ room_alias: roomAlias,
+ }, true);
+
+ return openRoom;
+ }
+
+ _setSingleOpenRoom(payload) {
+ this._setState({
+ rooms: [this._createOpenRoom(payload.room_id, payload.room_alias)],
+ currentIndex: 0,
+ });
+ }
+
+ _setGroupOpenRooms(groupId) {
+ this._cleanupOpenRooms();
+ // TODO: register to GroupStore updates
+ const rooms = GroupStore.getGroupRooms(groupId);
+ const openRooms = rooms.map((room) => {
+ return this._createOpenRoom(room.roomId);
+ });
+ this._setState({
+ rooms: openRooms,
+ group_id: groupId,
+ currentIndex: 0,
+ });
+ }
+
+ _forwardAction(payload) {
+ // don't forward an event to a room dispatcher
+ // if the event originated from that dispatcher, as this
+ // would cause the event to be observed twice in that
+ // dispatcher
+ if (payload.grid_src_room_id || payload.grid_src_room_alias) {
+ const srcPayload = {
+ room_id: payload.grid_src_room_id,
+ room_alias: payload.grid_src_room_alias,
+ };
+ const srcIndex = this._roomIndex(srcPayload);
+ if (srcIndex === this._state.currentIndex) {
+ return;
+ }
+ }
+ const currentRoom = this._getActiveOpenRoom();
+ if (currentRoom) {
+ currentRoom.dispatcher.dispatch(payload, true);
+ }
+ }
+
+ async _resolveRoomAlias(payload) {
+ try {
+ const result = await MatrixClientPeg.get()
+ .getRoomIdForAlias(payload.room_alias);
+ this.getDispatcher().dispatch({
+ action: 'view_room',
+ room_id: result.room_id,
+ event_id: payload.event_id,
+ highlighted: payload.highlighted,
+ room_alias: payload.room_alias,
+ auto_join: payload.auto_join,
+ oob_data: payload.oob_data,
+ });
+ } catch (err) {
+ this._forwardAction({
+ action: 'view_room_error',
+ room_id: null,
+ room_alias: payload.room_alias,
+ err: err,
+ });
+ }
+ }
+
+ _viewRoom(payload) {
+ console.log("!!! OpenRoomsStore: view_room", payload);
+ if (!payload.room_id && payload.room_alias) {
+ this._resolveRoomAlias(payload);
+ }
+ const currentStore = this.getActiveRoomStore();
+ if (!matchesRoom(payload, currentStore)) {
+ if (this._hasRoom(payload)) {
+ const roomIndex = this._roomIndex(payload);
+ this._setState({currentIndex: roomIndex});
+ } else {
+ this._cleanupOpenRooms();
+ }
+ }
+ if (!this.getActiveRoomStore()) {
+ console.log("OpenRoomsStore: _setSingleOpenRoom");
+ this._setSingleOpenRoom(payload);
+ }
+ console.log("OpenRoomsStore: _forwardAction");
+ this._forwardAction(payload);
+ if (this._forwardingEvent) {
+ this.getDispatcher().dispatch({
+ action: 'send_event',
+ room_id: payload.room_id,
+ event: this._forwardingEvent,
+ });
+ this._forwardingEvent = null;
+ }
+ }
+
+ __onDispatch(payload) {
+ let proposedIndex;
+ switch (payload.action) {
+ // view_room:
+ // - room_alias: '#somealias:matrix.org'
+ // - room_id: '!roomid123:matrix.org'
+ // - event_id: '$213456782:matrix.org'
+ // - event_offset: 100
+ // - highlighted: true
+ case 'view_room':
+ this._viewRoom(payload);
+ break;
+ case 'view_my_groups':
+ case 'view_group':
+ this._forwardAction(payload);
+ this._cleanupOpenRooms();
+ break;
+ case 'will_join':
+ case 'cancel_join':
+ case 'join_room':
+ case 'join_room_error':
+ case 'on_logged_out':
+ case 'reply_to_event':
+ case 'open_room_settings':
+ case 'close_settings':
+ case 'focus_composer':
+ this._forwardAction(payload);
+ break;
+ case 'forward_event':
+ this._forwardingEvent = payload.event;
+ break;
+ case 'group_grid_set_active':
+ proposedIndex = this._roomIndex(payload);
+ if (proposedIndex !== -1) {
+ this._setState({
+ currentIndex: proposedIndex,
+ });
+ }
+ break;
+ case 'group_grid_view':
+ if (payload.group_id !== this._state.group_id) {
+ this._setGroupOpenRooms(payload.group_id);
+ }
+ break;
+ }
+ }
+}
+
+let singletonOpenRoomsStore = null;
+if (!singletonOpenRoomsStore) {
+ singletonOpenRoomsStore = new OpenRoomsStore();
+}
+module.exports = singletonOpenRoomsStore;
diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js
index 9e048e5d8e..a0b831ad17 100644
--- a/src/stores/RoomViewStore.js
+++ b/src/stores/RoomViewStore.js
@@ -14,7 +14,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-import dis from '../dispatcher';
import {Store} from 'flux/utils';
import MatrixClientPeg from '../MatrixClientPeg';
import sdk from '../index';
@@ -53,12 +52,12 @@ const INITIAL_STATE = {
* with a subset of the js-sdk.
* ```
*/
-class RoomViewStore extends Store {
- constructor() {
- super(dis);
+export class RoomViewStore extends Store {
+ constructor(dispatcher) {
+ super(dispatcher);
// Initialise state
- this._state = INITIAL_STATE;
+ this._state = Object.assign({}, INITIAL_STATE);
}
_setState(newState) {
@@ -85,6 +84,8 @@ class RoomViewStore extends Store {
});
break;
case 'view_room_error':
+ // should not go over dispatcher anymore
+ // but be internal to RoomViewStore
this._viewRoomError(payload);
break;
case 'will_join':
@@ -150,22 +151,11 @@ class RoomViewStore extends Store {
// pull the user out of Room Settings
isEditingSettings: false,
};
-
- if (this._state.forwardingEvent) {
- dis.dispatch({
- action: 'send_event',
- room_id: newState.roomId,
- event: this._state.forwardingEvent,
- });
- }
-
this._setState(newState);
-
if (payload.auto_join) {
this._joinRoom(payload);
}
} else if (payload.room_alias) {
- // Resolve the alias and then do a second dispatch with the room ID acquired
this._setState({
roomId: null,
initialEventId: null,
@@ -175,25 +165,6 @@ class RoomViewStore extends Store {
roomLoading: true,
roomLoadError: null,
});
- MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias).done(
- (result) => {
- dis.dispatch({
- action: 'view_room',
- room_id: result.room_id,
- event_id: payload.event_id,
- highlighted: payload.highlighted,
- room_alias: payload.room_alias,
- auto_join: payload.auto_join,
- oob_data: payload.oob_data,
- });
- }, (err) => {
- dis.dispatch({
- action: 'view_room_error',
- room_id: null,
- room_alias: payload.room_alias,
- err: err,
- });
- });
}
}
@@ -219,7 +190,7 @@ class RoomViewStore extends Store {
// stream yet, and that's the point at which we'd consider
// the user joined to the room.
}, (err) => {
- dis.dispatch({
+ this.getDispatcher().dispatch({
action: 'join_room_error',
err: err,
});
@@ -335,8 +306,7 @@ class RoomViewStore extends Store {
}
}
-let singletonRoomViewStore = null;
-if (!singletonRoomViewStore) {
- singletonRoomViewStore = new RoomViewStore();
-}
-module.exports = singletonRoomViewStore;
+const MatrixDispatcher = require("../matrix-dispatcher");
+const backwardsCompatInstance = new RoomViewStore(new MatrixDispatcher());
+
+export default backwardsCompatInstance;
diff --git a/src/utils/Timer.js b/src/utils/Timer.js
index aeac0887c9..ca06237fbf 100644
--- a/src/utils/Timer.js
+++ b/src/utils/Timer.js
@@ -26,7 +26,6 @@ Once a timer is finished or aborted, it can't be started again
a new one through `clone()` or `cloneIfRun()`.
*/
export default class Timer {
-
constructor(timeout) {
this._timeout = timeout;
this._onTimeout = this._onTimeout.bind(this);
@@ -70,6 +69,7 @@ export default class Timer {
/**
* if not started before, starts the timer.
+ * @returns {Timer} the same timer
*/
start() {
if (!this.isRunning()) {
@@ -81,6 +81,7 @@ export default class Timer {
/**
* (re)start the timer. If it's running, reset the timeout. If not, start it.
+ * @returns {Timer} the same timer
*/
restart() {
if (this.isRunning()) {
@@ -98,6 +99,7 @@ export default class Timer {
/**
* if the timer is running, abort it,
* and reject the promise for this timer.
+ * @returns {Timer} the same timer
*/
abort() {
if (this.isRunning()) {