Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/17128-1
Conflicts: src/dispatcher/actions.ts
This commit is contained in:
commit
89221bde0f
204 changed files with 8170 additions and 3477 deletions
|
@ -126,7 +126,7 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
|
|||
if (membership === EffectiveMembership.Invite) {
|
||||
try {
|
||||
const path = utils.encodeUri("/rooms/$roomId/group_info", {$roomId: room.roomId});
|
||||
const profile = await this.matrixClient._http.authedRequest(
|
||||
const profile = await this.matrixClient.http.authedRequest(
|
||||
undefined, "GET", path,
|
||||
undefined, undefined,
|
||||
{prefix: "/_matrix/client/unstable/im.vector.custom"});
|
||||
|
|
|
@ -65,6 +65,10 @@ class FlairStore extends EventEmitter {
|
|||
delete this._userGroups[userId];
|
||||
}
|
||||
|
||||
cachedPublicisedGroups(userId) {
|
||||
return this._userGroups[userId];
|
||||
}
|
||||
|
||||
getPublicisedGroupsCached(matrixClient, userId) {
|
||||
if (this._userGroups[userId]) {
|
||||
return Promise.resolve(this._userGroups[userId]);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2017-2021 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.
|
||||
|
@ -15,11 +13,18 @@ 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 { Store } from 'flux/utils';
|
||||
|
||||
import dis from '../dispatcher/dispatcher';
|
||||
import {Store} from 'flux/utils';
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
|
||||
interface IState {
|
||||
deferredAction: any;
|
||||
}
|
||||
|
||||
const INITIAL_STATE = {
|
||||
deferred_action: null,
|
||||
deferredAction: null,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -27,39 +32,38 @@ const INITIAL_STATE = {
|
|||
* store that listens for actions and updates its state accordingly, informing any
|
||||
* listeners (views) of state changes.
|
||||
*/
|
||||
class LifecycleStore extends Store {
|
||||
class LifecycleStore extends Store<ActionPayload> {
|
||||
private state: IState = INITIAL_STATE;
|
||||
|
||||
constructor() {
|
||||
super(dis);
|
||||
|
||||
// Initialise state
|
||||
this._state = INITIAL_STATE;
|
||||
}
|
||||
|
||||
_setState(newState) {
|
||||
this._state = Object.assign(this._state, newState);
|
||||
private setState(newState: Partial<IState>) {
|
||||
this.state = Object.assign(this.state, newState);
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
__onDispatch(payload) {
|
||||
protected __onDispatch(payload: ActionPayload) {
|
||||
switch (payload.action) {
|
||||
case 'do_after_sync_prepared':
|
||||
this._setState({
|
||||
deferred_action: payload.deferred_action,
|
||||
this.setState({
|
||||
deferredAction: payload.deferred_action,
|
||||
});
|
||||
break;
|
||||
case 'cancel_after_sync_prepared':
|
||||
this._setState({
|
||||
deferred_action: null,
|
||||
this.setState({
|
||||
deferredAction: null,
|
||||
});
|
||||
break;
|
||||
case 'sync_state': {
|
||||
case 'syncstate': {
|
||||
if (payload.state !== 'PREPARED') {
|
||||
break;
|
||||
}
|
||||
if (!this._state.deferred_action) break;
|
||||
const deferredAction = Object.assign({}, this._state.deferred_action);
|
||||
this._setState({
|
||||
deferred_action: null,
|
||||
if (!this.state.deferredAction) break;
|
||||
const deferredAction = Object.assign({}, this.state.deferredAction);
|
||||
this.setState({
|
||||
deferredAction: null,
|
||||
});
|
||||
dis.dispatch(deferredAction);
|
||||
break;
|
||||
|
@ -71,8 +75,8 @@ class LifecycleStore extends Store {
|
|||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._state = Object.assign({}, INITIAL_STATE);
|
||||
private reset() {
|
||||
this.state = Object.assign({}, INITIAL_STATE);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ export enum RightPanelPhases {
|
|||
EncryptionPanel = 'EncryptionPanel',
|
||||
RoomSummary = 'RoomSummary',
|
||||
Widget = 'Widget',
|
||||
PinnedMessages = "PinnedMessages",
|
||||
|
||||
Room3pidMemberInfo = 'Room3pidMemberInfo',
|
||||
// Group stuff
|
||||
|
@ -43,6 +44,7 @@ export enum RightPanelPhases {
|
|||
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
||||
RightPanelPhases.RoomSummary,
|
||||
RightPanelPhases.NotificationPanel,
|
||||
RightPanelPhases.PinnedMessages,
|
||||
RightPanelPhases.FilePanel,
|
||||
RightPanelPhases.RoomMemberList,
|
||||
RightPanelPhases.GroupMemberList,
|
||||
|
|
|
@ -17,17 +17,18 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Store} from 'flux/utils';
|
||||
import {MatrixError} from "matrix-js-sdk/src/http-api";
|
||||
import { Store } from 'flux/utils';
|
||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||
|
||||
import dis from '../dispatcher/dispatcher';
|
||||
import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||
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";
|
||||
import {retry} from "../utils/promise";
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { retry } from "../utils/promise";
|
||||
import CountlyAnalytics from "../CountlyAnalytics";
|
||||
|
||||
const NUM_JOIN_RETRY = 5;
|
||||
|
@ -53,8 +54,6 @@ const INITIAL_STATE = {
|
|||
// Any error that has occurred during loading
|
||||
roomLoadError: null,
|
||||
|
||||
forwardingEvent: null,
|
||||
|
||||
quotingEvent: null,
|
||||
|
||||
replyingToEvent: null,
|
||||
|
@ -136,24 +135,19 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
break;
|
||||
// join_room:
|
||||
// - opts: options for joinRoom
|
||||
case 'join_room':
|
||||
case Action.JoinRoom:
|
||||
this.joinRoom(payload);
|
||||
break;
|
||||
case 'join_room_error':
|
||||
case Action.JoinRoomError:
|
||||
this.joinRoomError(payload);
|
||||
break;
|
||||
case 'join_room_ready':
|
||||
case Action.JoinRoomReady:
|
||||
this.setState({ shouldPeek: false });
|
||||
break;
|
||||
case 'on_client_not_viable':
|
||||
case 'on_logged_out':
|
||||
this.reset();
|
||||
break;
|
||||
case 'forward_event':
|
||||
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
|
||||
|
@ -173,6 +167,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
|
||||
Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
|
||||
roomId: payload.room_id || this.state.roomId,
|
||||
initialTabId: payload.initial_tab_id,
|
||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||
break;
|
||||
}
|
||||
|
@ -186,7 +181,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
roomAlias: payload.room_alias,
|
||||
initialEventId: payload.event_id,
|
||||
isInitialEventHighlighted: payload.highlighted,
|
||||
forwardingEvent: null,
|
||||
roomLoading: false,
|
||||
roomLoadError: null,
|
||||
// should peek by default
|
||||
|
@ -206,18 +200,14 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
newState.replyingToEvent = payload.replyingToEvent;
|
||||
}
|
||||
|
||||
if (this.state.forwardingEvent) {
|
||||
dis.dispatch({
|
||||
action: 'send_event',
|
||||
room_id: newState.roomId,
|
||||
event: this.state.forwardingEvent,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
if (payload.auto_join) {
|
||||
this.joinRoom(payload);
|
||||
dis.dispatch({
|
||||
...payload,
|
||||
action: Action.JoinRoom,
|
||||
roomId: payload.room_id,
|
||||
});
|
||||
}
|
||||
} else if (payload.room_alias) {
|
||||
// Try the room alias to room ID navigation cache first to avoid
|
||||
|
@ -298,41 +288,16 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
// 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
|
||||
// room.
|
||||
dis.dispatch({ action: 'join_room_ready' });
|
||||
dis.dispatch({
|
||||
action: Action.JoinRoomReady,
|
||||
roomId: this.state.roomId,
|
||||
});
|
||||
} catch (err) {
|
||||
dis.dispatch({
|
||||
action: 'join_room_error',
|
||||
action: Action.JoinRoomError,
|
||||
roomId: this.state.roomId,
|
||||
err: err,
|
||||
});
|
||||
|
||||
let msg = err.message ? err.message : JSON.stringify(err);
|
||||
console.log("Failed to join room:", msg);
|
||||
|
||||
if (err.name === "ConnectionError") {
|
||||
msg = _t("There was an error joining the room");
|
||||
} else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
|
||||
msg = <div>
|
||||
{_t("Sorry, your homeserver is too old to participate in this room.")}<br />
|
||||
{_t("Please contact your homeserver administrator.")}
|
||||
</div>;
|
||||
} else if (err.httpStatus === 404) {
|
||||
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.
|
||||
if (invitingUserId.endsWith(`:${MatrixClientPeg.get().getDomain()}`)) {
|
||||
msg = _t("The person who invited you already left the room.");
|
||||
} else {
|
||||
msg = _t("The person who invited you already left the room, or their server is offline.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
|
||||
title: _t("Failed to join room"),
|
||||
description: msg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,6 +316,35 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
joining: false,
|
||||
joinError: payload.err,
|
||||
});
|
||||
const err = payload.err;
|
||||
let msg = err.message ? err.message : JSON.stringify(err);
|
||||
console.log("Failed to join room:", msg);
|
||||
|
||||
if (err.name === "ConnectionError") {
|
||||
msg = _t("There was an error joining the room");
|
||||
} else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
|
||||
msg = <div>
|
||||
{_t("Sorry, your homeserver is too old to participate in this room.")}<br />
|
||||
{_t("Please contact your homeserver administrator.")}
|
||||
</div>;
|
||||
} else if (err.httpStatus === 404) {
|
||||
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.
|
||||
if (invitingUserId.endsWith(`:${MatrixClientPeg.get().getDomain()}`)) {
|
||||
msg = _t("The person who invited you already left the room.");
|
||||
} else {
|
||||
msg = _t("The person who invited you already left the room, or their server is offline.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
|
||||
title: _t("Failed to join room"),
|
||||
description: msg,
|
||||
});
|
||||
}
|
||||
|
||||
public reset() {
|
||||
|
@ -419,11 +413,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
|||
return this.state.joinError;
|
||||
}
|
||||
|
||||
// The mxEvent if one is about to be forwarded
|
||||
public getForwardingEvent() {
|
||||
return this.state.forwardingEvent;
|
||||
}
|
||||
|
||||
// The mxEvent if one is currently being replied to/quoted
|
||||
public getQuotingEvent() {
|
||||
return this.state.replyingToEvent;
|
||||
|
|
|
@ -196,7 +196,7 @@ export class SetupEncryptionStore extends EventEmitter {
|
|||
this.phase = PHASE_FINISHED;
|
||||
this.emit("update");
|
||||
// async - ask other clients for keys, if necessary
|
||||
MatrixClientPeg.get()._crypto.cancelAndResendAllOutgoingKeyRequests();
|
||||
MatrixClientPeg.get().crypto.cancelAndResendAllOutgoingKeyRequests();
|
||||
}
|
||||
|
||||
async _setActiveVerificationRequest(request) {
|
||||
|
|
112
src/stores/UIStore.ts
Normal file
112
src/stores/UIStore.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Copyright 2021 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 EventEmitter from "events";
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
import ResizeObserverEntry from 'resize-observer-polyfill/src/ResizeObserverEntry';
|
||||
|
||||
export enum UI_EVENTS {
|
||||
Resize = "resize"
|
||||
}
|
||||
|
||||
export type ResizeObserverCallbackFunction = (entries: ResizeObserverEntry[]) => void;
|
||||
|
||||
export default class UIStore extends EventEmitter {
|
||||
private static _instance: UIStore = null;
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
|
||||
private uiElementDimensions = new Map<string, DOMRectReadOnly>();
|
||||
private trackedUiElements = new Map<Element, string>();
|
||||
|
||||
public windowWidth: number;
|
||||
public windowHeight: number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
this.windowWidth = window.innerWidth;
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
this.windowHeight = window.innerHeight;
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.resizeObserverCallback);
|
||||
this.resizeObserver.observe(document.body);
|
||||
}
|
||||
|
||||
public static get instance(): UIStore {
|
||||
if (!UIStore._instance) {
|
||||
UIStore._instance = new UIStore();
|
||||
}
|
||||
return UIStore._instance;
|
||||
}
|
||||
|
||||
public static destroy(): void {
|
||||
if (UIStore._instance) {
|
||||
UIStore._instance.resizeObserver.disconnect();
|
||||
UIStore._instance.removeAllListeners();
|
||||
UIStore._instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public getElementDimensions(name: string): DOMRectReadOnly {
|
||||
return this.uiElementDimensions.get(name);
|
||||
}
|
||||
|
||||
public trackElementDimensions(name: string, element: Element): void {
|
||||
this.trackedUiElements.set(element, name);
|
||||
this.resizeObserver.observe(element);
|
||||
}
|
||||
|
||||
public stopTrackingElementDimensions(name: string): void {
|
||||
let trackedElement: Element;
|
||||
this.trackedUiElements.forEach((trackedElementName, element) => {
|
||||
if (trackedElementName === name) {
|
||||
trackedElement = element;
|
||||
}
|
||||
});
|
||||
if (trackedElement) {
|
||||
this.resizeObserver.unobserve(trackedElement);
|
||||
this.uiElementDimensions.delete(name);
|
||||
this.trackedUiElements.delete(trackedElement);
|
||||
}
|
||||
}
|
||||
|
||||
public isTrackingElementDimensions(name: string): boolean {
|
||||
return this.uiElementDimensions.has(name);
|
||||
}
|
||||
|
||||
private resizeObserverCallback = (entries: ResizeObserverEntry[]) => {
|
||||
const windowEntry = entries.find(entry => entry.target === document.body);
|
||||
|
||||
if (windowEntry) {
|
||||
this.windowWidth = windowEntry.contentRect.width;
|
||||
this.windowHeight = windowEntry.contentRect.height;
|
||||
}
|
||||
|
||||
entries.forEach(entry => {
|
||||
const trackedElementName = this.trackedUiElements.get(entry.target);
|
||||
if (trackedElementName) {
|
||||
this.uiElementDimensions.set(trackedElementName, entry.contentRect);
|
||||
this.emit(trackedElementName, UI_EVENTS.Resize, entry);
|
||||
}
|
||||
});
|
||||
|
||||
this.emit(UI_EVENTS.Resize, entries);
|
||||
}
|
||||
}
|
||||
|
||||
window.mxUIStore = UIStore.instance;
|
|
@ -176,7 +176,8 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
|
||||
if (payload.action === 'MatrixActions.Room.timeline' || payload.action === 'MatrixActions.Event.decrypted') {
|
||||
const event = payload.event; // TODO: Type out the dispatcher
|
||||
if (!this.previews.has(event.getRoomId())) return; // not important
|
||||
const isHistoricalEvent = payload.hasOwnProperty("isLiveEvent") && !payload.isLiveEvent
|
||||
if (!this.previews.has(event.getRoomId()) || isHistoricalEvent) return; // not important
|
||||
await this.generatePreview(this.matrixClient.getRoom(event.getRoomId()), TAG_ANY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -426,8 +426,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
|||
return; // don't do anything on rooms that aren't visible
|
||||
}
|
||||
|
||||
if (cause === RoomUpdateCause.NewRoom && !this.prefilterConditions.every(c => c.isVisible(room))) {
|
||||
return; // don't do anything on new rooms which ought not to be shown
|
||||
if ((cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.PossibleTagChange) &&
|
||||
!this.prefilterConditions.every(c => c.isVisible(room))
|
||||
) {
|
||||
return; // don't do anything on new/moved rooms which ought not to be shown
|
||||
}
|
||||
|
||||
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { TagID } from "../../models";
|
||||
import { IAlgorithm } from "./IAlgorithm";
|
||||
import { compare } from "../../../../utils/strings";
|
||||
|
||||
/**
|
||||
* Sorts rooms according to the browser's determination of alphabetic.
|
||||
|
@ -24,7 +25,7 @@ import { IAlgorithm } from "./IAlgorithm";
|
|||
export class AlphabeticAlgorithm implements IAlgorithm {
|
||||
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
|
||||
return rooms.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
return compare(a.name, b.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|||
import { SettingLevel } from "../../settings/SettingLevel";
|
||||
import { arrayFastClone } from "../../utils/arrays";
|
||||
import { UPDATE_EVENT } from "../AsyncStore";
|
||||
import { compare } from "../../utils/strings";
|
||||
|
||||
export const WIDGET_LAYOUT_EVENT_TYPE = "io.element.widgets.layout";
|
||||
|
||||
|
@ -240,7 +241,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
|||
|
||||
if (orderA === orderB) {
|
||||
// We just need a tiebreak
|
||||
return a.id.localeCompare(b.id);
|
||||
return compare(a.id, b.id);
|
||||
}
|
||||
|
||||
return orderA - orderB;
|
||||
|
@ -331,7 +332,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
|||
}
|
||||
|
||||
public getContainerWidgets(room: Room, container: Container): IApp[] {
|
||||
return this.byRoom[room.roomId]?.[container]?.ordered || [];
|
||||
return this.byRoom[room?.roomId]?.[container]?.ordered || [];
|
||||
}
|
||||
|
||||
public isInContainer(room: Room, widget: IApp, container: Container): boolean {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue