Move state update listeners from constructor to componentDidMount (#28341)
* Move state update listeners from constructor to componentDidMount Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
2d9982f9f0
commit
0899165d9e
72 changed files with 377 additions and 309 deletions
|
@ -37,6 +37,7 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
|
||||||
public state: IState = {};
|
public state: IState = {};
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.props.prom
|
this.props.prom
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
|
@ -117,8 +117,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
||||||
// signing key upload as well. This avoids hitting the server to
|
// signing key upload as well. This avoids hitting the server to
|
||||||
// test auth flows, which may be slow under high load.
|
// test auth flows, which may be slow under high load.
|
||||||
canUploadKeysWithPasswordOnly = true;
|
canUploadKeysWithPasswordOnly = true;
|
||||||
} else {
|
|
||||||
this.queryKeyUploadAuth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
|
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
|
||||||
|
@ -140,8 +138,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
||||||
passPhraseKeySelected,
|
passPhraseKeySelected,
|
||||||
accountPassword,
|
accountPassword,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
|
||||||
if (keyFromCustomisations) this.initExtension(keyFromCustomisations);
|
if (keyFromCustomisations) this.initExtension(keyFromCustomisations);
|
||||||
|
|
||||||
|
if (this.state.canUploadKeysWithPasswordOnly === null) {
|
||||||
|
this.queryKeyUploadAuth();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initExtension(keyFromCustomisations: Uint8Array): void {
|
private initExtension(keyFromCustomisations: Uint8Array): void {
|
||||||
|
|
|
@ -56,6 +56,10 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ interface IState {
|
||||||
|
|
||||||
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
|
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
|
||||||
private readonly authLogic: InteractiveAuth<T>;
|
private readonly authLogic: InteractiveAuth<T>;
|
||||||
private readonly intervalId: number | null = null;
|
|
||||||
private readonly stageComponent = createRef<IStageComponent>();
|
private readonly stageComponent = createRef<IStageComponent>();
|
||||||
|
private intervalId: number | null = null;
|
||||||
|
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
|
@ -126,15 +126,17 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
|
||||||
AuthType.SsoUnstable,
|
AuthType.SsoUnstable,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
if (this.props.poll) {
|
if (this.props.poll) {
|
||||||
this.intervalId = window.setInterval(() => {
|
this.intervalId = window.setInterval(() => {
|
||||||
this.authLogic.poll();
|
this.authLogic.poll();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this.authLogic
|
this.authLogic
|
||||||
.attemptAuth()
|
.attemptAuth()
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
|
|
|
@ -67,10 +67,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
activeSpace: SpaceStore.instance.activeSpace,
|
activeSpace: SpaceStore.instance.activeSpace,
|
||||||
showBreadcrumbs: LeftPanel.breadcrumbsMode,
|
showBreadcrumbs: LeftPanel.breadcrumbsMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
|
||||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
|
||||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static get breadcrumbsMode(): BreadcrumbsMode {
|
private static get breadcrumbsMode(): BreadcrumbsMode {
|
||||||
|
@ -78,6 +74,10 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
|
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
|
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
||||||
|
|
||||||
if (this.listContainerRef.current) {
|
if (this.listContainerRef.current) {
|
||||||
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
||||||
// Using the passive option to not block the main thread
|
// Using the passive option to not block the main thread
|
||||||
|
|
|
@ -231,10 +231,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
private prevWindowWidth: number;
|
private prevWindowWidth: number;
|
||||||
private voiceBroadcastResumer?: VoiceBroadcastResumer;
|
private voiceBroadcastResumer?: VoiceBroadcastResumer;
|
||||||
|
|
||||||
private readonly loggedInView: React.RefObject<LoggedInViewType>;
|
private readonly loggedInView = createRef<LoggedInViewType>();
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private readonly themeWatcher: ThemeWatcher;
|
private themeWatcher?: ThemeWatcher;
|
||||||
private readonly fontWatcher: FontWatcher;
|
private fontWatcher?: FontWatcher;
|
||||||
private readonly stores: SdkContextClass;
|
private readonly stores: SdkContextClass;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
|
@ -256,8 +256,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
ready: false,
|
ready: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.loggedInView = createRef();
|
|
||||||
|
|
||||||
SdkConfig.put(this.props.config);
|
SdkConfig.put(this.props.config);
|
||||||
|
|
||||||
// Used by _viewRoom before getting state from sync
|
// Used by _viewRoom before getting state from sync
|
||||||
|
@ -282,32 +280,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.prevWindowWidth = UIStore.instance.windowWidth || 1000;
|
this.prevWindowWidth = UIStore.instance.windowWidth || 1000;
|
||||||
UIStore.instance.on(UI_EVENTS.Resize, this.handleResize);
|
|
||||||
|
|
||||||
// For PersistentElement
|
|
||||||
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);
|
|
||||||
|
|
||||||
RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatusIndicator);
|
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
|
|
||||||
this.themeWatcher = new ThemeWatcher();
|
|
||||||
this.fontWatcher = new FontWatcher();
|
|
||||||
this.themeWatcher.start();
|
|
||||||
this.fontWatcher.start();
|
|
||||||
|
|
||||||
// object field used for tracking the status info appended to the title tag.
|
// object field used for tracking the status info appended to the title tag.
|
||||||
// we don't do it as react state as i'm scared about triggering needless react refreshes.
|
// we don't do it as react state as i'm scared about triggering needless react refreshes.
|
||||||
this.subTitleStatus = "";
|
this.subTitleStatus = "";
|
||||||
|
|
||||||
initSentry(SdkConfig.get("sentry"));
|
|
||||||
|
|
||||||
if (!checkSessionLockFree()) {
|
|
||||||
// another instance holds the lock; confirm its theft before proceeding
|
|
||||||
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
|
|
||||||
} else {
|
|
||||||
this.startInitSession();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -476,6 +452,29 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
UIStore.instance.on(UI_EVENTS.Resize, this.handleResize);
|
||||||
|
|
||||||
|
// For PersistentElement
|
||||||
|
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);
|
||||||
|
|
||||||
|
RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatusIndicator);
|
||||||
|
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
|
||||||
|
this.themeWatcher = new ThemeWatcher();
|
||||||
|
this.fontWatcher = new FontWatcher();
|
||||||
|
this.themeWatcher.start();
|
||||||
|
this.fontWatcher.start();
|
||||||
|
|
||||||
|
initSentry(SdkConfig.get("sentry"));
|
||||||
|
|
||||||
|
if (!checkSessionLockFree()) {
|
||||||
|
// another instance holds the lock; confirm its theft before proceeding
|
||||||
|
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
|
||||||
|
} else {
|
||||||
|
this.startInitSession();
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", this.onWindowResized);
|
window.addEventListener("resize", this.onWindowResized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,8 +496,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
Lifecycle.stopMatrixClient();
|
Lifecycle.stopMatrixClient();
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
this.themeWatcher.stop();
|
this.themeWatcher?.stop();
|
||||||
this.fontWatcher.stop();
|
this.fontWatcher?.stop();
|
||||||
UIStore.destroy();
|
UIStore.destroy();
|
||||||
this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize);
|
this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize);
|
||||||
window.removeEventListener("resize", this.onWindowResized);
|
window.removeEventListener("resize", this.onWindowResized);
|
||||||
|
@ -1011,7 +1010,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
this.setStateForNewView(newState);
|
this.setStateForNewView(newState);
|
||||||
ThemeController.isLogin = true;
|
ThemeController.isLogin = true;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register");
|
this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1088,7 +1087,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
ThemeController.isLogin = false;
|
ThemeController.isLogin = false;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
this.notifyNewScreen("room/" + presentedId, replaceLast);
|
this.notifyNewScreen("room/" + presentedId, replaceLast);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1113,7 +1112,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
});
|
});
|
||||||
this.notifyNewScreen("welcome");
|
this.notifyNewScreen("welcome");
|
||||||
ThemeController.isLogin = true;
|
ThemeController.isLogin = true;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewLogin(otherState?: any): void {
|
private viewLogin(otherState?: any): void {
|
||||||
|
@ -1123,7 +1122,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
});
|
});
|
||||||
this.notifyNewScreen("login");
|
this.notifyNewScreen("login");
|
||||||
ThemeController.isLogin = true;
|
ThemeController.isLogin = true;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewHome(justRegistered = false): void {
|
private viewHome(justRegistered = false): void {
|
||||||
|
@ -1136,7 +1135,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.setPage(PageType.HomePage);
|
this.setPage(PageType.HomePage);
|
||||||
this.notifyNewScreen("home");
|
this.notifyNewScreen("home");
|
||||||
ThemeController.isLogin = false;
|
ThemeController.isLogin = false;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewUser(userId: string, subAction: string): void {
|
private viewUser(userId: string, subAction: string): void {
|
||||||
|
@ -1357,7 +1356,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
*/
|
*/
|
||||||
private async onLoggedIn(): Promise<void> {
|
private async onLoggedIn(): Promise<void> {
|
||||||
ThemeController.isLogin = false;
|
ThemeController.isLogin = false;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher?.recheck();
|
||||||
StorageManager.tryPersistStorage();
|
StorageManager.tryPersistStorage();
|
||||||
|
|
||||||
await this.onShowPostLoginScreen();
|
await this.onShowPostLoginScreen();
|
||||||
|
|
|
@ -240,13 +240,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
private readReceiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
private readReceiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
||||||
|
|
||||||
private readonly _showHiddenEvents: boolean;
|
private readonly _showHiddenEvents: boolean;
|
||||||
private isMounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
private readMarkerNode = createRef<HTMLLIElement>();
|
private readMarkerNode = createRef<HTMLLIElement>();
|
||||||
private whoIsTyping = createRef<WhoIsTypingTile>();
|
private whoIsTyping = createRef<WhoIsTypingTile>();
|
||||||
public scrollPanel = createRef<ScrollPanel>();
|
public scrollPanel = createRef<ScrollPanel>();
|
||||||
|
|
||||||
private readonly showTypingNotificationsWatcherRef: string;
|
private showTypingNotificationsWatcherRef?: string;
|
||||||
private eventTiles: Record<string, UnwrappedEventTile> = {};
|
private eventTiles: Record<string, UnwrappedEventTile> = {};
|
||||||
|
|
||||||
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
|
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
|
||||||
|
@ -267,22 +267,21 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// and we check this in a hot code path. This is also cached in our
|
// and we check this in a hot code path. This is also cached in our
|
||||||
// RoomContext, however we still need a fallback for roomless MessagePanels.
|
// RoomContext, however we still need a fallback for roomless MessagePanels.
|
||||||
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
|
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
|
this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
|
||||||
"showTypingNotifications",
|
"showTypingNotifications",
|
||||||
null,
|
null,
|
||||||
this.onShowTypingNotificationsChange,
|
this.onShowTypingNotificationsChange,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this.calculateRoomMembersCount();
|
this.calculateRoomMembersCount();
|
||||||
this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
||||||
this.isMounted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.isMounted = false;
|
this.unmounted = true;
|
||||||
this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
||||||
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
|
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
|
||||||
this.readReceiptMap = {};
|
this.readReceiptMap = {};
|
||||||
|
@ -441,7 +440,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private isUnmounting = (): boolean => {
|
private isUnmounting = (): boolean => {
|
||||||
return !this.isMounted;
|
return this.unmounted;
|
||||||
};
|
};
|
||||||
|
|
||||||
public get showHiddenEvents(): boolean {
|
public get showHiddenEvents(): boolean {
|
||||||
|
|
|
@ -25,7 +25,9 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
|
||||||
this.state = {
|
this.state = {
|
||||||
toasts: NonUrgentToastStore.instance.components,
|
toasts: NonUrgentToastStore.instance.components,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
|
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,9 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class RoomSearch extends React.PureComponent<IProps> {
|
export default class RoomSearch extends React.PureComponent<IProps> {
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,8 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
const client = this.context;
|
const client = this.context;
|
||||||
client.on(ClientEvent.Sync, this.onSyncStateChange);
|
client.on(ClientEvent.Sync, this.onSyncStateChange);
|
||||||
client.on(RoomEvent.LocalEchoUpdated, this.onRoomLocalEchoUpdated);
|
client.on(RoomEvent.LocalEchoUpdated, this.onRoomLocalEchoUpdated);
|
||||||
|
|
|
@ -351,8 +351,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
private static e2eStatusCache = new Map<string, E2EStatus>();
|
private static e2eStatusCache = new Map<string, E2EStatus>();
|
||||||
|
|
||||||
private readonly askToJoinEnabled: boolean;
|
private readonly askToJoinEnabled: boolean;
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private settingWatchers: string[];
|
private settingWatchers: string[] = [];
|
||||||
|
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
||||||
|
@ -418,62 +418,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
context.client.on(ClientEvent.Room, this.onRoom);
|
|
||||||
context.client.on(RoomEvent.Timeline, this.onRoomTimeline);
|
|
||||||
context.client.on(RoomEvent.TimelineReset, this.onRoomTimelineReset);
|
|
||||||
context.client.on(RoomEvent.Name, this.onRoomName);
|
|
||||||
context.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
|
||||||
context.client.on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
|
||||||
context.client.on(RoomEvent.MyMembership, this.onMyMembership);
|
|
||||||
context.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
|
||||||
context.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
|
||||||
context.client.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
|
|
||||||
context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
|
||||||
// Start listening for RoomViewStore updates
|
|
||||||
context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
|
||||||
|
|
||||||
context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
|
||||||
|
|
||||||
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
|
|
||||||
context.widgetStore.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
|
|
||||||
|
|
||||||
CallStore.instance.on(CallStoreEvent.ConnectedCalls, this.onConnectedCalls);
|
|
||||||
|
|
||||||
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
|
|
||||||
|
|
||||||
this.settingWatchers = [
|
|
||||||
SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
|
|
||||||
this.setState({ layout: value as Layout }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("lowBandwidth", null, (...[, , , value]) =>
|
|
||||||
this.setState({ lowBandwidth: value as boolean }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("alwaysShowTimestamps", null, (...[, , , value]) =>
|
|
||||||
this.setState({ alwaysShowTimestamps: value as boolean }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("showTwelveHourTimestamps", null, (...[, , , value]) =>
|
|
||||||
this.setState({ showTwelveHourTimestamps: value as boolean }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting(TimezoneHandler.USER_TIMEZONE_KEY, null, (...[, , , value]) =>
|
|
||||||
this.setState({ userTimezone: value as string }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("readMarkerInViewThresholdMs", null, (...[, , , value]) =>
|
|
||||||
this.setState({ readMarkerInViewThresholdMs: value as number }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("readMarkerOutOfViewThresholdMs", null, (...[, , , value]) =>
|
|
||||||
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[, , , value]) =>
|
|
||||||
this.setState({ showHiddenEvents: value as boolean }),
|
|
||||||
),
|
|
||||||
SettingsStore.watchSetting("urlPreviewsEnabled", null, this.onUrlPreviewsEnabledChange),
|
|
||||||
SettingsStore.watchSetting("urlPreviewsEnabled_e2ee", null, this.onUrlPreviewsEnabledChange),
|
|
||||||
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, (...[, , , value]) =>
|
|
||||||
this.setState({ msc3946ProcessDynamicPredecessor: value as boolean }),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onIsResizing = (resizing: boolean): void => {
|
private onIsResizing = (resizing: boolean): void => {
|
||||||
|
@ -904,6 +848,66 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
if (this.context.client) {
|
||||||
|
this.context.client.on(ClientEvent.Room, this.onRoom);
|
||||||
|
this.context.client.on(RoomEvent.Timeline, this.onRoomTimeline);
|
||||||
|
this.context.client.on(RoomEvent.TimelineReset, this.onRoomTimelineReset);
|
||||||
|
this.context.client.on(RoomEvent.Name, this.onRoomName);
|
||||||
|
this.context.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||||
|
this.context.client.on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||||
|
this.context.client.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||||
|
this.context.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
||||||
|
this.context.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
||||||
|
this.context.client.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
|
||||||
|
this.context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||||
|
}
|
||||||
|
// Start listening for RoomViewStore updates
|
||||||
|
this.context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||||
|
|
||||||
|
this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||||
|
|
||||||
|
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
|
||||||
|
this.context.widgetStore.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
|
||||||
|
|
||||||
|
CallStore.instance.on(CallStoreEvent.ConnectedCalls, this.onConnectedCalls);
|
||||||
|
|
||||||
|
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
|
||||||
|
|
||||||
|
this.settingWatchers = [
|
||||||
|
SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
|
||||||
|
this.setState({ layout: value as Layout }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("lowBandwidth", null, (...[, , , value]) =>
|
||||||
|
this.setState({ lowBandwidth: value as boolean }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("alwaysShowTimestamps", null, (...[, , , value]) =>
|
||||||
|
this.setState({ alwaysShowTimestamps: value as boolean }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showTwelveHourTimestamps", null, (...[, , , value]) =>
|
||||||
|
this.setState({ showTwelveHourTimestamps: value as boolean }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting(TimezoneHandler.USER_TIMEZONE_KEY, null, (...[, , , value]) =>
|
||||||
|
this.setState({ userTimezone: value as string }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("readMarkerInViewThresholdMs", null, (...[, , , value]) =>
|
||||||
|
this.setState({ readMarkerInViewThresholdMs: value as number }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("readMarkerOutOfViewThresholdMs", null, (...[, , , value]) =>
|
||||||
|
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[, , , value]) =>
|
||||||
|
this.setState({ showHiddenEvents: value as boolean }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("urlPreviewsEnabled", null, this.onUrlPreviewsEnabledChange),
|
||||||
|
SettingsStore.watchSetting("urlPreviewsEnabled_e2ee", null, this.onUrlPreviewsEnabledChange),
|
||||||
|
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, (...[, , , value]) =>
|
||||||
|
this.setState({ msc3946ProcessDynamicPredecessor: value as boolean }),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
this.onRoomViewStoreUpdate(true);
|
this.onRoomViewStoreUpdate(true);
|
||||||
|
|
||||||
const call = this.getCallForRoom();
|
const call = this.getCallForRoom();
|
||||||
|
|
|
@ -191,12 +191,12 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize);
|
|
||||||
|
|
||||||
this.resetScrollState();
|
this.resetScrollState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize);
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -599,7 +599,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
public static contextType = MatrixClientContext;
|
public static contextType = MatrixClientContext;
|
||||||
public declare context: React.ContextType<typeof MatrixClientContext>;
|
public declare context: React.ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
|
|
||||||
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -621,12 +621,11 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
||||||
myMembership: this.props.space.getMyMembership(),
|
myMembership: this.props.space.getMyMembership(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
|
||||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
|
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||||
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
|
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
public declare context: React.ContextType<typeof RoomContext>;
|
public declare context: React.ContextType<typeof RoomContext>;
|
||||||
|
|
||||||
private dispatcherRef?: string;
|
private dispatcherRef?: string;
|
||||||
private readonly layoutWatcherRef: string;
|
private layoutWatcherRef?: string;
|
||||||
private timelinePanel = createRef<TimelinePanel>();
|
private timelinePanel = createRef<TimelinePanel>();
|
||||||
private card = createRef<HTMLDivElement>();
|
private card = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
@ -91,7 +91,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
this.setEventId(this.props.mxEvent);
|
this.setEventId(this.props.mxEvent);
|
||||||
const thread = this.props.room.getThread(this.eventId) ?? undefined;
|
const thread = this.props.room.getThread(this.eventId) ?? undefined;
|
||||||
|
|
||||||
this.setupThreadListeners(thread);
|
|
||||||
this.state = {
|
this.state = {
|
||||||
layout: SettingsStore.getValue("layout"),
|
layout: SettingsStore.getValue("layout"),
|
||||||
narrow: false,
|
narrow: false,
|
||||||
|
@ -100,13 +99,15 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status;
|
return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.setupThreadListeners(this.state.thread);
|
||||||
|
|
||||||
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
|
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
|
||||||
this.setState({ layout: value as Layout }),
|
this.setState({ layout: value as Layout }),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
if (this.state.thread) {
|
if (this.state.thread) {
|
||||||
this.postThreadUpdate(this.state.thread);
|
this.postThreadUpdate(this.state.thread);
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,7 +248,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
private lastRMSentEventId: string | null | undefined = undefined;
|
private lastRMSentEventId: string | null | undefined = undefined;
|
||||||
|
|
||||||
private readonly messagePanel = createRef<MessagePanel>();
|
private readonly messagePanel = createRef<MessagePanel>();
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private timelineWindow?: TimelineWindow;
|
private timelineWindow?: TimelineWindow;
|
||||||
private overlayTimelineWindow?: TimelineWindow;
|
private overlayTimelineWindow?: TimelineWindow;
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
@ -291,6 +291,10 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"),
|
readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"),
|
||||||
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
const cli = MatrixClientPeg.safeGet();
|
const cli = MatrixClientPeg.safeGet();
|
||||||
|
@ -312,9 +316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
cli.on(ClientEvent.Sync, this.onSync);
|
cli.on(ClientEvent.Sync, this.onSync);
|
||||||
|
|
||||||
this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
|
this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
if (this.props.manageReadReceipts) {
|
if (this.props.manageReadReceipts) {
|
||||||
this.updateReadReceiptOnUserActivity();
|
this.updateReadReceiptOnUserActivity();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,11 @@ export default class ToastContainer extends React.Component<{}, IState> {
|
||||||
toasts: ToastStore.sharedInstance().getToasts(),
|
toasts: ToastStore.sharedInstance().getToasts(),
|
||||||
countSeen: ToastStore.sharedInstance().getCountSeen(),
|
countSeen: ToastStore.sharedInstance().getCountSeen(),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Start listening here rather than in componentDidMount because
|
public componentDidMount(): void {
|
||||||
// toasts may dismiss themselves in their didMount if they find
|
|
||||||
// they're already irrelevant by the time they're mounted, and
|
|
||||||
// our own componentDidMount is too late.
|
|
||||||
ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
|
ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
|
||||||
|
this.onToastStoreUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
|
|
@ -46,7 +46,7 @@ function isUploadPayload(payload: ActionPayload): payload is UploadPayload {
|
||||||
|
|
||||||
export default class UploadBar extends React.PureComponent<IProps, IState> {
|
export default class UploadBar extends React.PureComponent<IProps, IState> {
|
||||||
private dispatcherRef: Optional<string>;
|
private dispatcherRef: Optional<string>;
|
||||||
private mounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -57,12 +57,12 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this.mounted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.mounted = false;
|
this.unmounted = true;
|
||||||
dis.unregister(this.dispatcherRef!);
|
dis.unregister(this.dispatcherRef!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload): void => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (!this.mounted) return;
|
if (this.unmounted) return;
|
||||||
if (isUploadPayload(payload)) {
|
if (isUploadPayload(payload)) {
|
||||||
this.setState(this.calculateState());
|
this.setState(this.calculateState());
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,9 +96,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
||||||
showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(),
|
showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(),
|
||||||
};
|
};
|
||||||
|
|
||||||
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
|
||||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get hasHomePage(): boolean {
|
private get hasHomePage(): boolean {
|
||||||
|
@ -112,6 +109,8 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
||||||
|
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
|
||||||
this.context.voiceBroadcastRecordingsStore.on(
|
this.context.voiceBroadcastRecordingsStore.on(
|
||||||
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
|
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
|
||||||
this.onCurrentVoiceBroadcastRecordingChanged,
|
this.onCurrentVoiceBroadcastRecordingChanged,
|
||||||
|
|
|
@ -29,11 +29,15 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
store.on("update", this.onStoreUpdate);
|
|
||||||
store.start();
|
store.start();
|
||||||
this.state = { phase: store.phase, lostKeys: store.lostKeys() };
|
this.state = { phase: store.phase, lostKeys: store.lostKeys() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
|
store.on("update", this.onStoreUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
private onStoreUpdate = (): void => {
|
private onStoreUpdate = (): void => {
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
this.setState({ phase: store.phase, lostKeys: store.lostKeys() });
|
this.setState({ phase: store.phase, lostKeys: store.lostKeys() });
|
||||||
|
|
|
@ -134,6 +134,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.initLoginLogic(this.props.serverConfig);
|
this.initLoginLogic(this.props.serverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
store.on("update", this.onStoreUpdate);
|
|
||||||
store.start();
|
store.start();
|
||||||
this.state = {
|
this.state = {
|
||||||
phase: store.phase,
|
phase: store.phase,
|
||||||
|
@ -52,6 +51,11 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
|
store.on("update", this.onStoreUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
private onStoreUpdate = (): void => {
|
private onStoreUpdate = (): void => {
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
if (store.phase === Phase.Finished) {
|
if (store.phase === Phase.Finished) {
|
||||||
|
|
|
@ -41,7 +41,9 @@ export default abstract class AudioPlayerBase<T extends IProps = IProps> extends
|
||||||
this.state = {
|
this.state = {
|
||||||
playbackPhase: this.props.playback.currentState,
|
playbackPhase: this.props.playback.currentState,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
// We don't need to de-register: the class handles this for us internally
|
// We don't need to de-register: the class handles this for us internally
|
||||||
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,6 @@ export default class Clock extends React.Component<Props> {
|
||||||
formatFn: formatSeconds,
|
formatFn: formatSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
public constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
|
public shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
|
||||||
const currentFloor = Math.floor(this.props.seconds);
|
const currentFloor = Math.floor(this.props.seconds);
|
||||||
const nextFloor = Math.floor(nextProps.seconds);
|
const nextFloor = Math.floor(nextProps.seconds);
|
||||||
|
|
|
@ -33,6 +33,9 @@ export default class DurationClock extends React.PureComponent<IProps, IState> {
|
||||||
// member property to track "did we get a duration".
|
// member property to track "did we get a duration".
|
||||||
durationSeconds: this.props.playback.clockInfo.durationSeconds,
|
durationSeconds: this.props.playback.clockInfo.durationSeconds,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,6 @@ type Props = Omit<ButtonProps<"div">, "title" | "onClick" | "disabled" | "elemen
|
||||||
* to be displayed in reference to a recording.
|
* to be displayed in reference to a recording.
|
||||||
*/
|
*/
|
||||||
export default class PlayPauseButton extends React.PureComponent<Props> {
|
export default class PlayPauseButton extends React.PureComponent<Props> {
|
||||||
public constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onClick = (): void => {
|
private onClick = (): void => {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.toggleState();
|
this.toggleState();
|
||||||
|
|
|
@ -43,6 +43,9 @@ export default class PlaybackClock extends React.PureComponent<IProps, IState> {
|
||||||
durationSeconds: this.props.playback.clockInfo.durationSeconds,
|
durationSeconds: this.props.playback.clockInfo.durationSeconds,
|
||||||
playbackPhase: PlaybackState.Stopped, // assume not started, so full clock
|
playbackPhase: PlaybackState.Stopped, // assume not started, so full clock
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
||||||
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,9 @@ export default class PlaybackWaveform extends React.PureComponent<IProps, IState
|
||||||
heights: this.toHeights(this.props.playback.waveform),
|
heights: this.toHeights(this.props.playback.waveform),
|
||||||
progress: 0, // default no progress
|
progress: 0, // default no progress
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.props.playback.waveformData.onUpdate(this.onWaveformUpdate);
|
this.props.playback.waveformData.onUpdate(this.onWaveformUpdate);
|
||||||
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
this.props.playback.clockInfo.liveData.onUpdate(this.onTimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,9 @@ export default class SeekBar extends React.PureComponent<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
percentage: percentageOf(this.props.playback.timeSeconds, 0, this.props.playback.durationSeconds),
|
percentage: percentageOf(this.props.playback.timeSeconds, 0, this.props.playback.durationSeconds),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
// We don't need to de-register: the class handles this for us internally
|
// We don't need to de-register: the class handles this for us internally
|
||||||
this.props.playback.liveData.onUpdate(() => this.animationFrameFn.mark());
|
this.props.playback.liveData.onUpdate(() => this.animationFrameFn.mark());
|
||||||
}
|
}
|
||||||
|
|
|
@ -801,7 +801,6 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
this.ssoUrl = props.matrixClient.getFallbackAuthUrl(this.props.loginType, this.props.authSessionId);
|
this.ssoUrl = props.matrixClient.getFallbackAuthUrl(this.props.loginType, this.props.authSessionId);
|
||||||
|
|
||||||
this.popupWindow = null;
|
this.popupWindow = null;
|
||||||
window.addEventListener("message", this.onReceiveMessage);
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
phase: SSOAuthEntry.PHASE_PREAUTH,
|
phase: SSOAuthEntry.PHASE_PREAUTH,
|
||||||
|
@ -810,6 +809,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
window.addEventListener("message", this.onReceiveMessage);
|
||||||
this.props.onPhaseChange(SSOAuthEntry.PHASE_PREAUTH);
|
this.props.onPhaseChange(SSOAuthEntry.PHASE_PREAUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,10 +918,10 @@ export class FallbackAuthEntry<T = {}> extends React.Component<IAuthEntryProps &
|
||||||
// we have to make the user click a button, as browsers will block
|
// we have to make the user click a button, as browsers will block
|
||||||
// the popup if we open it immediately.
|
// the popup if we open it immediately.
|
||||||
this.popupWindow = null;
|
this.popupWindow = null;
|
||||||
window.addEventListener("message", this.onReceiveMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
window.addEventListener("message", this.onReceiveMessage);
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,6 @@ interface Props {
|
||||||
export default class LoginWithQRFlow extends React.Component<Props> {
|
export default class LoginWithQRFlow extends React.Component<Props> {
|
||||||
private checkCodeInput = createRef<HTMLInputElement>();
|
private checkCodeInput = createRef<HTMLInputElement>();
|
||||||
|
|
||||||
public constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick = (type: Click): ((e: React.FormEvent) => Promise<void>) => {
|
private handleClick = (type: Click): ((e: React.FormEvent) => Promise<void>) => {
|
||||||
return async (e: React.FormEvent): Promise<void> => {
|
return async (e: React.FormEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -20,10 +20,6 @@ interface IProps {
|
||||||
* menu.
|
* menu.
|
||||||
*/
|
*/
|
||||||
export default class GenericElementContextMenu extends React.Component<IProps> {
|
export default class GenericElementContextMenu extends React.Component<IProps> {
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
window.addEventListener("resize", this.resize);
|
window.addEventListener("resize", this.resize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,6 @@ interface IProps extends IContextMenuProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LegacyCallContextMenu extends React.Component<IProps> {
|
export default class LegacyCallContextMenu extends React.Component<IProps> {
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onHoldClick = (): void => {
|
public onHoldClick = (): void => {
|
||||||
this.props.call.setRemoteOnHold(true);
|
this.props.call.setRemoteOnHold(true);
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
|
|
|
@ -64,6 +64,11 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
this.issueRef = React.createRef();
|
this.issueRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
this.issueRef.current?.focus();
|
||||||
|
|
||||||
// Get all of the extra info dumped to the console when someone is about
|
// Get all of the extra info dumped to the console when someone is about
|
||||||
// to send debug logs. Since this is a fire and forget action, we do
|
// to send debug logs. Since this is a fire and forget action, we do
|
||||||
|
@ -76,10 +81,6 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this.issueRef.current?.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,14 +113,6 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
nameIsValid: false,
|
nameIsValid: false,
|
||||||
canChangeEncryption: false,
|
canChangeEncryption: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
checkUserIsAllowedToChangeEncryption(cli, Preset.PrivateChat).then(({ allowChange, forcedValue }) =>
|
|
||||||
this.setState((state) => ({
|
|
||||||
canChangeEncryption: allowChange,
|
|
||||||
// override with forcedValue if it is set
|
|
||||||
isEncrypted: forcedValue ?? state.isEncrypted,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private roomCreateOptions(): IOpts {
|
private roomCreateOptions(): IOpts {
|
||||||
|
@ -160,6 +152,15 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
const cli = MatrixClientPeg.safeGet();
|
||||||
|
checkUserIsAllowedToChangeEncryption(cli, Preset.PrivateChat).then(({ allowChange, forcedValue }) =>
|
||||||
|
this.setState((state) => ({
|
||||||
|
canChangeEncryption: allowChange,
|
||||||
|
// override with forcedValue if it is set
|
||||||
|
isEncrypted: forcedValue ?? state.isEncrypted,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
// move focus to first field when showing dialog
|
// move focus to first field when showing dialog
|
||||||
this.nameField.current?.focus();
|
this.nameField.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,9 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
authData: null, // for UIA
|
authData: null, // for UIA
|
||||||
authEnabled: true, // see usages for information
|
authEnabled: true, // see usages for information
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.initAuth(/* shouldErase= */ false);
|
this.initAuth(/* shouldErase= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,9 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
|
||||||
opponentProfileError: null,
|
opponentProfileError: null,
|
||||||
sas: null,
|
sas: null,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.props.verifier.on(VerifierEvent.ShowSas, this.onVerifierShowSas);
|
this.props.verifier.on(VerifierEvent.ShowSas, this.onVerifierShowSas);
|
||||||
this.props.verifier.on(VerifierEvent.Cancel, this.onVerifierCancel);
|
this.props.verifier.on(VerifierEvent.Cancel, this.onVerifierCancel);
|
||||||
this.fetchOpponentProfile();
|
this.fetchOpponentProfile();
|
||||||
|
|
|
@ -397,6 +397,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.encryptionByDefault = privateShouldBeEncrypted(MatrixClientPeg.safeGet());
|
this.encryptionByDefault = privateShouldBeEncrypted(MatrixClientPeg.safeGet());
|
||||||
|
|
||||||
if (this.props.initialText) {
|
if (this.props.initialText) {
|
||||||
|
|
|
@ -81,9 +81,10 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
backupStatus: BackupStatus.LOADING,
|
backupStatus: BackupStatus.LOADING,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// we can't call setState() immediately, so wait a beat
|
public componentDidMount(): void {
|
||||||
window.setTimeout(() => this.startLoadBackupStatus(), 0);
|
this.startLoadBackupStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** kick off the asynchronous calls to populate `state.backupStatus` in the background */
|
/** kick off the asynchronous calls to populate `state.backupStatus` in the background */
|
||||||
|
|
|
@ -32,6 +32,9 @@ export default class VerificationRequestDialog extends React.Component<IProps, I
|
||||||
this.state = {
|
this.state = {
|
||||||
verificationRequest: this.props.verificationRequest,
|
verificationRequest: this.props.verificationRequest,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.props.verificationRequestPromise?.then((r) => {
|
this.props.verificationRequestPromise?.then((r) => {
|
||||||
this.setState({ verificationRequest: r });
|
this.setState({ verificationRequest: r });
|
||||||
});
|
});
|
||||||
|
|
|
@ -134,29 +134,20 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
private iframe?: HTMLIFrameElement; // ref to the iframe (callback style)
|
private iframe?: HTMLIFrameElement; // ref to the iframe (callback style)
|
||||||
private allowedWidgetsWatchRef?: string;
|
private allowedWidgetsWatchRef?: string;
|
||||||
private persistKey: string;
|
private persistKey: string;
|
||||||
private sgWidget: StopGapWidget | null;
|
private sgWidget?: StopGapWidget;
|
||||||
private dispatcherRef?: string;
|
private dispatcherRef?: string;
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
public constructor(props: IProps, context: ContextType<typeof MatrixClientContext>) {
|
public constructor(props: IProps, context: ContextType<typeof MatrixClientContext>) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
// Tiles in miniMode are floating, and therefore not docked
|
|
||||||
if (!this.props.miniMode) {
|
|
||||||
ActiveWidgetStore.instance.dockWidget(
|
|
||||||
this.props.app.id,
|
|
||||||
isAppWidget(this.props.app) ? this.props.app.roomId : null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The key used for PersistedElement
|
// The key used for PersistedElement
|
||||||
this.persistKey = getPersistKey(WidgetUtils.getWidgetUid(this.props.app));
|
this.persistKey = getPersistKey(WidgetUtils.getWidgetUid(this.props.app));
|
||||||
try {
|
try {
|
||||||
this.sgWidget = new StopGapWidget(this.props);
|
this.sgWidget = new StopGapWidget(this.props);
|
||||||
this.setupSgListeners();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.log("Failed to construct widget", e);
|
logger.log("Failed to construct widget", e);
|
||||||
this.sgWidget = null;
|
this.sgWidget = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = this.getNewState(props);
|
this.state = this.getNewState(props);
|
||||||
|
@ -303,6 +294,20 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
|
// Tiles in miniMode are floating, and therefore not docked
|
||||||
|
if (!this.props.miniMode) {
|
||||||
|
ActiveWidgetStore.instance.dockWidget(
|
||||||
|
this.props.app.id,
|
||||||
|
isAppWidget(this.props.app) ? this.props.app.roomId : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sgWidget) {
|
||||||
|
this.setupSgListeners();
|
||||||
|
}
|
||||||
|
|
||||||
// Only fetch IM token on mount if we're showing and have permission to load
|
// Only fetch IM token on mount if we're showing and have permission to load
|
||||||
if (this.sgWidget && this.state.hasPermissionToLoad) {
|
if (this.sgWidget && this.state.hasPermissionToLoad) {
|
||||||
this.startWidget();
|
this.startWidget();
|
||||||
|
@ -374,7 +379,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
this.startWidget();
|
this.startWidget();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("Failed to construct widget", e);
|
logger.error("Failed to construct widget", e);
|
||||||
this.sgWidget = null;
|
this.sgWidget = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +612,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let appTileBody: JSX.Element;
|
let appTileBody: JSX.Element | undefined;
|
||||||
|
|
||||||
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
|
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
|
||||||
// because that would allow the iframe to programmatically remove the sandbox attribute, but
|
// because that would allow the iframe to programmatically remove the sandbox attribute, but
|
||||||
|
@ -650,7 +655,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
<AppWarning errorMsg={_t("widget|error_loading")} />
|
<AppWarning errorMsg={_t("widget|error_loading")} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (!this.state.hasPermissionToLoad && this.props.room) {
|
} else if (!this.state.hasPermissionToLoad && this.props.room && this.sgWidget) {
|
||||||
// only possible for room widgets, can assert this.props.room here
|
// only possible for room widgets, can assert this.props.room here
|
||||||
const isEncrypted = this.context.isRoomEncrypted(this.props.room.roomId);
|
const isEncrypted = this.context.isRoomEncrypted(this.props.room.roomId);
|
||||||
appTileBody = (
|
appTileBody = (
|
||||||
|
@ -677,7 +682,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
<AppWarning errorMsg={_t("widget|error_mixed_content")} />
|
<AppWarning errorMsg={_t("widget|error_mixed_content")} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else if (this.sgWidget) {
|
||||||
appTileBody = (
|
appTileBody = (
|
||||||
<>
|
<>
|
||||||
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
||||||
|
|
|
@ -41,10 +41,6 @@ export interface ExistingSourceIProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExistingSource extends React.Component<ExistingSourceIProps> {
|
export class ExistingSource extends React.Component<ExistingSourceIProps> {
|
||||||
public constructor(props: ExistingSourceIProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onClick = (): void => {
|
private onClick = (): void => {
|
||||||
this.props.onSelect(this.props.source);
|
this.props.onSelect(this.props.source);
|
||||||
};
|
};
|
||||||
|
|
|
@ -127,7 +127,9 @@ export default class Dropdown extends React.Component<DropdownProps, IState> {
|
||||||
// the current search query
|
// the current search query
|
||||||
searchQuery: "",
|
searchQuery: "",
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
// Listen for all clicks on the document so we can close the
|
// Listen for all clicks on the document so we can close the
|
||||||
// menu when the user clicks somewhere else
|
// menu when the user clicks somewhere else
|
||||||
document.addEventListener("click", this.onDocumentClick, false);
|
document.addEventListener("click", this.onDocumentClick, false);
|
||||||
|
|
|
@ -15,10 +15,6 @@ interface IProps extends Omit<React.ComponentProps<typeof TextWithTooltip>, "tab
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LinkWithTooltip extends React.Component<IProps> {
|
export default class LinkWithTooltip extends React.Component<IProps> {
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { children, tooltip, ...props } = this.props;
|
const { children, tooltip, ...props } = this.props;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ interface IProps {
|
||||||
*/
|
*/
|
||||||
export default class PersistedElement extends React.Component<IProps> {
|
export default class PersistedElement extends React.Component<IProps> {
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
private dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private childContainer?: HTMLDivElement;
|
private childContainer?: HTMLDivElement;
|
||||||
private child?: HTMLDivElement;
|
private child?: HTMLDivElement;
|
||||||
|
|
||||||
|
@ -87,13 +87,6 @@ export default class PersistedElement extends React.Component<IProps> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.resizeObserver = new ResizeObserver(this.repositionChild);
|
this.resizeObserver = new ResizeObserver(this.repositionChild);
|
||||||
// Annoyingly, a resize observer is insufficient, since we also care
|
|
||||||
// about when the element moves on the screen without changing its
|
|
||||||
// dimensions. Doesn't look like there's a ResizeObserver equivalent
|
|
||||||
// for this, so we bodge it by listening for document resize and
|
|
||||||
// the timeline_resize action.
|
|
||||||
window.addEventListener("resize", this.repositionChild);
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
|
|
||||||
if (this.props.moveRef) this.props.moveRef.current = this.repositionChild;
|
if (this.props.moveRef) this.props.moveRef.current = this.repositionChild;
|
||||||
}
|
}
|
||||||
|
@ -132,6 +125,14 @@ export default class PersistedElement extends React.Component<IProps> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
// Annoyingly, a resize observer is insufficient, since we also care
|
||||||
|
// about when the element moves on the screen without changing its
|
||||||
|
// dimensions. Doesn't look like there's a ResizeObserver equivalent
|
||||||
|
// for this, so we bodge it by listening for document resize and
|
||||||
|
// the timeline_resize action.
|
||||||
|
window.addEventListener("resize", this.repositionChild);
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
|
||||||
this.updateChild();
|
this.updateChild();
|
||||||
this.renderApp();
|
this.renderApp();
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ export default class PowerSelector<K extends undefined | string> extends React.C
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.initStateFromProps();
|
this.initStateFromProps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.initialize();
|
this.initialize();
|
||||||
this.trySetExpandableQuotes();
|
this.trySetExpandableQuotes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,6 @@ interface IProps extends HTMLAttributes<HTMLSpanElement> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TextWithTooltip extends React.Component<IProps> {
|
export default class TextWithTooltip extends React.Component<IProps> {
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { className, children, tooltip, tooltipProps } = this.props;
|
const { className, children, tooltip, tooltipProps } = this.props;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,9 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedEmojis: new Set(Object.keys(this.getReactions())),
|
selectedEmojis: new Set(Object.keys(this.getReactions())),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.addListeners();
|
this.addListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,9 @@ export default class DateSeparator extends React.Component<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
jumpToDateEnabled: SettingsStore.getValue("feature_jump_to_date"),
|
jumpToDateEnabled: SettingsStore.getValue("feature_jump_to_date"),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
// We're using a watcher so the date headers in the timeline are updated
|
// We're using a watcher so the date headers in the timeline are updated
|
||||||
// when the lab setting is toggled.
|
// when the lab setting is toggled.
|
||||||
this.settingWatcherRef = SettingsStore.watchSetting(
|
this.settingWatcherRef = SettingsStore.watchSetting(
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
public static contextType = RoomContext;
|
public static contextType = RoomContext;
|
||||||
public declare context: React.ContextType<typeof RoomContext>;
|
public declare context: React.ContextType<typeof RoomContext>;
|
||||||
|
|
||||||
private unmounted = true;
|
private unmounted = false;
|
||||||
private image = createRef<HTMLImageElement>();
|
private image = createRef<HTMLImageElement>();
|
||||||
private placeholder = createRef<HTMLDivElement>();
|
private placeholder = createRef<HTMLDivElement>();
|
||||||
private timeout?: number;
|
private timeout?: number;
|
||||||
|
|
|
@ -21,10 +21,6 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
|
export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
|
||||||
public constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const url = this.props.mxEvent.getContent()["url"];
|
const url = this.props.mxEvent.getContent()["url"];
|
||||||
const prevUrl = this.props.mxEvent.getPrevContent()["url"];
|
const prevUrl = this.props.mxEvent.getPrevContent()["url"];
|
||||||
|
|
|
@ -75,6 +75,10 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
|
||||||
this.context.on(ClientEvent.Sync, this.reconnectedListener);
|
this.context.on(ClientEvent.Sync, this.reconnectedListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
this.context.off(ClientEvent.Sync, this.reconnectedListener);
|
this.context.off(ClientEvent.Sync, this.reconnectedListener);
|
||||||
|
|
|
@ -68,11 +68,13 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.resizer = this.createResizer();
|
this.resizer = this.createResizer();
|
||||||
|
|
||||||
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
|
||||||
|
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
|
||||||
|
|
||||||
ScalarMessaging.startListening();
|
ScalarMessaging.startListening();
|
||||||
WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
|
|
@ -128,10 +128,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
private lastCaret!: DocumentOffset;
|
private lastCaret!: DocumentOffset;
|
||||||
private lastSelection: ReturnType<typeof cloneSelection> | null = null;
|
private lastSelection: ReturnType<typeof cloneSelection> | null = null;
|
||||||
|
|
||||||
private readonly useMarkdownHandle: string;
|
private useMarkdownHandle?: string;
|
||||||
private readonly emoticonSettingHandle: string;
|
private emoticonSettingHandle?: string;
|
||||||
private readonly shouldShowPillAvatarSettingHandle: string;
|
private shouldShowPillAvatarSettingHandle?: string;
|
||||||
private readonly surroundWithHandle: string;
|
private surroundWithHandle?: string;
|
||||||
private readonly historyManager = new HistoryManager();
|
private readonly historyManager = new HistoryManager();
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
|
@ -145,28 +145,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
const ua = navigator.userAgent.toLowerCase();
|
const ua = navigator.userAgent.toLowerCase();
|
||||||
this.isSafari = ua.includes("safari/") && !ua.includes("chrome/");
|
this.isSafari = ua.includes("safari/") && !ua.includes("chrome/");
|
||||||
|
|
||||||
this.useMarkdownHandle = SettingsStore.watchSetting(
|
|
||||||
"MessageComposerInput.useMarkdown",
|
|
||||||
null,
|
|
||||||
this.configureUseMarkdown,
|
|
||||||
);
|
|
||||||
this.emoticonSettingHandle = SettingsStore.watchSetting(
|
|
||||||
"MessageComposerInput.autoReplaceEmoji",
|
|
||||||
null,
|
|
||||||
this.configureEmoticonAutoReplace,
|
|
||||||
);
|
|
||||||
this.configureEmoticonAutoReplace();
|
this.configureEmoticonAutoReplace();
|
||||||
this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting(
|
|
||||||
"Pill.shouldShowPillAvatar",
|
|
||||||
null,
|
|
||||||
this.configureShouldShowPillAvatar,
|
|
||||||
);
|
|
||||||
this.surroundWithHandle = SettingsStore.watchSetting(
|
|
||||||
"MessageComposerInput.surroundWith",
|
|
||||||
null,
|
|
||||||
this.surroundWithSettingChanged,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: IProps): void {
|
public componentDidUpdate(prevProps: IProps): void {
|
||||||
|
@ -737,6 +716,27 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.useMarkdownHandle = SettingsStore.watchSetting(
|
||||||
|
"MessageComposerInput.useMarkdown",
|
||||||
|
null,
|
||||||
|
this.configureUseMarkdown,
|
||||||
|
);
|
||||||
|
this.emoticonSettingHandle = SettingsStore.watchSetting(
|
||||||
|
"MessageComposerInput.autoReplaceEmoji",
|
||||||
|
null,
|
||||||
|
this.configureEmoticonAutoReplace,
|
||||||
|
);
|
||||||
|
this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting(
|
||||||
|
"Pill.shouldShowPillAvatar",
|
||||||
|
null,
|
||||||
|
this.configureShouldShowPillAvatar,
|
||||||
|
);
|
||||||
|
this.surroundWithHandle = SettingsStore.watchSetting(
|
||||||
|
"MessageComposerInput.surroundWith",
|
||||||
|
null,
|
||||||
|
this.surroundWithSettingChanged,
|
||||||
|
);
|
||||||
|
|
||||||
const model = this.props.model;
|
const model = this.props.model;
|
||||||
model.setUpdateCallback(this.updateEditorState);
|
model.setUpdateCallback(this.updateEditorState);
|
||||||
const partCreator = model.partCreator;
|
const partCreator = model.partCreator;
|
||||||
|
|
|
@ -124,7 +124,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
public declare context: React.ContextType<typeof RoomContext>;
|
public declare context: React.ContextType<typeof RoomContext>;
|
||||||
|
|
||||||
private readonly editorRef = createRef<BasicMessageComposer>();
|
private readonly editorRef = createRef<BasicMessageComposer>();
|
||||||
private readonly dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private readonly replyToEvent?: MatrixEvent;
|
private readonly replyToEvent?: MatrixEvent;
|
||||||
private model!: EditorModel;
|
private model!: EditorModel;
|
||||||
|
|
||||||
|
@ -140,7 +140,9 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
this.state = {
|
this.state = {
|
||||||
saveDisabled: !isRestored || !this.isContentModified(editContent["m.new_content"]!),
|
saveDisabled: !isRestored || !this.isContentModified(editContent["m.new_content"]!),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.suppressReadReceiptAnimation = false;
|
this.suppressReadReceiptAnimation = false;
|
||||||
const client = MatrixClientPeg.safeGet();
|
const client = MatrixClientPeg.safeGet();
|
||||||
if (!this.props.forExport) {
|
if (!this.props.forExport) {
|
||||||
|
|
|
@ -72,7 +72,7 @@ interface IState {
|
||||||
|
|
||||||
export default class MemberList extends React.Component<IProps, IState> {
|
export default class MemberList extends React.Component<IProps, IState> {
|
||||||
private readonly showPresence: boolean;
|
private readonly showPresence: boolean;
|
||||||
private mounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
public static contextType = SDKContext;
|
public static contextType = SDKContext;
|
||||||
public declare context: React.ContextType<typeof SDKContext>;
|
public declare context: React.ContextType<typeof SDKContext>;
|
||||||
|
@ -82,8 +82,6 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.getMembersState([], []);
|
this.state = this.getMembersState([], []);
|
||||||
this.showPresence = context?.memberListStore.isPresenceEnabled() ?? true;
|
this.showPresence = context?.memberListStore.isPresenceEnabled() ?? true;
|
||||||
this.mounted = true;
|
|
||||||
this.listenForMembersChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private listenForMembersChanges(): void {
|
private listenForMembersChanges(): void {
|
||||||
|
@ -102,11 +100,13 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
|
this.listenForMembersChanges();
|
||||||
this.updateListNow(true);
|
this.updateListNow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.mounted = false;
|
this.unmounted = true;
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
cli.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
cli.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||||
|
@ -205,7 +205,7 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
// XXX: exported for tests
|
// XXX: exported for tests
|
||||||
public async updateListNow(showLoadingSpinner?: boolean): Promise<void> {
|
public async updateListNow(showLoadingSpinner?: boolean): Promise<void> {
|
||||||
if (!this.mounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (showLoadingSpinner) {
|
if (showLoadingSpinner) {
|
||||||
|
@ -215,7 +215,7 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
this.props.roomId,
|
this.props.roomId,
|
||||||
this.props.searchQuery,
|
this.props.searchQuery,
|
||||||
);
|
);
|
||||||
if (!this.mounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -134,9 +134,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.context = context; // otherwise React will only set it prior to render due to type def above
|
this.context = context; // otherwise React will only set it prior to render due to type def above
|
||||||
|
|
||||||
VoiceRecordingStore.instance.on(UPDATE_EVENT, this.onVoiceStoreUpdate);
|
|
||||||
|
|
||||||
window.addEventListener("beforeunload", this.saveWysiwygEditorState);
|
|
||||||
const isWysiwygLabEnabled = SettingsStore.getValue<boolean>("feature_wysiwyg_composer");
|
const isWysiwygLabEnabled = SettingsStore.getValue<boolean>("feature_wysiwyg_composer");
|
||||||
let isRichTextEnabled = true;
|
let isRichTextEnabled = true;
|
||||||
let initialComposerContent = "";
|
let initialComposerContent = "";
|
||||||
|
@ -145,13 +142,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
if (wysiwygState) {
|
if (wysiwygState) {
|
||||||
isRichTextEnabled = wysiwygState.isRichText;
|
isRichTextEnabled = wysiwygState.isRichText;
|
||||||
initialComposerContent = wysiwygState.content;
|
initialComposerContent = wysiwygState.content;
|
||||||
if (wysiwygState.replyEventId) {
|
|
||||||
dis.dispatch({
|
|
||||||
action: "reply_to_event",
|
|
||||||
event: this.props.room.findEventById(wysiwygState.replyEventId),
|
|
||||||
context: this.context.timelineRenderingType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,11 +161,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.instanceId = instanceCount++;
|
this.instanceId = instanceCount++;
|
||||||
|
|
||||||
SettingsStore.monitorSetting("MessageComposerInput.showStickersButton", null);
|
|
||||||
SettingsStore.monitorSetting("MessageComposerInput.showPollsButton", null);
|
|
||||||
SettingsStore.monitorSetting(Features.VoiceBroadcast, null);
|
|
||||||
SettingsStore.monitorSetting("feature_wysiwyg_composer", null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get editorStateKey(): string {
|
private get editorStateKey(): string {
|
||||||
|
@ -248,6 +233,25 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
VoiceRecordingStore.instance.on(UPDATE_EVENT, this.onVoiceStoreUpdate);
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", this.saveWysiwygEditorState);
|
||||||
|
if (this.state.isWysiwygLabEnabled) {
|
||||||
|
const wysiwygState = this.restoreWysiwygEditorState();
|
||||||
|
if (wysiwygState?.replyEventId) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: "reply_to_event",
|
||||||
|
event: this.props.room.findEventById(wysiwygState.replyEventId),
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsStore.monitorSetting("MessageComposerInput.showStickersButton", null);
|
||||||
|
SettingsStore.monitorSetting("MessageComposerInput.showPollsButton", null);
|
||||||
|
SettingsStore.monitorSetting(Features.VoiceBroadcast, null);
|
||||||
|
SettingsStore.monitorSetting("feature_wysiwyg_composer", null);
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this.waitForOwnMember();
|
this.waitForOwnMember();
|
||||||
UIStore.instance.trackElementDimensions(`MessageComposer${this.instanceId}`, this.ref.current!);
|
UIStore.instance.trackElementDimensions(`MessageComposer${this.instanceId}`, this.ref.current!);
|
||||||
|
|
|
@ -44,15 +44,23 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class NotificationBadge extends React.PureComponent<XOR<IProps, IClickableProps>, IState> {
|
export default class NotificationBadge extends React.PureComponent<XOR<IProps, IClickableProps>, IState> {
|
||||||
private countWatcherRef: string;
|
private countWatcherRef?: string;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.props.notification.on(NotificationStateEvents.Update, this.onNotificationUpdate);
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showCounts: SettingsStore.getValue("Notifications.alwaysShowBadgeCounts", this.roomId),
|
showCounts: SettingsStore.getValue("Notifications.alwaysShowBadgeCounts", this.roomId),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get roomId(): string | null {
|
||||||
|
// We should convert this to null for safety with the SettingsStore
|
||||||
|
return this.props.roomId || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.props.notification.on(NotificationStateEvents.Update, this.onNotificationUpdate);
|
||||||
|
|
||||||
this.countWatcherRef = SettingsStore.watchSetting(
|
this.countWatcherRef = SettingsStore.watchSetting(
|
||||||
"Notifications.alwaysShowBadgeCounts",
|
"Notifications.alwaysShowBadgeCounts",
|
||||||
|
@ -61,11 +69,6 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get roomId(): string | null {
|
|
||||||
// We should convert this to null for safety with the SettingsStore
|
|
||||||
return this.props.roomId || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
SettingsStore.unwatchSetting(this.countWatcherRef);
|
SettingsStore.unwatchSetting(this.countWatcherRef);
|
||||||
this.props.notification.off(NotificationStateEvents.Update, this.onNotificationUpdate);
|
this.props.notification.off(NotificationStateEvents.Update, this.onNotificationUpdate);
|
||||||
|
|
|
@ -60,7 +60,7 @@ const RoomBreadcrumbTile: React.FC<{ room: Room; onClick: (ev: ButtonEvent) => v
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState> {
|
export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState> {
|
||||||
private isMounted = true;
|
private unmounted = false;
|
||||||
private toolbar = createRef<HTMLDivElement>();
|
private toolbar = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
|
@ -70,17 +70,20 @@ export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState>
|
||||||
doAnimation: true, // technically we want animation on mount, but it won't be perfect
|
doAnimation: true, // technically we want animation on mount, but it won't be perfect
|
||||||
skipFirst: false, // render the thing, as boring as it is
|
skipFirst: false, // render the thing, as boring as it is
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.isMounted = false;
|
this.unmounted = true;
|
||||||
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onBreadcrumbsUpdate = (): void => {
|
private onBreadcrumbsUpdate = (): void => {
|
||||||
if (!this.isMounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// We need to trick the CSSTransition component into updating, which means we need to
|
// We need to trick the CSSTransition component into updating, which means we need to
|
||||||
// tell it to not animate, then to animate a moment later. This causes two updates
|
// tell it to not animate, then to animate a moment later. This causes two updates
|
||||||
|
|
|
@ -94,7 +94,6 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
||||||
// generatePreview() will return nothing if the user has previews disabled
|
// generatePreview() will return nothing if the user has previews disabled
|
||||||
messagePreview: null,
|
messagePreview: null,
|
||||||
};
|
};
|
||||||
this.generatePreview();
|
|
||||||
|
|
||||||
this.notificationState = RoomNotificationStateStore.instance.getRoomState(this.props.room);
|
this.notificationState = RoomNotificationStateStore.instance.getRoomState(this.props.room);
|
||||||
this.roomProps = EchoChamber.forRoom(this.props.room);
|
this.roomProps = EchoChamber.forRoom(this.props.room);
|
||||||
|
@ -147,6 +146,8 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.generatePreview();
|
||||||
|
|
||||||
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
|
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
|
||||||
if (this.state.selected) {
|
if (this.state.selected) {
|
||||||
this.scrollIntoView();
|
this.scrollIntoView();
|
||||||
|
|
|
@ -255,7 +255,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
private readonly editorRef = createRef<BasicMessageComposer>();
|
private readonly editorRef = createRef<BasicMessageComposer>();
|
||||||
private model: EditorModel;
|
private model: EditorModel;
|
||||||
private currentlyComposedEditorState: SerializedPart[] | null = null;
|
private currentlyComposedEditorState: SerializedPart[] | null = null;
|
||||||
private dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private sendHistoryManager: SendHistoryManager;
|
private sendHistoryManager: SendHistoryManager;
|
||||||
|
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
|
@ -275,15 +275,17 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
|
||||||
|
|
||||||
const partCreator = new CommandPartCreator(this.props.room, this.props.mxClient);
|
const partCreator = new CommandPartCreator(this.props.room, this.props.mxClient);
|
||||||
const parts = this.restoreStoredEditorState(partCreator) || [];
|
const parts = this.restoreStoredEditorState(partCreator) || [];
|
||||||
this.model = new EditorModel(parts, partCreator);
|
this.model = new EditorModel(parts, partCreator);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
this.sendHistoryManager = new SendHistoryManager(this.props.room.roomId, "mx_cider_history_");
|
this.sendHistoryManager = new SendHistoryManager(this.props.room.roomId, "mx_cider_history_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: ISendMessageComposerProps): void {
|
public componentDidUpdate(prevProps: ISendMessageComposerProps): void {
|
||||||
const replyingToThread = this.props.relation?.key === THREAD_RELATION_TYPE.name;
|
const replyingToThread = this.props.relation?.key === THREAD_RELATION_TYPE.name;
|
||||||
const differentEventTarget = this.props.relation?.event_id !== prevProps.relation?.event_id;
|
const differentEventTarget = this.props.relation?.event_id !== prevProps.relation?.event_id;
|
||||||
|
|
|
@ -45,6 +45,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
const cli = MatrixClientPeg.safeGet();
|
const cli = MatrixClientPeg.safeGet();
|
||||||
cli.on(ClientEvent.AccountData, this.onAccountData);
|
cli.on(ClientEvent.AccountData, this.onAccountData);
|
||||||
cli.on(CryptoEvent.UserTrustStatusChanged, this.onStatusChanged);
|
cli.on(CryptoEvent.UserTrustStatusChanged, this.onStatusChanged);
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog";
|
import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog";
|
||||||
import type ImportE2eKeysDialog from "../../../async-components/views/dialogs/security/ImportE2eKeysDialog";
|
import type ImportE2eKeysDialog from "../../../async-components/views/dialogs/security/ImportE2eKeysDialog";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
@ -20,6 +19,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import SettingsFlag from "../elements/SettingsFlag";
|
import SettingsFlag from "../elements/SettingsFlag";
|
||||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||||
import SettingsSubsection, { SettingsSubsectionText } from "./shared/SettingsSubsection";
|
import SettingsSubsection, { SettingsSubsectionText } from "./shared/SettingsSubsection";
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
|
@ -33,17 +33,24 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CryptographyPanel extends React.Component<IProps, IState> {
|
export default class CryptographyPanel extends React.Component<IProps, IState> {
|
||||||
public constructor(props: IProps) {
|
public static contextType = MatrixClientContext;
|
||||||
|
public declare context: React.ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
|
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const client = MatrixClientPeg.safeGet();
|
if (!context.getCrypto()) {
|
||||||
const crypto = client.getCrypto();
|
|
||||||
if (!crypto) {
|
|
||||||
this.state = { deviceIdentityKey: null };
|
this.state = { deviceIdentityKey: null };
|
||||||
} else {
|
} else {
|
||||||
this.state = { deviceIdentityKey: undefined };
|
this.state = { deviceIdentityKey: undefined };
|
||||||
crypto
|
}
|
||||||
.getOwnDeviceKeys()
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
if (this.state.deviceIdentityKey === undefined) {
|
||||||
|
this.context
|
||||||
|
.getCrypto()
|
||||||
|
?.getOwnDeviceKeys()
|
||||||
.then((keys) => {
|
.then((keys) => {
|
||||||
this.setState({ deviceIdentityKey: keys.ed25519 });
|
this.setState({ deviceIdentityKey: keys.ed25519 });
|
||||||
})
|
})
|
||||||
|
@ -55,7 +62,7 @@ export default class CryptographyPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const client = MatrixClientPeg.safeGet();
|
const client = this.context;
|
||||||
const deviceId = client.deviceId;
|
const deviceId = client.deviceId;
|
||||||
let identityKey = this.state.deviceIdentityKey;
|
let identityKey = this.state.deviceIdentityKey;
|
||||||
if (identityKey === undefined) {
|
if (identityKey === undefined) {
|
||||||
|
@ -126,7 +133,7 @@ export default class CryptographyPanel extends React.Component<IProps, IState> {
|
||||||
import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise<
|
import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise<
|
||||||
typeof ExportE2eKeysDialog
|
typeof ExportE2eKeysDialog
|
||||||
>,
|
>,
|
||||||
{ matrixClient: MatrixClientPeg.safeGet() },
|
{ matrixClient: this.context },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,12 +142,12 @@ export default class CryptographyPanel extends React.Component<IProps, IState> {
|
||||||
import("../../../async-components/views/dialogs/security/ImportE2eKeysDialog") as unknown as Promise<
|
import("../../../async-components/views/dialogs/security/ImportE2eKeysDialog") as unknown as Promise<
|
||||||
typeof ImportE2eKeysDialog
|
typeof ImportE2eKeysDialog
|
||||||
>,
|
>,
|
||||||
{ matrixClient: MatrixClientPeg.safeGet() },
|
{ matrixClient: this.context },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateBlacklistDevicesFlag = (checked: boolean): void => {
|
private updateBlacklistDevicesFlag = (checked: boolean): void => {
|
||||||
const crypto = MatrixClientPeg.safeGet().getCrypto();
|
const crypto = this.context.getCrypto();
|
||||||
if (crypto) crypto.globalBlacklistUnverifiedDevices = checked;
|
if (crypto) crypto.globalBlacklistUnverifiedDevices = checked;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount(): Promise<void> {
|
public async componentDidMount(): Promise<void> {
|
||||||
|
this.unmounted = false;
|
||||||
// Fetch the current user profile for the message preview
|
// Fetch the current user profile for the message preview
|
||||||
const client = MatrixClientPeg.safeGet();
|
const client = MatrixClientPeg.safeGet();
|
||||||
const userId = client.getSafeUserId();
|
const userId = client.getSafeUserId();
|
||||||
|
|
|
@ -206,7 +206,7 @@ const NotificationActivitySettings = (): JSX.Element => {
|
||||||
* The old, deprecated notifications tab view, only displayed if the user has the labs flag disabled.
|
* The old, deprecated notifications tab view, only displayed if the user has the labs flag disabled.
|
||||||
*/
|
*/
|
||||||
export default class Notifications extends React.PureComponent<IProps, IState> {
|
export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
private settingWatchers: string[];
|
private settingWatchers: string[] = [];
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -220,7 +220,17 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
clearingNotifications: false,
|
clearingNotifications: false,
|
||||||
ruleIdsWithError: {},
|
ruleIdsWithError: {},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isInhibited(): boolean {
|
||||||
|
// Caution: The master rule's enabled state is inverted from expectation. When
|
||||||
|
// the master rule is *enabled* it means all other rules are *disabled* (or
|
||||||
|
// inhibited). Conversely, when the master rule is *disabled* then all other rules
|
||||||
|
// are *enabled* (or operate fine).
|
||||||
|
return !!this.state.masterPushRule?.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
this.settingWatchers = [
|
this.settingWatchers = [
|
||||||
SettingsStore.watchSetting("notificationsEnabled", null, (...[, , , , value]) =>
|
SettingsStore.watchSetting("notificationsEnabled", null, (...[, , , , value]) =>
|
||||||
this.setState({ desktopNotifications: value as boolean }),
|
this.setState({ desktopNotifications: value as boolean }),
|
||||||
|
@ -235,17 +245,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ audioNotifications: value as boolean }),
|
this.setState({ audioNotifications: value as boolean }),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
|
||||||
private get isInhibited(): boolean {
|
|
||||||
// Caution: The master rule's enabled state is inverted from expectation. When
|
|
||||||
// the master rule is *enabled* it means all other rules are *disabled* (or
|
|
||||||
// inhibited). Conversely, when the master rule is *disabled* then all other rules
|
|
||||||
// are *enabled* (or operate fine).
|
|
||||||
return !!this.state.masterPushRule?.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.refreshFromServer();
|
this.refreshFromServer();
|
||||||
this.refreshFromAccountData();
|
this.refreshFromAccountData();
|
||||||
|
|
|
@ -83,6 +83,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.unmounted = false;
|
||||||
this.loadBackupStatus();
|
this.loadBackupStatus();
|
||||||
|
|
||||||
MatrixClientPeg.safeGet().on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
MatrixClientPeg.safeGet().on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
||||||
|
|
|
@ -53,9 +53,11 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors");
|
|
||||||
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
const msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors");
|
||||||
|
|
||||||
// we handle lack of this object gracefully later, so don't worry about it failing here.
|
// we handle lack of this object gracefully later, so don't worry about it failing here.
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
|
|
|
@ -205,7 +205,9 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
||||||
collapsed,
|
collapsed,
|
||||||
childSpaces: this.childSpaces,
|
childSpaces: this.childSpaces,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
SpaceStore.instance.on(this.props.space.roomId, this.onSpaceUpdate);
|
SpaceStore.instance.on(this.props.space.roomId, this.onSpaceUpdate);
|
||||||
this.props.space.on(RoomEvent.Name, this.onRoomNameChange);
|
this.props.space.on(RoomEvent.Name, this.onRoomNameChange);
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,11 +110,10 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
||||||
sidebarFeeds: sidebar,
|
sidebarFeeds: sidebar,
|
||||||
sidebarShown: true,
|
sidebarShown: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateCallListeners(null, this.props.call);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
this.updateCallListeners(null, this.props.call);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
document.addEventListener("keydown", this.onNativeKeyDown);
|
document.addEventListener("keydown", this.onNativeKeyDown);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { mocked } from "jest-mock";
|
||||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||||
import * as TestUtils from "../../../../test-utils";
|
import * as TestUtils from "../../../../test-utils";
|
||||||
import CryptographyPanel from "../../../../../src/components/views/settings/CryptographyPanel";
|
import CryptographyPanel from "../../../../../src/components/views/settings/CryptographyPanel";
|
||||||
|
import { withClientContextRenderOptions } from "../../../../test-utils";
|
||||||
|
|
||||||
describe("CryptographyPanel", () => {
|
describe("CryptographyPanel", () => {
|
||||||
it("shows the session ID and key", async () => {
|
it("shows the session ID and key", async () => {
|
||||||
|
@ -28,7 +29,7 @@ describe("CryptographyPanel", () => {
|
||||||
mocked(client.getCrypto()!.getOwnDeviceKeys).mockResolvedValue({ ed25519: sessionKey, curve25519: "1234" });
|
mocked(client.getCrypto()!.getOwnDeviceKeys).mockResolvedValue({ ed25519: sessionKey, curve25519: "1234" });
|
||||||
|
|
||||||
// When we render the CryptographyPanel
|
// When we render the CryptographyPanel
|
||||||
const rendered = render(<CryptographyPanel />);
|
const rendered = render(<CryptographyPanel />, withClientContextRenderOptions(client));
|
||||||
|
|
||||||
// Then it displays info about the user's session
|
// Then it displays info about the user's session
|
||||||
const codes = rendered.container.querySelectorAll("code");
|
const codes = rendered.container.querySelectorAll("code");
|
||||||
|
@ -52,7 +53,7 @@ describe("CryptographyPanel", () => {
|
||||||
mocked(client.getCrypto()!.getOwnDeviceKeys).mockRejectedValue(new Error("bleh"));
|
mocked(client.getCrypto()!.getOwnDeviceKeys).mockRejectedValue(new Error("bleh"));
|
||||||
|
|
||||||
// When we render the CryptographyPanel
|
// When we render the CryptographyPanel
|
||||||
const rendered = render(<CryptographyPanel />);
|
const rendered = render(<CryptographyPanel />, withClientContextRenderOptions(client));
|
||||||
|
|
||||||
// Then it displays info about the user's session
|
// Then it displays info about the user's session
|
||||||
const codes = rendered.container.querySelectorAll("code");
|
const codes = rendered.container.querySelectorAll("code");
|
||||||
|
|
Loading…
Reference in a new issue