mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-18 14:44:58 +08:00
Merge pull request #921 from matrix-org/luke/new-guest-access-room-view-store
Implement a store for RoomView and join the intended room after set a mxid
This commit is contained in:
commit
454134661e
@ -40,7 +40,6 @@ export default React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
||||||
page_type: React.PropTypes.string.isRequired,
|
page_type: React.PropTypes.string.isRequired,
|
||||||
onRoomIdResolved: React.PropTypes.func,
|
|
||||||
onRoomCreated: React.PropTypes.func,
|
onRoomCreated: React.PropTypes.func,
|
||||||
onUserSettingsClose: React.PropTypes.func,
|
onUserSettingsClose: React.PropTypes.func,
|
||||||
|
|
||||||
@ -190,16 +189,14 @@ export default React.createClass({
|
|||||||
case PageTypes.RoomView:
|
case PageTypes.RoomView:
|
||||||
page_element = <RoomView
|
page_element = <RoomView
|
||||||
ref='roomView'
|
ref='roomView'
|
||||||
roomAddress={this.props.currentRoomAlias || this.props.currentRoomId}
|
|
||||||
autoJoin={this.props.autoJoin}
|
autoJoin={this.props.autoJoin}
|
||||||
onRoomIdResolved={this.props.onRoomIdResolved}
|
|
||||||
onRegistered={this.props.onRegistered}
|
onRegistered={this.props.onRegistered}
|
||||||
eventId={this.props.initialEventId}
|
eventId={this.props.initialEventId}
|
||||||
thirdPartyInvite={this.props.thirdPartyInvite}
|
thirdPartyInvite={this.props.thirdPartyInvite}
|
||||||
oobData={this.props.roomOobData}
|
oobData={this.props.roomOobData}
|
||||||
highlightedEventId={this.props.highlightedEventId}
|
highlightedEventId={this.props.highlightedEventId}
|
||||||
eventPixelOffset={this.props.initialEventPixelOffset}
|
eventPixelOffset={this.props.initialEventPixelOffset}
|
||||||
key={this.props.currentRoomAlias || this.props.currentRoomId}
|
key={this.props.currentRoomId || 'roomview'}
|
||||||
opacity={this.props.middleOpacity}
|
opacity={this.props.middleOpacity}
|
||||||
collapsedRhs={this.props.collapse_rhs}
|
collapsedRhs={this.props.collapse_rhs}
|
||||||
ConferenceHandler={this.props.ConferenceHandler}
|
ConferenceHandler={this.props.ConferenceHandler}
|
||||||
|
@ -32,6 +32,8 @@ import sdk from '../../index';
|
|||||||
import * as Rooms from '../../Rooms';
|
import * as Rooms from '../../Rooms';
|
||||||
import linkifyMatrix from "../../linkify-matrix";
|
import linkifyMatrix from "../../linkify-matrix";
|
||||||
import * as Lifecycle from '../../Lifecycle';
|
import * as Lifecycle from '../../Lifecycle';
|
||||||
|
import LifecycleStore from '../../stores/LifecycleStore';
|
||||||
|
import RoomViewStore from '../../stores/RoomViewStore';
|
||||||
import PageTypes from '../../PageTypes';
|
import PageTypes from '../../PageTypes';
|
||||||
|
|
||||||
import createRoom from "../../createRoom";
|
import createRoom from "../../createRoom";
|
||||||
@ -99,9 +101,6 @@ module.exports = React.createClass({
|
|||||||
// What the LoggedInView would be showing if visible
|
// What the LoggedInView would be showing if visible
|
||||||
page_type: null,
|
page_type: null,
|
||||||
|
|
||||||
// If we are viewing a room by alias, this contains the alias
|
|
||||||
currentRoomAlias: null,
|
|
||||||
|
|
||||||
// The ID of the room we're viewing. This is either populated directly
|
// The ID of the room we're viewing. This is either populated directly
|
||||||
// in the case where we view a room by ID or by RoomView when it resolves
|
// in the case where we view a room by ID or by RoomView when it resolves
|
||||||
// what ID an alias points at.
|
// what ID an alias points at.
|
||||||
@ -187,6 +186,9 @@ module.exports = React.createClass({
|
|||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
SdkConfig.put(this.props.config);
|
SdkConfig.put(this.props.config);
|
||||||
|
|
||||||
|
RoomViewStore.addListener(this._onRoomViewStoreUpdated);
|
||||||
|
this._onRoomViewStoreUpdated();
|
||||||
|
|
||||||
// Used by _viewRoom before getting state from sync
|
// Used by _viewRoom before getting state from sync
|
||||||
this.firstSyncComplete = false;
|
this.firstSyncComplete = false;
|
||||||
this.firstSyncPromise = q.defer();
|
this.firstSyncPromise = q.defer();
|
||||||
@ -529,6 +531,10 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onRoomViewStoreUpdated: function() {
|
||||||
|
this.setState({ currentRoomId: RoomViewStore.getRoomId() });
|
||||||
|
},
|
||||||
|
|
||||||
_setPage: function(pageType) {
|
_setPage: function(pageType) {
|
||||||
this.setState({
|
this.setState({
|
||||||
page_type: pageType,
|
page_type: pageType,
|
||||||
@ -602,14 +608,9 @@ module.exports = React.createClass({
|
|||||||
page_type: PageTypes.RoomView,
|
page_type: PageTypes.RoomView,
|
||||||
thirdPartyInvite: room_info.third_party_invite,
|
thirdPartyInvite: room_info.third_party_invite,
|
||||||
roomOobData: room_info.oob_data,
|
roomOobData: room_info.oob_data,
|
||||||
currentRoomAlias: room_info.room_alias,
|
|
||||||
autoJoin: room_info.auto_join,
|
autoJoin: room_info.auto_join,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!room_info.room_alias) {
|
|
||||||
newState.currentRoomId = room_info.room_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we aren't given an explicit event id, look for one in the
|
// if we aren't given an explicit event id, look for one in the
|
||||||
// scrollStateMap.
|
// scrollStateMap.
|
||||||
//
|
//
|
||||||
@ -712,7 +713,7 @@ module.exports = React.createClass({
|
|||||||
|
|
||||||
d.then(() => {
|
d.then(() => {
|
||||||
modal.close();
|
modal.close();
|
||||||
if (this.currentRoomId === roomId) {
|
if (this.state.currentRoomId === roomId) {
|
||||||
dis.dispatch({action: 'view_next_room'});
|
dis.dispatch({action: 'view_next_room'});
|
||||||
}
|
}
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
@ -807,8 +808,12 @@ module.exports = React.createClass({
|
|||||||
this._teamToken = teamToken;
|
this._teamToken = teamToken;
|
||||||
dis.dispatch({action: 'view_home_page'});
|
dis.dispatch({action: 'view_home_page'});
|
||||||
} else if (this._is_registered) {
|
} else if (this._is_registered) {
|
||||||
|
this._is_registered = false;
|
||||||
if (this.props.config.welcomeUserId) {
|
if (this.props.config.welcomeUserId) {
|
||||||
createRoom({dmUserId: this.props.config.welcomeUserId});
|
createRoom({
|
||||||
|
dmUserId: this.props.config.welcomeUserId,
|
||||||
|
andView: false,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// The user has just logged in after registering
|
// The user has just logged in after registering
|
||||||
@ -853,7 +858,6 @@ module.exports = React.createClass({
|
|||||||
ready: false,
|
ready: false,
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
currentRoomAlias: null,
|
|
||||||
currentRoomId: null,
|
currentRoomId: null,
|
||||||
page_type: PageTypes.RoomDirectory,
|
page_type: PageTypes.RoomDirectory,
|
||||||
});
|
});
|
||||||
@ -891,6 +895,12 @@ module.exports = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
cli.on('sync', function(state, prevState) {
|
cli.on('sync', function(state, prevState) {
|
||||||
|
// LifecycleStore and others cannot directly subscribe to matrix client for
|
||||||
|
// events because flux only allows store state changes during flux dispatches.
|
||||||
|
// So dispatch directly from here. Ideally we'd use a SyncStateStore that
|
||||||
|
// would do this dispatch and expose the sync state itself (by listening to
|
||||||
|
// its own dispatch).
|
||||||
|
dis.dispatch({action: 'sync_state', prevState, state});
|
||||||
self.updateStatusIndicator(state, prevState);
|
self.updateStatusIndicator(state, prevState);
|
||||||
if (state === "SYNCING" && prevState === "SYNCING") {
|
if (state === "SYNCING" && prevState === "SYNCING") {
|
||||||
return;
|
return;
|
||||||
@ -1102,6 +1112,8 @@ module.exports = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onRegistered: function(credentials, teamToken) {
|
onRegistered: function(credentials, teamToken) {
|
||||||
|
// XXX: These both should be in state or ideally store(s) because we risk not
|
||||||
|
// rendering the most up-to-date view of state otherwise.
|
||||||
// teamToken may not be truthy
|
// teamToken may not be truthy
|
||||||
this._teamToken = teamToken;
|
this._teamToken = teamToken;
|
||||||
this._is_registered = true;
|
this._is_registered = true;
|
||||||
@ -1163,13 +1175,6 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomIdResolved: function(roomId) {
|
|
||||||
// It's the RoomView's resposibility to look up room aliases, but we need the
|
|
||||||
// ID to pass into things like the Member List, so the Room View tells us when
|
|
||||||
// its done that resolution so we can display things that take a room ID.
|
|
||||||
this.setState({currentRoomId: roomId});
|
|
||||||
},
|
|
||||||
|
|
||||||
_makeRegistrationUrl: function(params) {
|
_makeRegistrationUrl: function(params) {
|
||||||
if (this.props.startingFragmentQueryParams.referrer) {
|
if (this.props.startingFragmentQueryParams.referrer) {
|
||||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
||||||
@ -1211,10 +1216,10 @@ module.exports = React.createClass({
|
|||||||
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
||||||
return (
|
return (
|
||||||
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
||||||
onRoomIdResolved={this.onRoomIdResolved}
|
|
||||||
onRoomCreated={this.onRoomCreated}
|
onRoomCreated={this.onRoomCreated}
|
||||||
onUserSettingsClose={this.onUserSettingsClose}
|
onUserSettingsClose={this.onUserSettingsClose}
|
||||||
onRegistered={this.onRegistered}
|
onRegistered={this.onRegistered}
|
||||||
|
currentRoomId={this.state.currentRoomId}
|
||||||
teamToken={this._teamToken}
|
teamToken={this._teamToken}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
{...this.state}
|
{...this.state}
|
||||||
|
@ -44,6 +44,8 @@ import KeyCode from '../../KeyCode';
|
|||||||
|
|
||||||
import UserProvider from '../../autocomplete/UserProvider';
|
import UserProvider from '../../autocomplete/UserProvider';
|
||||||
|
|
||||||
|
import RoomViewStore from '../../stores/RoomViewStore';
|
||||||
|
|
||||||
var DEBUG = false;
|
var DEBUG = false;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -58,17 +60,6 @@ module.exports = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
ConferenceHandler: React.PropTypes.any,
|
ConferenceHandler: React.PropTypes.any,
|
||||||
|
|
||||||
// Either a room ID or room alias for the room to display.
|
|
||||||
// If the room is being displayed as a result of the user clicking
|
|
||||||
// on a room alias, the alias should be supplied. Otherwise, a room
|
|
||||||
// ID should be supplied.
|
|
||||||
roomAddress: React.PropTypes.string.isRequired,
|
|
||||||
|
|
||||||
// If a room alias is passed to roomAddress, a function can be
|
|
||||||
// provided here that will be called with the ID of the room
|
|
||||||
// once it has been resolved.
|
|
||||||
onRoomIdResolved: React.PropTypes.func,
|
|
||||||
|
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
// transitioned to PWLU)
|
// transitioned to PWLU)
|
||||||
onRegistered: React.PropTypes.func,
|
onRegistered: React.PropTypes.func,
|
||||||
@ -173,40 +164,27 @@ module.exports = React.createClass({
|
|||||||
onClickCompletes: true,
|
onClickCompletes: true,
|
||||||
onStateChange: (isCompleting) => {
|
onStateChange: (isCompleting) => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.props.roomAddress[0] == '#') {
|
// Start listening for RoomViewStore updates
|
||||||
// we always look up the alias from the directory server:
|
RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||||
// we want the room that the given alias is pointing to
|
this._onRoomViewStoreUpdate(true);
|
||||||
// right now. We may have joined that alias before but there's
|
},
|
||||||
// no guarantee the alias hasn't subsequently been remapped.
|
|
||||||
MatrixClientPeg.get().getRoomIdForAlias(this.props.roomAddress).done((result) => {
|
_onRoomViewStoreUpdate: function(initial) {
|
||||||
if (this.props.onRoomIdResolved) {
|
if (this.unmounted) {
|
||||||
this.props.onRoomIdResolved(result.room_id);
|
return;
|
||||||
}
|
}
|
||||||
var room = MatrixClientPeg.get().getRoom(result.room_id);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
room: room,
|
roomId: RoomViewStore.getRoomId(),
|
||||||
roomId: result.room_id,
|
roomAlias: RoomViewStore.getRoomAlias(),
|
||||||
roomLoading: !room,
|
joining: RoomViewStore.isJoining(),
|
||||||
unsentMessageError: this._getUnsentMessageError(room),
|
joinError: RoomViewStore.getJoinError(),
|
||||||
}, this._onHaveRoom);
|
}, () => {
|
||||||
}, (err) => {
|
this._onHaveRoom();
|
||||||
this.setState({
|
this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId));
|
||||||
roomLoading: false,
|
|
||||||
roomLoadError: err,
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var room = MatrixClientPeg.get().getRoom(this.props.roomAddress);
|
|
||||||
this.setState({
|
|
||||||
roomId: this.props.roomAddress,
|
|
||||||
room: room,
|
|
||||||
roomLoading: !room,
|
|
||||||
unsentMessageError: this._getUnsentMessageError(room),
|
|
||||||
}, this._onHaveRoom);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onHaveRoom: function() {
|
_onHaveRoom: function() {
|
||||||
@ -224,17 +202,17 @@ module.exports = React.createClass({
|
|||||||
// NB. We peek if we are not in the room, although if we try to peek into
|
// NB. We peek if we are not in the room, although if we try to peek into
|
||||||
// a room in which we have a member event (ie. we've left) synapse will just
|
// a room in which we have a member event (ie. we've left) synapse will just
|
||||||
// send us the same data as we get in the sync (ie. the last events we saw).
|
// send us the same data as we get in the sync (ie. the last events we saw).
|
||||||
var user_is_in_room = null;
|
const room = MatrixClientPeg.get().getRoom(this.state.roomId);
|
||||||
if (this.state.room) {
|
let isUserJoined = null;
|
||||||
user_is_in_room = this.state.room.hasMembershipState(
|
if (room) {
|
||||||
MatrixClientPeg.get().credentials.userId, 'join'
|
isUserJoined = room.hasMembershipState(
|
||||||
|
MatrixClientPeg.get().credentials.userId, 'join',
|
||||||
);
|
);
|
||||||
|
|
||||||
this._updateAutoComplete();
|
this._updateAutoComplete(room);
|
||||||
this.tabComplete.loadEntries(this.state.room);
|
this.tabComplete.loadEntries(room);
|
||||||
}
|
}
|
||||||
|
if (!isUserJoined && !this.state.joining && this.state.roomId) {
|
||||||
if (!user_is_in_room && this.state.roomId) {
|
|
||||||
if (this.props.autoJoin) {
|
if (this.props.autoJoin) {
|
||||||
this.onJoinButtonClicked();
|
this.onJoinButtonClicked();
|
||||||
} else if (this.state.roomId) {
|
} else if (this.state.roomId) {
|
||||||
@ -260,9 +238,12 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
}).done();
|
}).done();
|
||||||
}
|
}
|
||||||
} else if (user_is_in_room) {
|
} else if (isUserJoined) {
|
||||||
MatrixClientPeg.get().stopPeeking();
|
MatrixClientPeg.get().stopPeeking();
|
||||||
this._onRoomLoaded(this.state.room);
|
this.setState({
|
||||||
|
unsentMessageError: this._getUnsentMessageError(room),
|
||||||
|
});
|
||||||
|
this._onRoomLoaded(room);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -299,10 +280,6 @@ module.exports = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
componentWillReceiveProps: function(newProps) {
|
||||||
if (newProps.roomAddress != this.props.roomAddress) {
|
|
||||||
throw new Error("changing room on a RoomView is not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newProps.eventId != this.props.eventId) {
|
if (newProps.eventId != this.props.eventId) {
|
||||||
// when we change focussed event id, hide the search results.
|
// when we change focussed event id, hide the search results.
|
||||||
this.setState({searchResults: null});
|
this.setState({searchResults: null});
|
||||||
@ -523,7 +500,7 @@ module.exports = React.createClass({
|
|||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
},
|
},
|
||||||
|
|
||||||
_warnAboutEncryption: function (room) {
|
_warnAboutEncryption: function(room) {
|
||||||
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
|
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -604,20 +581,14 @@ module.exports = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onRoom: function(room) {
|
onRoom: function(room) {
|
||||||
// This event is fired when the room is 'stored' by the JS SDK, which
|
if (!room || room.roomId !== this.state.roomId) {
|
||||||
// means it's now a fully-fledged room object ready to be used, so
|
return;
|
||||||
// set it in our state and start using it (ie. init the timeline)
|
}
|
||||||
// This will happen if we start off viewing a room we're not joined,
|
|
||||||
// then join it whilst RoomView is looking at that room.
|
|
||||||
if (!this.state.room && room.roomId == this._joiningRoomId) {
|
|
||||||
this._joiningRoomId = undefined;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
room: room,
|
room: room,
|
||||||
joining: false,
|
}, () => {
|
||||||
});
|
|
||||||
|
|
||||||
this._onRoomLoaded(room);
|
this._onRoomLoaded(room);
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTint: function() {
|
updateTint: function() {
|
||||||
@ -683,7 +654,7 @@ module.exports = React.createClass({
|
|||||||
|
|
||||||
// refresh the tab complete list
|
// refresh the tab complete list
|
||||||
this.tabComplete.loadEntries(this.state.room);
|
this.tabComplete.loadEntries(this.state.room);
|
||||||
this._updateAutoComplete();
|
this._updateAutoComplete(this.state.room);
|
||||||
|
|
||||||
// if we are now a member of the room, where we were not before, that
|
// if we are now a member of the room, where we were not before, that
|
||||||
// means we have finished joining a room we were previously peeking
|
// means we have finished joining a room we were previously peeking
|
||||||
@ -778,37 +749,43 @@ module.exports = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onJoinButtonClicked: function(ev) {
|
onJoinButtonClicked: function(ev) {
|
||||||
var self = this;
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
var cli = MatrixClientPeg.get();
|
|
||||||
var mxIdPromise = q();
|
|
||||||
|
|
||||||
// If the user is a ROU, allow them to transition to a PWLU
|
// If the user is a ROU, allow them to transition to a PWLU
|
||||||
if (cli && cli.isGuest()) {
|
if (cli && cli.isGuest()) {
|
||||||
|
// Join this room once the user has registered and logged in
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'do_after_sync_prepared',
|
||||||
|
deferred_action: {
|
||||||
|
action: 'join_room',
|
||||||
|
room_id: this.state.roomId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
|
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
|
||||||
const defered = q.defer();
|
|
||||||
mxIdPromise = defered.promise;
|
|
||||||
const close = Modal.createDialog(SetMxIdDialog, {
|
const close = Modal.createDialog(SetMxIdDialog, {
|
||||||
homeserverUrl: cli.getHomeserverUrl(),
|
homeserverUrl: cli.getHomeserverUrl(),
|
||||||
onFinished: (submitted, credentials) => {
|
onFinished: (submitted, credentials) => {
|
||||||
if (!submitted) {
|
if (submitted) {
|
||||||
defered.reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onRegistered(credentials);
|
this.props.onRegistered(credentials);
|
||||||
defered.resolve();
|
}
|
||||||
},
|
},
|
||||||
onDifferentServerClicked: (ev) => {
|
onDifferentServerClicked: (ev) => {
|
||||||
dis.dispatch({action: 'start_registration'});
|
dis.dispatch({action: 'start_registration'});
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
}).close;
|
}).close;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mxIdPromise.then(() => {
|
q().then(() => {
|
||||||
this.setState({
|
const signUrl = this.props.thirdPartyInvite ?
|
||||||
joining: true
|
this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'join_room',
|
||||||
|
opts: { inviteSignUrl: signUrl },
|
||||||
});
|
});
|
||||||
|
|
||||||
// if this is an invite and has the 'direct' hint set, mark it as a DM room now.
|
// if this is an invite and has the 'direct' hint set, mark it as a DM room now.
|
||||||
if (this.state.room) {
|
if (this.state.room) {
|
||||||
const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId);
|
const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||||
@ -820,65 +797,8 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return q();
|
return q();
|
||||||
}).then(() => {
|
|
||||||
var sign_url = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
|
||||||
return MatrixClientPeg.get().joinRoom(this.props.roomAddress,
|
|
||||||
{ inviteSignUrl: sign_url } );
|
|
||||||
}).then(function(resp) {
|
|
||||||
var roomId = resp.roomId;
|
|
||||||
|
|
||||||
// It is possible that there is no Room yet if state hasn't come down
|
|
||||||
// from /sync - joinRoom will resolve when the HTTP request to join succeeds,
|
|
||||||
// NOT when it comes down /sync. If there is no room, we'll keep the
|
|
||||||
// joining flag set until we see it.
|
|
||||||
|
|
||||||
// We'll need to initialise the timeline when joining, but due to
|
|
||||||
// the above, we can't do it here: we do it in onRoom instead,
|
|
||||||
// once we have a useable room object.
|
|
||||||
var room = MatrixClientPeg.get().getRoom(roomId);
|
|
||||||
if (!room) {
|
|
||||||
// wait for the room to turn up in onRoom.
|
|
||||||
self._joiningRoomId = roomId;
|
|
||||||
} else {
|
|
||||||
// we've got a valid room, but that might also just mean that
|
|
||||||
// it was peekable (so we had one before anyway). If we are
|
|
||||||
// not yet a member of the room, we will need to wait for that
|
|
||||||
// to happen, in onRoomStateMember.
|
|
||||||
var me = MatrixClientPeg.get().credentials.userId;
|
|
||||||
self.setState({
|
|
||||||
joining: !room.hasMembershipState(me, "join"),
|
|
||||||
room: room
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}).catch(function(error) {
|
|
||||||
self.setState({
|
|
||||||
joining: false,
|
|
||||||
joinError: error
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!error) return;
|
|
||||||
|
|
||||||
// https://matrix.org/jira/browse/SYN-659
|
|
||||||
// Need specific error message if joining a room is refused because the user is a guest and guest access is not allowed
|
|
||||||
if (
|
|
||||||
error.errcode == 'M_GUEST_ACCESS_FORBIDDEN' ||
|
|
||||||
(
|
|
||||||
error.errcode == 'M_FORBIDDEN' &&
|
|
||||||
MatrixClientPeg.get().isGuest()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
dis.dispatch({action: 'view_set_mxid'});
|
|
||||||
} else {
|
|
||||||
var msg = error.message ? error.message : JSON.stringify(error);
|
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
Modal.createDialog(ErrorDialog, {
|
|
||||||
title: "Failed to join room",
|
|
||||||
description: msg
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).done();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onMessageListScroll: function(ev) {
|
onMessageListScroll: function(ev) {
|
||||||
@ -1451,9 +1371,9 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateAutoComplete: function() {
|
_updateAutoComplete: function(room) {
|
||||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
const members = this.state.room.getJoinedMembers().filter(function(member) {
|
const members = room.getJoinedMembers().filter(function(member) {
|
||||||
if (member.userId !== myUserId) return true;
|
if (member.userId !== myUserId) return true;
|
||||||
});
|
});
|
||||||
UserProvider.getInstance().setUserList(members);
|
UserProvider.getInstance().setUserList(members);
|
||||||
@ -1491,7 +1411,7 @@ module.exports = React.createClass({
|
|||||||
|
|
||||||
// We have no room object for this room, only the ID.
|
// We have no room object for this room, only the ID.
|
||||||
// We've got to this room by following a link, possibly a third party invite.
|
// We've got to this room by following a link, possibly a third party invite.
|
||||||
var room_alias = this.props.roomAddress[0] == '#' ? this.props.roomAddress : null;
|
var room_alias = this.state.room_alias;
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomView">
|
<div className="mx_RoomView">
|
||||||
<RoomHeader ref="header"
|
<RoomHeader ref="header"
|
||||||
|
@ -232,6 +232,7 @@ export default React.createClass({
|
|||||||
!this.state.usernameBusy;
|
!this.state.usernameBusy;
|
||||||
|
|
||||||
if (this.state.success) {
|
if (this.state.success) {
|
||||||
|
// XXX BaseDialog needs an onFinished
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_SetMxIdDialog"
|
<BaseDialog className="mx_SetMxIdDialog"
|
||||||
title="You have successfully picked a username!"
|
title="You have successfully picked a username!"
|
||||||
|
@ -57,6 +57,11 @@ function createRoom(opts) {
|
|||||||
createOpts.is_direct = true;
|
createOpts.is_direct = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// By default, view the room after creating it
|
||||||
|
if (opts.andView === undefined) {
|
||||||
|
opts.andView = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Allow guests by default since the room is private and they'd
|
// Allow guests by default since the room is private and they'd
|
||||||
// need an invite. This means clicking on a 3pid invite email can
|
// need an invite. This means clicking on a 3pid invite email can
|
||||||
// actually drop you right in to a chat.
|
// actually drop you right in to a chat.
|
||||||
@ -90,10 +95,12 @@ function createRoom(opts) {
|
|||||||
// room has been created, so we race here with the client knowing that
|
// room has been created, so we race here with the client knowing that
|
||||||
// the room exists, causing things like
|
// the room exists, causing things like
|
||||||
// https://github.com/vector-im/vector-web/issues/1813
|
// https://github.com/vector-im/vector-web/issues/1813
|
||||||
|
if (opts.andView) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: roomId
|
room_id: roomId,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return roomId;
|
return roomId;
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error("Failed to create room " + roomId + " " + err);
|
console.error("Failed to create room " + roomId + " " + err);
|
||||||
|
72
src/stores/LifecycleStore.js
Normal file
72
src/stores/LifecycleStore.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations 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 dis from '../dispatcher';
|
||||||
|
import {Store} from 'flux/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for storing application state to do with login/registration. This is a simple
|
||||||
|
* flux store that listens for actions and updates its state accordingly, informing any
|
||||||
|
* listeners (views) of state changes.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* ```
|
||||||
|
* lifecycleStore.addListener(() => {
|
||||||
|
* this.setState({ cachedPassword: lifecycleStore.getCachedPassword() })
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class LifecycleStore extends Store {
|
||||||
|
constructor() {
|
||||||
|
super(dis);
|
||||||
|
|
||||||
|
// Initialise state
|
||||||
|
this._state = {
|
||||||
|
deferred_action: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_setState(newState) {
|
||||||
|
this._state = Object.assign(this._state, newState);
|
||||||
|
this.__emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
__onDispatch(payload) {
|
||||||
|
switch (payload.action) {
|
||||||
|
case 'do_after_sync_prepared':
|
||||||
|
this._setState({
|
||||||
|
deferred_action: payload.deferred_action,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'sync_state':
|
||||||
|
if (payload.state !== 'PREPARED') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!this._state.deferred_action) break;
|
||||||
|
const deferredAction = Object.assign({}, this._state.deferred_action);
|
||||||
|
this._setState({
|
||||||
|
deferred_action: null,
|
||||||
|
});
|
||||||
|
dis.dispatch(deferredAction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let singletonLifecycleStore = null;
|
||||||
|
if (!singletonLifecycleStore) {
|
||||||
|
singletonLifecycleStore = new LifecycleStore();
|
||||||
|
}
|
||||||
|
module.exports = singletonLifecycleStore;
|
145
src/stores/RoomViewStore.js
Normal file
145
src/stores/RoomViewStore.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations 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 dis from '../dispatcher';
|
||||||
|
import {Store} from 'flux/utils';
|
||||||
|
import MatrixClientPeg from '../MatrixClientPeg';
|
||||||
|
|
||||||
|
const INITIAL_STATE = {
|
||||||
|
// Whether we're joining the currently viewed room
|
||||||
|
joining: false,
|
||||||
|
// Any error occurred during joining
|
||||||
|
joinError: null,
|
||||||
|
// The room ID of the room
|
||||||
|
roomId: null,
|
||||||
|
// The room alias of the room (or null if not originally specified in view_room)
|
||||||
|
roomAlias: null,
|
||||||
|
// Whether the current room is loading
|
||||||
|
roomLoading: false,
|
||||||
|
// Any error that has occurred during loading
|
||||||
|
roomLoadError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for storing application state for RoomView. This is the RoomView's interface
|
||||||
|
* with a subset of the js-sdk.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class RoomViewStore extends Store {
|
||||||
|
constructor() {
|
||||||
|
super(dis);
|
||||||
|
|
||||||
|
// Initialise state
|
||||||
|
this._state = INITIAL_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setState(newState) {
|
||||||
|
this._state = Object.assign(this._state, newState);
|
||||||
|
this.__emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
__onDispatch(payload) {
|
||||||
|
switch (payload.action) {
|
||||||
|
// view_room:
|
||||||
|
// - room_alias: '#somealias:matrix.org'
|
||||||
|
// - room_id: '!roomid123:matrix.org'
|
||||||
|
case 'view_room':
|
||||||
|
this._viewRoom(payload);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// join_room:
|
||||||
|
// - opts: options for joinRoom
|
||||||
|
case 'join_room':
|
||||||
|
this._joinRoom(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewRoom(payload) {
|
||||||
|
const address = payload.room_alias || payload.room_id;
|
||||||
|
if (address[0] == '#') {
|
||||||
|
this._setState({
|
||||||
|
roomLoading: true,
|
||||||
|
});
|
||||||
|
MatrixClientPeg.get().getRoomIdForAlias(address).then(
|
||||||
|
(result) => {
|
||||||
|
this._setState({
|
||||||
|
roomId: result.room_id,
|
||||||
|
roomAlias: address,
|
||||||
|
roomLoading: false,
|
||||||
|
roomLoadError: null,
|
||||||
|
});
|
||||||
|
}, (err) => {
|
||||||
|
console.error(err);
|
||||||
|
this._setState({
|
||||||
|
roomLoading: false,
|
||||||
|
roomLoadError: err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._setState({
|
||||||
|
roomId: address,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_joinRoom(payload) {
|
||||||
|
this._setState({
|
||||||
|
joining: true,
|
||||||
|
});
|
||||||
|
MatrixClientPeg.get().joinRoom(this._state.roomId, payload.opts).then(
|
||||||
|
() => {
|
||||||
|
this._setState({
|
||||||
|
joining: false,
|
||||||
|
});
|
||||||
|
}, (err) => {
|
||||||
|
this._setState({
|
||||||
|
joining: false,
|
||||||
|
joinError: err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this._state = Object.assign({}, INITIAL_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoomId() {
|
||||||
|
return this._state.roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoomAlias() {
|
||||||
|
return this._state.roomAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRoomLoading() {
|
||||||
|
return this._state.roomLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
isJoining() {
|
||||||
|
return this._state.joining;
|
||||||
|
}
|
||||||
|
|
||||||
|
getJoinError() {
|
||||||
|
return this._state.joinError;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let singletonRoomViewStore = null;
|
||||||
|
if (!singletonRoomViewStore) {
|
||||||
|
singletonRoomViewStore = new RoomViewStore();
|
||||||
|
}
|
||||||
|
module.exports = singletonRoomViewStore;
|
@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations 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 dis from '../dispatcher';
|
import dis from '../dispatcher';
|
||||||
import {Store} from 'flux/utils';
|
import {Store} from 'flux/utils';
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
var React = require('react');
|
|
||||||
var expect = require('expect');
|
|
||||||
var sinon = require('sinon');
|
|
||||||
var ReactDOM = require("react-dom");
|
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk');
|
|
||||||
var RoomView = sdk.getComponent('structures.RoomView');
|
|
||||||
var peg = require('../../../src/MatrixClientPeg');
|
|
||||||
|
|
||||||
var test_utils = require('../../test-utils');
|
|
||||||
var q = require('q');
|
|
||||||
|
|
||||||
var Skinner = require("../../../src/Skinner");
|
|
||||||
var stubComponent = require('../../components/stub-component.js');
|
|
||||||
|
|
||||||
describe('RoomView', function () {
|
|
||||||
var sandbox;
|
|
||||||
var parentDiv;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
test_utils.beforeEach(this);
|
|
||||||
sandbox = test_utils.stubClient();
|
|
||||||
parentDiv = document.createElement('div');
|
|
||||||
|
|
||||||
this.oldTimelinePanel = Skinner.getComponent('structures.TimelinePanel');
|
|
||||||
this.oldRoomHeader = Skinner.getComponent('views.rooms.RoomHeader');
|
|
||||||
Skinner.addComponent('structures.TimelinePanel', stubComponent());
|
|
||||||
Skinner.addComponent('views.rooms.RoomHeader', stubComponent());
|
|
||||||
|
|
||||||
peg.get().credentials = { userId: "@test:example.com" };
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
sandbox.restore();
|
|
||||||
|
|
||||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
|
||||||
|
|
||||||
Skinner.addComponent('structures.TimelinePanel', this.oldTimelinePanel);
|
|
||||||
Skinner.addComponent('views.rooms.RoomHeader', this.oldRoomHeader);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('resolves a room alias to a room id', function (done) {
|
|
||||||
peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
|
|
||||||
|
|
||||||
function onRoomIdResolved(room_id) {
|
|
||||||
expect(room_id).toEqual("!randomcharacters:aser.ver");
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(<RoomView roomAddress="#alias:ser.ver" onRoomIdResolved={onRoomIdResolved} />, parentDiv);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('joins by alias if given an alias', function (done) {
|
|
||||||
peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
|
|
||||||
peg.get().getProfileInfo.returns(q({displayname: "foo"}));
|
|
||||||
var roomView = ReactDOM.render(<RoomView roomAddress="#alias:ser.ver" />, parentDiv);
|
|
||||||
|
|
||||||
peg.get().joinRoom = function(x) {
|
|
||||||
expect(x).toEqual('#alias:ser.ver');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
process.nextTick(function() {
|
|
||||||
roomView.onJoinButtonClicked();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
56
test/stores/RoomViewStore-test.js
Normal file
56
test/stores/RoomViewStore-test.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
|
||||||
|
import dis from '../../src/dispatcher';
|
||||||
|
import RoomViewStore from '../../src/stores/RoomViewStore';
|
||||||
|
|
||||||
|
|
||||||
|
import peg from '../../src/MatrixClientPeg';
|
||||||
|
|
||||||
|
import * as testUtils from '../test-utils';
|
||||||
|
import q from 'q';
|
||||||
|
|
||||||
|
const dispatch = testUtils.getDispatchForStore(RoomViewStore);
|
||||||
|
|
||||||
|
describe('RoomViewStore', function() {
|
||||||
|
let sandbox;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
testUtils.beforeEach(this);
|
||||||
|
sandbox = testUtils.stubClient();
|
||||||
|
peg.get().credentials = { userId: "@test:example.com" };
|
||||||
|
|
||||||
|
// Reset the state of the store
|
||||||
|
RoomViewStore.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be used to view a room by ID and join', function(done) {
|
||||||
|
peg.get().joinRoom = (roomId) => {
|
||||||
|
expect(roomId).toBe("!randomcharacters:aser.ver");
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({ action: 'view_room', room_id: '!randomcharacters:aser.ver' });
|
||||||
|
dispatch({ action: 'join_room' });
|
||||||
|
expect(RoomViewStore.isJoining()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be used to view a room by alias and join', function(done) {
|
||||||
|
peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
|
||||||
|
peg.get().joinRoom = (roomId) => {
|
||||||
|
expect(roomId).toBe("!randomcharacters:aser.ver");
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({ action: 'view_room', room_alias: '#somealias2:aser.ver' });
|
||||||
|
|
||||||
|
// Wait for the next event loop to allow for room alias resolution
|
||||||
|
setTimeout(() => {
|
||||||
|
dispatch({ action: 'join_room' });
|
||||||
|
expect(RoomViewStore.isJoining()).toBe(true);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
});
|
@ -4,7 +4,8 @@ import sinon from 'sinon';
|
|||||||
import q from 'q';
|
import q from 'q';
|
||||||
import ReactTestUtils from 'react-addons-test-utils';
|
import ReactTestUtils from 'react-addons-test-utils';
|
||||||
|
|
||||||
import peg from '../src/MatrixClientPeg.js';
|
import peg from '../src/MatrixClientPeg';
|
||||||
|
import dis from '../src/dispatcher';
|
||||||
import jssdk from 'matrix-js-sdk';
|
import jssdk from 'matrix-js-sdk';
|
||||||
const MatrixEvent = jssdk.MatrixEvent;
|
const MatrixEvent = jssdk.MatrixEvent;
|
||||||
|
|
||||||
@ -290,3 +291,13 @@ export function mkStubRoom(roomId = null) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDispatchForStore(store) {
|
||||||
|
// Mock the dispatcher by gut-wrenching. Stores can only __emitChange whilst a
|
||||||
|
// dispatcher `_isDispatching` is true.
|
||||||
|
return (payload) => {
|
||||||
|
dis._isDispatching = true;
|
||||||
|
dis._callbacks[store._dispatchToken](payload);
|
||||||
|
dis._isDispatching = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user