From 3bcb3195c4bc5b59fe3a792605caa5b58f9094a5 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 30 Nov 2017 14:48:54 +0000 Subject: [PATCH] Implement shift-click and ctrl-click semantics for TP --- src/components/structures/TagPanel.js | 6 +++ src/stores/FilterStore.js | 60 ++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/components/structures/TagPanel.js b/src/components/structures/TagPanel.js index 242b71cd16..a275f5864a 100644 --- a/src/components/structures/TagPanel.js +++ b/src/components/structures/TagPanel.js @@ -50,6 +50,8 @@ const TagTile = React.createClass({ dis.dispatch({ action: 'select_tag', tag: this.props.groupProfile.groupId, + ctrlOrCmdKey: e.metaKey || e.ctrlKey, + shiftKey: e.shiftKey, }); }, @@ -148,6 +150,10 @@ export default React.createClass({ const joinedGroupProfiles = await Promise.all(joinedGroupIds.map( (groupId) => FlairStore.getGroupProfileCached(this.context.matrixClient, groupId), )); + dis.dispatch({ + action: 'all_tags', + tags: joinedGroupIds, + }); this.setState({joinedGroupProfiles}); }, diff --git a/src/stores/FilterStore.js b/src/stores/FilterStore.js index 6e2a7f4739..8078a13ffd 100644 --- a/src/stores/FilterStore.js +++ b/src/stores/FilterStore.js @@ -18,7 +18,10 @@ import dis from '../dispatcher'; import Analytics from '../Analytics'; const INITIAL_STATE = { - tags: [], + allTags: [], + selectedTags: [], + // Last selected tag when shift was not being pressed + anchorTag: null, }; /** @@ -39,15 +42,62 @@ class FilterStore extends Store { __onDispatch(payload) { switch (payload.action) { - case 'select_tag': + case 'all_tags' : this._setState({ - tags: [payload.tag], + 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({ - tags: [], + selectedTags: [], }); Analytics.trackEvent('FilterStore', 'deselect_tags'); break; @@ -55,7 +105,7 @@ class FilterStore extends Store { } getSelectedTags() { - return this._state.tags; + return this._state.selectedTags; } }