diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index d52bbbb0d0..dc3893785d 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -26,6 +26,8 @@ import classNames from 'classnames'; import { _t } from '../../../languageHandler'; import SdkConfig from "../../../SdkConfig"; import IdentityAuthClient from '../../../IdentityAuthClient'; +import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore"; +import {UPDATE_EVENT} from "../../../stores/AsyncStore"; const MessageCase = Object.freeze({ NotLoggedIn: "NotLoggedIn", @@ -100,6 +102,7 @@ export default createReactClass({ componentDidMount: function() { this._checkInvitedEmail(); + CommunityPrototypeStore.instance.on(UPDATE_EVENT, this._onCommunityUpdate); }, componentDidUpdate: function(prevProps, prevState) { @@ -108,6 +111,10 @@ export default createReactClass({ } }, + componentWillUnmount: function() { + CommunityPrototypeStore.instance.off(UPDATE_EVENT, this._onCommunityUpdate); + }, + _checkInvitedEmail: async function() { // If this is an invite and we've been told what email address was // invited, fetch the user's account emails and discovery bindings so we @@ -143,6 +150,13 @@ export default createReactClass({ } }, + _onCommunityUpdate: function (roomId) { + if (this.props.room && this.props.room.roomId !== roomId) { + return; + } + this.forceUpdate(); // we have nothing to update + }, + _getMessageCase() { const isGuest = MatrixClientPeg.get().isGuest(); @@ -219,8 +233,15 @@ export default createReactClass({ } }, + _communityProfile: function() { + if (this.props.room) return CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId); + return {displayName: null, avatarMxc: null}; + }, + _roomName: function(atStart = false) { - const name = this.props.room ? this.props.room.name : this.props.roomAlias; + let name = this.props.room ? this.props.room.name : this.props.roomAlias; + const profile = this._communityProfile(); + if (profile.displayName) name = profile.displayName; if (name) { return name; } else if (atStart) { @@ -439,7 +460,10 @@ export default createReactClass({ } case MessageCase.Invite: { const RoomAvatar = sdk.getComponent("views.avatars.RoomAvatar"); - const avatar = ; + const oobData = Object.assign({}, this.props.oobData, { + avatarUrl: this._communityProfile().avatarMxc, + }); + const avatar = ; const inviteMember = this._getInviteMember(); let inviterElement; diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 0c99b98e1a..a09853d762 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -27,7 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; import ActiveRoomObserver from "../../../ActiveRoomObserver"; import { _t } from "../../../languageHandler"; -import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu"; +import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextMenu"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; @@ -47,8 +47,11 @@ import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber" import IconizedContextMenu, { IconizedContextMenuCheckbox, IconizedContextMenuOption, - IconizedContextMenuOptionList, IconizedContextMenuRadio + IconizedContextMenuOptionList, + IconizedContextMenuRadio } from "../context_menus/IconizedContextMenu"; +import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore"; +import { UPDATE_EVENT } from "../../../stores/AsyncStore"; interface IProps { room: Room; @@ -101,6 +104,7 @@ export default class RoomTile extends React.PureComponent { this.notificationState.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); this.roomProps = EchoChamber.forRoom(this.props.room); this.roomProps.on(PROPERTY_UPDATED, this.onRoomPropertyUpdate); + CommunityPrototypeStore.instance.on(UPDATE_EVENT, this.onCommunityUpdate); } private onNotificationUpdate = () => { @@ -140,6 +144,7 @@ export default class RoomTile extends React.PureComponent { defaultDispatcher.unregister(this.dispatcherRef); MessagePreviewStore.instance.off(ROOM_PREVIEW_CHANGED, this.onRoomPreviewChanged); this.notificationState.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); + CommunityPrototypeStore.instance.off(UPDATE_EVENT, this.onCommunityUpdate); } private onAction = (payload: ActionPayload) => { @@ -150,6 +155,11 @@ export default class RoomTile extends React.PureComponent { } }; + private onCommunityUpdate = (roomId: string) => { + if (roomId !== this.props.room.roomId) return; + this.forceUpdate(); // we don't have anything to actually update + }; + private onRoomPreviewChanged = (room: Room) => { if (this.props.room && room.roomId === this.props.room.roomId) { // generatePreview() will return nothing if the user has previews disabled @@ -461,11 +471,21 @@ export default class RoomTile extends React.PureComponent { 'mx_RoomTile_minimized': this.props.isMinimized, }); + let roomProfile: IRoomProfile = {displayName: null, avatarMxc: null}; + if (this.props.tag === DefaultTagID.Invite) { + roomProfile = CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId); + } + + let name = roomProfile.displayName || this.props.room.name; + if (typeof name !== 'string') name = ''; + name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon + const roomAvatar = ; let badge: React.ReactNode; @@ -482,10 +502,6 @@ export default class RoomTile extends React.PureComponent { ); } - 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 messagePreview = null; if (this.showMessagePreview && this.state.messagePreview) { messagePreview = ( diff --git a/src/stores/CommunityPrototypeStore.ts b/src/stores/CommunityPrototypeStore.ts new file mode 100644 index 0000000000..581f8a97c8 --- /dev/null +++ b/src/stores/CommunityPrototypeStore.ts @@ -0,0 +1,100 @@ +/* +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 { AsyncStoreWithClient } from "./AsyncStoreWithClient"; +import defaultDispatcher from "../dispatcher/dispatcher"; +import { ActionPayload } from "../dispatcher/payloads"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { EffectiveMembership, getEffectiveMembership } from "../utils/membership"; +import SettingsStore from "../settings/SettingsStore"; +import * as utils from "matrix-js-sdk/src/utils"; +import { UPDATE_EVENT } from "./AsyncStore"; + +interface IState { + // nothing of value - we use account data +} + +export interface IRoomProfile { + displayName: string; + avatarMxc: string; +} + +export class CommunityPrototypeStore extends AsyncStoreWithClient { + private static internalInstance = new CommunityPrototypeStore(); + + private constructor() { + super(defaultDispatcher, {}); + } + + public static get instance(): CommunityPrototypeStore { + return CommunityPrototypeStore.internalInstance; + } + + protected async onAction(payload: ActionPayload): Promise { + if (!this.matrixClient || !SettingsStore.getValue("feature_communities_v2_prototypes")) { + return; + } + + if (payload.action === "MatrixActions.Room.myMembership") { + const room: Room = payload.room; + const membership = getEffectiveMembership(payload.membership); + const oldMembership = getEffectiveMembership(payload.oldMembership); + if (membership === oldMembership) return; + + if (membership === EffectiveMembership.Invite) { + try { + const path = utils.encodeUri("/rooms/$roomId/group_info", {$roomId: room.roomId}); + const profile = await this.matrixClient._http.authedRequest( + undefined, "GET", path, + undefined, undefined, + {prefix: "/_matrix/client/unstable/im.vector.custom"}); + // we use global account data because per-room account data on invites is unreliable + await this.matrixClient.setAccountData("im.vector.group_info." + room.roomId, profile); + } catch (e) { + console.warn("Non-fatal error getting group information for invite:", e); + } + } + } else if (payload.action === "MatrixActions.accountData") { + if (payload.event_type.startsWith("im.vector.group_info.")) { + this.emit(UPDATE_EVENT, payload.event_type.substring("im.vector.group_info.".length)); + } + } + } + + public getInviteProfile(roomId: string): IRoomProfile { + if (!this.matrixClient) return {displayName: null, avatarMxc: null}; + const room = this.matrixClient.getRoom(roomId); + if (SettingsStore.getValue("feature_communities_v2_prototypes")) { + const data = this.matrixClient.getAccountData("im.vector.group_info." + roomId); + if (data && data.getContent()) { + return {displayName: data.getContent().name, avatarMxc: data.getContent().avatar_url}; + } + } + return {displayName: room.name, avatarMxc: room.avatar_url}; + } + + protected async onReady(): Promise { + for (const room of this.matrixClient.getRooms()) { + const myMember = room.currentState.getMembers().find(m => m.userId === this.matrixClient.getUserId()); + if (!myMember) continue; + if (getEffectiveMembership(myMember.membership) === EffectiveMembership.Invite) { + // Fake an update for anything that might have started listening before the invite + // data was available (eg: RoomPreviewBar after a refresh) + this.emit(UPDATE_EVENT, room.roomId); + } + } + } +}