type Actions (#7862)

* type ViewHomPage action

Signed-off-by: Kerry Archibald <kerrya@element.io>

* type spacestore actions

Signed-off-by: Kerry Archibald <kerrya@element.io>

* lint

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add action types

Signed-off-by: Kerry Archibald <kerrya@element.io>

* use new action types in stores

Signed-off-by: Kerry Archibald <kerrya@element.io>

* remove debug change

Signed-off-by: Kerry Archibald <kerrya@element.io>

* stricter keyboard shortcut types

Signed-off-by: Kerry Archibald <kerrya@element.io>

* action comments

Signed-off-by: Kerry Archibald <kerrya@element.io>
pull/21833/head
Kerry 2022-02-22 11:04:27 +01:00 committed by GitHub
parent 57595bc593
commit 5b8d440406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 304 additions and 73 deletions

View File

@ -26,6 +26,7 @@ import dis from './dispatcher/dispatcher';
import Modal from './Modal';
import { _t } from './languageHandler';
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import { Action } from "./dispatcher/actions";
// Regex for what a "safe" or "Matrix-looking" localpart would be.
// TODO: Update as needed for https://github.com/matrix-org/matrix-doc/issues/1514
@ -69,7 +70,7 @@ export async function startAnyRegistrationFlow(
if (proceed) {
dis.dispatch({ action: 'start_registration', screenAfterLogin: options.screen_after });
} else if (options.go_home_on_cancel) {
dis.dispatch({ action: 'view_home_page' });
dis.dispatch({ action: Action.ViewHomePage });
} else if (options.go_welcome_on_cancel) {
dis.dispatch({ action: 'view_welcome_page' });
}

View File

@ -17,7 +17,7 @@ limitations under the License.
import { _td } from "../languageHandler";
import { isMac, Key } from "../Keyboard";
import { ISetting } from "../settings/Settings";
import { IBaseSetting } from "../settings/Settings";
import SettingsStore from "../settings/SettingsStore";
import IncompatibleController from "../settings/controllers/IncompatibleController";
@ -119,9 +119,20 @@ export enum KeyBindingAction {
ToggleHiddenEventVisibility = 'KeyBinding.toggleHiddenEventVisibility',
}
export type KeyBindingConfig = {
key: string;
ctrlOrCmdKey?: boolean;
ctrlKey?: boolean;
altKey?: boolean;
shiftKey?: boolean;
metaKey?: boolean;
};
type KeyboardShortcutSetting = IBaseSetting<KeyBindingConfig>;
type IKeyboardShortcuts = {
// TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager
[k in (KeyBindingAction | string)]: ISetting;
[k in (KeyBindingAction | string)]: KeyboardShortcutSetting;
};
export interface ICategory {
@ -614,7 +625,11 @@ export const getKeyboardShortcuts = (): IKeyboardShortcuts => {
}, {});
};
export const registerShortcut = (shortcutName: string, categoryName: CategoryName, shortcut: ISetting): void => {
export const registerShortcut = (
shortcutName: string,
categoryName: CategoryName,
shortcut: KeyboardShortcutSetting,
): void => {
KEYBOARD_SHORTCUTS[shortcutName] = shortcut;
CATEGORIES[categoryName].settingNames.push(shortcutName);
};

View File

@ -342,7 +342,7 @@ class FeaturedUser extends React.Component {
e.stopPropagation();
dis.dispatch({
action: 'view_start_chat_or_reuse',
action: Action.ViewStartChatOrReuse,
user_id: this.props.summaryInfo.user_id,
});
};
@ -491,7 +491,7 @@ export default class GroupView extends React.Component {
if (this._unmounted || groupId !== errorGroupId) return;
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN' && !willDoOnboarding) {
dis.dispatch({
action: 'do_after_sync_prepared',
action: Action.DoAfterSyncPrepared,
deferred_action: {
action: 'view_group',
group_id: groupId,

View File

@ -490,7 +490,7 @@ class LoggedInView extends React.Component<IProps, IState> {
break;
case KeyBindingAction.GoToHome:
dis.dispatch({
action: 'view_home_page',
action: Action.ViewHomePage,
});
Modal.closeCurrentModal("homeKeyboardShortcut");
handled = true;

View File

@ -119,6 +119,10 @@ import { SummarizedNotificationState } from "../../stores/notifications/Summariz
import GenericToast from '../views/toasts/GenericToast';
import Views from '../../Views';
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { ViewHomePagePayload } from '../../dispatcher/payloads/ViewHomePagePayload';
import { AfterLeaveRoomPayload } from '../../dispatcher/payloads/AfterLeaveRoomPayload';
import { DoAfterSyncPreparedPayload } from '../../dispatcher/payloads/DoAfterSyncPreparedPayload';
import { ViewStartChatOrReusePayload } from '../../dispatcher/payloads/ViewStartChatOrReusePayload';
// legacy export
export { default as Views } from "../../Views";
@ -541,7 +545,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// will cause a full login and sync and finally the deferred
// action will be dispatched.
dis.dispatch({
action: 'do_after_sync_prepared',
action: Action.DoAfterSyncPrepared,
deferred_action: payload,
});
dis.dispatch({ action: 'require_registration' });
@ -632,7 +636,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
MatrixClientPeg.get().leave(payload.room_id).then(() => {
modal.close();
if (this.state.currentRoomId === payload.room_id) {
dis.dispatch({ action: 'view_home_page' });
dis.dispatch({ action: Action.ViewHomePage });
}
}, (err) => {
modal.close();
@ -705,10 +709,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
case 'view_welcome_page':
this.viewWelcome();
break;
case 'view_home_page':
case Action.ViewHomePage:
this.viewHome(payload.justRegistered);
break;
case 'view_start_chat_or_reuse':
case Action.ViewStartChatOrReuse:
this.chatCreateOrReuse(payload.user_id);
break;
case 'view_create_chat':
@ -1057,10 +1061,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// No point in making 2 DMs with welcome bot. This assumes view_set_mxid will
// result in a new DM with the welcome user.
if (userId !== this.props.config.welcomeUserId) {
dis.dispatch({
action: 'do_after_sync_prepared',
dis.dispatch<DoAfterSyncPreparedPayload<ViewStartChatOrReusePayload>>({
action: Action.DoAfterSyncPrepared,
deferred_action: {
action: 'view_start_chat_or_reuse',
action: Action.ViewStartChatOrReuse,
user_id: userId,
},
});
@ -1162,8 +1166,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (shouldLeave) {
leaveRoomBehaviour(roomId);
dis.dispatch({
action: "after_leave_room",
dis.dispatch<AfterLeaveRoomPayload>({
action: Action.AfterLeaveRoom,
room_id: roomId,
});
}
@ -1176,7 +1180,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
MatrixClientPeg.get().forget(roomId).then(() => {
// Switch to home page if we're currently viewing the forgotten room
if (this.state.currentRoomId === roomId) {
dis.dispatch({ action: "view_home_page" });
dis.dispatch({ action: Action.ViewHomePage });
}
// We have to manually update the room list because the forgotten room will not
@ -1275,7 +1279,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (welcomeUserRoom === null) {
// We didn't redirect to the welcome user room, so show
// the homepage.
dis.dispatch({ action: 'view_home_page', justRegistered: true });
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage, justRegistered: true });
}
} else if (ThreepidInviteStore.instance.pickBestInvite()) {
// The user has a 3pid invite pending - show them that
@ -1288,7 +1292,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else {
// The user has just logged in after registering,
// so show the homepage.
dis.dispatch({ action: 'view_home_page', justRegistered: true });
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage, justRegistered: true });
}
} else {
this.showScreenAfterLogin();
@ -1353,7 +1357,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({ action: 'view_welcome_page' });
} else {
dis.dispatch({ action: 'view_home_page' });
dis.dispatch({ action: Action.ViewHomePage });
}
}
}
@ -1666,7 +1670,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const isLoggedOutOrGuest = !cli || cli.isGuest();
if (!isLoggedOutOrGuest && AUTH_SCREENS.includes(screen)) {
// user is logged in and landing on an auth page which will uproot their session, redirect them home instead
dis.dispatch({ action: "view_home_page" });
dis.dispatch({ action: Action.ViewHomePage });
return;
}
@ -1714,7 +1718,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
});
} else if (screen === 'home') {
dis.dispatch({
action: 'view_home_page',
action: Action.ViewHomePage,
});
} else if (screen === 'start') {
this.showScreen('home');
@ -1737,7 +1741,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin());
} else if (screen === 'groups') {
if (SpaceStore.spacesEnabled) {
dis.dispatch({ action: "view_home_page" });
dis.dispatch({ action: Action.ViewHomePage });
return;
}
dis.dispatch({

View File

@ -47,6 +47,7 @@ import { E2EStatus } from '../../utils/ShieldUtils';
import TimelineCard from '../views/right_panel/TimelineCard';
import { UPDATE_EVENT } from '../../stores/AsyncStore';
import { IRightPanelCard, IRightPanelCardState } from '../../stores/right-panel/RightPanelStoreIPanelState';
import { Action } from '../../dispatcher/actions';
interface IProps {
room?: Room; // if showing panels for a given room, this is set
@ -134,7 +135,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
// to the home page which is not obviously the correct thing to do, but I'm not sure
// anything else is - we could hide the close button altogether?)
dis.dispatch({
action: "view_home_page",
action: Action.ViewHomePage,
});
} else if (
this.state.phase === RightPanelPhases.EncryptionPanel &&

View File

@ -103,6 +103,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { JoinRoomPayload } from "../../dispatcher/payloads/JoinRoomPayload";
import { DoAfterSyncPreparedPayload } from '../../dispatcher/payloads/DoAfterSyncPreparedPayload';
const DEBUG = false;
let debuglog = function(msg: string) {};
@ -1273,11 +1274,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (this.context && this.context.isGuest()) {
// Join this room once the user has registered and logged in
// (If we failed to peek, we may not have a valid room object.)
dis.dispatch({
action: 'do_after_sync_prepared',
dis.dispatch<DoAfterSyncPreparedPayload<ViewRoomPayload>>({
action: Action.DoAfterSyncPrepared,
deferred_action: {
action: Action.ViewRoom,
room_id: this.getRoomId(),
metricsTrigger: undefined,
},
});
dis.dispatch({ action: 'require_registration' });
@ -1568,7 +1570,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
rejecting: true,
});
this.context.leave(this.state.roomId).then(() => {
dis.dispatch({ action: 'view_home_page' });
dis.dispatch({ action: Action.ViewHomePage });
this.setState({
rejecting: false,
});
@ -1601,7 +1603,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
await this.context.setIgnoredUsers(ignoredUsers);
await this.context.leave(this.state.roomId);
dis.dispatch({ action: 'view_home_page' });
dis.dispatch({ action: Action.ViewHomePage });
this.setState({
rejecting: false,
});

View File

@ -61,6 +61,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext";
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
import UserIdentifierCustomisations from "../../customisations/UserIdentifier";
import PosthogTrackers from "../../PosthogTrackers";
import { ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
const CustomStatusSection = () => {
const cli = useContext(MatrixClientContext);
@ -360,7 +361,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
ev.preventDefault();
ev.stopPropagation();
defaultDispatcher.dispatch({ action: 'view_home_page' });
defaultDispatcher.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
this.setState({ contextMenuPosition: null }); // also close the menu
};

View File

@ -31,6 +31,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import { Action } from '../../../dispatcher/actions';
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB";
@ -75,7 +76,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
private onAction = (payload): void => {
// When view changes below us, close the room settings
// whilst the modal is open this can only be triggered when someone hits Leave Room
if (payload.action === 'view_home_page') {
if (payload.action === Action.ViewHomePage) {
this.props.onFinished(true);
}
};

View File

@ -30,6 +30,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature";
import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab";
import { Action } from '../../../dispatcher/actions';
export enum SpaceSettingsTab {
General = "SPACE_GENERAL_TAB",
@ -44,8 +45,8 @@ interface IProps extends IDialogProps {
}
const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFinished }) => {
useDispatcher(defaultDispatcher, ({ action, ...params }) => {
if (action === "after_leave_room" && params.room_id === space.roomId) {
useDispatcher(defaultDispatcher, (payload) => {
if (payload.action === Action.AfterLeaveRoom && payload.room_id === space.roomId) {
onFinished(false);
}
});

View File

@ -49,6 +49,7 @@ import RoomViewStore from '../../../stores/RoomViewStore';
import WidgetUtils from '../../../utils/WidgetUtils';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { ActionPayload } from "../../../dispatcher/payloads";
import { Action } from '../../../dispatcher/actions';
interface IProps {
app: IApp;
@ -447,7 +448,7 @@ export default class AppTile extends React.Component<IProps, IState> {
}
break;
case "after_leave_room":
case Action.AfterLeaveRoom:
if (payload.room_id === this.props.room?.roomId) {
// call this before we get it echoed down /sync, so it doesn't hang around as long and look jarring
this.onUserLeftRoom();

View File

@ -24,13 +24,14 @@ import {
ICategory,
CATEGORIES,
CategoryName,
KeyBindingConfig,
} from "../../../../../accessibility/KeyboardShortcuts";
import SdkConfig from "../../../../../SdkConfig";
import { isMac, Key } from "../../../../../Keyboard";
import { _t } from "../../../../../languageHandler";
// TODO: This should return KeyCombo but it has ctrlOrCmd instead of ctrlOrCmdKey
const getKeyboardShortcutValue = (name: string) => {
const getKeyboardShortcutValue = (name: string): KeyBindingConfig => {
return getKeyboardShortcuts()[name]?.default;
};

View File

@ -45,6 +45,16 @@ export enum Action {
*/
ViewRoomDirectory = "view_room_directory",
/**
* Fires when viewing room by room_alias fails to find room
*/
ViewRoomError = "view_room_error",
/**
* Navigates to app home
*/
ViewHomePage = "view_home_page",
/**
* Forces the theme to reload. No additional payload information required.
*/
@ -224,4 +234,20 @@ export enum Action {
* Used to trigger auto rageshakes when configured
*/
ReportKeyBackupNotEnabled = "report_key_backup_not_enabled",
/**
* Dispatched after leave room or space is finished
*/
AfterLeaveRoom = "after_leave_room",
/**
* Used to defer actions until after sync is complete
* LifecycleStore will emit deferredAction payload after 'MatrixActions.sync'
*/
DoAfterSyncPrepared = "do_after_sync_prepared",
/**
* Fired when clicking user name from group view
*/
ViewStartChatOrReuse = "view_start_chat_or_reuse",
}

View File

@ -0,0 +1,26 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { Room } from "matrix-js-sdk";
import { Action } from "../actions";
import { ActionPayload } from "../payloads";
export interface AfterLeaveRoomPayload extends ActionPayload {
action: Action.AfterLeaveRoom;
// eslint-disable-next-line camelcase
room_id?: Room["roomId"];
}

View File

@ -0,0 +1,24 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface DoAfterSyncPreparedPayload<T extends ActionPayload> extends Pick<ActionPayload, "action"> {
action: Action.DoAfterSyncPrepared;
// eslint-disable-next-line camelcase
deferred_action: T;
}

View File

@ -0,0 +1,27 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixError } from "matrix-js-sdk";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface JoinRoomErrorPayload extends Pick<ActionPayload, "action"> {
action: Action.JoinRoomError;
roomId: string;
err?: MatrixError;
}

View File

@ -17,6 +17,7 @@ limitations under the License.
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
import { SettingLevel } from "../../settings/SettingLevel";
import { SettingValueType } from "../../settings/Settings";
export interface SettingUpdatedPayload extends ActionPayload {
action: Action.SettingUpdated;
@ -25,5 +26,5 @@ export interface SettingUpdatedPayload extends ActionPayload {
roomId: string;
level: SettingLevel;
newValueAtLevel: SettingLevel;
newValue: any;
newValue: SettingValueType;
}

View File

@ -0,0 +1,25 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { Action } from "../actions";
import { ActionPayload } from "../payloads";
export interface ViewHomePagePayload extends ActionPayload {
action: Action.ViewHomePage;
// eslint-disable-next-line camelcase
context_switch?: boolean;
justRegistered?: boolean;
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixError, Room } from "matrix-js-sdk";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface ViewRoomErrorPayload extends Pick<ActionPayload, "action"> {
action: Action.ViewRoomError;
// eslint-disable-next-line camelcase
room_id: Room["roomId"];
// eslint-disable-next-line camelcase
room_alias?: string;
err?: MatrixError;
}

View File

@ -0,0 +1,26 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { User } from "matrix-js-sdk";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface ViewStartChatOrReusePayload extends Pick<ActionPayload, "action"> {
action: Action.ViewStartChatOrReuse;
// eslint-disable-next-line camelcase
user_id: User["userId"];
}

View File

@ -25,6 +25,8 @@ import { _t } from "../languageHandler";
import dis from "../dispatcher/dispatcher";
import { SettingLevel } from "../settings/SettingLevel";
import { ActionPayload } from "../dispatcher/payloads";
import { DoAfterSyncPreparedPayload } from "../dispatcher/payloads/DoAfterSyncPreparedPayload";
import { Action } from "../dispatcher/actions";
// TODO: Move this and related files to the js-sdk or something once finalized.
@ -48,8 +50,8 @@ export class Mjolnir {
this.mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this.onListsChanged.bind(this));
this.dispatcherRef = dis.register(this.onAction);
dis.dispatch({
action: 'do_after_sync_prepared',
dis.dispatch<DoAfterSyncPreparedPayload<ActionPayload>>({
action: Action.DoAfterSyncPrepared,
deferred_action: { action: 'setup_mjolnir' },
});
}

View File

@ -114,7 +114,14 @@ export const labGroupNames: Record<LabGroup, string> = {
[LabGroup.Developer]: _td("Developer"),
};
interface IBaseSetting {
export type SettingValueType = boolean |
number |
string |
number[] |
string[] |
Record<string, unknown>;
export interface IBaseSetting<T extends SettingValueType = SettingValueType> {
isFeature?: false | undefined;
// Display names are strongly recommended for clarity.
@ -134,7 +141,7 @@ interface IBaseSetting {
// Required. Can be any data type. The value specified here should match
// the data being stored (ie: if a boolean is used, the setting should
// represent a boolean).
default: any;
default: T;
// Optional settings controller. See SettingsController for more information.
controller?: SettingController;
@ -166,7 +173,7 @@ interface IBaseSetting {
};
}
export interface IFeature extends Omit<IBaseSetting, "isFeature"> {
export interface IFeature extends Omit<IBaseSetting<boolean>, "isFeature"> {
// Must be set to true for features.
isFeature: true;
labsGroup: LabGroup;

View File

@ -18,13 +18,13 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
import SettingsStore from "../settings/SettingsStore";
import { ActionPayload } from "../dispatcher/payloads";
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher";
import { arrayHasDiff } from "../utils/arrays";
import { SettingLevel } from "../settings/SettingLevel";
import { Action } from "../dispatcher/actions";
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
const MAX_ROOMS = 20; // arbitrary
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
@ -64,15 +64,14 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
return this.matrixClient?.getVisibleRooms().length >= 20;
}
protected async onAction(payload: ActionPayload) {
protected async onAction(payload: SettingUpdatedPayload | ViewRoomPayload) {
if (!this.matrixClient) return;
if (payload.action === Action.SettingUpdated) {
const settingUpdatedPayload = payload as SettingUpdatedPayload;
if (settingUpdatedPayload.settingName === 'breadcrumb_rooms') {
if (payload.settingName === 'breadcrumb_rooms') {
await this.updateRooms();
} else if (settingUpdatedPayload.settingName === 'breadcrumbs' ||
settingUpdatedPayload.settingName === 'feature_breadcrumbs_v2'
} else if (payload.settingName === 'breadcrumbs' ||
payload.settingName === 'feature_breadcrumbs_v2'
) {
await this.updateState({ enabled: SettingsStore.getValue("breadcrumbs", null) });
}

View File

@ -16,8 +16,10 @@ limitations under the License.
import { Store } from 'flux/utils';
import { Action } from '../dispatcher/actions';
import dis from '../dispatcher/dispatcher';
import { ActionPayload } from "../dispatcher/payloads";
import { DoAfterSyncPreparedPayload } from '../dispatcher/payloads/DoAfterSyncPreparedPayload';
interface IState {
deferredAction: any;
@ -44,9 +46,9 @@ class LifecycleStore extends Store<ActionPayload> {
this.__emitChange();
}
protected __onDispatch(payload: ActionPayload) { // eslint-disable-line @typescript-eslint/naming-convention
protected __onDispatch(payload: ActionPayload | DoAfterSyncPreparedPayload<ActionPayload>) { // eslint-disable-line @typescript-eslint/naming-convention
switch (payload.action) {
case 'do_after_sync_prepared':
case Action.DoAfterSyncPrepared:
this.setState({
deferredAction: payload.deferred_action,
});

View File

@ -42,6 +42,8 @@ import SpaceStore from "./spaces/SpaceStore";
import { isMetaSpace, MetaSpace } from "./spaces";
import { JoinRoomPayload } from "../dispatcher/payloads/JoinRoomPayload";
import { JoinRoomReadyPayload } from "../dispatcher/payloads/JoinRoomReadyPayload";
import { JoinRoomErrorPayload } from "../dispatcher/payloads/JoinRoomErrorPayload";
import { ViewRoomErrorPayload } from "../dispatcher/payloads/ViewRoomErrorPayload";
const NUM_JOIN_RETRY = 5;
@ -122,7 +124,7 @@ class RoomViewStore extends Store<ActionPayload> {
// for these events blank out the roomId as we are no longer in the RoomView
case 'view_create_group':
case 'view_welcome_page':
case 'view_home_page':
case Action.ViewHomePage:
case 'view_my_groups':
case 'view_group':
this.setState({
@ -132,7 +134,7 @@ class RoomViewStore extends Store<ActionPayload> {
wasContextSwitch: false,
});
break;
case 'view_room_error':
case Action.ViewRoomError:
this.viewRoomError(payload);
break;
case 'will_join':
@ -306,8 +308,8 @@ class RoomViewStore extends Store<ActionPayload> {
roomId = result.room_id;
} catch (err) {
logger.error("RVS failed to get room id for alias: ", err);
dis.dispatch({
action: 'view_room_error',
dis.dispatch<ViewRoomErrorPayload>({
action: Action.ViewRoomError,
room_id: null,
room_alias: payload.room_alias,
err,
@ -324,7 +326,7 @@ class RoomViewStore extends Store<ActionPayload> {
}
}
private viewRoomError(payload: ActionPayload) {
private viewRoomError(payload: ViewRoomErrorPayload) {
this.setState({
roomId: payload.room_id,
roomAlias: payload.room_alias,
@ -411,7 +413,7 @@ class RoomViewStore extends Store<ActionPayload> {
});
}
private joinRoomError(payload: ActionPayload) {
private joinRoomError(payload: JoinRoomErrorPayload) {
this.setState({
joining: false,
joinError: payload.err,

View File

@ -98,8 +98,6 @@ export class EchoStore extends AsyncStoreWithClient<IState> {
}
}
protected async onAction(payload: ActionPayload): Promise<any> {
// We have nothing to actually listen for
return Promise.resolve();
protected async onAction(payload: ActionPayload): Promise<void> {
}
}

View File

@ -140,7 +140,6 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
}
// We don't need this, but our contract says we do.
protected async onAction(payload: ActionPayload) {
return Promise.resolve();
protected async onAction(payload: ActionPayload): Promise<void> {
}
}

View File

@ -67,8 +67,7 @@ export default class RoomListLayoutStore extends AsyncStoreWithClient<IState> {
}
// We don't need this function, but our contract says we do
protected async onAction(payload: ActionPayload): Promise<any> {
return Promise.resolve();
protected async onAction(payload: ActionPayload): Promise<void> {
}
}

View File

@ -24,7 +24,6 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
import defaultDispatcher from "../../dispatcher/dispatcher";
import { ActionPayload } from "../../dispatcher/payloads";
import RoomListStore from "../room-list/RoomListStore";
import SettingsStore from "../../settings/SettingsStore";
import DMRoomMap from "../../utils/DMRoomMap";
@ -61,6 +60,9 @@ import {
} from "./flattenSpaceHierarchy";
import { PosthogAnalytics } from "../../PosthogAnalytics";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
import { SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload";
import { AfterLeaveRoomPayload } from "../../dispatcher/payloads/AfterLeaveRoomPayload";
interface IState { }
@ -92,7 +94,7 @@ const validOrder = (order: string): string | undefined => {
};
// For sorting space children using a validated `order`, `origin_server_ts`, `room_id`
export const getChildOrder = (order: string, ts: number, roomId: string): Array<Many<ListIteratee<any>>> => {
export const getChildOrder = (order: string, ts: number, roomId: string): Array<Many<ListIteratee<unknown>>> => {
return [validOrder(order) ?? NaN, ts, roomId]; // NaN has lodash sort it at the end in asc
};
@ -100,6 +102,13 @@ const getRoomFn: FetchRoomFn = (room: Room) => {
return RoomNotificationStateStore.instance.getRoomState(room);
};
type SpaceStoreActions =
| SettingUpdatedPayload
| ViewRoomPayload
| ViewHomePagePayload
| SwitchSpacePayload
| AfterLeaveRoomPayload;
export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// The spaces representing the roots of the various tree-like hierarchies
private rootSpaces: Room[] = [];
@ -254,8 +263,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
metricsTrigger: "WebSpaceContextSwitch",
});
} else {
defaultDispatcher.dispatch({
action: "view_home_page",
defaultDispatcher.dispatch<ViewHomePagePayload>({
action: Action.ViewHomePage,
context_switch: true,
});
}
@ -1097,7 +1106,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.setActiveSpace(this.enabledMetaSpaces[0] ?? this.spacePanelSpaces[0]?.roomId, contextSwitch);
}
protected async onAction(payload: ActionPayload) {
protected async onAction(payload: SpaceStoreActions) {
if (!spacesEnabled || !this.matrixClient) return;
switch (payload.action) {
@ -1129,14 +1138,14 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
break;
}
case "view_home_page":
case Action.ViewHomePage:
if (!payload.context_switch && this.enabledMetaSpaces.includes(MetaSpace.Home)) {
this.setActiveSpace(MetaSpace.Home, false);
window.localStorage.setItem(getSpaceContextKey(this.activeSpace), "");
}
break;
case "after_leave_room":
case Action.AfterLeaveRoom:
if (!isMetaSpace(this._activeSpace) && payload.room_id === this._activeSpace) {
// User has left the current space, go to first space
this.goToFirstSpace(true);

View File

@ -40,7 +40,7 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<unknown> {
return WidgetMessagingStore.internalInstance;
}
protected async onAction(payload: ActionPayload): Promise<any> {
protected async onAction(payload: ActionPayload): Promise<void> {
// nothing to do
}

View File

@ -30,6 +30,7 @@ import { isMetaSpace } from "../stores/spaces";
import SpaceStore from "../stores/spaces/SpaceStore";
import { Action } from "../dispatcher/actions";
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
import { ViewHomePagePayload } from "../dispatcher/payloads/ViewHomePagePayload";
/**
* Approximation of a membership status for a given room.
@ -194,6 +195,6 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
metricsTrigger: undefined, // other
});
} else {
dis.dispatch({ action: 'view_home_page' });
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
}
}

View File

@ -43,6 +43,7 @@ import CreateSpaceFromCommunityDialog from "../components/views/dialogs/CreateSp
import SpacePreferencesDialog, { SpacePreferenceTab } from "../components/views/dialogs/SpacePreferencesDialog";
import PosthogTrackers from "../PosthogTrackers";
import { ButtonEvent } from "../components/views/elements/AccessibleButton";
import { AfterLeaveRoomPayload } from "../dispatcher/payloads/AfterLeaveRoomPayload";
export const shouldShowSpaceSettings = (space: Room) => {
const userId = space.client.getUserId();
@ -189,8 +190,8 @@ export const leaveSpace = (space: Room) => {
if (!leave) return;
await bulkSpaceBehaviour(space, rooms, room => leaveRoomBehaviour(room.roomId));
dis.dispatch({
action: "after_leave_room",
dis.dispatch<AfterLeaveRoomPayload>({
action: Action.AfterLeaveRoom,
room_id: space.roomId,
});
},

View File

@ -853,7 +853,7 @@ describe("SpaceStore", () => {
await run();
dispatcherRef = defaultDispatcher.register(payload => {
if (payload.action === Action.ViewRoom || payload.action === "view_home_page") {
if (payload.action === Action.ViewRoom || payload.action === Action.ViewHomePage) {
currentRoom = payload.room_id || null;
}
});