Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into t3chguy/notifications/14880
Conflicts: src/@types/global.d.tspull/21833/head
						commit
						764b94c7ae
					
				|  | @ -26,6 +26,7 @@ import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; | |||
| import {IntegrationManagers} from "../integrations/IntegrationManagers"; | ||||
| import {ModalManager} from "../Modal"; | ||||
| import SettingsStore from "../settings/SettingsStore"; | ||||
| import {ActiveRoomObserver} from "../ActiveRoomObserver"; | ||||
| import {Notifier} from "../Notifier"; | ||||
| 
 | ||||
| declare global { | ||||
|  | @ -42,6 +43,7 @@ declare global { | |||
|         mxRebrandListener: RebrandListener; | ||||
|         mxRoomListStore: RoomListStoreClass; | ||||
|         mxRoomListLayoutStore: RoomListLayoutStore; | ||||
|         mxActiveRoomObserver: ActiveRoomObserver; | ||||
|         mxPlatformPeg: PlatformPeg; | ||||
|         mxIntegrationManagers: typeof IntegrationManagers; | ||||
|         singletonModalManager: ModalManager; | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ limitations under the License. | |||
| 
 | ||||
| import RoomViewStore from './stores/RoomViewStore'; | ||||
| 
 | ||||
| type Listener = (isActive: boolean) => void; | ||||
| 
 | ||||
| /** | ||||
|  * Consumes changes from the RoomViewStore and notifies specific things | ||||
|  * about when the active room changes. Unlike listening for RoomViewStore | ||||
|  | @ -25,57 +27,57 @@ import RoomViewStore from './stores/RoomViewStore'; | |||
|  * TODO: If we introduce an observer for something else, factor out | ||||
|  * the adding / removing of listeners & emitting into a common class. | ||||
|  */ | ||||
| class ActiveRoomObserver { | ||||
|     constructor() { | ||||
|         this._listeners = {}; // key=roomId, value=function(isActive:boolean)
 | ||||
| export class ActiveRoomObserver { | ||||
|     private listeners: {[key: string]: Listener[]} = {}; | ||||
|     private _activeRoomId = RoomViewStore.getRoomId(); | ||||
|     private readonly roomStoreToken: string; | ||||
| 
 | ||||
|         this._activeRoomId = RoomViewStore.getRoomId(); | ||||
|         // TODO: We could self-destruct when the last listener goes away, or at least
 | ||||
|         // stop listening.
 | ||||
|         this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this)); | ||||
|     constructor() { | ||||
|         // TODO: We could self-destruct when the last listener goes away, or at least stop listening.
 | ||||
|         this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); | ||||
|     } | ||||
| 
 | ||||
|     get activeRoomId(): string { | ||||
|     public get activeRoomId(): string { | ||||
|         return this._activeRoomId; | ||||
|     } | ||||
| 
 | ||||
|     addListener(roomId, listener) { | ||||
|         if (!this._listeners[roomId]) this._listeners[roomId] = []; | ||||
|         this._listeners[roomId].push(listener); | ||||
|     public addListener(roomId, listener) { | ||||
|         if (!this.listeners[roomId]) this.listeners[roomId] = []; | ||||
|         this.listeners[roomId].push(listener); | ||||
|     } | ||||
| 
 | ||||
|     removeListener(roomId, listener) { | ||||
|         if (this._listeners[roomId]) { | ||||
|             const i = this._listeners[roomId].indexOf(listener); | ||||
|     public removeListener(roomId, listener) { | ||||
|         if (this.listeners[roomId]) { | ||||
|             const i = this.listeners[roomId].indexOf(listener); | ||||
|             if (i > -1) { | ||||
|                 this._listeners[roomId].splice(i, 1); | ||||
|                 this.listeners[roomId].splice(i, 1); | ||||
|             } | ||||
|         } else { | ||||
|             console.warn("Unregistering unrecognised listener (roomId=" + roomId + ")"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _emit(roomId, isActive: boolean) { | ||||
|         if (!this._listeners[roomId]) return; | ||||
|     private emit(roomId, isActive: boolean) { | ||||
|         if (!this.listeners[roomId]) return; | ||||
| 
 | ||||
|         for (const l of this._listeners[roomId]) { | ||||
|         for (const l of this.listeners[roomId]) { | ||||
|             l.call(null, isActive); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _onRoomViewStoreUpdate() { | ||||
|     private onRoomViewStoreUpdate = () => { | ||||
|         // emit for the old room ID
 | ||||
|         if (this._activeRoomId) this._emit(this._activeRoomId, false); | ||||
|         if (this._activeRoomId) this.emit(this._activeRoomId, false); | ||||
| 
 | ||||
|         // update our cache
 | ||||
|         this._activeRoomId = RoomViewStore.getRoomId(); | ||||
| 
 | ||||
|         // and emit for the new one
 | ||||
|         if (this._activeRoomId) this._emit(this._activeRoomId, true); | ||||
|     } | ||||
|         if (this._activeRoomId) this.emit(this._activeRoomId, true); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| if (global.mx_ActiveRoomObserver === undefined) { | ||||
|     global.mx_ActiveRoomObserver = new ActiveRoomObserver(); | ||||
| if (window.mxActiveRoomObserver === undefined) { | ||||
|     window.mxActiveRoomObserver = new ActiveRoomObserver(); | ||||
| } | ||||
| export default global.mx_ActiveRoomObserver; | ||||
| export default window.mxActiveRoomObserver; | ||||
|  | @ -15,13 +15,17 @@ 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 dis from '../dispatcher/dispatcher'; | ||||
| 
 | ||||
| import React from "react"; | ||||
| import {Store} from 'flux/utils'; | ||||
| 
 | ||||
| import dis from '../dispatcher/dispatcher'; | ||||
| import {MatrixClientPeg} from '../MatrixClientPeg'; | ||||
| import * as sdk from '../index'; | ||||
| import Modal from '../Modal'; | ||||
| import { _t } from '../languageHandler'; | ||||
| import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache'; | ||||
| import {ActionPayload} from "../dispatcher/payloads"; | ||||
| 
 | ||||
| const INITIAL_STATE = { | ||||
|     // Whether we're joining the currently viewed room (see isJoining())
 | ||||
|  | @ -33,6 +37,7 @@ const INITIAL_STATE = { | |||
| 
 | ||||
|     // The event to scroll to when the room is first viewed
 | ||||
|     initialEventId: null, | ||||
|     initialEventPixelOffset: null, | ||||
|     // Whether to highlight the initial event
 | ||||
|     isInitialEventHighlighted: false, | ||||
| 
 | ||||
|  | @ -46,6 +51,10 @@ const INITIAL_STATE = { | |||
|     forwardingEvent: null, | ||||
| 
 | ||||
|     quotingEvent: null, | ||||
| 
 | ||||
|     replyingToEvent: null, | ||||
| 
 | ||||
|     shouldPeek: false, | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  | @ -53,21 +62,20 @@ const INITIAL_STATE = { | |||
| *  with a subset of the js-sdk. | ||||
|  *  ``` | ||||
|  */ | ||||
| class RoomViewStore extends Store { | ||||
| class RoomViewStore extends Store<ActionPayload> { | ||||
|     private state = INITIAL_STATE; // initialize state
 | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(dis); | ||||
| 
 | ||||
|         // Initialise state
 | ||||
|         this._state = INITIAL_STATE; | ||||
|     } | ||||
| 
 | ||||
|     _setState(newState) { | ||||
|     setState(newState: Partial<typeof INITIAL_STATE>) { | ||||
|         // If values haven't changed, there's nothing to do.
 | ||||
|         // This only tries a shallow comparison, so unchanged objects will slip
 | ||||
|         // through, but that's probably okay for now.
 | ||||
|         let stateChanged = false; | ||||
|         for (const key of Object.keys(newState)) { | ||||
|             if (this._state[key] !== newState[key]) { | ||||
|             if (this.state[key] !== newState[key]) { | ||||
|                 stateChanged = true; | ||||
|                 break; | ||||
|             } | ||||
|  | @ -76,7 +84,7 @@ class RoomViewStore extends Store { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this._state = Object.assign(this._state, newState); | ||||
|         this.state = Object.assign(this.state, newState); | ||||
|         this.__emitChange(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -89,59 +97,63 @@ class RoomViewStore extends Store { | |||
|             //      - event_offset: 100
 | ||||
|             //      - highlighted:  true
 | ||||
|             case 'view_room': | ||||
|                 this._viewRoom(payload); | ||||
|                 this.viewRoom(payload); | ||||
|                 break; | ||||
|             // for these events blank out the roomId as we are no longer in the RoomView
 | ||||
|             case 'view_create_group': | ||||
|             case 'view_welcome_page': | ||||
|             case 'view_home_page': | ||||
|             case 'view_my_groups': | ||||
|             case 'view_group': | ||||
|                 this._setState({ | ||||
|                 this.setState({ | ||||
|                     roomId: null, | ||||
|                     roomAlias: null, | ||||
|                 }); | ||||
|                 break; | ||||
|             case 'view_room_error': | ||||
|                 this._viewRoomError(payload); | ||||
|                 this.viewRoomError(payload); | ||||
|                 break; | ||||
|             case 'will_join': | ||||
|                 this._setState({ | ||||
|                 this.setState({ | ||||
|                     joining: true, | ||||
|                 }); | ||||
|                 break; | ||||
|             case 'cancel_join': | ||||
|                 this._setState({ | ||||
|                 this.setState({ | ||||
|                     joining: false, | ||||
|                 }); | ||||
|                 break; | ||||
|             // join_room:
 | ||||
|             //      - opts: options for joinRoom
 | ||||
|             case 'join_room': | ||||
|                 this._joinRoom(payload); | ||||
|                 this.joinRoom(payload); | ||||
|                 break; | ||||
|             case 'join_room_error': | ||||
|                 this._joinRoomError(payload); | ||||
|                 this.joinRoomError(payload); | ||||
|                 break; | ||||
|             case 'join_room_ready': | ||||
|                 this._setState({ shouldPeek: false }); | ||||
|                 this.setState({ shouldPeek: false }); | ||||
|                 break; | ||||
|             case 'on_client_not_viable': | ||||
|             case 'on_logged_out': | ||||
|                 this.reset(); | ||||
|                 break; | ||||
|             case 'forward_event': | ||||
|                 this._setState({ | ||||
|                 this.setState({ | ||||
|                     forwardingEvent: payload.event, | ||||
|                 }); | ||||
|                 break; | ||||
|             case 'reply_to_event': | ||||
|                 // If currently viewed room does not match the room in which we wish to reply then change rooms
 | ||||
|                 // this can happen when performing a search across all rooms
 | ||||
|                 if (payload.event && payload.event.getRoomId() !== this._state.roomId) { | ||||
|                 if (payload.event && payload.event.getRoomId() !== this.state.roomId) { | ||||
|                     dis.dispatch({ | ||||
|                         action: 'view_room', | ||||
|                         room_id: payload.event.getRoomId(), | ||||
|                         replyingToEvent: payload.event, | ||||
|                     }); | ||||
|                 } else { | ||||
|                     this._setState({ | ||||
|                     this.setState({ | ||||
|                         replyingToEvent: payload.event, | ||||
|                     }); | ||||
|                 } | ||||
|  | @ -149,14 +161,14 @@ class RoomViewStore extends Store { | |||
|             case 'open_room_settings': { | ||||
|                 const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog"); | ||||
|                 Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, { | ||||
|                     roomId: payload.room_id || this._state.roomId, | ||||
|                     roomId: payload.room_id || this.state.roomId, | ||||
|                 }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async _viewRoom(payload) { | ||||
|     private async viewRoom(payload: ActionPayload) { | ||||
|         if (payload.room_id) { | ||||
|             const newState = { | ||||
|                 roomId: payload.room_id, | ||||
|  | @ -181,18 +193,18 @@ class RoomViewStore extends Store { | |||
|                 newState.replyingToEvent = payload.replyingToEvent; | ||||
|             } | ||||
| 
 | ||||
|             if (this._state.forwardingEvent) { | ||||
|             if (this.state.forwardingEvent) { | ||||
|                 dis.dispatch({ | ||||
|                     action: 'send_event', | ||||
|                     room_id: newState.roomId, | ||||
|                     event: this._state.forwardingEvent, | ||||
|                     event: this.state.forwardingEvent, | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             this._setState(newState); | ||||
|             this.setState(newState); | ||||
| 
 | ||||
|             if (payload.auto_join) { | ||||
|                 this._joinRoom(payload); | ||||
|                 this.joinRoom(payload); | ||||
|             } | ||||
|         } else if (payload.room_alias) { | ||||
|             // Try the room alias to room ID navigation cache first to avoid
 | ||||
|  | @ -201,7 +213,7 @@ class RoomViewStore extends Store { | |||
|             if (!roomId) { | ||||
|                 // Room alias cache miss, so let's ask the homeserver. Resolve the alias
 | ||||
|                 // and then do a second dispatch with the room ID acquired.
 | ||||
|                 this._setState({ | ||||
|                 this.setState({ | ||||
|                     roomId: null, | ||||
|                     initialEventId: null, | ||||
|                     initialEventPixelOffset: null, | ||||
|  | @ -238,8 +250,8 @@ class RoomViewStore extends Store { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _viewRoomError(payload) { | ||||
|         this._setState({ | ||||
|     private viewRoomError(payload: ActionPayload) { | ||||
|         this.setState({ | ||||
|             roomId: payload.room_id, | ||||
|             roomAlias: payload.room_alias, | ||||
|             roomLoading: false, | ||||
|  | @ -247,12 +259,12 @@ class RoomViewStore extends Store { | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     _joinRoom(payload) { | ||||
|         this._setState({ | ||||
|     private joinRoom(payload: ActionPayload) { | ||||
|         this.setState({ | ||||
|             joining: true, | ||||
|         }); | ||||
|         MatrixClientPeg.get().joinRoom( | ||||
|             this._state.roomAlias || this._state.roomId, payload.opts, | ||||
|             this.state.roomAlias || this.state.roomId, payload.opts, | ||||
|         ).then(() => { | ||||
|             // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
 | ||||
|             // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
 | ||||
|  | @ -273,7 +285,7 @@ class RoomViewStore extends Store { | |||
|                     {_t("Please contact your homeserver administrator.")} | ||||
|                 </div>; | ||||
|             } else if (err.httpStatus === 404) { | ||||
|                 const invitingUserId = this._getInvitingUserId(this._state.roomId); | ||||
|                 const invitingUserId = this.getInvitingUserId(this.state.roomId); | ||||
|                 // only provide a better error message for invites
 | ||||
|                 if (invitingUserId) { | ||||
|                     // if the inviting user is on the same HS, there can only be one cause: they left.
 | ||||
|  | @ -292,7 +304,7 @@ class RoomViewStore extends Store { | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     _getInvitingUserId(roomId) { | ||||
|     private getInvitingUserId(roomId: string): string { | ||||
|         const cli = MatrixClientPeg.get(); | ||||
|         const room = cli.getRoom(roomId); | ||||
|         if (room && room.getMyMembership() === "invite") { | ||||
|  | @ -302,45 +314,45 @@ class RoomViewStore extends Store { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _joinRoomError(payload) { | ||||
|         this._setState({ | ||||
|     private joinRoomError(payload: ActionPayload) { | ||||
|         this.setState({ | ||||
|             joining: false, | ||||
|             joinError: payload.err, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     reset() { | ||||
|         this._state = Object.assign({}, INITIAL_STATE); | ||||
|     public reset() { | ||||
|         this.state = Object.assign({}, INITIAL_STATE); | ||||
|     } | ||||
| 
 | ||||
|     // The room ID of the room currently being viewed
 | ||||
|     getRoomId() { | ||||
|         return this._state.roomId; | ||||
|     public getRoomId() { | ||||
|         return this.state.roomId; | ||||
|     } | ||||
| 
 | ||||
|     // The event to scroll to when the room is first viewed
 | ||||
|     getInitialEventId() { | ||||
|         return this._state.initialEventId; | ||||
|     public getInitialEventId() { | ||||
|         return this.state.initialEventId; | ||||
|     } | ||||
| 
 | ||||
|     // Whether to highlight the initial event
 | ||||
|     isInitialEventHighlighted() { | ||||
|         return this._state.isInitialEventHighlighted; | ||||
|     public isInitialEventHighlighted() { | ||||
|         return this.state.isInitialEventHighlighted; | ||||
|     } | ||||
| 
 | ||||
|     // The room alias of the room (or null if not originally specified in view_room)
 | ||||
|     getRoomAlias() { | ||||
|         return this._state.roomAlias; | ||||
|     public getRoomAlias() { | ||||
|         return this.state.roomAlias; | ||||
|     } | ||||
| 
 | ||||
|     // Whether the current room is loading (true whilst resolving an alias)
 | ||||
|     isRoomLoading() { | ||||
|         return this._state.roomLoading; | ||||
|     public isRoomLoading() { | ||||
|         return this.state.roomLoading; | ||||
|     } | ||||
| 
 | ||||
|     // Any error that has occurred during loading
 | ||||
|     getRoomLoadError() { | ||||
|         return this._state.roomLoadError; | ||||
|     public getRoomLoadError() { | ||||
|         return this.state.roomLoadError; | ||||
|     } | ||||
| 
 | ||||
|     // True if we're expecting the user to be joined to the room currently being
 | ||||
|  | @ -366,27 +378,27 @@ class RoomViewStore extends Store { | |||
|     //         // show join prompt
 | ||||
|     //     }
 | ||||
|     // }
 | ||||
|     isJoining() { | ||||
|         return this._state.joining; | ||||
|     public isJoining() { | ||||
|         return this.state.joining; | ||||
|     } | ||||
| 
 | ||||
|     // Any error that has occurred during joining
 | ||||
|     getJoinError() { | ||||
|         return this._state.joinError; | ||||
|     public getJoinError() { | ||||
|         return this.state.joinError; | ||||
|     } | ||||
| 
 | ||||
|     // The mxEvent if one is about to be forwarded
 | ||||
|     getForwardingEvent() { | ||||
|         return this._state.forwardingEvent; | ||||
|     public getForwardingEvent() { | ||||
|         return this.state.forwardingEvent; | ||||
|     } | ||||
| 
 | ||||
|     // The mxEvent if one is currently being replied to/quoted
 | ||||
|     getQuotingEvent() { | ||||
|         return this._state.replyingToEvent; | ||||
|     public getQuotingEvent() { | ||||
|         return this.state.replyingToEvent; | ||||
|     } | ||||
| 
 | ||||
|     shouldPeek() { | ||||
|         return this._state.shouldPeek; | ||||
|     public shouldPeek() { | ||||
|         return this.state.shouldPeek; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski