diff --git a/src/Keyboard.js b/src/Keyboard.js
index 9c872e1c66..bf83a1a05f 100644
--- a/src/Keyboard.js
+++ b/src/Keyboard.js
@@ -68,3 +68,12 @@ export function isOnlyCtrlOrCmdKeyEvent(ev) {
return ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey;
}
}
+
+export function isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) {
+ const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
+ if (isMac) {
+ return ev.metaKey && !ev.altKey && !ev.ctrlKey;
+ } else {
+ return ev.ctrlKey && !ev.altKey && !ev.metaKey;
+ }
+}
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 38b7634edb..6f40aa559a 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -333,7 +333,6 @@ const LoggedInView = React.createClass({
{ SettingsStore.isFeatureEnabled("feature_tag_panel") ?
:
}
diff --git a/src/components/structures/TagPanel.js b/src/components/structures/TagPanel.js
index aa35101197..531c247ea6 100644
--- a/src/components/structures/TagPanel.js
+++ b/src/components/structures/TagPanel.js
@@ -17,7 +17,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
-import FilterStore from '../../stores/FilterStore';
import TagOrderStore from '../../stores/TagOrderStore';
import GroupActions from '../../actions/GroupActions';
@@ -44,20 +43,13 @@ const TagPanel = React.createClass({
this.unmounted = false;
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
- this._filterStoreToken = FilterStore.addListener(() => {
- if (this.unmounted) {
- return;
- }
- this.setState({
- selectedTags: FilterStore.getSelectedTags(),
- });
- });
this._tagOrderStoreToken = TagOrderStore.addListener(() => {
if (this.unmounted) {
return;
}
this.setState({
orderedTags: TagOrderStore.getOrderedTags() || [],
+ selectedTags: TagOrderStore.getSelectedTags(),
});
});
// This could be done by anything with a matrix client
diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js
index eb9a5cbdd9..a98cca9609 100644
--- a/src/components/views/elements/TagTile.js
+++ b/src/components/views/elements/TagTile.js
@@ -20,7 +20,7 @@ import classNames from 'classnames';
import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
-import { isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard';
+import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent } from '../../../Keyboard';
import FlairStore from '../../../stores/FlairStore';
@@ -76,7 +76,7 @@ export default React.createClass({
dis.dispatch({
action: 'select_tag',
tag: this.props.tag,
- ctrlOrCmdKey: isOnlyCtrlOrCmdKeyEvent(e),
+ ctrlOrCmdKey: isOnlyCtrlOrCmdIgnoreShiftKeyEvent(e),
shiftKey: e.shiftKey,
});
},
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index cb6cb6c0f3..119355bc82 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -41,7 +41,6 @@ import AccessibleButton from '../elements/AccessibleButton';
import GeminiScrollbar from 'react-gemini-scrollbar';
import RoomViewStore from '../../../stores/RoomViewStore';
-
module.exports = withMatrixClient(React.createClass({
displayName: 'MemberInfo',
@@ -713,6 +712,10 @@ module.exports = withMatrixClient(React.createClass({
if (this.props.member.userId !== this.props.matrixClient.credentials.userId) {
const dmRoomMap = new DMRoomMap(this.props.matrixClient);
+ // dmRooms will not include dmRooms that we have been invited into but did not join.
+ // Because DMRoomMap runs off account_data[m.direct] which is only set on join of dm room.
+ // XXX: we potentially want DMs we have been invited to, to also show up here :L
+ // especially as logic below concerns specially if we haven't joined but have been invited
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.member.userId);
const RoomTile = sdk.getComponent("rooms.RoomTile");
@@ -722,10 +725,15 @@ module.exports = withMatrixClient(React.createClass({
const room = this.props.matrixClient.getRoom(roomId);
if (room) {
const me = room.getMember(this.props.matrixClient.credentials.userId);
- const highlight = (
- room.getUnreadNotificationCount('highlight') > 0 ||
- me.membership === "invite"
- );
+
+ // not a DM room if we have are not joined
+ if (!me.membership || me.membership !== 'join') continue;
+ // not a DM room if they are not joined
+ const them = this.props.member;
+ if (!them.membership || them.membership !== 'join') continue;
+
+ const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === 'invite';
+
tiles.push(
{
- FilterStore.getSelectedTags().forEach((tag) => {
+ this._tagStoreToken = TagOrderStore.addListener(() => {
+ TagOrderStore.getSelectedTags().forEach((tag) => {
if (tag[0] !== '+' || this._groupStores[tag]) {
return;
}
@@ -183,8 +183,8 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
}
- if (this._filterStoreToken) {
- this._filterStoreToken.remove();
+ if (this._tagStoreToken) {
+ this._tagStoreToken.remove();
}
if (this._groupStoreTokens.length > 0) {
@@ -298,12 +298,11 @@ module.exports = React.createClass({
// Update which rooms and users should appear according to which tags are selected
updateVisibleRooms: function() {
- const selectedTags = FilterStore.getSelectedTags();
-
- let visibleGroupRooms = [];
+ const selectedTags = TagOrderStore.getSelectedTags();
+ const visibleGroupRooms = [];
selectedTags.forEach((tag) => {
- visibleGroupRooms = visibleGroupRooms.concat(
- this._visibleRoomsForGroup[tag] || [],
+ (this._visibleRoomsForGroup[tag] || []).forEach(
+ (roomId) => visibleGroupRooms.push(roomId),
);
});
@@ -378,7 +377,6 @@ module.exports = React.createClass({
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
// Used to split rooms via tags
const tagNames = Object.keys(room.tags);
-
if (tagNames.length) {
for (let i = 0; i < tagNames.length; i++) {
const tagName = tagNames[i];
@@ -672,7 +670,6 @@ module.exports = React.createClass({
editable={false}
order="recent"
isInvite={true}
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
@@ -686,7 +683,6 @@ module.exports = React.createClass({
emptyContent={this._getEmptyContent('m.favourite')}
editable={true}
order="manual"
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
@@ -700,7 +696,6 @@ module.exports = React.createClass({
headerItems={this._getHeaderItems('im.vector.fake.direct')}
editable={true}
order="recent"
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
alwaysShowHeader={true}
@@ -714,7 +709,6 @@ module.exports = React.createClass({
emptyContent={this._getEmptyContent('im.vector.fake.recent')}
headerItems={this._getHeaderItems('im.vector.fake.recent')}
order="recent"
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
@@ -730,7 +724,6 @@ module.exports = React.createClass({
emptyContent={this._getEmptyContent(tagName)}
editable={true}
order="manual"
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
@@ -745,7 +738,6 @@ module.exports = React.createClass({
emptyContent={this._getEmptyContent('m.lowpriority')}
editable={true}
order="recent"
- selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
@@ -756,7 +748,6 @@ module.exports = React.createClass({
label={_t('Historical')}
editable={false}
order="recent"
- selectedRoom={self.props.selectedRoom}
collapsed={self.props.collapsed}
alwaysShowHeader={true}
startAsHidden={true}
diff --git a/src/stores/FilterStore.js b/src/stores/FilterStore.js
deleted file mode 100644
index 8078a13ffd..0000000000
--- a/src/stores/FilterStore.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-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 {Store} from 'flux/utils';
-import dis from '../dispatcher';
-import Analytics from '../Analytics';
-
-const INITIAL_STATE = {
- allTags: [],
- selectedTags: [],
- // Last selected tag when shift was not being pressed
- anchorTag: null,
-};
-
-/**
- * A class for storing application state for filtering via TagPanel.
- */
-class FilterStore 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) {
- case 'all_tags' :
- this._setState({
- allTags: payload.tags,
- });
- break;
- case 'select_tag': {
- let newTags = [];
- // Shift-click semantics
- if (payload.shiftKey) {
- // Select range of tags
- let start = this._state.allTags.indexOf(this._state.anchorTag);
- let end = this._state.allTags.indexOf(payload.tag);
-
- if (start === -1) {
- start = end;
- }
- if (start > end) {
- const temp = start;
- start = end;
- end = temp;
- }
- newTags = payload.ctrlOrCmdKey ? this._state.selectedTags : [];
- newTags = [...new Set(
- this._state.allTags.slice(start, end + 1).concat(newTags),
- )];
- } else {
- if (payload.ctrlOrCmdKey) {
- // Toggle individual tag
- if (this._state.selectedTags.includes(payload.tag)) {
- newTags = this._state.selectedTags.filter((t) => t !== payload.tag);
- } else {
- newTags = [...this._state.selectedTags, payload.tag];
- }
- } else {
- // Select individual tag
- newTags = [payload.tag];
- }
- // Only set the anchor tag if the tag was previously unselected, otherwise
- // the next range starts with an unselected tag.
- if (!this._state.selectedTags.includes(payload.tag)) {
- this._setState({
- anchorTag: payload.tag,
- });
- }
- }
-
- this._setState({
- selectedTags: newTags,
- });
-
- Analytics.trackEvent('FilterStore', 'select_tag');
- }
- break;
- case 'deselect_tags':
- this._setState({
- selectedTags: [],
- });
- Analytics.trackEvent('FilterStore', 'deselect_tags');
- break;
- }
- }
-
- getSelectedTags() {
- return this._state.selectedTags;
- }
-}
-
-if (global.singletonFilterStore === undefined) {
- global.singletonFilterStore = new FilterStore();
-}
-export default global.singletonFilterStore;
diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js
index 633ffc7e9c..9c9427284e 100644
--- a/src/stores/TagOrderStore.js
+++ b/src/stores/TagOrderStore.js
@@ -15,12 +15,17 @@ limitations under the License.
*/
import {Store} from 'flux/utils';
import dis from '../dispatcher';
+import Analytics from '../Analytics';
const INITIAL_STATE = {
orderedTags: null,
orderedTagsAccountData: null,
hasSynced: false,
joinedGroupIds: null,
+
+ selectedTags: [],
+ // Last selected tag when shift was not being pressed
+ anchorTag: null,
};
/**
@@ -93,6 +98,60 @@ class TagOrderStore extends Store {
this._setState({orderedTags});
break;
}
+ case 'select_tag': {
+ let newTags = [];
+ // Shift-click semantics
+ if (payload.shiftKey) {
+ // Select range of tags
+ let start = this._state.orderedTags.indexOf(this._state.anchorTag);
+ let end = this._state.orderedTags.indexOf(payload.tag);
+
+ if (start === -1) {
+ start = end;
+ }
+ if (start > end) {
+ const temp = start;
+ start = end;
+ end = temp;
+ }
+ newTags = payload.ctrlOrCmdKey ? this._state.selectedTags : [];
+ newTags = [...new Set(
+ this._state.orderedTags.slice(start, end + 1).concat(newTags),
+ )];
+ } else {
+ if (payload.ctrlOrCmdKey) {
+ // Toggle individual tag
+ if (this._state.selectedTags.includes(payload.tag)) {
+ newTags = this._state.selectedTags.filter((t) => t !== payload.tag);
+ } else {
+ newTags = [...this._state.selectedTags, payload.tag];
+ }
+ } else {
+ // Select individual tag
+ newTags = [payload.tag];
+ }
+ // Only set the anchor tag if the tag was previously unselected, otherwise
+ // the next range starts with an unselected tag.
+ if (!this._state.selectedTags.includes(payload.tag)) {
+ this._setState({
+ anchorTag: payload.tag,
+ });
+ }
+ }
+
+ this._setState({
+ selectedTags: newTags,
+ });
+
+ Analytics.trackEvent('FilterStore', 'select_tag');
+ }
+ break;
+ case 'deselect_tags':
+ this._setState({
+ selectedTags: [],
+ });
+ Analytics.trackEvent('FilterStore', 'deselect_tags');
+ break;
case 'on_logged_out': {
// Reset state without pushing an update to the view, which generally assumes that
// the matrix client isn't `null` and so causing a re-render will cause NPEs.
@@ -129,6 +188,10 @@ class TagOrderStore extends Store {
getOrderedTags() {
return this._state.orderedTags;
}
+
+ getSelectedTags() {
+ return this._state.selectedTags;
+ }
}
if (global.singletonTagOrderStore === undefined) {