From 354089b1c39e0ee656192d772d25218903546711 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 12:40:20 +0100 Subject: [PATCH 01/23] Show rooms in common with another user --- res/css/_components.scss | 1 + .../right_panel/_UserInfoSharedRooms.scss | 7 ++ src/components/views/right_panel/UserInfo.js | 5 + .../views/right_panel/UserInfoSharedRooms.tsx | 100 ++++++++++++++++++ src/components/views/rooms/RoomTile.js | 6 +- src/i18n/strings/en_EN.json | 3 + src/settings/Settings.js | 6 ++ 7 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 res/css/views/right_panel/_UserInfoSharedRooms.scss create mode 100644 src/components/views/right_panel/UserInfoSharedRooms.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 8288cf34f64..1a0a1e2af6a 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -154,6 +154,7 @@ @import "./views/messages/_common_CryptoEvent.scss"; @import "./views/right_panel/_EncryptionInfo.scss"; @import "./views/right_panel/_UserInfo.scss"; +@import "./views/right_panel/_UserInfoSharedRooms.scss"; @import "./views/right_panel/_VerificationPanel.scss"; @import "./views/room_settings/_AliasSettings.scss"; @import "./views/room_settings/_ColorSettings.scss"; diff --git a/res/css/views/right_panel/_UserInfoSharedRooms.scss b/res/css/views/right_panel/_UserInfoSharedRooms.scss new file mode 100644 index 00000000000..e86d6822da2 --- /dev/null +++ b/res/css/views/right_panel/_UserInfoSharedRooms.scss @@ -0,0 +1,7 @@ +.mx_UserInfoSharedRooms ul { + padding-left: 0px; + > li { + padding-left: 0px; + list-style: none; + } +} \ No newline at end of file diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 1fd5221cdb3..62448abb553 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -45,6 +45,7 @@ import EncryptionPanel from "./EncryptionPanel"; import { useAsyncMemo } from '../../../hooks/useAsyncMemo'; import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification'; import {Action} from "../../../dispatcher/actions"; +import UserInfoSharedRooms from "./UserInfoSharedRooms"; const _disambiguateDevices = (devices) => { const names = Object.create(null); @@ -1310,6 +1311,8 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => { const isMe = member.userId === cli.getUserId(); const canVerify = homeserverSupportsCrossSigning && !userVerified && !isMe; + const isSharedRoomsFeatureEnabled = SettingsStore.isFeatureEnabled("feature_show_shared_rooms"); + const setUpdating = (updating) => { setPendingUpdateCount(count => count + (updating ? 1 : -1)); }; @@ -1361,6 +1364,8 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => { } { securitySection } + { isSharedRoomsFeatureEnabled &&!isMe && } { + + constructor(props: IProps) { + super(props); + + this.state = { + error: false, + }; + } + + async componentDidMount() { + try { + const roomIds = await MatrixClientPeg.get()._unstable_getSharedRooms(this.props.userId); + this.setState({roomIds}); + } catch (ex) { + console.log(`Failed to get shared rooms for ${this.props.userId}`, ex); + this.setState({ error: true }); + } + } + + private onRoomTileClick(roomId) { + dis.dispatch({ + action: 'view_room', + show_room_tile: true, // to make sure the room gets scrolled into view + room_id: roomId, + }); + }; + + private renderRoomTile(roomId) { + const peg = MatrixClientPeg.get(); + const room = peg.getRoom(roomId); + if (!room) { + return roomId; + } + return + } + + render(): React.ReactNode { + let content; + if (this.state.roomIds && this.state.roomIds.length > 0) { + content =
    + {this.state.roomIds.map((roomId) =>
  • {this.renderRoomTile(roomId)}
  • )} +
; + } else if (this.state.roomIds) { + content =

{_t("You share no rooms in common with this user.")}

+ } else if (this.state.error) { + content =

{_t("There was an error fetching shared rooms with this user.")}

+ } else { + // We're still loading + content = + } + return
+

{ _t("Shared Rooms") }

+
    + {content} +
+
; + } +} \ No newline at end of file diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 5917f2ae778..7949d8c5f90 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -44,7 +44,7 @@ export default createReactClass({ propTypes: { onClick: PropTypes.func, - + refreshSubList: PropTypes.func, room: PropTypes.object.isRequired, collapsed: PropTypes.bool.isRequired, unread: PropTypes.bool.isRequired, @@ -385,7 +385,9 @@ export default createReactClass({ this.setState({ contextMenuPosition: null, }); - this.props.refreshSubList(); + if (this.props.refreshSubList) { + this.props.refreshSubList(); + } }, render: function() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b23264a297a..206b2b94fd1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1359,6 +1359,9 @@ "Failed to deactivate user": "Failed to deactivate user", "This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.", "Security": "Security", + "You share no rooms in common with this user.": "You share no rooms in common with this user.", + "There was an error fetching shared rooms with this user.": "There was an error fetching shared rooms with this user.", + "Shared Rooms": "Shared Rooms", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.", "Verify by scanning": "Verify by scanning", "Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 58d9ed4f314..50cd589b2ac 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -165,6 +165,12 @@ export const SETTINGS = { default: false, isFeature: true, }, + "feature_show_shared_rooms": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Show rooms in common with another user in the member info panel'), + default: false, + isFeature: true, + }, "mjolnirRooms": { supportedLevels: ['account'], default: [], From e901aaea58119b810994c50dfc7abb5925d7ee23 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 13:54:13 +0100 Subject: [PATCH 02/23] Fix lint --- .../right_panel/_UserInfoSharedRooms.scss | 2 +- .../views/right_panel/UserInfoSharedRooms.tsx | 42 +++++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/res/css/views/right_panel/_UserInfoSharedRooms.scss b/res/css/views/right_panel/_UserInfoSharedRooms.scss index e86d6822da2..63d3724b213 100644 --- a/res/css/views/right_panel/_UserInfoSharedRooms.scss +++ b/res/css/views/right_panel/_UserInfoSharedRooms.scss @@ -4,4 +4,4 @@ padding-left: 0px; list-style: none; } -} \ No newline at end of file +} diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index da4d4fca3aa..2c34eeeb239 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -27,7 +27,7 @@ interface IProps { interface IState { roomIds?: []; - error: boolean, + error: boolean; } export default class UserInfoSharedRooms extends React.PureComponent { @@ -43,7 +43,7 @@ export default class UserInfoSharedRooms extends React.PureComponent{roomId}; + } + const tombstone = room.getStateEvents("m.room.tombstone", ""); + if (tombstone) { + return null; } - return + return
  • + +
  • ; } render(): React.ReactNode { let content; if (this.state.roomIds && this.state.roomIds.length > 0) { content =
      - {this.state.roomIds.map((roomId) =>
    • {this.renderRoomTile(roomId)}
    • )} + {this.state.roomIds.map((roomId) => this.renderRoomTile(roomId))}
    ; } else if (this.state.roomIds) { - content =

    {_t("You share no rooms in common with this user.")}

    + content =

    {_t("You share no rooms in common with this user.")}

    ; } else if (this.state.error) { - content =

    {_t("There was an error fetching shared rooms with this user.")}

    + content =

    {_t("There was an error fetching shared rooms with this user.")}

    ; } else { // We're still loading - content = + content = ; } return

    { _t("Shared Rooms") }

    From d06b90bba0862c15ad577876eb387224fad34daf Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 13:54:49 +0100 Subject: [PATCH 03/23] Add strings --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 206b2b94fd1..373ac32c8d3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -490,6 +490,7 @@ "Use the improved room list (will refresh to apply changes)": "Use the improved room list (will refresh to apply changes)", "Support adding custom themes": "Support adding custom themes", "Enable IRC layout option in the appearance tab": "Enable IRC layout option in the appearance tab", + "Show rooms in common with another user in the member info panel": "Show rooms in common with another user in the member info panel", "Show info about bridges in room settings": "Show info about bridges in room settings", "Font size": "Font size", "Use custom size": "Use custom size", From f6d2e49ac2087b1001f5cd2593b524791cc9c15b Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 15:43:22 +0100 Subject: [PATCH 04/23] Add "compact" view for SharedRooms view --- .../views/right_panel/UserInfoSharedRooms.tsx | 48 +++++++++++++++++-- src/i18n/strings/en_EN.json | 2 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 2c34eeeb239..48a77692da6 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -20,9 +20,12 @@ import Spinner from "../elements/Spinner"; import RoomTile from "../rooms/RoomTile"; import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher/dispatcher'; +import Pill from '../../views/elements/Pill'; +import SpecPermalinkConstructor from '../../../utils/permalinks/SpecPermalinkConstructor'; interface IProps { userId: string; + compact: boolean; } interface IState { @@ -30,6 +33,8 @@ interface IState { error: boolean; } +const COMPACT_VIEW_SHOW_COUNT = 3; + export default class UserInfoSharedRooms extends React.PureComponent { constructor(props: IProps) { @@ -64,10 +69,29 @@ export default class UserInfoSharedRooms extends React.PureComponent{roomId}; } - const tombstone = room.getStateEvents("m.room.tombstone", ""); + const tombstone = room.currentState.getStateEvents("m.room.tombstone", ""); if (tombstone) { return null; } + + if (this.props.compact) { + // XXX: This is inefficent as we only render COMPACT_VIEW_SHOW_COUNT rooms at a time, the other pills are wasted. + const alias = room.getCanonicalAlias(); + if (!alias) { + // Without an alias, we get ugly room_ids. + return null; + } + return ; + } + return
  • 0) { - content =
      - {this.state.roomIds.map((roomId) => this.renderRoomTile(roomId))} -
    ; + content = this.state.roomIds.map((roomId) => this.renderRoomTile(roomId)); } else if (this.state.roomIds) { content =

    {_t("You share no rooms in common with this user.")}

    ; } else if (this.state.error) { @@ -96,6 +119,23 @@ export default class UserInfoSharedRooms extends React.PureComponent; } + + // Compact view: Show as a single line. + if (this.props.compact && content.length) { + if (content.length <= COMPACT_VIEW_SHOW_COUNT) { + return

    {_t("You are both participating in ", {}, {rooms: content})}

    + } else { + return

    {_t("You are both participating in and %(hidden)s more", { + hidden: content.length - COMPACT_VIEW_SHOW_COUNT, + }, { + rooms: content.slice(0, COMPACT_VIEW_SHOW_COUNT) + })}

    + } + } else if (this.props.compact) { + return content; + } + + // Normal view: Show as a list with a header return

    { _t("Shared Rooms") }

      diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 373ac32c8d3..2146d5a9149 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1362,6 +1362,8 @@ "Security": "Security", "You share no rooms in common with this user.": "You share no rooms in common with this user.", "There was an error fetching shared rooms with this user.": "There was an error fetching shared rooms with this user.", + "You are both participating in ": "You are both participating in ", + "You are both participating in and %(hidden)s more": "You are both participating in and %(hidden)s more", "Shared Rooms": "Shared Rooms", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.", "Verify by scanning": "Verify by scanning", From 9aed1c04fb76d1f6a804de28fe2606b158b5fbd8 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 15:43:46 +0100 Subject: [PATCH 05/23] Show shared rooms to users when being invited --- src/components/views/rooms/RoomPreviewBar.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 30e6ae9c586..c41d24470a7 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -25,6 +25,8 @@ import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; import { _t } from '../../../languageHandler'; import IdentityAuthClient from '../../../IdentityAuthClient'; +import SettingsStore from '../../../settings/SettingsStore'; +import UserInfoSharedRooms from '../right_panel/UserInfoSharedRooms'; const MessageCase = Object.freeze({ NotLoggedIn: "NotLoggedIn", @@ -294,6 +296,7 @@ export default createReactClass({ let secondaryActionHandler; let secondaryActionLabel; let footer; + let extraContext; const extraComponents = []; const messageCase = this._getMessageCase(); @@ -473,6 +476,10 @@ export default createReactClass({ secondaryActionLabel = _t("Reject"); secondaryActionHandler = this.props.onRejectClick; + if (SettingsStore.isFeatureEnabled("feature_show_shared_rooms")) { + extraContext = ; + } + if (this.props.onRejectAndIgnoreClick) { extraComponents.push( @@ -560,6 +567,7 @@ export default createReactClass({
      { titleElement } { subTitleElements } + { extraContext }
      { secondaryButton } From db2b642dc70aee12458364ab92f57fa7432ce50e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jul 2020 15:46:13 +0100 Subject: [PATCH 06/23] Lint --- .../views/right_panel/UserInfoSharedRooms.tsx | 10 +++++----- src/components/views/rooms/RoomPreviewBar.js | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 48a77692da6..64486aa5fc7 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -108,7 +108,7 @@ export default class UserInfoSharedRooms extends React.PureComponent 0) { content = this.state.roomIds.map((roomId) => this.renderRoomTile(roomId)); } else if (this.state.roomIds) { @@ -119,22 +119,22 @@ export default class UserInfoSharedRooms extends React.PureComponent; } - + // Compact view: Show as a single line. if (this.props.compact && content.length) { if (content.length <= COMPACT_VIEW_SHOW_COUNT) { - return

      {_t("You are both participating in ", {}, {rooms: content})}

      + return

      {_t("You are both participating in ", {}, {rooms: content})}

      ; } else { return

      {_t("You are both participating in and %(hidden)s more", { hidden: content.length - COMPACT_VIEW_SHOW_COUNT, }, { rooms: content.slice(0, COMPACT_VIEW_SHOW_COUNT) - })}

      + })}

      ; } } else if (this.props.compact) { return content; } - + // Normal view: Show as a list with a header return

      { _t("Shared Rooms") }

      diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index c41d24470a7..9fc2d8f9251 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -487,6 +487,7 @@ export default createReactClass({ , ); } + break; } case MessageCase.ViewingRoom: { From e6d2c895eff2b9de5f8e39010f2eca4d5c618a07 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 11:55:06 +0100 Subject: [PATCH 07/23] Typescriptify RoomListSorter --- src/{RoomListSorter.js => RoomListSorter.ts} | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename src/{RoomListSorter.js => RoomListSorter.ts} (89%) diff --git a/src/RoomListSorter.js b/src/RoomListSorter.ts similarity index 89% rename from src/RoomListSorter.js rename to src/RoomListSorter.ts index 0ff37a6af22..552caadb722 100644 --- a/src/RoomListSorter.js +++ b/src/RoomListSorter.ts @@ -14,9 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - -function tsOfNewestEvent(room) { +function tsOfNewestEvent(room: any) { if (room.timeline.length) { return room.timeline[room.timeline.length - 1].getTs(); } else { @@ -24,7 +22,7 @@ function tsOfNewestEvent(room) { } } -export function mostRecentActivityFirst(roomList) { +export function mostRecentActivityFirst(roomList: any[]) { return roomList.sort(function(a, b) { return tsOfNewestEvent(b) - tsOfNewestEvent(a); }); From 29ce3818f8d3ded287feb882b627e9fc95ed1627 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 11:55:49 +0100 Subject: [PATCH 08/23] Order by activity --- .../views/right_panel/UserInfoSharedRooms.tsx | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 64486aa5fc7..9e0dec8c9ac 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -22,6 +22,7 @@ import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher/dispatcher'; import Pill from '../../views/elements/Pill'; import SpecPermalinkConstructor from '../../../utils/permalinks/SpecPermalinkConstructor'; +import { mostRecentActivityFirst } from '../../../RoomListSorter'; interface IProps { userId: string; @@ -66,9 +67,13 @@ export default class UserInfoSharedRooms extends React.PureComponent{roomId}; + return null; } + + // If the room has been upgraded, hide it. const tombstone = room.currentState.getStateEvents("m.room.tombstone", ""); if (tombstone) { return null; @@ -78,11 +83,11 @@ export default class UserInfoSharedRooms extends React.PureComponent; } - return
    • + return
    • ; } + private renderRoomTiles() { + const peg = MatrixClientPeg.get(); + const orderedActiveRooms = mostRecentActivityFirst(this.state.roomIds.map( + (roomId) => peg.getRoom(roomId) + )); + + // We must remove the null values in order for the slice to work in render() + return orderedActiveRooms.map((room) => this.renderRoomTile(room)).filter((tile => tile !== null)); + } + render(): React.ReactNode { let content; if (this.state.roomIds && this.state.roomIds.length > 0) { - content = this.state.roomIds.map((roomId) => this.renderRoomTile(roomId)); + content = this.renderRoomTiles(); } else if (this.state.roomIds) { content =

      {_t("You share no rooms in common with this user.")}

      ; } else if (this.state.error) { From 23fe7d06fcfcda14514faadade530118aa3bba52 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 11:55:57 +0100 Subject: [PATCH 09/23] Limit to 3 rooms, but allow expansion --- .../views/right_panel/UserInfoSharedRooms.tsx | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 9e0dec8c9ac..460fc10eb6c 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -21,6 +21,7 @@ import RoomTile from "../rooms/RoomTile"; import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher/dispatcher'; import Pill from '../../views/elements/Pill'; +import AccessibleButton from '../../views/elements/AccessibleButton'; import SpecPermalinkConstructor from '../../../utils/permalinks/SpecPermalinkConstructor'; import { mostRecentActivityFirst } from '../../../RoomListSorter'; @@ -32,9 +33,10 @@ interface IProps { interface IState { roomIds?: []; error: boolean; + showAll: boolean, } -const COMPACT_VIEW_SHOW_COUNT = 3; +const LIMITED_VIEW_SHOW_COUNT = 3; export default class UserInfoSharedRooms extends React.PureComponent { @@ -43,6 +45,7 @@ export default class UserInfoSharedRooms extends React.PureComponent 0) { content = this.renderRoomTiles(); + realCount = content.length; + if (!this.state.showAll) { + content = content.slice(0, LIMITED_VIEW_SHOW_COUNT); + } } else if (this.state.roomIds) { content =

      {_t("You share no rooms in common with this user.")}

      ; } else if (this.state.error) { @@ -137,25 +149,29 @@ export default class UserInfoSharedRooms extends React.PureComponent {_t("You are both participating in ", {}, {rooms: content})}

      ; } else { return

      {_t("You are both participating in and %(hidden)s more", { - hidden: content.length - COMPACT_VIEW_SHOW_COUNT, + hidden: realCount - content.length, }, { - rooms: content.slice(0, COMPACT_VIEW_SHOW_COUNT) + rooms: content })}

      ; } } else if (this.props.compact) { return content; } + const canShowMore = !this.state.showAll && realCount > LIMITED_VIEW_SHOW_COUNT; // Normal view: Show as a list with a header return

      { _t("Shared Rooms") }

        {content}
      + { canShowMore && this.onShowMoreClick()}> + { _t("Show more") } + }
      ; } } \ No newline at end of file From a617eb5f52ffb0cc0e79cdbbca4a1d348f16a10f Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 12:05:38 +0100 Subject: [PATCH 10/23] Fix lint --- src/components/views/right_panel/UserInfoSharedRooms.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 460fc10eb6c..224dc427b42 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -33,7 +33,7 @@ interface IProps { interface IState { roomIds?: []; error: boolean; - showAll: boolean, + showAll: boolean; } const LIMITED_VIEW_SHOW_COUNT = 3; @@ -123,7 +123,7 @@ export default class UserInfoSharedRooms extends React.PureComponent peg.getRoom(roomId) )); - + // We must remove the null values in order for the slice to work in render() return orderedActiveRooms.map((room) => this.renderRoomTile(room)).filter((tile => tile !== null)); } From a3a9f1978d97ce07d2247e8440ff189619235459 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 12:09:24 +0100 Subject: [PATCH 11/23] Show count more --- src/components/views/right_panel/UserInfoSharedRooms.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 224dc427b42..ac7bcd01f40 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -170,7 +170,7 @@ export default class UserInfoSharedRooms extends React.PureComponent { canShowMore && this.onShowMoreClick()}> - { _t("Show more") } + { _t("Show %(count)s more", { count: realCount - content.length}) } }
    • ; } From 81c68aaf51f1f3aede87c6137cc4e79afd213b75 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 6 Jul 2020 12:16:57 +0100 Subject: [PATCH 12/23] Update if a new user is selected --- .../views/right_panel/UserInfoSharedRooms.tsx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index ac7bcd01f40..ec8fc560507 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -49,10 +49,28 @@ export default class UserInfoSharedRooms extends React.PureComponent Date: Tue, 18 Aug 2020 19:31:26 +0100 Subject: [PATCH 13/23] Add UserInfoRoomTile --- .../views/elements/UserInfoRoomTile.tsx | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/components/views/elements/UserInfoRoomTile.tsx diff --git a/src/components/views/elements/UserInfoRoomTile.tsx b/src/components/views/elements/UserInfoRoomTile.tsx new file mode 100644 index 00000000000..f92976ba5cb --- /dev/null +++ b/src/components/views/elements/UserInfoRoomTile.tsx @@ -0,0 +1,126 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 New Vector Ltd +Copyright 2018 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019, 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 { Room } from "matrix-js-sdk/src/models/room"; +import classNames from "classnames"; +import AccessibleButton from "../../views/elements/AccessibleButton"; +import ActiveRoomObserver from "../../../ActiveRoomObserver"; +import { _t } from "../../../languageHandler"; +import { TagID } from "../../../stores/room-list/models"; +import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; +import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; +import dis from '../../../dispatcher/dispatcher'; +import { Key } from "../../../Keyboard"; + +interface IProps { + room: Room; +} + +type PartialDOMRect = Pick; + +interface IState { + selected: boolean; + notificationsMenuPosition: PartialDOMRect; + generalMenuPosition: PartialDOMRect; + messagePreview?: string; +} + +export default class UserInfoRoomTile extends React.PureComponent { + constructor(props: IProps) { + super(props); + + this.state = { + selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId, + notificationsMenuPosition: null, + generalMenuPosition: null, + }; + } + + private onTileClick = (ev: React.KeyboardEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + dis.dispatch({ + action: 'view_room', + show_room_tile: true, // make sure the room is visible in the list + room_id: this.props.room.roomId, + clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)), + }); + }; + + public render(): React.ReactElement { + const classes = classNames({ + 'mx_RoomTile': true, + 'mx_RoomTile_selected': this.state.selected, + }); + + const roomAvatar = ; + + let badge: React.ReactNode; + + let name = this.props.room.name; + if (typeof name !== 'string') name = ''; + name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon + + let nameContainer = ( +
      +
      + {name} +
      +
      + ); + + if (this.props.isMinimized) nameContainer = null; + + let ariaLabel = name; + let ariaDescribedBy: string; + + const props: Partial> = {}; + let Button: React.ComponentType> = AccessibleButton; + if (this.props.isMinimized) { + Button = AccessibleTooltipButton; + props.title = name; + // force the tooltip to hide whilst we are showing the context menu + props.forceHide = !!this.state.generalMenuPosition; + } + + return ( + + + + ); + } +} \ No newline at end of file From f9cfa603dff780f97435357ecb8e8e3b9ae8c18d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 18 Aug 2020 19:31:39 +0100 Subject: [PATCH 14/23] Refactor to work with new room tiles --- .../views/right_panel/UserInfoSharedRooms.tsx | 22 ++----------------- src/i18n/strings/en_EN.json | 6 +++++ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index ec8fc560507..688f25ecb61 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -17,13 +17,12 @@ limitations under the License. import React from 'react'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import Spinner from "../elements/Spinner"; -import RoomTile from "../rooms/RoomTile"; import { _t } from '../../../languageHandler'; -import dis from '../../../dispatcher/dispatcher'; import Pill from '../../views/elements/Pill'; import AccessibleButton from '../../views/elements/AccessibleButton'; import SpecPermalinkConstructor from '../../../utils/permalinks/SpecPermalinkConstructor'; import { mostRecentActivityFirst } from '../../../RoomListSorter'; +import UserInfoRoomTile from "../elements/UserInfoRoomTile"; interface IProps { userId: string; @@ -77,14 +76,6 @@ export default class UserInfoSharedRooms extends React.PureComponent - + ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8bfc3ed703d..e63db030bac 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -451,6 +451,7 @@ "Show message previews for reactions in DMs": "Show message previews for reactions in DMs", "Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms", "Enable advanced debugging for the room list": "Enable advanced debugging for the room list", + "Show rooms in common with another user in the member info panel": "Show rooms in common with another user in the member info panel", "Show info about bridges in room settings": "Show info about bridges in room settings", "Font size": "Font size", "Use custom size": "Use custom size", @@ -1328,6 +1329,11 @@ "Failed to deactivate user": "Failed to deactivate user", "This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.", "Security": "Security", + "You share no rooms in common with this user.": "You share no rooms in common with this user.", + "There was an error fetching shared rooms with this user.": "There was an error fetching shared rooms with this user.", + "You are both participating in ": "You are both participating in ", + "You are both participating in and %(hidden)s more": "You are both participating in and %(hidden)s more", + "Shared Rooms": "Shared Rooms", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", "Verify by scanning": "Verify by scanning", "Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:", From 5473f639209381b259fe4fae12c595f2c4ab9784 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 18 Aug 2020 19:37:46 +0100 Subject: [PATCH 15/23] Fix builds --- src/components/views/elements/UserInfoRoomTile.tsx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/UserInfoRoomTile.tsx b/src/components/views/elements/UserInfoRoomTile.tsx index f92976ba5cb..c1a44fce391 100644 --- a/src/components/views/elements/UserInfoRoomTile.tsx +++ b/src/components/views/elements/UserInfoRoomTile.tsx @@ -23,7 +23,7 @@ import classNames from "classnames"; import AccessibleButton from "../../views/elements/AccessibleButton"; import ActiveRoomObserver from "../../../ActiveRoomObserver"; import { _t } from "../../../languageHandler"; -import { TagID } from "../../../stores/room-list/models"; +import { TagID, DefaultTagID } from "../../../stores/room-list/models"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import dis from '../../../dispatcher/dispatcher'; @@ -73,8 +73,8 @@ export default class UserInfoRoomTile extends React.PureComponent; let badge: React.ReactNode; @@ -91,19 +91,11 @@ export default class UserInfoRoomTile extends React.PureComponent ); - if (this.props.isMinimized) nameContainer = null; - let ariaLabel = name; let ariaDescribedBy: string; const props: Partial> = {}; let Button: React.ComponentType> = AccessibleButton; - if (this.props.isMinimized) { - Button = AccessibleTooltipButton; - props.title = name; - // force the tooltip to hide whilst we are showing the context menu - props.forceHide = !!this.state.generalMenuPosition; - } return ( From f73f93702b683edbe37b15c9358fe11d6b17d14f Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 20 Aug 2020 23:07:54 +0100 Subject: [PATCH 16/23] Fix feature code Co-authored-by: Tulir Asokan --- src/components/views/right_panel/UserInfo.js | 2 +- src/components/views/rooms/RoomPreviewBar.js | 2 +- src/settings/Settings.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index f4edb0a36b8..a50833f4d58 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1313,7 +1313,7 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => { const isMe = member.userId === cli.getUserId(); const canVerify = cryptoEnabled && homeserverSupportsCrossSigning && !userVerified && !isMe; - const isSharedRoomsFeatureEnabled = SettingsStore.isFeatureEnabled("feature_show_shared_rooms"); + const isSharedRoomsFeatureEnabled = SettingsStore.getValue("feature_show_shared_rooms"); const setUpdating = (updating) => { setPendingUpdateCount(count => count + (updating ? 1 : -1)); diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index ffe959ad984..ffa04649449 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -479,7 +479,7 @@ export default createReactClass({ secondaryActionLabel = _t("Reject"); secondaryActionHandler = this.props.onRejectClick; - if (SettingsStore.isFeatureEnabled("feature_show_shared_rooms")) { + if (SettingsStore.getValue("feature_show_shared_rooms")) { extraContext = ; } diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index f8f15fd2554..210c1d3e681 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -177,7 +177,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: false, }, "feature_show_shared_rooms": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, + supportedLevels: LEVELS_FEATURE, displayName: _td('Show rooms in common with another user in the member info panel'), default: false, isFeature: true, From 30384ac6d7ffe445ec22c2587d9e591abcd3da2c Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 20 Nov 2020 17:18:52 +0000 Subject: [PATCH 17/23] Apply suggestions from code review begone fragment! Co-authored-by: Resynth --- src/components/views/elements/UserInfoRoomTile.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/UserInfoRoomTile.tsx b/src/components/views/elements/UserInfoRoomTile.tsx index c1a44fce391..a8b9e649d8a 100644 --- a/src/components/views/elements/UserInfoRoomTile.tsx +++ b/src/components/views/elements/UserInfoRoomTile.tsx @@ -98,7 +98,7 @@ export default class UserInfoRoomTile extends React.PureComponent> = AccessibleButton; return ( - + <> - + + { roomAvatar } + { nameContainer } + ); } } diff --git a/src/components/views/right_panel/SharedRoomsList.tsx b/src/components/views/right_panel/SharedRoomsList.tsx index fd45cdfa38e..087d31f72d9 100644 --- a/src/components/views/right_panel/SharedRoomsList.tsx +++ b/src/components/views/right_panel/SharedRoomsList.tsx @@ -23,6 +23,7 @@ import { Room } from "matrix-js-sdk/src/models/room"; import Spinner from "../elements/Spinner"; import TruncatedList from "../elements/TruncatedList"; import UserInfoRoomTile from '../elements/UserInfoRoomTile'; +import { _t } from "../../../languageHandler"; interface IProps { onClose: () => void; @@ -51,33 +52,27 @@ export default class SharedRoomList extends React.PureComponent async componentDidMount() { try { - if (!this.props.userId) { - throw Error('userId is not defined'); - } const rooms = await UserInfoSharedRooms.getSharedRoomsForUser(this.props.userId); - const sortedRooms = await this.algorithm.sortRooms( - rooms, - DefaultTagID.Untagged, - ); - this.setState({rooms: sortedRooms}); + const sortedRooms = await this.algorithm.sortRooms(rooms, DefaultTagID.Untagged); + this.setState({ rooms: sortedRooms }); } catch (ex) { console.log("Error fetching shared rooms for user", ex); - this.setState({error: true}); + this.setState({ error: true }); } } - makeRoomTiles() { - return this.state.rooms.map(r => ); + private makeRoomTiles() { + return this.state.rooms.map(r => ); } - renderContent() { + private renderContent() { if (this.state.error) { // In theory this shouldn't happen, because the button for this view // validates that the client can fetch shared rooms for this user. - return

      Could not fetch shared rooms for user.

      + return

      { _t("Could not fetch shared rooms for user.") }

      ; } if (!this.state.rooms) { - return + return ; } return { this.makeRoomTiles() } diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index 3d0ccab818e..a2e83dacbdb 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -15,11 +15,12 @@ limitations under the License. */ import React from 'react'; +import { Room } from "matrix-js-sdk/src/models/room"; + import { MatrixClientPeg } from '../../../MatrixClientPeg'; import Spinner from "../elements/Spinner"; import { _t } from '../../../languageHandler'; import AccessibleButton from '../../views/elements/AccessibleButton'; -import { Room } from "matrix-js-sdk/src/models/room"; import { SetRightPanelPhasePayload } from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; import { defaultDispatcher } from '../../../dispatcher/dispatcher'; import { Action } from '../../../dispatcher/actions'; @@ -34,13 +35,8 @@ interface IState { error: boolean; } -interface IButtonProps { - className: string; - onClick(): void; -} - export default class UserInfoSharedRooms extends React.PureComponent { - static async getSharedRoomsForUser(userId: string): Promise { + public static async getSharedRoomsForUser(userId: string): Promise { const peg = MatrixClientPeg.get(); const roomIds = await MatrixClientPeg.get()._unstable_getSharedRooms(userId); @@ -89,37 +85,25 @@ export default class UserInfoSharedRooms extends React.PureComponent { + private onShowClicked = () => { defaultDispatcher.dispatch({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.SharedRoomsList, userId: this.props.userId, }); - } + }; render(): React.ReactNode { const { sharedRoomCount } = this.state; if (this.state.error) { - return

      {_t("There was an error fetching shared rooms with this user.")}

      ; - } else if (typeof sharedRoomCount === 'number') { - console.log("Shared room count:", sharedRoomCount); - let text; - if (sharedRoomCount === 1) { - text = _t("1 room in common"); - } else if (sharedRoomCount > 1) { - text = _t("%(count)s rooms in common", { count: sharedRoomCount }); - } else { - text = _t("No rooms in common"); - } - return - {text} - ; + return

      { _t("There was an error fetching shared rooms with this user.") }

      ; } else if (sharedRoomCount === 0) { - return

      {_t("You share no rooms in common with this user.")}

      ; + return

      { _t("You share no rooms in common with this user.") }

      ; + } else if (typeof sharedRoomCount === "number") { + return + { _t("%(count)s rooms in common", { count: sharedRoomCount }) } + ; } else { return ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1ed1bc7184b..879995f04c4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1773,6 +1773,7 @@ "Show files": "Show files", "Share room": "Share room", "Room settings": "Room settings", + "Could not fetch shared rooms for user.": "Could not fetch shared rooms for user.", "Trusted": "Trusted", "Not trusted": "Not trusted", "%(count)s verified sessions|other": "%(count)s verified sessions", @@ -1827,12 +1828,11 @@ "This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.", "Edit devices": "Edit devices", "Security": "Security", + "Shared Rooms": "Shared Rooms", "There was an error fetching shared rooms with this user.": "There was an error fetching shared rooms with this user.", - "1 room in common": "1 room in common", - "%(count)s rooms in common": "%(count)s rooms in common", - "No rooms in common": "No rooms in common", "You share no rooms in common with this user.": "You share no rooms in common with this user.", - "Shared Rooms": "Shared Rooms", + "%(count)s rooms in common|other": "%(count)s rooms in common", + "%(count)s rooms in common|one": "%(count)s room in common", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", "Scan this unique code": "Scan this unique code", "Compare unique emoji": "Compare unique emoji", From 135c2f7d7e0adb2bf7466f50cb31e83f74a50172 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 6 Jul 2021 16:10:41 +0100 Subject: [PATCH 23/23] Iterate PR some more --- src/components/views/right_panel/UserInfo.tsx | 4 +-- .../views/right_panel/UserInfoSharedRooms.tsx | 29 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 1c6a956bd88..280c2492438 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -1404,8 +1404,8 @@ const BasicUserInfo: React.FC<{ if (isSharedRoomsFeatureEnabled && !isMe) { sharedRooms =

      { _t("Shared Rooms") }

      - { } -
      + +
      ; } return diff --git a/src/components/views/right_panel/UserInfoSharedRooms.tsx b/src/components/views/right_panel/UserInfoSharedRooms.tsx index a2e83dacbdb..a3fa1c40be4 100644 --- a/src/components/views/right_panel/UserInfoSharedRooms.tsx +++ b/src/components/views/right_panel/UserInfoSharedRooms.tsx @@ -16,6 +16,7 @@ limitations under the License. import React from 'react'; import { Room } from "matrix-js-sdk/src/models/room"; +import { EventType } from 'matrix-js-sdk/src/@types/event'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import Spinner from "../elements/Spinner"; @@ -37,18 +38,10 @@ interface IState { export default class UserInfoSharedRooms extends React.PureComponent { public static async getSharedRoomsForUser(userId: string): Promise { - const peg = MatrixClientPeg.get(); - - const roomIds = await MatrixClientPeg.get()._unstable_getSharedRooms(userId); - return roomIds.map(roomId => peg.getRoom(roomId)).filter(room => { - if (room === null) { - return false; - } - const tombstone = room.currentState.getStateEvents("m.room.tombstone", ""); - if (tombstone) { - return false; - } - return true; + const client = MatrixClientPeg.get(); + const roomIds = await client._unstable_getSharedRooms(userId); + return roomIds.map(roomId => client.getRoom(roomId)).filter(room => { + return room && !room.currentState.getStateEvents(EventType.RoomTombstone, ""); }); } @@ -64,10 +57,8 @@ export default class UserInfoSharedRooms extends React.PureComponent