Documentation and symbolic constants for dispatcher actions (#31278)

* Remove unreachable code

`view_last_screen` is never used.

* Remove unused action `user_activity_started`

Nothing listens to this, so it's pointless.

* Symbolic constant for `Action.UserActivity`

* Define symbolic constants for more `Action`s

Constants for some actions that are emitted by `Lifecycle`
This commit is contained in:
Richard van der Hoff 2025-11-20 18:18:04 +00:00 committed by GitHub
parent c203f02731
commit 1285b73be6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 60 additions and 40 deletions

View File

@ -88,7 +88,7 @@ export default abstract class BasePlatform {
protected onAction(payload: ActionPayload): void {
switch (payload.action) {
case "on_client_not_viable":
case Action.ClientNotViable:
case Action.OnLoggedOut:
this.setNotificationCount(0);
break;

View File

@ -1001,7 +1001,7 @@ export function softLogout(): void {
// Ensure that we dispatch a view change **before** stopping the client so
// so that React components unmount first. This avoids React soft crashes
// that can occur when components try to use a null client.
dis.dispatch({ action: "on_client_not_viable" }); // generic version of on_logged_out
dis.dispatch({ action: Action.ClientNotViable }); // generic version of on_logged_out
stopMatrixClient(/*unsetClient=*/ false);
// DO NOT CALL LOGOUT. A soft logout preserves data, logout does not.
@ -1034,7 +1034,7 @@ async function startMatrixClient(
// to add listeners for the 'sync' event so otherwise we'd have
// a race condition (and we need to dispatch synchronously for this
// to work).
dis.dispatch({ action: "will_start_client" }, true);
dis.dispatch({ action: Action.WillStartClient }, true);
// reset things first just in case
SdkContextClass.instance.typingStore.reset();
@ -1080,7 +1080,7 @@ async function startMatrixClient(
// dispatch that we finished starting up to wire up any other bits
// of the matrix client that cannot be set prior to starting up.
dis.dispatch({ action: "client_started" });
dis.dispatch({ action: Action.ClientStarted });
if (isSoftLogout()) {
softLogout();

View File

@ -15,6 +15,7 @@ import { MatrixClientPeg } from "./MatrixClientPeg";
import dis from "./dispatcher/dispatcher";
import Timer from "./utils/Timer";
import { type ActionPayload } from "./dispatcher/payloads";
import { Action } from "./dispatcher/actions.ts";
// Time in ms after that a user is considered as unavailable/away
const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
@ -61,7 +62,7 @@ class Presence {
}
private onAction = (payload: ActionPayload): void => {
if (payload.action === "user_activity") {
if (payload.action === Action.UserActivity) {
this.setState(SetPresence.Online);
this.unavailableTimer?.restart();
}

View File

@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import dis from "./dispatcher/dispatcher";
import Timer from "./utils/Timer";
import { Action } from "./dispatcher/actions.ts";
// important these are larger than the timeouts of timers
// used with UserActivity.timeWhileActive*,
@ -190,11 +191,9 @@ export default class UserActivity {
this.lastScreenY = event.screenY;
}
dis.dispatch({ action: "user_activity" });
dis.dispatch({ action: Action.UserActivity });
if (!this.activeNowTimeout.isRunning()) {
this.activeNowTimeout.start();
dis.dispatch({ action: "user_activity_start" });
UserActivity.runTimersUntilTimeout(this.attachedActiveNowTimers, this.activeNowTimeout);
} else {
this.activeNowTimeout.restart();

View File

@ -18,6 +18,7 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";
import { type ActionPayload } from "../../dispatcher/payloads";
import { Action } from "../../dispatcher/actions.ts";
interface IProps {
// URL to request embedded page content from
@ -109,7 +110,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
private onAction = (payload: ActionPayload): void => {
// HACK: Workaround for the context's MatrixClient not being set up at render time.
if (payload.action === "client_started") {
if (payload.action === Action.ClientStarted) {
this.forceUpdate();
}
};

View File

@ -812,13 +812,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
break;
}
case "view_last_screen":
// This function does what we want, despite the name. The idea is that it shows
// the last room we were looking at or some reasonable default/guess. We don't
// have to worry about email invites or similar being re-triggered because the
// function will have cleared that state and not execute that path.
this.showScreenAfterLogin();
break;
case "hide_left_panel":
this.setState(
{
@ -856,13 +849,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.onLoggedIn();
}
break;
case "on_client_not_viable":
case Action.ClientNotViable:
this.onSoftLogout();
break;
case Action.OnLoggedOut:
this.onLoggedOut();
break;
case "will_start_client":
case Action.WillStartClient:
this.setState({ ready: false }, () => {
// if the client is about to start, we are, by definition, not ready.
// Set ready to false now, then it'll be set to true when the sync
@ -870,7 +863,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.onWillStartClient();
});
break;
case "client_started":
case Action.ClientStarted:
// No need to make this handler async to wait for the result of this
this.onClientStarted().catch((e) => {
logger.error("Exception in onClientStarted", e);

View File

@ -316,16 +316,39 @@ export enum Action {
*/
ShowRoomTopic = "show_room_topic",
/**
* Fired when the client is no longer viable to use: specifically, that we have been "soft-logged out".
*/
ClientNotViable = "client_not_viable",
/**
* Fired when the client was logged out. No additional payload information required.
*/
OnLoggedOut = "on_logged_out",
/**
* Fired when the client was logged in. No additional payload information required.
* Fired when the client was logged in, or has otherwise been set up with authentication data (e.g., by loading the
* access token from local storage). Note that this does not necessarily mean that a login action has happened,
* just that authentication creds have been set up.
*
* No additional payload information required.
*/
OnLoggedIn = "on_logged_in",
/**
* Fired when the client is about to be started, shortly after {@link OnLoggedIn}.
*
* No additional payload information required.
*/
WillStartClient = "will_start_client",
/**
* Fired when the client has started, shortly after {@link WillStartClient}.
*
* No additional payload information required.
*/
ClientStarted = "client_started",
/**
* Overwrites the existing login with fresh session credentials. Use with a OverwriteLoginPayload.
*/
@ -380,4 +403,10 @@ export enum Action {
* Open the create room dialog
*/
CreateRoom = "view_create_room",
/**
* The `UserActivity` tracker determined that there was some activity from the user (typically a mouse movement
* or keyboard event).
*/
UserActivity = "user_activity",
}

View File

@ -69,7 +69,7 @@ class LifecycleStore extends AsyncStore<IState> {
dis.dispatch(deferredAction);
break;
}
case "on_client_not_viable":
case Action.ClientNotViable:
case Action.OnLoggedOut:
this.reset();
break;

View File

@ -77,7 +77,7 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
this.matrixClient = payload.matrixClient;
await this.onReady();
}
} else if (payload.action === "on_client_not_viable" || payload.action === Action.OnLoggedOut) {
} else if (payload.action === Action.ClientNotViable || payload.action === Action.OnLoggedOut) {
if (this.matrixClient) {
await this.onNotReady();
this.matrixClient = undefined;

View File

@ -285,7 +285,7 @@ export class RoomViewStore extends EventEmitter {
break;
}
case "on_client_not_viable":
case Action.ClientNotViable:
case Action.OnLoggedOut:
this.reset();
break;

View File

@ -218,7 +218,7 @@ export default class ElectronPlatform extends BasePlatform {
this.electron.send("app_onAction", payload);
}
if (payload.action === "client_started") {
if (payload.action === Action.ClientStarted) {
this.clientStartedPromiseWithResolvers.resolve();
}
}

View File

@ -53,7 +53,7 @@ export default class WebPlatform extends BasePlatform {
super.onAction(payload);
switch (payload.action) {
case "client_started":
case Action.ClientStarted:
// Defer drawing the toast until the client is started as the lifecycle methods reset the ToastStore right before
this.registerServiceWorkerPromise.catch(this.handleServiceWorkerRegistrationError);
break;

View File

@ -547,11 +547,11 @@ describe("<MatrixChat />", () => {
getComponent({ realQueryParams });
defaultDispatcher.dispatch({
action: "will_start_client",
action: Action.WillStartClient,
});
// client successfully started
await waitFor(() =>
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "client_started" }),
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ClientStarted }),
);
// set up keys screen is rendered
@ -1172,10 +1172,10 @@ describe("<MatrixChat />", () => {
getComponent({ realQueryParams });
defaultDispatcher.dispatch({
action: "will_start_client",
action: Action.WillStartClient,
});
await waitFor(() =>
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "client_started" }),
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ClientStarted }),
);
// Then we are not allowed in - we are being asked to verify
@ -1568,7 +1568,7 @@ describe("<MatrixChat />", () => {
it("should continue to post login setup when no session is found in local storage", async () => {
getComponent({ realQueryParams });
defaultDispatcher.dispatch({
action: "will_start_client",
action: Action.WillStartClient,
});
// set up keys screen is rendered
@ -1828,7 +1828,7 @@ describe("<MatrixChat />", () => {
getComponent({});
defaultDispatcher.dispatch({
action: "will_start_client",
action: Action.WillStartClient,
});
await flushPromises();
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");
@ -1851,7 +1851,7 @@ describe("<MatrixChat />", () => {
getComponent({});
defaultDispatcher.dispatch({
action: "will_start_client",
action: Action.WillStartClient,
});
await flushPromises();
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");

View File

@ -14,6 +14,7 @@ import { Container, WidgetLayoutStore } from "../../../src/stores/widgets/Widget
import { stubClient } from "../../test-utils";
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
import SettingsStore from "../../../src/settings/SettingsStore";
import { Action } from "../../../src/dispatcher/actions.ts";
// setup test env values
const roomId = "!room:server";
@ -196,12 +197,7 @@ describe("WidgetLayoutStore", () => {
it("should clear the layout if the client is not viable", () => {
store.recalculateRoom(mockRoom);
defaultDispatcher.dispatch(
{
action: "on_client_not_viable",
},
true,
);
defaultDispatcher.dispatch({ action: Action.ClientNotViable }, true);
expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([]);
expect(store.getContainerWidgets(mockRoom, Container.Center)).toEqual([]);

View File

@ -132,7 +132,7 @@ describe("ElectronPlatform", () => {
new ElectronPlatform();
dispatcher.dispatch(
{
action: "client_started",
action: Action.ClientStarted,
},
true,
);

View File

@ -15,6 +15,7 @@ import { setupLanguageMock } from "../../../setup/setupLanguage";
import ToastStore from "../../../../src/stores/ToastStore.ts";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher.ts";
import { emitPromise } from "../../../test-utils";
import { Action } from "../../../../src/dispatcher/actions.ts";
fetchMock.config.overwriteRoutes = true;
@ -49,7 +50,7 @@ describe("WebPlatform", () => {
};
new WebPlatform();
defaultDispatcher.dispatch({ action: "client_started" });
defaultDispatcher.dispatch({ action: Action.ClientStarted });
await emitPromise(ToastStore.sharedInstance(), "update");
const toasts = ToastStore.sharedInstance().getToasts();
expect(toasts).toHaveLength(1);