Merge remote-tracking branch 'origin/experimental' into dbkr/fix_recovery_reminder_cancel
This commit is contained in:
commit
3555b02f34
42 changed files with 914 additions and 212 deletions
127
src/components/structures/GroupGridView.js
Normal file
127
src/components/structures/GroupGridView.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd.
|
||||
Copyright 2017, 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 React from 'react';
|
||||
import OpenRoomsStore from '../../stores/OpenRoomsStore';
|
||||
import dis from '../../dispatcher';
|
||||
import {_t} from '../../languageHandler';
|
||||
import RoomView from './RoomView';
|
||||
import classNames from 'classnames';
|
||||
import MainSplit from './MainSplit';
|
||||
import RightPanel from './RightPanel';
|
||||
import RoomHeaderButtons from '../views/right_panel/RoomHeaderButtons';
|
||||
|
||||
export default class RoomGridView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
roomStores: OpenRoomsStore.getRoomStores(),
|
||||
activeRoomStore: OpenRoomsStore.getActiveRoomStore(),
|
||||
};
|
||||
this.onRoomsChanged = this.onRoomsChanged.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(_, prevState) {
|
||||
const store = this.state.activeRoomStore;
|
||||
if (store) {
|
||||
store.getDispatcher().dispatch({action: 'focus_composer'});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.componentDidUpdate();
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this._unmounted = false;
|
||||
this._openRoomsStoreRegistration = OpenRoomsStore.addListener(this.onRoomsChanged);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
if (this._openRoomsStoreRegistration) {
|
||||
this._openRoomsStoreRegistration.remove();
|
||||
}
|
||||
}
|
||||
|
||||
onRoomsChanged() {
|
||||
if (this._unmounted) return;
|
||||
this.setState({
|
||||
roomStores: OpenRoomsStore.getRoomStores(),
|
||||
activeRoomStore: OpenRoomsStore.getActiveRoomStore(),
|
||||
});
|
||||
}
|
||||
|
||||
_setActive(i) {
|
||||
const store = OpenRoomsStore.getRoomStoreAt(i);
|
||||
if (store !== this.state.activeRoomStore) {
|
||||
dis.dispatch({
|
||||
action: 'group_grid_set_active',
|
||||
room_id: store.getRoomId(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let roomStores = this.state.roomStores.slice(0, 6);
|
||||
const emptyCount = 6 - roomStores.length;
|
||||
if (emptyCount) {
|
||||
const emptyTiles = Array.from({length: emptyCount}, () => null);
|
||||
roomStores = roomStores.concat(emptyTiles);
|
||||
}
|
||||
const activeRoomId = this.state.activeRoomStore && this.state.activeRoomStore.getRoomId();
|
||||
let rightPanel;
|
||||
if (activeRoomId) {
|
||||
rightPanel = (
|
||||
<div className="mx_GroupGridView_rightPanel">
|
||||
<div className="mx_GroupGridView_tabs"><RoomHeaderButtons /></div>
|
||||
<RightPanel roomId={activeRoomId} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (<main className="mx_GroupGridView">
|
||||
<MainSplit panel={rightPanel} collapsedRhs={this.props.collapsedRhs} >
|
||||
<div className="mx_GroupGridView_rooms">
|
||||
{ roomStores.map((roomStore, i) => {
|
||||
if (roomStore) {
|
||||
const isActive = roomStore === this.state.activeRoomStore;
|
||||
const tileClasses = classNames({
|
||||
"mx_GroupGridView_tile": true,
|
||||
"mx_GroupGridView_activeTile": isActive,
|
||||
});
|
||||
return (<section
|
||||
onClick={() => {this._setActive(i);}}
|
||||
key={roomStore.getRoomId()}
|
||||
className={tileClasses}
|
||||
>
|
||||
<RoomView
|
||||
collapsedRhs={this.props.collapsedRhs}
|
||||
isGrid={true}
|
||||
roomViewStore={roomStore}
|
||||
isActive={isActive}
|
||||
/>
|
||||
</section>);
|
||||
} else {
|
||||
return (<section className={"mx_GroupGridView_emptyTile"} key={`empty-${i}`}>{_t("No room in this tile yet.")}</section>);
|
||||
}
|
||||
}) }
|
||||
</div>
|
||||
</MainSplit>
|
||||
</main>);
|
||||
}
|
||||
}
|
|
@ -781,7 +781,7 @@ export default React.createClass({
|
|||
),
|
||||
button: _t("Leave"),
|
||||
danger: this.state.isUserPrivileged,
|
||||
onFinished: async(confirmed) => {
|
||||
onFinished: async (confirmed) => {
|
||||
if (!confirmed) return;
|
||||
|
||||
this.setState({membershipBusy: true});
|
||||
|
|
|
@ -31,6 +31,7 @@ import sessionStore from '../../stores/SessionStore';
|
|||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import RoomListStore from "../../stores/RoomListStore";
|
||||
import OpenRoomsStore from "../../stores/OpenRoomsStore";
|
||||
|
||||
import TagOrderActions from '../../actions/TagOrderActions';
|
||||
import RoomListActions from '../../actions/RoomListActions';
|
||||
|
@ -416,6 +417,7 @@ const LoggedInView = React.createClass({
|
|||
const RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
const HomePage = sdk.getComponent('structures.HomePage');
|
||||
const GroupView = sdk.getComponent('structures.GroupView');
|
||||
const GroupGridView = sdk.getComponent('structures.GroupGridView');
|
||||
const MyGroups = sdk.getComponent('structures.MyGroups');
|
||||
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
||||
const CookieBar = sdk.getComponent('globals.CookieBar');
|
||||
|
@ -428,7 +430,14 @@ const LoggedInView = React.createClass({
|
|||
|
||||
switch (this.props.page_type) {
|
||||
case PageTypes.RoomView:
|
||||
if (!OpenRoomsStore.getActiveRoomStore()) {
|
||||
console.warn(`LoggedInView: getCurrentRoomStore not set!`);
|
||||
}
|
||||
else if (OpenRoomsStore.getActiveRoomStore().getRoomId() !== this.props.currentRoomId) {
|
||||
console.warn(`LoggedInView: room id in store not the same as in props: ${OpenRoomsStore.getActiveRoomStore().getRoomId()} & ${this.props.currentRoomId}`);
|
||||
}
|
||||
page_element = <RoomView
|
||||
roomViewStore={OpenRoomsStore.getActiveRoomStore()}
|
||||
ref='roomView'
|
||||
autoJoin={this.props.autoJoin}
|
||||
onRegistered={this.props.onRegistered}
|
||||
|
@ -442,7 +451,9 @@ const LoggedInView = React.createClass({
|
|||
ConferenceHandler={this.props.ConferenceHandler}
|
||||
/>;
|
||||
break;
|
||||
|
||||
case PageTypes.GroupGridView:
|
||||
page_element = <GroupGridView collapsedRhs={this.props.collapsedRhs} />;
|
||||
break;
|
||||
case PageTypes.UserSettings:
|
||||
page_element = <UserSettings
|
||||
onClose={this.props.onCloseAllSettings}
|
||||
|
|
|
@ -71,14 +71,13 @@ export default class MainSplit extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const wasExpanded = !this.props.collapsedRhs && prevProps.collapsedRhs;
|
||||
const wasCollapsed = this.props.collapsedRhs && !prevProps.collapsedRhs;
|
||||
const wasPanelSet = this.props.panel && !prevProps.panel;
|
||||
const wasPanelCleared = !this.props.panel && prevProps.panel;
|
||||
const shouldAllowResizing =
|
||||
!this.props.collapsedRhs &&
|
||||
this.props.panel;
|
||||
|
||||
if (wasExpanded || wasPanelSet) {
|
||||
if (shouldAllowResizing && !this.resizer) {
|
||||
this._createResizer();
|
||||
} else if (wasCollapsed || wasPanelCleared) {
|
||||
} else if (!shouldAllowResizing && this.resizer) {
|
||||
this.resizer.detach();
|
||||
this.resizer = null;
|
||||
}
|
||||
|
|
|
@ -651,6 +651,9 @@ export default React.createClass({
|
|||
case 'view_group':
|
||||
this._viewGroup(payload);
|
||||
break;
|
||||
case 'group_grid_view':
|
||||
this._viewGroupGrid(payload);
|
||||
break;
|
||||
case 'view_home_page':
|
||||
this._viewHome();
|
||||
break;
|
||||
|
@ -862,6 +865,7 @@ export default React.createClass({
|
|||
// room name and avatar from an invite email)
|
||||
_viewRoom: function(roomInfo) {
|
||||
this.focusComposer = true;
|
||||
console.log("!!! MatrixChat._viewRoom", roomInfo);
|
||||
|
||||
const newState = {
|
||||
currentRoomId: roomInfo.room_id || null,
|
||||
|
@ -910,6 +914,9 @@ export default React.createClass({
|
|||
if (roomInfo.event_id && roomInfo.highlighted) {
|
||||
presentedId += "/" + roomInfo.event_id;
|
||||
}
|
||||
|
||||
|
||||
// TODO: only emit this when we're not in grid mode?
|
||||
this.notifyNewScreen('room/' + presentedId);
|
||||
newState.ready = true;
|
||||
this.setState(newState);
|
||||
|
@ -926,6 +933,11 @@ export default React.createClass({
|
|||
this.notifyNewScreen('group/' + groupId);
|
||||
},
|
||||
|
||||
_viewGroupGrid: function(payload) {
|
||||
this._setPage(PageTypes.GroupGridView);
|
||||
// this.notifyNewScreen('grid/' + payload.group_id);
|
||||
},
|
||||
|
||||
_viewHome: function() {
|
||||
// The home page requires the "logged in" view, so we'll set that.
|
||||
this.setStateForNewView({
|
||||
|
|
|
@ -165,7 +165,7 @@ export default class RightPanel extends React.Component {
|
|||
} else if (this.state.phase === RightPanel.Phase.GroupRoomList) {
|
||||
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||
} else if (this.state.phase === RightPanel.Phase.RoomMemberInfo) {
|
||||
panel = <MemberInfo member={this.state.member} key={this.props.roomId || this.state.member.userId} />;
|
||||
panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId || this.state.member.userId} />;
|
||||
} else if (this.state.phase === RightPanel.Phase.GroupMemberInfo) {
|
||||
panel = <GroupMemberInfo
|
||||
groupMember={this.state.member}
|
||||
|
|
|
@ -36,7 +36,6 @@ const ContentMessages = require("../../ContentMessages");
|
|||
const Modal = require("../../Modal");
|
||||
const sdk = require('../../index');
|
||||
const CallHandler = require('../../CallHandler');
|
||||
const dis = require("../../dispatcher");
|
||||
const Tinter = require("../../Tinter");
|
||||
const rate_limited_func = require('../../ratelimitedfunc');
|
||||
const ObjectUtils = require('../../ObjectUtils');
|
||||
|
@ -46,7 +45,6 @@ import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
|||
|
||||
import MainSplit from './MainSplit';
|
||||
import RightPanel from './RightPanel';
|
||||
import RoomViewStore from '../../stores/RoomViewStore';
|
||||
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
|
||||
import WidgetEchoStore from '../../stores/WidgetEchoStore';
|
||||
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
||||
|
@ -94,6 +92,8 @@ module.exports = React.createClass({
|
|||
|
||||
// Servers the RoomView can use to try and assist joins
|
||||
viaServers: PropTypes.arrayOf(PropTypes.string),
|
||||
// the store for this room view
|
||||
roomViewStore: PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -155,7 +155,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.dispatcherRef = this.props.roomViewStore.getDispatcher().register(this.onAction);
|
||||
MatrixClientPeg.get().on("Room", this.onRoom);
|
||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||
MatrixClientPeg.get().on("Room.name", this.onRoomName);
|
||||
|
@ -166,7 +166,7 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||
this._fetchMediaConfig();
|
||||
// Start listening for RoomViewStore updates
|
||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||
this._roomStoreToken = this.props.roomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||
this._onRoomViewStoreUpdate(true);
|
||||
|
||||
WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
|
||||
|
@ -197,8 +197,8 @@ module.exports = React.createClass({
|
|||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initial && this.state.roomId !== RoomViewStore.getRoomId()) {
|
||||
const store = this.props.roomViewStore;
|
||||
if (!initial && this.state.roomId !== store.getRoomId()) {
|
||||
// RoomView explicitly does not support changing what room
|
||||
// is being viewed: instead it should just be re-mounted when
|
||||
// switching rooms. Therefore, if the room ID changes, we
|
||||
|
@ -212,22 +212,21 @@ module.exports = React.createClass({
|
|||
// it was, it means we're about to be unmounted.
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = {
|
||||
roomId: RoomViewStore.getRoomId(),
|
||||
roomAlias: RoomViewStore.getRoomAlias(),
|
||||
roomLoading: RoomViewStore.isRoomLoading(),
|
||||
roomLoadError: RoomViewStore.getRoomLoadError(),
|
||||
joining: RoomViewStore.isJoining(),
|
||||
initialEventId: RoomViewStore.getInitialEventId(),
|
||||
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
||||
shouldPeek: RoomViewStore.shouldPeek(),
|
||||
showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", RoomViewStore.getRoomId()),
|
||||
editingRoomSettings: RoomViewStore.isEditingSettings(),
|
||||
roomId: store.getRoomId(),
|
||||
roomAlias: store.getRoomAlias(),
|
||||
roomLoading: store.isRoomLoading(),
|
||||
roomLoadError: store.getRoomLoadError(),
|
||||
joining: store.isJoining(),
|
||||
initialEventId: store.getInitialEventId(),
|
||||
isInitialEventHighlighted: store.isInitialEventHighlighted(),
|
||||
forwardingEvent: store.getForwardingEvent(),
|
||||
shouldPeek: store.shouldPeek(),
|
||||
showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", store.getRoomId()),
|
||||
editingRoomSettings: store.isEditingSettings(),
|
||||
};
|
||||
|
||||
if (this.state.editingRoomSettings && !newState.editingRoomSettings) dis.dispatch({action: 'focus_composer'});
|
||||
if (this.state.editingRoomSettings && !newState.editingRoomSettings) this.props.roomViewStore.getDispatcher().dispatch({action: 'focus_composer'});
|
||||
|
||||
// Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307
|
||||
console.log(
|
||||
|
@ -389,7 +388,7 @@ module.exports = React.createClass({
|
|||
|
||||
// XXX: EVIL HACK to autofocus inviting on empty rooms.
|
||||
// We use the setTimeout to avoid racing with focus_composer.
|
||||
if (this.state.room &&
|
||||
if (this.props.isActive !== false && this.state.room &&
|
||||
this.state.room.getJoinedMemberCount() == 1 &&
|
||||
this.state.room.getLiveTimeline() &&
|
||||
this.state.room.getLiveTimeline().getEvents() &&
|
||||
|
@ -443,7 +442,7 @@ module.exports = React.createClass({
|
|||
roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd);
|
||||
roomView.removeEventListener('dragend', this.onDragLeaveOrEnd);
|
||||
}
|
||||
dis.unregister(this.dispatcherRef);
|
||||
this.props.roomViewStore.getDispatcher().unregister(this.dispatcherRef);
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
||||
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
||||
|
@ -835,7 +834,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onSearchResultsResize: function() {
|
||||
dis.dispatch({ action: 'timeline_resize' }, true);
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'timeline_resize' }, true);
|
||||
},
|
||||
|
||||
onSearchResultsFillRequest: function(backwards) {
|
||||
|
@ -856,7 +855,7 @@ module.exports = React.createClass({
|
|||
|
||||
onInviteButtonClick: function() {
|
||||
// call AddressPickerDialog
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'view_invite',
|
||||
roomId: this.state.room.roomId,
|
||||
});
|
||||
|
@ -878,7 +877,7 @@ module.exports = React.createClass({
|
|||
// Join this room once the user has registered and logged in
|
||||
const signUrl = this.props.thirdPartyInvite ?
|
||||
this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'do_after_sync_prepared',
|
||||
deferred_action: {
|
||||
action: 'join_room',
|
||||
|
@ -888,7 +887,7 @@ module.exports = React.createClass({
|
|||
|
||||
// Don't peek whilst registering otherwise getPendingEventList complains
|
||||
// Do this by indicating our intention to join
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'will_join',
|
||||
});
|
||||
|
||||
|
@ -899,20 +898,20 @@ module.exports = React.createClass({
|
|||
if (submitted) {
|
||||
this.props.onRegistered(credentials);
|
||||
} else {
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'cancel_after_sync_prepared',
|
||||
});
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'cancel_join',
|
||||
});
|
||||
}
|
||||
},
|
||||
onDifferentServerClicked: (ev) => {
|
||||
dis.dispatch({action: 'start_registration'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'start_registration'});
|
||||
close();
|
||||
},
|
||||
onLoginClick: (ev) => {
|
||||
dis.dispatch({action: 'start_login'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'start_login'});
|
||||
close();
|
||||
},
|
||||
}).close;
|
||||
|
@ -922,7 +921,7 @@ module.exports = React.createClass({
|
|||
Promise.resolve().then(() => {
|
||||
const signUrl = this.props.thirdPartyInvite ?
|
||||
this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'join_room',
|
||||
opts: { inviteSignUrl: signUrl, viaServers: this.props.viaServers },
|
||||
});
|
||||
|
@ -987,10 +986,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
uploadFile: async function(file) {
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'focus_composer'});
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'require_registration'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1014,14 +1013,14 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
// Send message_sent callback, for things like _checkIfAlone because after all a file is still a message.
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'message_sent',
|
||||
});
|
||||
},
|
||||
|
||||
injectSticker: function(url, info, text) {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'require_registration'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1222,7 +1221,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onSettingsClick: function() {
|
||||
dis.dispatch({ action: 'open_room_settings' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'open_room_settings' });
|
||||
},
|
||||
|
||||
onSettingsSaveClick: function() {
|
||||
|
@ -1255,31 +1254,31 @@ module.exports = React.createClass({
|
|||
});
|
||||
// still editing room settings
|
||||
} else {
|
||||
dis.dispatch({ action: 'close_settings' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'close_settings' });
|
||||
}
|
||||
}).finally(() => {
|
||||
this.setState({
|
||||
uploadingRoomSettings: false,
|
||||
});
|
||||
dis.dispatch({ action: 'close_settings' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'close_settings' });
|
||||
}).done();
|
||||
},
|
||||
|
||||
onCancelClick: function() {
|
||||
console.log("updateTint from onCancelClick");
|
||||
this.updateTint();
|
||||
dis.dispatch({ action: 'close_settings' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'close_settings' });
|
||||
if (this.state.forwardingEvent) {
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'forward_event',
|
||||
event: null,
|
||||
});
|
||||
}
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
onLeaveClick: function() {
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
|
@ -1287,7 +1286,7 @@ module.exports = React.createClass({
|
|||
|
||||
onForgetClick: function() {
|
||||
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'view_next_room' });
|
||||
}, function(err) {
|
||||
const errCode = err.errcode || _t("unknown error code");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -1304,7 +1303,7 @@ module.exports = React.createClass({
|
|||
rejecting: true,
|
||||
});
|
||||
MatrixClientPeg.get().leave(this.state.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
this.props.roomViewStore.getDispatcher().dispatch({ action: 'view_next_room' });
|
||||
self.setState({
|
||||
rejecting: false,
|
||||
});
|
||||
|
@ -1330,7 +1329,7 @@ module.exports = React.createClass({
|
|||
// using /leave rather than /join. In the short term though, we
|
||||
// just ignore them.
|
||||
// https://github.com/vector-im/vector-web/issues/1134
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'view_room_directory',
|
||||
});
|
||||
},
|
||||
|
@ -1349,7 +1348,7 @@ module.exports = React.createClass({
|
|||
// jump down to the bottom of this room, where new events are arriving
|
||||
jumpToLiveTimeline: function() {
|
||||
this.refs.messagePanel.jumpToLiveTimeline();
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
this.props.roomViewStore.getDispatcher().dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
// jump up to wherever our read marker is
|
||||
|
@ -1439,7 +1438,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onFullscreenClick: function() {
|
||||
dis.dispatch({
|
||||
this.props.roomViewStore.getDispatcher().dispatch({
|
||||
action: 'video_fullscreen',
|
||||
fullscreen: true,
|
||||
}, true);
|
||||
|
@ -1564,6 +1563,7 @@ module.exports = React.createClass({
|
|||
<RoomHeader ref="header"
|
||||
room={this.state.room}
|
||||
oobData={this.props.oobData}
|
||||
isGrid={this.props.isGrid}
|
||||
collapsedRhs={this.props.collapsedRhs}
|
||||
/>
|
||||
<div className="mx_RoomView_body">
|
||||
|
@ -1610,6 +1610,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomView">
|
||||
<RoomHeader
|
||||
ref="header"
|
||||
isGrid={this.props.isGrid}
|
||||
room={this.state.room}
|
||||
collapsedRhs={this.props.collapsedRhs}
|
||||
/>
|
||||
|
@ -1751,7 +1752,9 @@ module.exports = React.createClass({
|
|||
if (canSpeak) {
|
||||
messageComposer =
|
||||
<MessageComposer
|
||||
roomViewStore={this.props.roomViewStore}
|
||||
room={this.state.room}
|
||||
isGrid={this.props.isGrid}
|
||||
onResize={this.onChildResize}
|
||||
uploadFile={this.uploadFile}
|
||||
callState={this.state.callState}
|
||||
|
@ -1878,11 +1881,14 @@ module.exports = React.createClass({
|
|||
},
|
||||
);
|
||||
|
||||
const rightPanel = this.state.room ? <RightPanel roomId={this.state.room.roomId} /> : undefined;
|
||||
const rightPanel = this.state.room && !this.props.isGrid ?
|
||||
<RightPanel roomId={this.state.room.roomId} /> :
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
|
||||
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
|
||||
isGrid={this.props.isGrid}
|
||||
oobData={this.props.oobData}
|
||||
editing={this.state.editingRoomSettings}
|
||||
saving={this.state.uploadingRoomSettings}
|
||||
|
|
|
@ -835,7 +835,7 @@ module.exports = React.createClass({
|
|||
SettingsStore.getLabsFeatures().forEach((featureId) => {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
const onChange = async(e) => {
|
||||
const onChange = async (e) => {
|
||||
const checked = e.target.checked;
|
||||
if (featureId === "feature_lazyloading") {
|
||||
const confirmed = await this._onLazyLoadChanging(checked);
|
||||
|
|
|
@ -48,7 +48,7 @@ export default class GroupInviteTileContextMenu extends React.Component {
|
|||
Modal.createTrackedDialog('Reject community invite', '', QuestionDialog, {
|
||||
title: _t('Reject invitation'),
|
||||
description: _t('Are you sure you want to reject the invitation?'),
|
||||
onFinished: async(shouldLeave) => {
|
||||
onFinished: async (shouldLeave) => {
|
||||
if (!shouldLeave) return;
|
||||
|
||||
// FIXME: controller shouldn't be loading a view :(
|
||||
|
|
|
@ -35,7 +35,7 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
_onClearClick = async(e) => {
|
||||
_onClearClick = async (e) => {
|
||||
await MatrixClientPeg.get()._unstable_setStatusMessage("");
|
||||
this.setState({message: ""});
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ import dis from '../../../dispatcher';
|
|||
import TagOrderActions from '../../../actions/TagOrderActions';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import sdk from '../../../index';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
export default class TagTileContextMenu extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -34,6 +35,7 @@ export default class TagTileContextMenu extends React.Component {
|
|||
|
||||
this._onViewCommunityClick = this._onViewCommunityClick.bind(this);
|
||||
this._onRemoveClick = this._onRemoveClick.bind(this);
|
||||
this._onViewAsGridClick = this._onViewAsGridClick.bind(this);
|
||||
}
|
||||
|
||||
_onViewCommunityClick() {
|
||||
|
@ -53,8 +55,28 @@ export default class TagTileContextMenu extends React.Component {
|
|||
this.props.onFinished();
|
||||
}
|
||||
|
||||
_onViewAsGridClick() {
|
||||
dis.dispatch({
|
||||
action: 'group_grid_view',
|
||||
group_id: this.props.tag,
|
||||
});
|
||||
this.props.onFinished();
|
||||
}
|
||||
|
||||
render() {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
let gridViewOption;
|
||||
if (SettingsStore.isFeatureEnabled("feature_gridview")) {
|
||||
gridViewOption = (<div className="mx_TagTileContextMenu_item" onClick={this._onViewAsGridClick} >
|
||||
<TintableSvg
|
||||
className="mx_TagTileContextMenu_item_icon"
|
||||
src="img/feather-icons/grid.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
/>
|
||||
{ _t('View as Grid') }
|
||||
</div>);
|
||||
}
|
||||
return <div>
|
||||
<div className="mx_TagTileContextMenu_item" onClick={this._onViewCommunityClick} >
|
||||
<TintableSvg
|
||||
|
@ -65,6 +87,7 @@ export default class TagTileContextMenu extends React.Component {
|
|||
/>
|
||||
{ _t('View Community') }
|
||||
</div>
|
||||
{ gridViewOption }
|
||||
<hr className="mx_TagTileContextMenu_separator" />
|
||||
<div className="mx_TagTileContextMenu_item" onClick={this._onRemoveClick} >
|
||||
<img className="mx_TagTileContextMenu_item_icon" src="img/icon_context_delete.svg" width="15" height="15" />
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,8 +713,8 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}
|
||||
|
||||
if (!member || !member.membership || member.membership === 'leave') {
|
||||
const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId();
|
||||
const onInviteUserButton = async() => {
|
||||
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
|
||||
// we're only inviting one user.
|
||||
|
|
|
@ -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 = <p>{
|
||||
|
@ -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(
|
||||
<MessageComposerInput
|
||||
roomViewStore={this.props.roomViewStore}
|
||||
ref={(c) => 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,
|
||||
};
|
||||
|
|
|
@ -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) => {
|
||||
|
@ -1260,7 +1274,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
selectHistory = async(up) => {
|
||||
selectHistory = async (up) => {
|
||||
const delta = up ? -1 : 1;
|
||||
|
||||
// True if we are not currently selecting history, but composing a message
|
||||
|
@ -1308,7 +1322,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
return true;
|
||||
};
|
||||
|
||||
onTab = async(e) => {
|
||||
onTab = async (e) => {
|
||||
this.setState({
|
||||
someCompletions: null,
|
||||
});
|
||||
|
@ -1330,7 +1344,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow();
|
||||
};
|
||||
|
||||
onEscape = async(e) => {
|
||||
onEscape = async (e) => {
|
||||
e.preventDefault();
|
||||
if (this.autocomplete) {
|
||||
this.autocomplete.onEscape(e);
|
||||
|
@ -1349,7 +1363,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
/* If passed null, restores the original editor content from state.originalEditorState.
|
||||
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
|
||||
*/
|
||||
setDisplayedCompletion = async(displayedCompletion: ?Completion): boolean => {
|
||||
setDisplayedCompletion = async (displayedCompletion: ?Completion): boolean => {
|
||||
const activeEditorState = this.state.originalEditorState || this.state.editorState;
|
||||
|
||||
if (displayedCompletion == null) {
|
||||
|
@ -1589,7 +1603,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
return (
|
||||
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
||||
<div className="mx_MessageComposer_autocomplete_wrapper">
|
||||
<ReplyPreview />
|
||||
<ReplyPreview roomViewStore={this.props.roomViewStore} />
|
||||
<Autocomplete
|
||||
ref={(e) => this.autocomplete = e}
|
||||
room={this.props.room}
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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({
|
|||
</div>;
|
||||
}
|
||||
|
||||
let toggleRightPanelButton;
|
||||
if (this.props.isGrid) {
|
||||
toggleRightPanelButton =
|
||||
<AccessibleButton
|
||||
className="mx_RoomHeader_button"
|
||||
onClick={this.onToggleRightPanelClick}
|
||||
title={_t('Toggle right panel')}>
|
||||
<TintableSvg src="img/feather-icons/toggle-right-panel.svg" width="20" height="20" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"mx_RoomHeader light-panel " + (this.props.editing ? "mx_RoomHeader_editing" : "")}>
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
|
@ -419,7 +439,8 @@ module.exports = React.createClass({
|
|||
{ saveButton }
|
||||
{ cancelButton }
|
||||
{ rightRow }
|
||||
<RoomHeaderButtons collapsedRhs={this.props.collapsedRhs} />
|
||||
{ !this.props.isGrid ? <RoomHeaderButtons collapsedRhs={this.props.collapsedRhs} /> : undefined }
|
||||
{ toggleRightPanelButton }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -21,13 +21,15 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
|
||||
export default class KeyBackupPanel extends React.Component {
|
||||
export default class KeyBackupPanel extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._startNewBackup = this._startNewBackup.bind(this);
|
||||
this._deleteBackup = this._deleteBackup.bind(this);
|
||||
this._verifyDevice = this._verifyDevice.bind(this);
|
||||
this._onKeyBackupSessionsRemaining =
|
||||
this._onKeyBackupSessionsRemaining.bind(this);
|
||||
this._onKeyBackupStatus = this._onKeyBackupStatus.bind(this);
|
||||
this._restoreBackup = this._restoreBackup.bind(this);
|
||||
|
||||
|
@ -36,6 +38,7 @@ export default class KeyBackupPanel extends React.Component {
|
|||
loading: true,
|
||||
error: null,
|
||||
backupInfo: null,
|
||||
sessionsRemaining: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -43,6 +46,10 @@ export default class KeyBackupPanel extends React.Component {
|
|||
this._loadBackupStatus();
|
||||
|
||||
MatrixClientPeg.get().on('crypto.keyBackupStatus', this._onKeyBackupStatus);
|
||||
MatrixClientPeg.get().on(
|
||||
'crypto.keyBackupSessionsRemaining',
|
||||
this._onKeyBackupSessionsRemaining,
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -50,9 +57,19 @@ export default class KeyBackupPanel extends React.Component {
|
|||
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatus);
|
||||
MatrixClientPeg.get().removeListener(
|
||||
'crypto.keyBackupSessionsRemaining',
|
||||
this._onKeyBackupSessionsRemaining,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_onKeyBackupSessionsRemaining(sessionsRemaining) {
|
||||
this.setState({
|
||||
sessionsRemaining,
|
||||
});
|
||||
}
|
||||
|
||||
_onKeyBackupStatus() {
|
||||
this._loadBackupStatus();
|
||||
}
|
||||
|
@ -144,15 +161,30 @@ export default class KeyBackupPanel extends React.Component {
|
|||
} else if (this.state.backupInfo) {
|
||||
let clientBackupStatus;
|
||||
if (MatrixClientPeg.get().getKeyBackupEnabled()) {
|
||||
clientBackupStatus = _t("This device is uploading keys to this backup");
|
||||
clientBackupStatus = _t("This device is using key backup");
|
||||
} else {
|
||||
// XXX: display why and how to fix it
|
||||
clientBackupStatus = _t(
|
||||
"This device is <b>not</b> uploading keys to this backup", {},
|
||||
"This device is <b>not</b> using key backup", {},
|
||||
{b: x => <b>{x}</b>},
|
||||
);
|
||||
}
|
||||
|
||||
let uploadStatus;
|
||||
const { sessionsRemaining } = this.state;
|
||||
if (!MatrixClientPeg.get().getKeyBackupEnabled()) {
|
||||
// No upload status to show when backup disabled.
|
||||
uploadStatus = "";
|
||||
} else if (sessionsRemaining > 0) {
|
||||
uploadStatus = <div>
|
||||
{_t("Backing up %(sessionsRemaining)s keys...", { sessionsRemaining })} <br />
|
||||
</div>;
|
||||
} else {
|
||||
uploadStatus = <div>
|
||||
{_t("All keys backed up")} <br />
|
||||
</div>;
|
||||
}
|
||||
|
||||
let backupSigStatuses = this.state.backupSigStatus.sigs.map((sig, i) => {
|
||||
const deviceName = sig.device.getDisplayName() || sig.device.deviceId;
|
||||
const validity = sub =>
|
||||
|
@ -217,6 +249,7 @@ export default class KeyBackupPanel extends React.Component {
|
|||
{_t("Backup version: ")}{this.state.backupInfo.version}<br />
|
||||
{_t("Algorithm: ")}{this.state.backupInfo.algorithm}<br />
|
||||
{clientBackupStatus}<br />
|
||||
{uploadStatus}
|
||||
<div>{backupSigStatuses}</div><br />
|
||||
<br />
|
||||
<AccessibleButton className="mx_UserSettings_button"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue