diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 387879ea7b..25e1153fce 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -26,23 +26,3 @@ limitations under the License. margin: 0 -10px 0 0; padding: 0 10px 0 0; } - -.mx_MainSplit > .mx_ResizeHandle_horizontal:hover { - position: relative; - - &::before { - position: absolute; - left: 4px; - top: 50%; - transform: translate(0, -50%); - - height: 30%; - width: 4px; - border-radius: 4px; - - content: ' '; - - background-color: $primary-fg-color; - opacity: 0.8; - } -} diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 926d10ee04..08ed9e5559 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -78,23 +78,3 @@ limitations under the License. */ height: 100%; } - -.mx_MatrixChat > .mx_ResizeHandle_horizontal:hover { - position: relative; - - &::before { - position: absolute; - left: -2px; - top: 50%; - transform: translate(0, -50%); - - height: 30%; - width: 4px; - border-radius: 4px; - - content: ' '; - - background-color: $primary-fg-color; - opacity: 0.8; - } -} diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index dd805805ab..6332079831 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -97,7 +97,7 @@ $irc-line-height: $font-18px; } > .mx_EventTile_e2eIcon { - position: relative; + position: absolute; right: unset; left: unset; top: 0; diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 11c955749d..ed69dd2204 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -43,6 +43,7 @@ import SdkConfig from "./SdkConfig"; import { ensureDMExists } from "./createRoom"; import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; import { Action } from "./dispatcher/actions"; +import { EffectiveMembership, getEffectiveMembership } from "./utils/membership"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -730,9 +731,11 @@ export const Commands = [ const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); if (!room) return reject(_t("Command failed")); - + const member = room.getMember(args); + if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) { + return reject(_t("Could not find user in room")); + } const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', ''); - if (!powerLevelEvent.getContent().users[args]) return reject(_t("Could not find user in room")); return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent)); } } diff --git a/src/accessibility/RovingTabIndex.tsx b/src/accessibility/RovingTabIndex.tsx index 388d67d9f3..5a650d4b6e 100644 --- a/src/accessibility/RovingTabIndex.tsx +++ b/src/accessibility/RovingTabIndex.tsx @@ -23,12 +23,11 @@ import React, { useRef, useReducer, Reducer, - RefObject, Dispatch, } from "react"; import {Key} from "../Keyboard"; -import AccessibleButton from "../components/views/elements/AccessibleButton"; +import {FocusHandler, Ref} from "./roving/types"; /** * Module to simplify implementing the Roving TabIndex accessibility technique @@ -45,9 +44,7 @@ import AccessibleButton from "../components/views/elements/AccessibleButton"; const DOCUMENT_POSITION_PRECEDING = 2; -type Ref = RefObject; - -interface IState { +export interface IState { activeRef: Ref; refs: Ref[]; } @@ -156,7 +153,7 @@ interface IProps { children(renderProps: { onKeyDownHandler(ev: React.KeyboardEvent); }); - onKeyDown?(ev: React.KeyboardEvent); + onKeyDown?(ev: React.KeyboardEvent, state: IState); } export const RovingTabIndexProvider: React.FC = ({children, handleHomeEnd, onKeyDown}) => { @@ -193,7 +190,7 @@ export const RovingTabIndexProvider: React.FC = ({children, handleHomeEn ev.preventDefault(); ev.stopPropagation(); } else if (onKeyDown) { - return onKeyDown(ev); + return onKeyDown(ev, state); } }, [context.state, onKeyDown, handleHomeEnd]); @@ -202,8 +199,6 @@ export const RovingTabIndexProvider: React.FC = ({children, handleHomeEn ; }; -type FocusHandler = () => void; - // Hook to register a roving tab index // inputRef parameter specifies the ref to use // onFocus should be called when the index gained focus in any manner @@ -244,28 +239,7 @@ export const useRovingTabIndex = (inputRef: Ref): [FocusHandler, boolean, Ref] = return [onFocus, isActive, ref]; }; -interface IRovingTabIndexWrapperProps { - inputRef?: Ref; - children(renderProps: { - onFocus: FocusHandler; - isActive: boolean; - ref: Ref; - }); -} - -// Wrapper to allow use of useRovingTabIndex outside of React Functional Components. -export const RovingTabIndexWrapper: React.FC = ({children, inputRef}) => { - const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); - return children({onFocus, isActive, ref}); -}; - -interface IRovingAccessibleButtonProps extends React.ComponentProps { - inputRef?: Ref; -} - -// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. -export const RovingAccessibleButton: React.FC = ({inputRef, ...props}) => { - const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); - return ; -}; - +// re-export the semantic helper components for simplicity +export {RovingTabIndexWrapper} from "./roving/RovingTabIndexWrapper"; +export {RovingAccessibleButton} from "./roving/RovingAccessibleButton"; +export {RovingAccessibleTooltipButton} from "./roving/RovingAccessibleTooltipButton"; diff --git a/src/accessibility/Toolbar.tsx b/src/accessibility/Toolbar.tsx new file mode 100644 index 0000000000..0e968461a8 --- /dev/null +++ b/src/accessibility/Toolbar.tsx @@ -0,0 +1,69 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 {IState, RovingTabIndexProvider} from "./RovingTabIndex"; +import {Key} from "../Keyboard"; + +interface IProps extends Omit, "onKeyDown"> { +} + +// This component implements the Toolbar design pattern from the WAI-ARIA Authoring Practices guidelines. +// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar +// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref` +const Toolbar: React.FC = ({children, ...props}) => { + const onKeyDown = (ev: React.KeyboardEvent, state: IState) => { + const target = ev.target as HTMLElement; + let handled = true; + + switch (ev.key) { + case Key.ARROW_UP: + case Key.ARROW_DOWN: + if (target.hasAttribute('aria-haspopup')) { + target.click(); + } + break; + + case Key.ARROW_LEFT: + case Key.ARROW_RIGHT: + if (state.refs.length > 0) { + const i = state.refs.findIndex(r => r === state.activeRef); + const delta = ev.key === Key.ARROW_RIGHT ? 1 : -1; + state.refs.slice((i + delta) % state.refs.length)[0].current.focus(); + } + break; + + // HOME and END are handled by RovingTabIndexProvider + + default: + handled = false; + } + + if (handled) { + ev.preventDefault(); + ev.stopPropagation(); + } + }; + + return + {({onKeyDownHandler}) =>
+ { children } +
} +
; +}; + +export default Toolbar; diff --git a/src/accessibility/roving/RovingAccessibleButton.tsx b/src/accessibility/roving/RovingAccessibleButton.tsx new file mode 100644 index 0000000000..3473ef1bc9 --- /dev/null +++ b/src/accessibility/roving/RovingAccessibleButton.tsx @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 AccessibleButton from "../../components/views/elements/AccessibleButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {Ref} from "./types"; + +interface IProps extends Omit, "onFocus" | "inputRef" | "tabIndex"> { + inputRef?: Ref; +} + +// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. +export const RovingAccessibleButton: React.FC = ({inputRef, ...props}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return ; +}; + diff --git a/src/accessibility/roving/RovingAccessibleTooltipButton.tsx b/src/accessibility/roving/RovingAccessibleTooltipButton.tsx new file mode 100644 index 0000000000..cc824fef22 --- /dev/null +++ b/src/accessibility/roving/RovingAccessibleTooltipButton.tsx @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {Ref} from "./types"; + +interface IProps extends Omit, "onFocus" | "inputRef" | "tabIndex"> { + inputRef?: Ref; +} + +// Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components. +export const RovingAccessibleTooltipButton: React.FC = ({inputRef, ...props}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return ; +}; + diff --git a/src/accessibility/roving/RovingTabIndexWrapper.tsx b/src/accessibility/roving/RovingTabIndexWrapper.tsx new file mode 100644 index 0000000000..c826b74497 --- /dev/null +++ b/src/accessibility/roving/RovingTabIndexWrapper.tsx @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 AccessibleButton from "../../components/views/elements/AccessibleButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {FocusHandler, Ref} from "./types"; + +interface IProps { + inputRef?: Ref; + children(renderProps: { + onFocus: FocusHandler; + isActive: boolean; + ref: Ref; + }); +} + +// Wrapper to allow use of useRovingTabIndex outside of React Functional Components. +export const RovingTabIndexWrapper: React.FC = ({children, inputRef}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return children({onFocus, isActive, ref}); +}; diff --git a/src/accessibility/roving/types.ts b/src/accessibility/roving/types.ts new file mode 100644 index 0000000000..f0a43e5fb8 --- /dev/null +++ b/src/accessibility/roving/types.ts @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 {RefObject} from "react"; + +export type Ref = RefObject; + +export type FocusHandler = () => void; diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index cb1349da4b..62964c5799 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -233,6 +233,9 @@ export class ContextMenu extends React.PureComponent { switch (ev.key) { case Key.TAB: case Key.ESCAPE: + // close on left and right arrows too for when it is a context menu on a + case Key.ARROW_LEFT: + case Key.ARROW_RIGHT: this.props.onFinished(); break; case Key.ARROW_UP: diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 012b518093..7fe1c24062 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -269,7 +269,7 @@ export default class LeftPanel2 extends React.Component { const firstRoom = this.listContainerRef.current.querySelector(".mx_RoomTile2"); if (firstRoom) { firstRoom.click(); - this.onSearch(""); // clear the search field + return true; // to get the field to clear } }; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index c7f3c5266d..920b7e4dec 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -675,12 +675,16 @@ export default class MatrixChat extends React.PureComponent { case 'hide_left_panel': this.setState({ collapseLhs: true, + }, () => { + this.state.resizeNotifier.notifyLeftHandleResized(); }); break; case 'focus_room_filter': // for CtrlOrCmd+K to work by expanding the left panel first case 'show_left_panel': this.setState({ collapseLhs: false, + }, () => { + this.state.resizeNotifier.notifyLeftHandleResized(); }); break; case 'panel_disable': { diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index 517a5f2580..1451630c97 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -28,8 +28,8 @@ import { Action } from "../../dispatcher/actions"; interface IProps { onQueryUpdate: (newQuery: string) => void; isMinimized: boolean; - onVerticalArrow(ev: React.KeyboardEvent); - onEnter(ev: React.KeyboardEvent); + onVerticalArrow(ev: React.KeyboardEvent): void; + onEnter(ev: React.KeyboardEvent): boolean; } interface IState { @@ -107,7 +107,13 @@ export default class RoomSearch extends React.PureComponent { } else if (ev.key === Key.ARROW_UP || ev.key === Key.ARROW_DOWN) { this.props.onVerticalArrow(ev); } else if (ev.key === Key.ENTER) { - this.props.onEnter(ev); + const shouldClear = this.props.onEnter(ev); + if (shouldClear) { + // wrap in set immediate to delay it so that we don't clear the filter & then change room + setImmediate(() => { + this.clearInput(); + }); + } } }; diff --git a/src/components/views/avatars/DecoratedRoomAvatar.tsx b/src/components/views/avatars/DecoratedRoomAvatar.tsx index 80bfac6787..bb737397dc 100644 --- a/src/components/views/avatars/DecoratedRoomAvatar.tsx +++ b/src/components/views/avatars/DecoratedRoomAvatar.tsx @@ -66,7 +66,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent - + {badge} ; } diff --git a/src/components/views/elements/AccessibleTooltipButton.tsx b/src/components/views/elements/AccessibleTooltipButton.tsx index af4316225f..641151bbfd 100644 --- a/src/components/views/elements/AccessibleTooltipButton.tsx +++ b/src/components/views/elements/AccessibleTooltipButton.tsx @@ -16,7 +16,7 @@ limitations under the License. */ import React from 'react'; -import classnames from 'classnames'; +import classNames from 'classnames'; import AccessibleButton from "./AccessibleButton"; import {IProps} from "./AccessibleButton"; @@ -53,15 +53,11 @@ export default class AccessibleTooltipButton extends React.PureComponent :
; return ( diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 95eb37b588..7959ad8a93 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -25,9 +25,12 @@ import dis from '../../../dispatcher/dispatcher'; import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu'; import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; import RoomContext from "../../../contexts/RoomContext"; +import Toolbar from "../../../accessibility/Toolbar"; +import {RovingAccessibleButton, useRovingTabIndex} from "../../../accessibility/RovingTabIndex"; const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange}) => { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + const [onFocus, isActive, ref] = useRovingTabIndex(button); useEffect(() => { onFocusChange(menuDisplayed); }, [onFocusChange, menuDisplayed]); @@ -57,7 +60,9 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo label={_t("Options")} onClick={openMenu} isExpanded={menuDisplayed} - inputRef={button} + inputRef={ref} + onFocus={onFocus} + tabIndex={isActive ? 0 : -1} /> { contextMenu } @@ -66,6 +71,7 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo const ReactButton = ({mxEvent, reactions, onFocusChange}) => { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + const [onFocus, isActive, ref] = useRovingTabIndex(button); useEffect(() => { onFocusChange(menuDisplayed); }, [onFocusChange, menuDisplayed]); @@ -85,7 +91,9 @@ const ReactButton = ({mxEvent, reactions, onFocusChange}) => { label={_t("React")} onClick={openMenu} isExpanded={menuDisplayed} - inputRef={button} + inputRef={ref} + onFocus={onFocus} + tabIndex={isActive ? 0 : -1} /> { contextMenu } @@ -148,8 +156,6 @@ export default class MessageActionBar extends React.PureComponent { }; render() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - let reactButton; let replyButton; let editButton; @@ -161,7 +167,7 @@ export default class MessageActionBar extends React.PureComponent { ); } if (this.context.canReply) { - replyButton = + return {reactButton} {replyButton} {editButton} @@ -188,6 +194,6 @@ export default class MessageActionBar extends React.PureComponent { permalinkCreator={this.props.permalinkCreator} onFocusChange={this.onFocusChange} /> -
; +
; } } diff --git a/src/components/views/rooms/RoomBreadcrumbs2.tsx b/src/components/views/rooms/RoomBreadcrumbs2.tsx index 619ad6da4d..fde24524cd 100644 --- a/src/components/views/rooms/RoomBreadcrumbs2.tsx +++ b/src/components/views/rooms/RoomBreadcrumbs2.tsx @@ -25,7 +25,8 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { CSSTransition } from "react-transition-group"; import RoomListStore from "../../../stores/room-list/RoomListStore2"; import { DefaultTagID } from "../../../stores/room-list/models"; -import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; +import { RovingAccessibleTooltipButton } from "../../../accessibility/RovingTabIndex"; +import Toolbar from "../../../accessibility/Toolbar"; // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 @@ -86,7 +87,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent this.viewRoom(r, i)} @@ -101,7 +102,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent -
+ ); }); @@ -112,9 +113,9 @@ export default class RoomBreadcrumbs2 extends React.PureComponent -
+ {tiles.slice(this.state.skipFirst ? 1 : 0)} -
+ ); } else { diff --git a/src/components/views/rooms/RoomTileIcon.tsx b/src/components/views/rooms/RoomTileIcon.tsx index cd7a18be7e..234840d28d 100644 --- a/src/components/views/rooms/RoomTileIcon.tsx +++ b/src/components/views/rooms/RoomTileIcon.tsx @@ -49,7 +49,6 @@ function tooltipText(variant: Icon) { interface IProps { room: Room; - tag: TagID; } interface IState { @@ -137,10 +136,11 @@ export default class RoomTileIcon extends React.Component { private calculateIcon(): Icon { let icon = Icon.None; - if (this.props.tag === DefaultTagID.DM && this.props.room.getJoinedMemberCount() === 2) { + // We look at the DMRoomMap and not the tag here so that we don't exclude DMs in Favourites + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); + if (otherUserId && this.props.room.getJoinedMemberCount() === 2) { // Track presence, if available if (isPresenceEnabled()) { - const otherUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); if (otherUserId) { this.dmUser = MatrixClientPeg.get().getUser(otherUserId); icon = this.getPresenceIcon(); diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index d66f7f9b05..4368120a2e 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -33,7 +33,6 @@ import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import RoomListLayoutStore from "./RoomListLayoutStore"; import { MarkedExecution } from "../../utils/MarkedExecution"; import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; interface IState { tagsEnabled?: boolean; diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 6bc58ecd57..6f718c09b2 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -158,6 +158,7 @@ export class Algorithm extends EventEmitter { filterCondition.off(FILTER_CHANGED, this.handleFilterChange.bind(this)); if (this.allowedByFilter.has(filterCondition)) { this.allowedByFilter.delete(filterCondition); + this.recalculateFilteredRooms(); // If we removed the last filter, tell consumers that we've "updated" our filtered // view. This will trick them into getting the complete room list. diff --git a/src/stores/room-list/filters/CommunityFilterCondition.ts b/src/stores/room-list/filters/CommunityFilterCondition.ts index 45e65fb4f4..924a85e86a 100644 --- a/src/stores/room-list/filters/CommunityFilterCondition.ts +++ b/src/stores/room-list/filters/CommunityFilterCondition.ts @@ -21,6 +21,7 @@ import { EventEmitter } from "events"; import GroupStore from "../../GroupStore"; import { arrayHasDiff } from "../../../utils/arrays"; import { IDestroyable } from "../../../utils/IDestroyable"; +import DMRoomMap from "../../../utils/DMRoomMap"; /** * A filter condition for the room list which reveals rooms which @@ -28,6 +29,7 @@ import { IDestroyable } from "../../../utils/IDestroyable"; */ export class CommunityFilterCondition extends EventEmitter implements IFilterCondition, IDestroyable { private roomIds: string[] = []; + private userIds: string[] = []; constructor(private community: Group) { super(); @@ -43,15 +45,19 @@ export class CommunityFilterCondition extends EventEmitter implements IFilterCon } public isVisible(room: Room): boolean { - return this.roomIds.includes(room.roomId); + return this.roomIds.includes(room.roomId) || + this.userIds.includes(DMRoomMap.shared().getUserIdForRoomId(room.roomId)); } private onStoreUpdate = async (): Promise => { - // We don't actually know if the room list changed for the community, so just - // check it again. + // We don't actually know if the room list changed for the community, so just check it again. const beforeRoomIds = this.roomIds; this.roomIds = (await GroupStore.getGroupRooms(this.community.groupId)).map(r => r.roomId); - if (arrayHasDiff(beforeRoomIds, this.roomIds)) { + + const beforeUserIds = this.userIds; + this.userIds = (await GroupStore.getGroupMembers(this.community.groupId)).map(u => u.userId); + + if (arrayHasDiff(beforeRoomIds, this.roomIds) || arrayHasDiff(beforeUserIds, this.userIds)) { this.emit(FILTER_CHANGED); } };