From 77670eb3697a104ce815bcde15717bc72d1ffd01 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 23 Feb 2026 15:37:58 +0000 Subject: [PATCH] Add lint rule to protect against `this` access on unbound methods (#32578) * Add Actions to ViewModel utility types and specify `this: void` signature Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add https://typescript-eslint.io/rules/unbound-method/ linter to shared-components also fix stray lint config which doesn't apply to SC Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add https://typescript-eslint.io/rules/unbound-method/ linter to element-web Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix genuine issues identified by the linter Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Specify this:void on i18napi Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update Module API Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add comment for MapToVoidThis Added utility type to map VM actions to unbound functions. --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintrc.cjs | 2 + docs/MVVM.md | 2 +- packages/shared-components/.eslintrc.cjs | 53 +++++++++---------- .../audio/AudioPlayerView/AudioPlayerView.tsx | 2 +- .../DecryptionFailureBodyView.tsx | 2 +- .../DisambiguatedProfileView.tsx | 6 ++- .../WidgetContextMenuView.tsx | 2 +- .../RoomListHeaderView/RoomListHeaderView.tsx | 2 +- .../RoomListItemMoreOptionsMenu.tsx | 2 +- .../RoomListItemNotificationMenu.tsx | 2 +- .../RoomListItemView/RoomListItemView.tsx | 2 +- .../RoomListSearchView/RoomListSearchView.tsx | 2 +- .../room-list/RoomListView/RoomListView.tsx | 6 +-- .../src/room-list/story-mocks.tsx | 9 ++-- .../room/RoomStatusBar/RoomStatusBarView.tsx | 2 +- .../shared-components/src/utils/Box/Box.tsx | 2 +- .../shared-components/src/utils/Flex/Flex.tsx | 2 + .../shared-components/src/utils/I18nApi.ts | 8 ++- .../utils/VirtualizedList/VirtualizedList.tsx | 4 +- .../src/viewmodel/ViewModel.ts | 13 +++-- .../src/viewmodel/useViewModel.ts | 2 +- pnpm-lock.yaml | 20 +++---- pnpm-workspace.yaml | 2 +- src/DateUtils.ts | 8 +-- src/Modal.tsx | 2 +- src/accessibility/RovingTabIndex.tsx | 10 +++- .../context_menu/StyledMenuItemCheckbox.tsx | 4 +- .../context_menu/StyledMenuItemRadio.tsx | 4 +- .../roving/RovingTabIndexWrapper.tsx | 13 +++-- src/async-components/structures/ErrorView.tsx | 2 +- .../security/NewRecoveryMethodDialog.tsx | 2 +- src/components/structures/ContextMenu.tsx | 4 +- src/components/structures/FileDropTarget.tsx | 2 +- src/components/structures/LoggedInView.tsx | 2 +- src/components/structures/MatrixChat.tsx | 6 +-- src/components/structures/RoomSearch.tsx | 2 +- src/components/structures/RoomSearchView.tsx | 2 +- src/components/structures/SpaceHierarchy.tsx | 16 +++--- src/components/structures/SpacePillButton.tsx | 2 +- src/components/structures/SpaceRoomView.tsx | 10 ++-- .../structures/auth/LoginSplashView.tsx | 2 +- src/components/views/avatars/RoomAvatar.tsx | 2 +- .../views/beacon/BeaconViewDialog.tsx | 2 +- .../views/context_menus/WidgetContextMenu.tsx | 4 +- .../dialogs/AddExistingSubspaceDialog.tsx | 4 +- .../dialogs/AddExistingToSpaceDialog.tsx | 12 ++--- .../dialogs/AnalyticsLearnMoreDialog.tsx | 2 +- .../views/dialogs/BetaFeedbackDialog.tsx | 2 +- .../views/dialogs/BulkRedactDialog.tsx | 2 +- .../views/dialogs/ChangelogDialog.tsx | 2 +- .../dialogs/ConfirmSpaceUserActionDialog.tsx | 4 +- .../views/dialogs/CreateSubspaceDialog.tsx | 4 +- .../views/dialogs/DevtoolsDialog.tsx | 2 +- src/components/views/dialogs/ExportDialog.tsx | 2 +- .../views/dialogs/ForwardDialog.tsx | 4 +- .../dialogs/GenericFeatureFeedbackDialog.tsx | 2 +- .../dialogs/IntegrationsDisabledDialog.tsx | 2 +- src/components/views/dialogs/InviteDialog.tsx | 2 +- .../views/dialogs/LeaveSpaceDialog.tsx | 2 +- .../ManageRestrictedJoinRuleDialog.tsx | 4 +- .../views/dialogs/PollHistoryDialog.tsx | 2 +- .../dialogs/RegistrationEmailPromptDialog.tsx | 4 +- .../views/dialogs/ReportRoomDialog.tsx | 2 +- src/components/views/dialogs/ShareDialog.tsx | 2 +- .../views/dialogs/SlashCommandHelpDialog.tsx | 2 +- .../views/dialogs/SpacePreferencesDialog.tsx | 2 +- .../views/dialogs/SpaceSettingsDialog.tsx | 2 +- .../views/dialogs/UntrustedDeviceDialog.tsx | 2 +- .../views/dialogs/UserSettingsDialog.tsx | 2 +- .../views/dialogs/devtools/BaseTool.tsx | 6 +-- .../views/dialogs/devtools/Crypto.tsx | 2 +- .../views/dialogs/devtools/Event.tsx | 2 +- .../views/dialogs/devtools/FilteredList.tsx | 2 +- .../views/dialogs/devtools/RoomState.tsx | 4 +- .../dialogs/devtools/SettingExplorer.tsx | 6 +-- .../views/dialogs/devtools/Users.tsx | 4 +- .../dialogs/spotlight/SpotlightDialog.tsx | 4 +- src/components/views/elements/Dropdown.tsx | 2 +- .../elements/GenericEventListSummary.tsx | 2 +- .../views/elements/JoinRuleDropdown.tsx | 2 +- .../views/elements/LabelledCheckbox.tsx | 2 +- .../views/elements/MiniAvatarUploader.tsx | 4 +- src/components/views/elements/RoomName.tsx | 2 +- .../views/elements/ServerPicker.tsx | 2 +- .../views/elements/SettingsDropdown.tsx | 2 +- .../views/elements/SettingsField.tsx | 2 +- .../elements/SpellCheckLanguagesDropdown.tsx | 4 +- .../views/elements/StyledRadioGroup.tsx | 2 +- .../views/elements/ToggleSwitch.tsx | 2 +- src/components/views/elements/Validation.tsx | 4 +- src/components/views/location/Map.tsx | 2 +- src/components/views/messages/CodeBlock.tsx | 2 +- .../views/messages/SenderProfile.tsx | 2 +- .../views/polls/pollHistory/PollHistory.tsx | 2 +- src/components/views/right_panel/BaseCard.tsx | 6 +-- .../views/right_panel/ExtensionsCard.tsx | 2 +- .../views/right_panel/PinnedMessagesCard.tsx | 2 +- src/components/views/right_panel/UserInfo.tsx | 2 +- .../views/right_panel/WidgetCard.tsx | 2 +- .../user_info/UserInfoAdminToolsContainer.tsx | 4 +- .../views/rooms/LegacyRoomListHeader.tsx | 2 +- .../views/rooms/LinkPreviewGroup.tsx | 2 +- .../StatelessNotificationBadge.tsx | 2 +- .../views/rooms/OverflowTileView.tsx | 2 +- .../rooms/RoomHeader/CallGuestLinkButton.tsx | 2 +- .../views/rooms/RoomSearchAuxPanel.tsx | 4 +- src/components/views/rooms/RoomSublist.tsx | 2 +- .../DynamicImportWysiwygComposer.tsx | 4 +- .../components/PlainTextComposer.tsx | 6 +-- .../wysiwyg_composer/hooks/useEditing.ts | 6 +-- .../wysiwyg_composer/hooks/useIsFocused.ts | 2 +- .../hooks/usePlainTextListeners.ts | 20 +++---- .../wysiwyg_composer/hooks/useSelection.ts | 2 +- .../views/settings/JoinRuleSettings.tsx | 6 +-- .../views/spaces/SpaceBasicSettings.tsx | 6 +-- .../views/spaces/SpaceChildrenPicker.tsx | 4 +- .../views/spaces/SpaceCreateMenu.tsx | 6 +-- .../views/spaces/SpacePublicShare.tsx | 2 +- .../spaces/SpaceSettingsVisibilityTab.tsx | 2 +- .../views/toasts/GenericExpiringToast.tsx | 2 +- src/components/views/toasts/GenericToast.tsx | 4 +- src/hooks/room/useRoomCall.tsx | 4 +- src/hooks/useProfileInfo.ts | 2 +- src/hooks/usePublicRoomDirectory.ts | 4 +- src/hooks/useTimeoutToggle.ts | 2 +- src/hooks/useUserDirectory.ts | 2 +- src/indexing/EventIndex.ts | 2 +- src/modules/Dialog.tsx | 2 +- src/slash-commands/command.ts | 2 +- src/stores/AutoRageshakeStore.ts | 14 ++--- .../room-list/filters/VisibilityProvider.ts | 5 +- src/stores/spaces/flattenSpaceHierarchy.ts | 2 +- src/utils/exportUtils/Exporter.ts | 2 +- .../WidgetContextMenuViewModel.tsx | 6 +-- src/workers/indexeddb.worker.ts | 1 + 135 files changed, 285 insertions(+), 263 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e4aaf87fb3..a5cc00f80b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -199,6 +199,7 @@ module.exports = { files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "playwright/**/*.ts", "*.ts"], extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"], rules: { + "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }], "@typescript-eslint/explicit-function-return-type": [ "error", { @@ -238,6 +239,7 @@ module.exports = { "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", "@typescript-eslint/no-empty-object-type": "off", + "@typescript-eslint/unbound-method": "off", // Jest/Playwright specific diff --git a/docs/MVVM.md b/docs/MVVM.md index 065a44e294..01efc71904 100644 --- a/docs/MVVM.md +++ b/docs/MVVM.md @@ -47,7 +47,7 @@ interface FooViewActions { // ViewModel is an object (usually a class) that implements both the interfaces listed above. // https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/ViewModel.ts -export type FooViewModel = ViewModel & FooViewActions; +export type FooViewModel = ViewModel; interface FooViewProps { // Ideally the view only depends on the view model i.e you don't expect any other props here. diff --git a/packages/shared-components/.eslintrc.cjs b/packages/shared-components/.eslintrc.cjs index 95fda95c64..19a66cf54c 100644 --- a/packages/shared-components/.eslintrc.cjs +++ b/packages/shared-components/.eslintrc.cjs @@ -9,9 +9,10 @@ module.exports = { root: true, plugins: ["matrix-org", "eslint-plugin-react-compiler"], extends: [ - "plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y", + "plugin:matrix-org/typescript", + "plugin:matrix-org/react", "plugin:storybook/recommended", ], parserOptions: { @@ -42,37 +43,35 @@ module.exports = { ], }, ], + + "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }], + "@typescript-eslint/explicit-function-return-type": [ + "error", + { + allowExpressions: true, + }, + ], + + // We're okay being explicit at the moment + // "@typescript-eslint/no-empty-interface": "off", + // We'd rather not do this but we do + // "@typescript-eslint/ban-ts-comment": "off", + // We're okay with assertion errors when we ask for them + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-empty-object-type": [ + "error", + { + // We do this sometimes to brand interfaces + allowInterfaces: "with-single-extends", + }, + ], }, overrides: [ { - files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"], - extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"], + files: ["src/**/*.test.{ts,tsx}"], rules: { - "@typescript-eslint/explicit-function-return-type": [ - "error", - { - allowExpressions: true, - }, - ], - - // Remove Babel things manually due to override limitations - "@babel/no-invalid-this": ["off"], - - // We're okay being explicit at the moment - "@typescript-eslint/no-empty-interface": "off", - // We disable this while we're transitioning + "@typescript-eslint/unbound-method": "off", "@typescript-eslint/no-explicit-any": "off", - // We'd rather not do this but we do - "@typescript-eslint/ban-ts-comment": "off", - // We're okay with assertion errors when we ask for them - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-empty-object-type": [ - "error", - { - // We do this sometimes to brand interfaces - allowInterfaces: "with-single-extends", - }, - ], }, }, ], diff --git a/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx b/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx index b68e06d2aa..43afc231b8 100644 --- a/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx +++ b/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx @@ -70,7 +70,7 @@ export interface AudioPlayerViewActions { /** * The view model for the audio player. */ -export type AudioPlayerViewModel = ViewModel & AudioPlayerViewActions; +export type AudioPlayerViewModel = ViewModel; interface AudioPlayerViewProps { /** diff --git a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx index 23b0d639d5..8ea36dc1b6 100644 --- a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx +++ b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx @@ -84,7 +84,7 @@ interface DecryptionFailureBodyViewProps { /** * React ref to attach to any React components returned */ - ref?: React.RefObject; + ref?: React.RefObject; } /** diff --git a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx index df5128b2d8..3489541cfd 100644 --- a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx +++ b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx @@ -57,8 +57,10 @@ export interface DisambiguatedProfileViewActions { /** * The view model for DisambiguatedProfileView. */ -export type DisambiguatedProfileViewModel = ViewModel & - DisambiguatedProfileViewActions; +export type DisambiguatedProfileViewModel = ViewModel< + DisambiguatedProfileViewSnapshot, + DisambiguatedProfileViewActions +>; interface DisambiguatedProfileViewProps { /** diff --git a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx index c8a150e041..9143ec6691 100644 --- a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx +++ b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx @@ -89,7 +89,7 @@ export interface WidgetContextMenuAction { onMoveButton: (direction: number) => void; } -export type WidgetContextMenuViewModel = ViewModel & WidgetContextMenuAction; +export type WidgetContextMenuViewModel = ViewModel; interface WidgetContextMenuViewProps { vm: WidgetContextMenuViewModel; diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx index 90998da655..ead33d7f81 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx @@ -103,7 +103,7 @@ export interface RoomListHeaderViewActions { /** * The view model for the room list header component. */ -export type RoomListHeaderViewModel = ViewModel & RoomListHeaderViewActions; +export type RoomListHeaderViewModel = ViewModel; interface RoomListHeaderViewProps { /** diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx index bcdb560cd2..62571baf4b 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx @@ -25,7 +25,7 @@ import type { RoomListItemSnapshot, RoomListItemActions } from "./RoomListItemVi /** * View model type for room list item */ -export type RoomItemViewModel = ViewModel & RoomListItemActions; +export type RoomItemViewModel = ViewModel; /** * Props for RoomListItemMoreOptionsMenu component diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx index e02e2a4ee3..db5ae2cf63 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx @@ -21,7 +21,7 @@ import type { RoomListItemSnapshot, RoomListItemActions } from "./RoomListItemVi /** * View model type for room list item */ -export type RoomItemViewModel = ViewModel & RoomListItemActions; +export type RoomItemViewModel = ViewModel; /** * Props for RoomListItemNotificationMenu component diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx index de6d9010c5..833f44e3b2 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx @@ -105,7 +105,7 @@ export interface RoomListItemActions { /** * The view model type for a room list item */ -export type RoomItemViewModel = ViewModel & RoomListItemActions; +export type RoomItemViewModel = ViewModel; /** * Props for RoomListItemView component diff --git a/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.tsx b/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.tsx index 3b4218e02e..7d45b09c29 100644 --- a/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.tsx +++ b/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.tsx @@ -50,7 +50,7 @@ export interface RoomListSearchViewActions { /** * The view model for the room list search component. */ -export type RoomListSearchViewModel = ViewModel & RoomListSearchViewActions; +export type RoomListSearchViewModel = ViewModel; interface RoomListSearchViewProps { /** diff --git a/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx b/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx index 1a08813f24..e72e9fb3ec 100644 --- a/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx +++ b/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx @@ -12,7 +12,7 @@ import { RoomListPrimaryFilters, type FilterId } from "../RoomListPrimaryFilters import { RoomListLoadingSkeleton } from "./RoomListLoadingSkeleton"; import { RoomListEmptyStateView } from "./RoomListEmptyStateView"; import { VirtualizedRoomListView, type RoomListViewState } from "../VirtualizedRoomListView"; -import { type Room } from "../RoomListItemView"; +import { type Room, type RoomItemViewModel } from "../RoomListItemView"; /** * Snapshot for the room list view @@ -49,7 +49,7 @@ export interface RoomListViewActions { /** Called to create a new room */ createRoom: () => void; /** Get view model for a specific room (virtualization API) */ - getRoomItemViewModel: (roomId: string) => any; + getRoomItemViewModel: (roomId: string) => RoomItemViewModel; /** Called when the visible range changes (virtualization API) */ updateVisibleRooms: (startIndex: number, endIndex: number) => void; } @@ -57,7 +57,7 @@ export interface RoomListViewActions { /** * The view model type for the room list view */ -export type RoomListViewModel = ViewModel & RoomListViewActions; +export type RoomListViewModel = ViewModel; /** * Props for RoomListView component diff --git a/packages/shared-components/src/room-list/story-mocks.tsx b/packages/shared-components/src/room-list/story-mocks.tsx index de2a4262f4..1958a06419 100644 --- a/packages/shared-components/src/room-list/story-mocks.tsx +++ b/packages/shared-components/src/room-list/story-mocks.tsx @@ -8,7 +8,7 @@ import React from "react"; import { fn } from "storybook/test"; -import { type Room, type RoomListItemSnapshot, RoomNotifState } from "./RoomListItemView"; +import { type Room, type RoomItemViewModel, type RoomListItemSnapshot, RoomNotifState } from "./RoomListItemView"; /** * Mock avatar component for stories @@ -39,6 +39,7 @@ export const mockAvatar = (name: string): React.ReactElement => ( */ export const renderAvatar = (room: Room): React.ReactElement => { // Cast to any to access properties - in real usage, the room object from the SDK will have these + // eslint-disable-next-line @typescript-eslint/no-explicit-any return mockAvatar((room as any)?.name || "Room"); }; @@ -102,8 +103,8 @@ export const createMockRoomSnapshot = (id: string, name: string, index: number): /** * Create a mock getRoomItemViewModel function for stories */ -export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) => any) => { - const viewModels = new Map(); +export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) => RoomItemViewModel) => { + const viewModels = new Map(); roomIds.forEach((roomId, index) => { const name = roomNames[index % roomNames.length]; const snapshot = createMockRoomSnapshot(roomId, name, index); @@ -125,7 +126,7 @@ export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) viewModels.set(roomId, mockViewModel); }); - return (roomId: string) => viewModels.get(roomId); + return (roomId: string) => viewModels.get(roomId)!; }; /** diff --git a/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.tsx b/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.tsx index 9746321616..fa60e88ff5 100644 --- a/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.tsx +++ b/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.tsx @@ -100,7 +100,7 @@ export type RoomStatusBarViewSnapshot = /** * The view model for RoomStatusBarView. */ -export type RoomStatusBarViewModel = ViewModel & RoomStatusBarViewActions; +export type RoomStatusBarViewModel = ViewModel; interface RoomStatusBarViewProps { /** diff --git a/packages/shared-components/src/utils/Box/Box.tsx b/packages/shared-components/src/utils/Box/Box.tsx index 8f31984664..d267cbc840 100644 --- a/packages/shared-components/src/utils/Box/Box.tsx +++ b/packages/shared-components/src/utils/Box/Box.tsx @@ -55,7 +55,7 @@ export function Box({ ...props }: React.PropsWithChildren): JSX.Element { const style = useMemo(() => { - const style: Record = {}; + const style: Record = {}; if (flex) style["--mx-box-flex"] = flex; if (shrink) style["--mx-box-shrink"] = shrink; if (grow) style["--mx-box-grow"] = grow; diff --git a/packages/shared-components/src/utils/Flex/Flex.tsx b/packages/shared-components/src/utils/Flex/Flex.tsx index 7404bf1874..7a7e9a1860 100644 --- a/packages/shared-components/src/utils/Flex/Flex.tsx +++ b/packages/shared-components/src/utils/Flex/Flex.tsx @@ -11,6 +11,7 @@ import React, { type JSX, type ComponentProps, type JSXElementConstructor, useMe import styles from "./Flex.module.css"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any type FlexProps> = { /** * The type of the HTML element @@ -60,6 +61,7 @@ type FlexProps = "div">({ as = "div", display = "flex", diff --git a/packages/shared-components/src/utils/I18nApi.ts b/packages/shared-components/src/utils/I18nApi.ts index df474e2775..a648f405f8 100644 --- a/packages/shared-components/src/utils/I18nApi.ts +++ b/packages/shared-components/src/utils/I18nApi.ts @@ -21,7 +21,7 @@ export class I18nApi implements II18nApi { /** * Register translations for the module, may override app's existing translations */ - public register(translations: Partial): void { + public register(this: void, translations: Partial): void { const langs: Record> = {}; for (const key in translations) { @@ -42,11 +42,9 @@ export class I18nApi implements II18nApi { * @param key - The key to translate * @param variables - Optional variables to interpolate into the translation */ - public translate(key: TranslationKey, variables?: Variables): string { + public translate(this: void, key: TranslationKey, variables?: Variables): string { return _t(key, variables); } - public humanizeTime(timeMillis: number): string { - return humanizeTime(timeMillis, this); - } + public humanizeTime = (timeMillis: number): string => humanizeTime(timeMillis, this); } diff --git a/packages/shared-components/src/utils/VirtualizedList/VirtualizedList.tsx b/packages/shared-components/src/utils/VirtualizedList/VirtualizedList.tsx index adea593d07..522e65bda3 100644 --- a/packages/shared-components/src/utils/VirtualizedList/VirtualizedList.tsx +++ b/packages/shared-components/src/utils/VirtualizedList/VirtualizedList.tsx @@ -113,7 +113,7 @@ export interface IVirtualizedListProps extends Omit< /** * Utility type for the prop scrollIntoViewOnChange allowing it to be memoised by a caller without repeating types */ -export type ScrollIntoViewOnChange = NonNullable< +export type ScrollIntoViewOnChange = NonNullable< VirtuosoProps>["scrollIntoViewOnChange"] >; @@ -124,7 +124,7 @@ export type ScrollIntoViewOnChange = NonNullable< * @template Item - The type of data items in the list * @template Context - The type of additional context data passed to items */ -export function VirtualizedList(props: IVirtualizedListProps): React.ReactElement { +export function VirtualizedList(props: IVirtualizedListProps): React.ReactElement { // Extract our custom props to avoid conflicts with Virtuoso props const { items, diff --git a/packages/shared-components/src/viewmodel/ViewModel.ts b/packages/shared-components/src/viewmodel/ViewModel.ts index 9f088c4300..cbc19aafb9 100644 --- a/packages/shared-components/src/viewmodel/ViewModel.ts +++ b/packages/shared-components/src/viewmodel/ViewModel.ts @@ -9,15 +9,22 @@ Please see LICENSE files in the repository root for full details. * The interface for a generic View Model passed to the shared components. * The snapshot is of type T which is a type specifying a snapshot for the view in question. */ -export interface ViewModel { + +// Utility type to map all VM actions to unbound functions so that they do not have +// to be called with the correct 'this' context. This prevents "cannot read X of undefined" bugs. +type MapToVoidThis = { + [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (this: void, ...args: A) => R : T[K]; +}; + +export type ViewModel = { /** * The current snapshot of the view model. */ - getSnapshot: () => T; + getSnapshot: () => Snapshot; /** * Subscribes to changes in the view model. * The listener will be called whenever the snapshot changes. */ subscribe: (listener: () => void) => () => void; -} +} & MapToVoidThis; diff --git a/packages/shared-components/src/viewmodel/useViewModel.ts b/packages/shared-components/src/viewmodel/useViewModel.ts index ef7b8ec0da..9a5a9a8662 100644 --- a/packages/shared-components/src/viewmodel/useViewModel.ts +++ b/packages/shared-components/src/viewmodel/useViewModel.ts @@ -14,7 +14,7 @@ import { type ViewModel } from "./ViewModel"; * @param vm The view model to use * @returns The current snapshot */ -export function useViewModel(vm: ViewModel): T { +export function useViewModel(vm: ViewModel): T { // We need to pass the same getSnapshot function as getServerSnapshot as this // is used when making the HTML chat export. return useSyncExternalStore(vm.subscribe, vm.getSnapshot, vm.getSnapshot); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 645281dd51..df316a1dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,8 +7,8 @@ settings: catalogs: default: '@element-hq/element-web-module-api': - specifier: 1.9.0 - version: 1.9.0 + specifier: 1.9.1 + version: 1.9.1 '@element-hq/element-web-playwright-common': specifier: 2.2.7 version: 2.2.7 @@ -91,7 +91,7 @@ importers: version: 7.28.6 '@element-hq/element-web-module-api': specifier: 'catalog:' - version: 1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) + version: 1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) '@element-hq/web-shared-components': specifier: workspace:* version: link:packages/shared-components @@ -371,7 +371,7 @@ importers: version: 0.16.3 '@element-hq/element-web-playwright-common': specifier: 'catalog:' - version: 2.2.7(@element-hq/element-web-module-api@1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2) + version: 2.2.7(@element-hq/element-web-module-api@1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2) '@element-hq/element-web-playwright-common-local': specifier: workspace:* version: link:packages/playwright-common @@ -743,7 +743,7 @@ importers: dependencies: '@element-hq/element-web-module-api': specifier: 'catalog:' - version: 1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) + version: 1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) '@matrix-org/spec': specifier: ^1.7.0 version: 1.16.0 @@ -2021,8 +2021,8 @@ packages: '@element-hq/element-call-embedded@0.16.3': resolution: {integrity: sha512-OViKJonDaDNVBUW9WdV9mk78/Ruh34C7XsEgt3O8D9z+64C39elbIgllHSoH5S12IRlv9RYrrV37FZLo6QWsDQ==} - '@element-hq/element-web-module-api@1.9.0': - resolution: {integrity: sha512-Ao/V9w+wysZK4bh61LlKlznF10n2ZbD6KcUI46/zUMttXbmJn3ahvbzhEpwYcD+Cjy3ag5ycxLIIGkKV/fncXg==} + '@element-hq/element-web-module-api@1.9.1': + resolution: {integrity: sha512-eCHHBkaDWc7Ai10b2VmOAkWIQAsur+YZ2kpFrPFVG41wMXn0PbFL+n6wvpbN+mU5Mg7uVIqXhQn4jflHESBUrA==} engines: {node: '>=20.0.0'} peerDependencies: '@matrix-org/react-sdk-module-api': '*' @@ -12085,7 +12085,7 @@ snapshots: '@element-hq/element-call-embedded@0.16.3': {} - '@element-hq/element-web-module-api@1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)': + '@element-hq/element-web-module-api@1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)': dependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) @@ -12094,10 +12094,10 @@ snapshots: '@matrix-org/react-sdk-module-api': 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4) matrix-web-i18n: 3.6.0 - '@element-hq/element-web-playwright-common@2.2.7(@element-hq/element-web-module-api@1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)': + '@element-hq/element-web-playwright-common@2.2.7(@element-hq/element-web-module-api@1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)': dependencies: '@axe-core/playwright': 4.11.1(playwright-core@1.58.2) - '@element-hq/element-web-module-api': 1.9.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) + '@element-hq/element-web-module-api': 1.9.1(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) '@playwright/test': 1.58.2 '@testcontainers/postgresql': 11.11.0 glob: 13.0.6 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 67e921c12e..3e591dd582 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,7 @@ catalog: "@element-hq/element-web-playwright-common": 2.2.7 "@playwright/test": 1.58.2 # Module API - "@element-hq/element-web-module-api": 1.9.0 + "@element-hq/element-web-module-api": 1.9.1 # Compound "@vector-im/compound-design-tokens": 6.9.0 "@vector-im/compound-web": 8.3.6 diff --git a/src/DateUtils.ts b/src/DateUtils.ts index 82e7bfa880..cd4a440372 100644 --- a/src/DateUtils.ts +++ b/src/DateUtils.ts @@ -23,8 +23,8 @@ export const DAY_MS = HOUR_MS * 24; */ export function getDaysArray(weekday: Intl.DateTimeFormatOptions["weekday"] = "short"): string[] { const sunday = 1672574400000; // 2023-01-01 12:00 UTC - const { format } = new Intl.DateTimeFormat(getUserLanguage(), { weekday, timeZone: "UTC" }); - return [...Array(7).keys()].map((day) => format(sunday + day * DAY_MS)); + const dateTimeFormat = new Intl.DateTimeFormat(getUserLanguage(), { weekday, timeZone: "UTC" }); + return [...Array(7).keys()].map((day) => dateTimeFormat.format(sunday + day * DAY_MS)); } /** @@ -32,8 +32,8 @@ export function getDaysArray(weekday: Intl.DateTimeFormatOptions["weekday"] = "s * @param month - format desired "numeric" | "2-digit" | "long" | "short" | "narrow" */ export function getMonthsArray(month: Intl.DateTimeFormatOptions["month"] = "short"): string[] { - const { format } = new Intl.DateTimeFormat(getUserLanguage(), { month, timeZone: "UTC" }); - return [...Array(12).keys()].map((m) => format(Date.UTC(2021, m))); + const dateTimeFormat = new Intl.DateTimeFormat(getUserLanguage(), { month, timeZone: "UTC" }); + return [...Array(12).keys()].map((m) => dateTimeFormat.format(Date.UTC(2021, m))); } // XXX: Ideally we could just specify `hour12: boolean` but it has issues on Chrome in the `en` locale diff --git a/src/Modal.tsx b/src/Modal.tsx index 26d04eb197..d92f83d7f1 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -93,7 +93,7 @@ export interface IHandle { * * @param args - Arguments to return to {@link finished}. */ - close(...args: OnFinishedParams): void; + close(this: void, ...args: OnFinishedParams): void; } interface IOptions { diff --git a/src/accessibility/RovingTabIndex.tsx b/src/accessibility/RovingTabIndex.tsx index 9942b4398c..947024661e 100644 --- a/src/accessibility/RovingTabIndex.tsx +++ b/src/accessibility/RovingTabIndex.tsx @@ -171,8 +171,14 @@ interface IProps { handleLeftRight?: boolean; handleInputFields?: boolean; scrollIntoView?: boolean | ScrollIntoViewOptions; - children(renderProps: { onKeyDownHandler(ev: React.KeyboardEvent): void; onDragEndHandler(): void }): ReactNode; - onKeyDown?(ev: React.KeyboardEvent, state: IState, dispatch: Dispatch): void; + children( + this: void, + renderProps: { + onKeyDownHandler(this: void, ev: React.KeyboardEvent): void; + onDragEndHandler(this: void): void; + }, + ): ReactNode; + onKeyDown?(this: void, ev: React.KeyboardEvent, state: IState, dispatch: Dispatch): void; } export const findSiblingElement = ( diff --git a/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx b/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx index f208eb1091..82175199ae 100644 --- a/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx +++ b/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx @@ -16,8 +16,8 @@ import { KeyBindingAction } from "../KeyboardShortcuts"; import { getKeyBindingsManager } from "../../KeyBindingsManager"; interface IProps extends React.ComponentProps { - onChange(): void; // we handle keyup/down ourselves so lose the ChangeEvent - onClose(): void; // gets called after onChange on KeyBindingAction.ActivateSelectedButton + onChange(this: void): void; // we handle keyup/down ourselves so lose the ChangeEvent + onClose(this: void): void; // gets called after onChange on KeyBindingAction.ActivateSelectedButton } // Semantic component for representing a styled role=menuitemcheckbox diff --git a/src/accessibility/context_menu/StyledMenuItemRadio.tsx b/src/accessibility/context_menu/StyledMenuItemRadio.tsx index 5bf6d4706c..62213b33c6 100644 --- a/src/accessibility/context_menu/StyledMenuItemRadio.tsx +++ b/src/accessibility/context_menu/StyledMenuItemRadio.tsx @@ -17,8 +17,8 @@ import { getKeyBindingsManager } from "../../KeyBindingsManager"; interface IProps extends React.ComponentProps { label?: string; - onChange(): void; // we handle keyup/down ourselves so lose the ChangeEvent - onClose(): void; // gets called after onChange on KeyBindingAction.Enter + onChange(this: void): void; // we handle keyup/down ourselves so lose the ChangeEvent + onClose(this: void): void; // gets called after onChange on KeyBindingAction.Enter } // Semantic component for representing a styled role=menuitemradio diff --git a/src/accessibility/roving/RovingTabIndexWrapper.tsx b/src/accessibility/roving/RovingTabIndexWrapper.tsx index f2c7c98dd3..5d2f001241 100644 --- a/src/accessibility/roving/RovingTabIndexWrapper.tsx +++ b/src/accessibility/roving/RovingTabIndexWrapper.tsx @@ -14,11 +14,14 @@ import { type FocusHandler } from "./types"; interface IProps { inputRef?: RefObject; - children(renderProps: { - onFocus: FocusHandler; - isActive: boolean; - ref: RefCallback; - }): ReactElement; + children( + this: void, + renderProps: { + onFocus: FocusHandler; + isActive: boolean; + ref: RefCallback; + }, + ): ReactElement; } // Wrapper to allow use of useRovingTabIndex outside of React Functional Components. diff --git a/src/async-components/structures/ErrorView.tsx b/src/async-components/structures/ErrorView.tsx index 80a3d8dba1..7271bac0cf 100644 --- a/src/async-components/structures/ErrorView.tsx +++ b/src/async-components/structures/ErrorView.tsx @@ -116,7 +116,7 @@ const linkFactory = ); export const UnsupportedBrowserView: React.FC<{ - onAccept?(): void; + onAccept?(this: void): void; }> = ({ onAccept }) => { const config = SdkConfig.get(); const brand = config.brand ?? "Element"; diff --git a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx index 2642f01560..de3583fef9 100644 --- a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx +++ b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx @@ -26,7 +26,7 @@ interface NewRecoveryMethodDialogProps { /** * Callback when the dialog is dismissed. */ - onFinished(): void; + onFinished(this: void): void; } // Export as default instead of a named export so that it can be dynamically imported with React lazy diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index 03538a21bf..9d4b76e38e 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -92,9 +92,9 @@ export interface IProps extends MenuProps { closeOnInteraction?: boolean; // Function to be called on menu close - onFinished(): void; + onFinished(this: void): void; // on resize callback - windowResize?(): void; + windowResize?(this: void): void; } interface IState { diff --git a/src/components/structures/FileDropTarget.tsx b/src/components/structures/FileDropTarget.tsx index 5ad16303bf..564383d9f4 100644 --- a/src/components/structures/FileDropTarget.tsx +++ b/src/components/structures/FileDropTarget.tsx @@ -16,7 +16,7 @@ import { useRoomState } from "../../hooks/useRoomState.ts"; interface IProps { room: Room; parent: HTMLElement | null; - onFileDrop(dataTransfer: DataTransfer): void; + onFileDrop(this: void, dataTransfer: DataTransfer): void; } interface IState { diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 70258e143c..03927a5689 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -87,7 +87,7 @@ interface IProps { matrixClient: MatrixClient; // Called with the credentials of a registered user (if they were a ROU that // transitioned to PWLU) - onRegistered: (credentials: IMatrixClientCreds) => Promise; + onRegistered: (this: void, credentials: IMatrixClientCreds) => Promise; hideToSRUsers: boolean; // eslint-disable-next-line camelcase page_type?: string; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 4831f9ed89..3ba87f5043 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -2023,7 +2023,7 @@ export default class MatrixChat extends React.PureComponent { this.setPageSubtitle(); } - private onLogoutClick(event: ButtonEvent): void { + private onLogoutClick(this: void, event: ButtonEvent): void { dis.dispatch({ action: "logout", }); @@ -2047,7 +2047,7 @@ export default class MatrixChat extends React.PureComponent { this.stores.resizeNotifier.notifyWindowResized(); }; - private dispatchTimelineResize(): void { + private dispatchTimelineResize(this: void): void { dis.dispatch({ action: "timeline_resize" }); } @@ -2068,7 +2068,7 @@ export default class MatrixChat extends React.PureComponent { }; // returns a promise which resolves to the new MatrixClient - private onRegistered(credentials: IMatrixClientCreds): Promise { + private onRegistered(this: void, credentials: IMatrixClientCreds): Promise { return Lifecycle.setLoggedIn(credentials); } diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index 4c8329b6dd..f099032a63 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -22,7 +22,7 @@ interface IProps { } export default class RoomSearch extends React.PureComponent { - private openSpotlight(): void { + private openSpotlight(this: void): void { defaultDispatcher.fire(Action.OpenSpotlight); } diff --git a/src/components/structures/RoomSearchView.tsx b/src/components/structures/RoomSearchView.tsx index f19c0e0dc5..80ad9ad11e 100644 --- a/src/components/structures/RoomSearchView.tsx +++ b/src/components/structures/RoomSearchView.tsx @@ -42,7 +42,7 @@ interface Props { promise: Promise; abortController?: AbortController; className: string; - onUpdate(inProgress: boolean, results: ISearchResults | null, error: Error | null): void; + onUpdate(this: void, inProgress: boolean, results: ISearchResults | null, error: Error | null): void; ref?: Ref; } diff --git a/src/components/structures/SpaceHierarchy.tsx b/src/components/structures/SpaceHierarchy.tsx index e2b05c54f1..e799868240 100644 --- a/src/components/structures/SpaceHierarchy.tsx +++ b/src/components/structures/SpaceHierarchy.tsx @@ -78,7 +78,7 @@ interface IProps { space: Room; initialText?: string; additionalButtons?: ReactNode; - showRoom(cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void; + showRoom(this: void, cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void; } interface ITileProps { @@ -88,9 +88,9 @@ interface ITileProps { numChildRooms?: number; hasPermissions?: boolean; children?: ReactNode; - onViewRoomClick(): void; - onJoinRoomClick(): Promise; - onToggleClick?(): void; + onViewRoomClick(this: void): void; + onJoinRoomClick(this: void): Promise; + onToggleClick?(this: void): void; } const Tile: React.FC = ({ @@ -468,9 +468,9 @@ interface IHierarchyLevelProps { hierarchy: RoomHierarchy; parents: Set; selectedMap?: Map>; - onViewRoomClick(roomId: string, roomType?: RoomType): void; - onJoinRoomClick(roomId: string, parents: Set): Promise; - onToggleClick?(parentId: string, childId: string): void; + onViewRoomClick(this: void, roomId: string, roomType?: RoomType): void; + onJoinRoomClick(this: void, roomId: string, parents: Set): Promise; + onToggleClick?(this: void, parentId: string, childId: string): void; } export const toLocalRoom = (cli: MatrixClient, room: HierarchyRoom, hierarchy: RoomHierarchy): HierarchyRoom => { @@ -600,7 +600,7 @@ export const useRoomHierarchy = ( rooms?: HierarchyRoom[]; hierarchy?: RoomHierarchy; error?: Error; - loadMore(pageSize?: number): Promise; + loadMore(this: void, pageSize?: number): Promise; } => { const [rooms, setRooms] = useState([]); const [hierarchy, setHierarchy] = useState(); diff --git a/src/components/structures/SpacePillButton.tsx b/src/components/structures/SpacePillButton.tsx index 0093e2911b..2755de6d14 100644 --- a/src/components/structures/SpacePillButton.tsx +++ b/src/components/structures/SpacePillButton.tsx @@ -13,7 +13,7 @@ const SpacePillButton: React.FC<{ title: string; icon: JSX.Element; description: string; - onClick(): void; + onClick(this: void): void; }> = ({ title, icon, description, onClick }) => { return ( diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index bf43369d9e..22962ed36e 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -304,7 +304,7 @@ const SpaceSetupFirstRooms: React.FC<{ space: Room; title: string; description: JSX.Element; - onFinished(firstRoomId?: string): void; + onFinished(this: void, firstRoomId?: string): void; }> = ({ space, title, description, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); @@ -399,7 +399,7 @@ const SpaceSetupFirstRooms: React.FC<{ const SpaceAddExistingRooms: React.FC<{ space: Room; - onFinished(): void; + onFinished(this: void): void; }> = ({ space, onFinished }) => { return (
@@ -423,7 +423,7 @@ const SpaceAddExistingRooms: React.FC<{ }; interface ISpaceSetupPublicShareProps extends Pick { - onFinished(): void; + onFinished(this: void): void; } const SpaceSetupPublicShare: React.FC = ({ @@ -455,7 +455,7 @@ const SpaceSetupPublicShare: React.FC = ({ const SpaceSetupPrivateScope: React.FC<{ space: Room; justCreatedOpts?: IOpts; - onFinished(createRooms: boolean): void; + onFinished(this: void, createRooms: boolean): void; }> = ({ space, justCreatedOpts, onFinished }) => { return (
@@ -498,7 +498,7 @@ const validateEmailRules = withValidation({ const SpaceSetupPrivateInvite: React.FC<{ space: Room; - onFinished(): void; + onFinished(this: void): void; }> = ({ space, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); diff --git a/src/components/structures/auth/LoginSplashView.tsx b/src/components/structures/auth/LoginSplashView.tsx index aae7980185..acdf3c86b3 100644 --- a/src/components/structures/auth/LoginSplashView.tsx +++ b/src/components/structures/auth/LoginSplashView.tsx @@ -27,7 +27,7 @@ interface Props { * * @param event - The click event */ - onLogoutClick: (event: ButtonEvent) => void; + onLogoutClick: (this: void, event: ButtonEvent) => void; /** * Error that caused `/sync` to fail. If set, an error message will be shown on the splash screen. diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx index 6490378731..dd1e5f0ce4 100644 --- a/src/components/views/avatars/RoomAvatar.tsx +++ b/src/components/views/avatars/RoomAvatar.tsx @@ -33,7 +33,7 @@ interface IProps extends Omit, "name" | "idNam roomId?: string; }; viewAvatarOnClick?: boolean; - onClick?(): void; + onClick?(this: void): void; } const RoomAvatar: React.FC = ({ room, viewAvatarOnClick, onClick, oobData, size = "36px", ...otherProps }) => { diff --git a/src/components/views/beacon/BeaconViewDialog.tsx b/src/components/views/beacon/BeaconViewDialog.tsx index d779ebbf2e..ab6c6cf3c0 100644 --- a/src/components/views/beacon/BeaconViewDialog.tsx +++ b/src/components/views/beacon/BeaconViewDialog.tsx @@ -33,7 +33,7 @@ export interface IProps { matrixClient: MatrixClient; // open the map centered on this beacon's location initialFocusedBeacon?: Beacon; - onFinished(): void; + onFinished(this: void): void; } // track the 'focused time' as ts diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index 4ba300370f..f974741cf5 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -36,9 +36,9 @@ interface IProps extends Omit, "child userWidget?: boolean; showUnpin?: boolean; // override delete handler - onDeleteClick?(): void; + onDeleteClick?(this: void): void; // override edit handler - onEditClick?(): void; + onEditClick?(this: void): void; } const showStreamAudioStreamButton = (app: IWidget): boolean => { diff --git a/src/components/views/dialogs/AddExistingSubspaceDialog.tsx b/src/components/views/dialogs/AddExistingSubspaceDialog.tsx index 09a828194a..8edf71a242 100644 --- a/src/components/views/dialogs/AddExistingSubspaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingSubspaceDialog.tsx @@ -17,8 +17,8 @@ import { AddExistingToSpace, defaultSpacesRenderer, SubspaceSelector } from "./A interface IProps { space: Room; - onCreateSubspaceClick(): void; - onFinished(added?: boolean): void; + onCreateSubspaceClick(this: void): void; + onFinished(this: void, added?: boolean): void; } const AddExistingSubspaceDialog: React.FC = ({ space, onCreateSubspaceClick, onFinished }) => { diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index d60e3edab6..fd1d4117bf 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -43,15 +43,15 @@ const GROUP_MARGIN = 24; interface IProps { space: Room; - onCreateRoomClick(ev: ButtonEvent): void; - onAddSubspaceClick(): void; - onFinished(added?: boolean): void; + onCreateRoomClick(this: void, ev: ButtonEvent): void; + onAddSubspaceClick(this: void): void; + onFinished(this: void, added?: boolean): void; } export const Entry: React.FC<{ room: Room; checked: boolean; - onChange?(value: boolean): void; + onChange?(this: void, value: boolean): void; }> = ({ room, checked, onChange }) => { const id = useId(); return ( @@ -86,7 +86,7 @@ interface IAddExistingToSpaceProps { footerPrompt?: ReactNode; filterPlaceholder: string; emptySelectionButton?: ReactNode; - onFinished(added: boolean): void; + onFinished(this: void, added: boolean): void; roomsRenderer?: Renderer; spacesRenderer?: Renderer; dmsRenderer?: Renderer; @@ -391,7 +391,7 @@ interface ISubspaceSelectorProps { title: string; space: Room; value: Room; - onChange(space: Room): void; + onChange(this: void, space: Room): void; } export const SubspaceSelector: React.FC = ({ title, space, value, onChange }) => { diff --git a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx index 77ef5d522f..f5481c09ce 100644 --- a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx +++ b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx @@ -23,7 +23,7 @@ export enum ButtonClicked { } interface IProps { - onFinished(buttonClicked?: ButtonClicked): void; + onFinished(this: void, buttonClicked?: ButtonClicked): void; analyticsOwner: string; privacyPolicyUrl?: string; primaryButton?: string; diff --git a/src/components/views/dialogs/BetaFeedbackDialog.tsx b/src/components/views/dialogs/BetaFeedbackDialog.tsx index 081f8beaca..8b00375100 100644 --- a/src/components/views/dialogs/BetaFeedbackDialog.tsx +++ b/src/components/views/dialogs/BetaFeedbackDialog.tsx @@ -21,7 +21,7 @@ import { type SettingKey } from "../../../settings/Settings.tsx"; interface IProps { featureId: SettingKey; - onFinished(sendFeedback?: boolean): void; + onFinished(this: void, sendFeedback?: boolean): void; } const BetaFeedbackDialog: React.FC = ({ featureId, onFinished }) => { diff --git a/src/components/views/dialogs/BulkRedactDialog.tsx b/src/components/views/dialogs/BulkRedactDialog.tsx index 1b8bf9b07e..891acab5b9 100644 --- a/src/components/views/dialogs/BulkRedactDialog.tsx +++ b/src/components/views/dialogs/BulkRedactDialog.tsx @@ -29,7 +29,7 @@ interface Props { matrixClient: MatrixClient; room: Room; member: RoomMember; - onFinished(redact?: boolean): void; + onFinished(this: void, redact?: boolean): void; } const BulkRedactDialog: React.FC = (props) => { diff --git a/src/components/views/dialogs/ChangelogDialog.tsx b/src/components/views/dialogs/ChangelogDialog.tsx index 6e12674653..e9ff59a720 100644 --- a/src/components/views/dialogs/ChangelogDialog.tsx +++ b/src/components/views/dialogs/ChangelogDialog.tsx @@ -88,7 +88,7 @@ export default class ChangelogDialog extends React.Component { } } - private elementsForCommit(commit: Commit): JSX.Element { + private elementsForCommit(this: void, commit: Commit): JSX.Element { return (
  • diff --git a/src/components/views/dialogs/ConfirmSpaceUserActionDialog.tsx b/src/components/views/dialogs/ConfirmSpaceUserActionDialog.tsx index 6d1bb38956..b686b4cc4a 100644 --- a/src/components/views/dialogs/ConfirmSpaceUserActionDialog.tsx +++ b/src/components/views/dialogs/ConfirmSpaceUserActionDialog.tsx @@ -21,8 +21,8 @@ interface IProps extends Omit = ({ diff --git a/src/components/views/dialogs/CreateSubspaceDialog.tsx b/src/components/views/dialogs/CreateSubspaceDialog.tsx index 290c606731..7223db55f9 100644 --- a/src/components/views/dialogs/CreateSubspaceDialog.tsx +++ b/src/components/views/dialogs/CreateSubspaceDialog.tsx @@ -23,8 +23,8 @@ import JoinRuleDropdown from "../elements/JoinRuleDropdown"; interface IProps { space: Room; - onAddExistingSpaceClick(): void; - onFinished(added?: boolean): void; + onAddExistingSpaceClick(this: void): void; + onFinished(this: void, added?: boolean): void; } const CreateSubspaceDialog: React.FC = ({ space, onAddExistingSpaceClick, onFinished }) => { diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index 10986095ea..93ffd8c0d3 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -61,7 +61,7 @@ const Tools: Record = { interface IProps { roomId: string; threadRootId?: string | null; - onFinished(finished?: boolean): void; + onFinished(this: void, finished?: boolean): void; } type ToolInfo = [label: TranslationKey, tool: Tool]; diff --git a/src/components/views/dialogs/ExportDialog.tsx b/src/components/views/dialogs/ExportDialog.tsx index c615f106fc..5767b1202b 100644 --- a/src/components/views/dialogs/ExportDialog.tsx +++ b/src/components/views/dialogs/ExportDialog.tsx @@ -37,7 +37,7 @@ import { validateNumberInRange } from "../../../utils/validate"; interface IProps { room: Room; - onFinished(doExport?: boolean): void; + onFinished(this: void, doExport?: boolean): void; } interface ExportConfig { diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index f97ccd61a2..a6a6954bc5 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -73,7 +73,7 @@ interface IProps { // We need a permalink creator for the source room to pass through to EventTile // in case the event is a reply (even though the user can't get at the link) permalinkCreator: RoomPermalinkCreator; - onFinished(): void; + onFinished(this: void): void; } interface IEntryProps { @@ -81,7 +81,7 @@ interface IEntryProps { type: K; content: TimelineEvents[K]; matrixClient: MatrixClient; - onFinished(success: boolean): void; + onFinished(this: void, success: boolean): void; } enum SendState { diff --git a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx index 8df5b8dbb4..29fee059dc 100644 --- a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx +++ b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx @@ -22,7 +22,7 @@ interface IProps { rageshakeLabel?: string; rageshakeData?: Record; children?: ReactNode; - onFinished(sendFeedback?: boolean): void; + onFinished(this: void, sendFeedback?: boolean): void; } const GenericFeatureFeedbackDialog: React.FC = ({ diff --git a/src/components/views/dialogs/IntegrationsDisabledDialog.tsx b/src/components/views/dialogs/IntegrationsDisabledDialog.tsx index 93cd5f82c9..2682e077ae 100644 --- a/src/components/views/dialogs/IntegrationsDisabledDialog.tsx +++ b/src/components/views/dialogs/IntegrationsDisabledDialog.tsx @@ -16,7 +16,7 @@ import DialogButtons from "../elements/DialogButtons"; import { UserTab } from "./UserTab"; interface IProps { - onFinished(): void; + onFinished(this: void): void; } export const IntegrationsDisabledDialog: React.FC = ({ onFinished }) => { diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 6f60f56fdb..dd29dbddce 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1124,7 +1124,7 @@ export default class InviteDialog extends React.PureComponent): Promise { + private async onLinkClick(this: void, e: React.MouseEvent): Promise { e.preventDefault(); selectText(e.currentTarget); } diff --git a/src/components/views/dialogs/LeaveSpaceDialog.tsx b/src/components/views/dialogs/LeaveSpaceDialog.tsx index 37838292ff..ffa780ccc9 100644 --- a/src/components/views/dialogs/LeaveSpaceDialog.tsx +++ b/src/components/views/dialogs/LeaveSpaceDialog.tsx @@ -20,7 +20,7 @@ import { isOnlyAdmin } from "../../../utils/membership"; interface IProps { space: Room; - onFinished(leave: boolean, rooms?: Room[]): void; + onFinished(this: void, leave: boolean, rooms?: Room[]): void; } const LeaveSpaceDialog: React.FC = ({ space, onFinished }) => { diff --git a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx index 2de5d23b55..811fb1a10c 100644 --- a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx +++ b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx @@ -25,13 +25,13 @@ import { filterBoolean } from "../../../utils/arrays"; interface IProps { room: Room; selected?: string[]; - onFinished(rooms?: string[]): void; + onFinished(this: void, rooms?: string[]): void; } const Entry: React.FC<{ room: Room; checked: boolean; - onChange(value: boolean): void; + onChange(this: void, value: boolean): void; }> = ({ room, checked, onChange }) => { const localRoom = room instanceof Room; diff --git a/src/components/views/dialogs/PollHistoryDialog.tsx b/src/components/views/dialogs/PollHistoryDialog.tsx index bcc92932e0..d222dde427 100644 --- a/src/components/views/dialogs/PollHistoryDialog.tsx +++ b/src/components/views/dialogs/PollHistoryDialog.tsx @@ -17,7 +17,7 @@ type PollHistoryDialogProps = { room: Room; matrixClient: MatrixClient; permalinkCreator: RoomPermalinkCreator; - onFinished(): void; + onFinished(this: void): void; }; export const PollHistoryDialog: React.FC = ({ diff --git a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx index b798035ab9..5a00d87bfe 100644 --- a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx +++ b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx @@ -15,8 +15,8 @@ import DialogButtons from "../elements/DialogButtons"; import EmailField from "../auth/EmailField"; interface IProps { - onFinished(continued: false, email?: undefined): void; - onFinished(continued: true, email: string): void; + onFinished(this: void, continued: false, email?: undefined): void; + onFinished(this: void, continued: true, email: string): void; } const RegistrationEmailPromptDialog: React.FC = ({ onFinished }) => { diff --git a/src/components/views/dialogs/ReportRoomDialog.tsx b/src/components/views/dialogs/ReportRoomDialog.tsx index 9a7ac2bd56..510778655b 100644 --- a/src/components/views/dialogs/ReportRoomDialog.tsx +++ b/src/components/views/dialogs/ReportRoomDialog.tsx @@ -25,7 +25,7 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IProps { roomId: string; - onFinished(leave: boolean): void; + onFinished(this: void, leave: boolean): void; } /* diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx index dd909376a0..993c411562 100644 --- a/src/components/views/dialogs/ShareDialog.tsx +++ b/src/components/views/dialogs/ShareDialog.tsx @@ -56,7 +56,7 @@ interface BaseProps { /** * A function that is called when the dialog is dismissed */ - onFinished(): void; + onFinished(this: void): void; /** * An optional string to use as the dialog title. * If not provided, an appropriate title for the target type will be used. diff --git a/src/components/views/dialogs/SlashCommandHelpDialog.tsx b/src/components/views/dialogs/SlashCommandHelpDialog.tsx index 6ceb80a1fd..d912c85616 100644 --- a/src/components/views/dialogs/SlashCommandHelpDialog.tsx +++ b/src/components/views/dialogs/SlashCommandHelpDialog.tsx @@ -20,7 +20,7 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; */ interface IProps { roomId: string; - onFinished(): void; + onFinished(this: void): void; } const SlashCommandHelpDialog: React.FC = ({ roomId, onFinished }) => { diff --git a/src/components/views/dialogs/SpacePreferencesDialog.tsx b/src/components/views/dialogs/SpacePreferencesDialog.tsx index 69fa897ec3..1c5cb1cde9 100644 --- a/src/components/views/dialogs/SpacePreferencesDialog.tsx +++ b/src/components/views/dialogs/SpacePreferencesDialog.tsx @@ -26,7 +26,7 @@ import { SettingsSubsection, SettingsSubsectionText } from "../settings/shared/S interface IProps { space: Room; - onFinished(): void; + onFinished(this: void): void; } const SpacePreferencesAppearanceTab: React.FC> = ({ space }) => { diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx index 7566cbb9c8..11cc82cadb 100644 --- a/src/components/views/dialogs/SpaceSettingsDialog.tsx +++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx @@ -39,7 +39,7 @@ export enum SpaceSettingsTab { interface IProps { matrixClient: MatrixClient; space: Room; - onFinished(): void; + onFinished(this: void): void; } const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFinished }) => { diff --git a/src/components/views/dialogs/UntrustedDeviceDialog.tsx b/src/components/views/dialogs/UntrustedDeviceDialog.tsx index 5a076a57b4..703dbec85f 100644 --- a/src/components/views/dialogs/UntrustedDeviceDialog.tsx +++ b/src/components/views/dialogs/UntrustedDeviceDialog.tsx @@ -31,7 +31,7 @@ interface IProps { * If mode is "sas", the user wants to verify the device with SAS. Otherwise, the dialog was dismissed normally. * @param mode The mode of dismissal. */ - onFinished(mode?: "sas"): void; + onFinished(this: void, mode?: "sas"): void; } const UntrustedDeviceDialog: React.FC = ({ device, user, onFinished }) => { diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 4d8b8ad3c0..a0802f8a57 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -58,7 +58,7 @@ interface IProps { */ initialEncryptionState?: State; sdkContext: SdkContextClass; - onFinished(): void; + onFinished(this: void): void; } function titleForTabID(tabId: UserTab): React.ReactNode { diff --git a/src/components/views/dialogs/devtools/BaseTool.tsx b/src/components/views/dialogs/devtools/BaseTool.tsx index 555d5b31ff..09120ecde2 100644 --- a/src/components/views/dialogs/devtools/BaseTool.tsx +++ b/src/components/views/dialogs/devtools/BaseTool.tsx @@ -16,8 +16,8 @@ import { type XOR } from "../../../../@types/common"; import { type Tool } from "../DevtoolsDialog"; export interface IDevtoolsProps { - onBack(): void; - setTool(label: TranslationKey, tool: Tool): void; + onBack(this: void): void; + setTool(this: void, label: TranslationKey, tool: Tool): void; } interface IMinProps extends Pick { @@ -28,7 +28,7 @@ interface IMinProps extends Pick { interface IProps extends IMinProps { actionLabel: TranslationKey; - onAction(): Promise; + onAction(this: void): Promise; } const BaseTool: React.FC> = ({ diff --git a/src/components/views/dialogs/devtools/Crypto.tsx b/src/components/views/dialogs/devtools/Crypto.tsx index af6d4935ba..1c02f062c3 100644 --- a/src/components/views/dialogs/devtools/Crypto.tsx +++ b/src/components/views/dialogs/devtools/Crypto.tsx @@ -20,7 +20,7 @@ interface KeyBackupProps { /** * Callback to invoke when the back button is clicked. */ - onBack(): void; + onBack(this: void): void; } /** diff --git a/src/components/views/dialogs/devtools/Event.tsx b/src/components/views/dialogs/devtools/Event.tsx index 63712b28d9..c205e89986 100644 --- a/src/components/views/dialogs/devtools/Event.tsx +++ b/src/components/views/dialogs/devtools/Event.tsx @@ -24,7 +24,7 @@ export const stringify = (object: object): string => { interface IEventEditorProps extends Pick { fieldDefs: IFieldDef[]; // immutable defaultContent?: string; - onSend(fields: string[], content: IContent): Promise; + onSend(this: void, fields: string[], content: IContent): Promise; } interface IFieldDef { diff --git a/src/components/views/dialogs/devtools/FilteredList.tsx b/src/components/views/dialogs/devtools/FilteredList.tsx index 2b7ed027db..e59059722a 100644 --- a/src/components/views/dialogs/devtools/FilteredList.tsx +++ b/src/components/views/dialogs/devtools/FilteredList.tsx @@ -19,7 +19,7 @@ const LOAD_TILES_STEP_SIZE = 50; interface IProps { children: React.ReactElement[]; query: string; - onChange(value: string): void; + onChange(this: void, value: string): void; } const FilteredList: React.FC = ({ children, query, onChange }) => { diff --git a/src/components/views/dialogs/devtools/RoomState.tsx b/src/components/views/dialogs/devtools/RoomState.tsx index b9863b69ff..0d7041355a 100644 --- a/src/components/views/dialogs/devtools/RoomState.tsx +++ b/src/components/views/dialogs/devtools/RoomState.tsx @@ -40,12 +40,12 @@ export const StateEventEditor: React.FC = ({ mxEvent, onBack }) => interface StateEventButtonProps { label: string; - onClick(): void; + onClick(this: void): void; } const RoomStateHistory: React.FC<{ mxEvent: MatrixEvent; - onBack(): void; + onBack(this: void): void; }> = ({ mxEvent, onBack }) => { const cli = useContext(MatrixClientContext); const events = useAsyncMemo( diff --git a/src/components/views/dialogs/devtools/SettingExplorer.tsx b/src/components/views/dialogs/devtools/SettingExplorer.tsx index 7fedcd2fa5..e45b082cf3 100644 --- a/src/components/views/dialogs/devtools/SettingExplorer.tsx +++ b/src/components/views/dialogs/devtools/SettingExplorer.tsx @@ -192,7 +192,7 @@ const EditSetting: React.FC = ({ setting, onBack }) => { interface IViewSettingProps extends Pick { setting: SettingKey; - onEdit(): Promise; + onEdit(this: void): Promise; } const ViewSetting: React.FC = ({ setting, onEdit, onBack }) => { @@ -249,8 +249,8 @@ function renderSettingValue(val: any): string { } interface ISettingsListProps extends Pick { - onView(setting: string): void; - onEdit(setting: string): void; + onView(this: void, setting: string): void; + onEdit(this: void, setting: string): void; } const SettingsList: React.FC = ({ onBack, onView, onEdit }) => { diff --git a/src/components/views/dialogs/devtools/Users.tsx b/src/components/views/dialogs/devtools/Users.tsx index 889d437355..aab47e672b 100644 --- a/src/components/views/dialogs/devtools/Users.tsx +++ b/src/components/views/dialogs/devtools/Users.tsx @@ -78,7 +78,7 @@ export const UserList: React.FC> = ({ onBack }) = interface UserButtonProps { member: RoomMember; - onClick(): void; + onClick(this: void): void; } /** @@ -216,7 +216,7 @@ const UserView: React.FC = ({ member, onBack }) => { interface DeviceButtonProps { crypto: CryptoApi; device: Device; - onClick(): void; + onClick(this: void): void; } /** diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx index 204c404fde..de68c42ee4 100644 --- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx +++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx @@ -106,7 +106,7 @@ const AVATAR_SIZE = "24px"; interface IProps { initialText?: string; initialFilter?: Filter; - onFinished(): void; + onFinished(this: void): void; } function nodeIsForRecentlyViewed(node?: HTMLElement): boolean { @@ -191,7 +191,7 @@ interface IResult extends IBaseResult { avatar: JSX.Element; name: string; description?: string; - onClick?(): void; + onClick?(this: void): void; } type Result = IRoomResult | IPublicRoomResult | IMemberResult | IResult; diff --git a/src/components/views/elements/Dropdown.tsx b/src/components/views/elements/Dropdown.tsx index cc298cd2a6..6a4af20e94 100644 --- a/src/components/views/elements/Dropdown.tsx +++ b/src/components/views/elements/Dropdown.tsx @@ -306,7 +306,7 @@ export default class Dropdown extends React.Component { return keys[index <= 0 ? keys.length - 1 : (index - 1) % keys.length]; } - private scrollIntoView(node: Element | null): void { + private scrollIntoView(this: void, node: Element | null): void { node?.scrollIntoView({ block: "nearest", behavior: "auto", diff --git a/src/components/views/elements/GenericEventListSummary.tsx b/src/components/views/elements/GenericEventListSummary.tsx index 3a6e377ba1..5b938d54a0 100644 --- a/src/components/views/elements/GenericEventListSummary.tsx +++ b/src/components/views/elements/GenericEventListSummary.tsx @@ -31,7 +31,7 @@ interface IProps { // An array of EventTiles to render when expanded "children": ReactNode[] | null; // Called when the event list expansion is toggled - onToggle?(): void; + onToggle?(this: void): void; // The layout currently used "layout"?: Layout; "data-testid"?: string; diff --git a/src/components/views/elements/JoinRuleDropdown.tsx b/src/components/views/elements/JoinRuleDropdown.tsx index f40493854e..df4a4521c2 100644 --- a/src/components/views/elements/JoinRuleDropdown.tsx +++ b/src/components/views/elements/JoinRuleDropdown.tsx @@ -26,7 +26,7 @@ interface IProps { labelKnock?: string; labelPublic?: string; labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported - onChange(value: JoinRule): void; + onChange(this: void, value: JoinRule): void; } const JoinRuleDropdown: React.FC = ({ diff --git a/src/components/views/elements/LabelledCheckbox.tsx b/src/components/views/elements/LabelledCheckbox.tsx index 45cef43da9..231b8cf0d3 100644 --- a/src/components/views/elements/LabelledCheckbox.tsx +++ b/src/components/views/elements/LabelledCheckbox.tsx @@ -21,7 +21,7 @@ interface IProps { // Whether or not to disable the checkbox disabled?: boolean; // The function to call when the value changes - onChange(checked: boolean): void; + onChange(this: void, checked: boolean): void; // Optional additional CSS class to apply to the label className?: string; // The id for the checkbox diff --git a/src/components/views/elements/MiniAvatarUploader.tsx b/src/components/views/elements/MiniAvatarUploader.tsx index fc9574b411..f7aea40751 100644 --- a/src/components/views/elements/MiniAvatarUploader.tsx +++ b/src/components/views/elements/MiniAvatarUploader.tsx @@ -24,9 +24,9 @@ interface IProps { hasAvatar: boolean; noAvatarLabel?: string; hasAvatarLabel?: string; - setAvatarUrl(url: string): Promise; + setAvatarUrl(this: void, url: string): Promise; isUserAvatar?: boolean; - onClick?(ev: MouseEvent): void; + onClick?(this: void, ev: MouseEvent): void; children?: ReactNode; } diff --git a/src/components/views/elements/RoomName.tsx b/src/components/views/elements/RoomName.tsx index 1ff0fc628e..318b9db4f4 100644 --- a/src/components/views/elements/RoomName.tsx +++ b/src/components/views/elements/RoomName.tsx @@ -13,7 +13,7 @@ import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; interface IProps { room?: Room; - children?(name: string): JSX.Element; + children?(this: void, name: string): JSX.Element; } /** diff --git a/src/components/views/elements/ServerPicker.tsx b/src/components/views/elements/ServerPicker.tsx index 1debb50645..fa25598c68 100644 --- a/src/components/views/elements/ServerPicker.tsx +++ b/src/components/views/elements/ServerPicker.tsx @@ -23,7 +23,7 @@ interface IProps { dialogTitle?: string; serverConfig: ValidatedServerConfig; disabled?: boolean; - onServerConfigChange?(config: ValidatedServerConfig): void; + onServerConfigChange?(this: void, config: ValidatedServerConfig): void; } const showPickerDialog = ( diff --git a/src/components/views/elements/SettingsDropdown.tsx b/src/components/views/elements/SettingsDropdown.tsx index db78e7cc36..85f448ea76 100644 --- a/src/components/views/elements/SettingsDropdown.tsx +++ b/src/components/views/elements/SettingsDropdown.tsx @@ -21,7 +21,7 @@ interface Props { label?: string; isExplicit?: boolean; hideIfCannotSet?: boolean; - onChange?(option: string): void; + onChange?(this: void, option: string): void; } const SettingsDropdown = ({ diff --git a/src/components/views/elements/SettingsField.tsx b/src/components/views/elements/SettingsField.tsx index d46b5b0f32..3ba2128347 100644 --- a/src/components/views/elements/SettingsField.tsx +++ b/src/components/views/elements/SettingsField.tsx @@ -19,7 +19,7 @@ interface Props { roomId?: string; // for per-room settings label?: string; isExplicit?: boolean; - onChange?(value: string): void; + onChange?(this: void, value: string): void; } const SettingsField = ({ settingKey, level, roomId, isExplicit, label, onChange: _onSave }: Props): JSX.Element => { diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index ffcbf972d7..763fe4b5f8 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -81,9 +81,9 @@ export default class SpellCheckLanguagesDropdown extends React.Component< } } - private onSearchChange(searchQuery: string): void { + private onSearchChange = (searchQuery: string): void => { this.setState({ searchQuery }); - } + }; public render(): React.ReactNode { if (!this.state.languages) { diff --git a/src/components/views/elements/StyledRadioGroup.tsx b/src/components/views/elements/StyledRadioGroup.tsx index 1dba78d912..37da341bb4 100644 --- a/src/components/views/elements/StyledRadioGroup.tsx +++ b/src/components/views/elements/StyledRadioGroup.tsx @@ -27,7 +27,7 @@ interface IProps { value?: T; // if not provided no options will be selected outlined?: boolean; disabled?: boolean; - onChange(newValue: T): void; + onChange(this: void, newValue: T): void; } function StyledRadioGroup({ diff --git a/src/components/views/elements/ToggleSwitch.tsx b/src/components/views/elements/ToggleSwitch.tsx index 3f6548c7a0..6139b32669 100644 --- a/src/components/views/elements/ToggleSwitch.tsx +++ b/src/components/views/elements/ToggleSwitch.tsx @@ -25,7 +25,7 @@ interface IProps { tooltip?: string; // Called when the checked state changes. First argument will be the new state. - onChange(checked: boolean): void; + onChange(this: void, checked: boolean): void; // id to bind with other elements id?: string; diff --git a/src/components/views/elements/Validation.tsx b/src/components/views/elements/Validation.tsx index ce1a9b457a..6346c22ffb 100644 --- a/src/components/views/elements/Validation.tsx +++ b/src/components/views/elements/Validation.tsx @@ -33,7 +33,7 @@ interface IArgs { rules: IRule[]; description?(this: T, derivedData: D, results: IResult[]): ReactNode; hideDescriptionIfValid?: boolean; - deriveData?(data: Data): Promise; + deriveData?(this: T, data: Data): Promise; memoize?: boolean; } @@ -78,8 +78,10 @@ export interface IValidationResult { * the overall validity and a feedback UI that can be rendered for more detail. */ export default function withValidation({ + // eslint-disable-next-line @typescript-eslint/unbound-method description, hideDescriptionIfValid, + // eslint-disable-next-line @typescript-eslint/unbound-method deriveData, rules, memoize, diff --git a/src/components/views/location/Map.tsx b/src/components/views/location/Map.tsx index 998dff1d15..cb6f64ad83 100644 --- a/src/components/views/location/Map.tsx +++ b/src/components/views/location/Map.tsx @@ -32,7 +32,7 @@ const useMapWithStyle = ({ }: { id: string; centerGeoUri?: string; - onError?(error: Error): void; + onError?(this: void, error: Error): void; interactive?: boolean; bounds?: Bounds; allowGeolocate?: boolean; diff --git a/src/components/views/messages/CodeBlock.tsx b/src/components/views/messages/CodeBlock.tsx index 0a3a80fb89..026cbba886 100644 --- a/src/components/views/messages/CodeBlock.tsx +++ b/src/components/views/messages/CodeBlock.tsx @@ -23,7 +23,7 @@ interface Props { const ExpandCollapseButton: React.FC<{ expanded: boolean; - onClick(): void; + onClick(this: void): void; }> = ({ expanded, onClick }) => { return ( diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index 82b4523fb7..89b483f735 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -16,7 +16,7 @@ import { useRoomMemberProfile } from "../../../hooks/room/useRoomMemberProfile"; interface IProps { mxEvent: MatrixEvent; - onClick?(): void; + onClick?(this: void): void; withTooltip?: boolean; } diff --git a/src/components/views/polls/pollHistory/PollHistory.tsx b/src/components/views/polls/pollHistory/PollHistory.tsx index 6967ef8d9c..147ca36fc7 100644 --- a/src/components/views/polls/pollHistory/PollHistory.tsx +++ b/src/components/views/polls/pollHistory/PollHistory.tsx @@ -23,7 +23,7 @@ type PollHistoryProps = { room: Room; matrixClient: MatrixClient; permalinkCreator: RoomPermalinkCreator; - onFinished(): void; + onFinished(this: void): void; }; const sortEventsByLatest = (left: MatrixEvent, right: MatrixEvent): number => right.getTs() - left.getTs(); diff --git a/src/components/views/right_panel/BaseCard.tsx b/src/components/views/right_panel/BaseCard.tsx index b308e1d973..e8b466ff44 100644 --- a/src/components/views/right_panel/BaseCard.tsx +++ b/src/components/views/right_panel/BaseCard.tsx @@ -28,9 +28,9 @@ interface IProps { ariaLabelledBy?: string; withoutScrollContainer?: boolean; closeLabel?: string; - onClose?(ev: MouseEvent): void; - onBack?(ev: MouseEvent): void; - onKeyDown?(ev: KeyboardEvent): void; + onClose?(this: void, ev: MouseEvent): void; + onBack?(this: void, ev: MouseEvent): void; + onKeyDown?(this: void, ev: KeyboardEvent): void; cardState?: any; ref?: Ref; // Ref for the 'close' button the card diff --git a/src/components/views/right_panel/ExtensionsCard.tsx b/src/components/views/right_panel/ExtensionsCard.tsx index 9f1d1d0e02..4ee41642e4 100644 --- a/src/components/views/right_panel/ExtensionsCard.tsx +++ b/src/components/views/right_panel/ExtensionsCard.tsx @@ -35,7 +35,7 @@ import { WidgetContextMenu } from "../../../viewmodels/right-panel/WidgetContext interface Props { room: Room; - onClose(): void; + onClose(this: void): void; } interface IAppRowProps { diff --git a/src/components/views/right_panel/PinnedMessagesCard.tsx b/src/components/views/right_panel/PinnedMessagesCard.tsx index daea698edd..611b485b78 100644 --- a/src/components/views/right_panel/PinnedMessagesCard.tsx +++ b/src/components/views/right_panel/PinnedMessagesCard.tsx @@ -44,7 +44,7 @@ interface PinnedMessagesCardProps { /** * Callback for when the card is closed. */ - onClose(): void; + onClose(this: void): void; } export function PinnedMessagesCard({ room, onClose, permalinkCreator }: PinnedMessagesCardProps): JSX.Element { diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 086777d141..7de3f9be88 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -192,7 +192,7 @@ interface IProps { user: Member; room?: Room; phase: RightPanelPhases.MemberInfo | RightPanelPhases.EncryptionPanel; - onClose(): void; + onClose(this: void): void; verificationRequest?: VerificationRequest; verificationRequestPromise?: Promise; } diff --git a/src/components/views/right_panel/WidgetCard.tsx b/src/components/views/right_panel/WidgetCard.tsx index 7ae9ae1b21..30d9f38b22 100644 --- a/src/components/views/right_panel/WidgetCard.tsx +++ b/src/components/views/right_panel/WidgetCard.tsx @@ -23,7 +23,7 @@ import { WidgetContextMenu } from "../../../viewmodels/right-panel/WidgetContext interface IProps { room: Room; widgetId: string; - onClose(): void; + onClose(this: void): void; } const WidgetCard: React.FC = ({ room, widgetId, onClose }) => { diff --git a/src/components/views/right_panel/user_info/UserInfoAdminToolsContainer.tsx b/src/components/views/right_panel/user_info/UserInfoAdminToolsContainer.tsx index 1f3eeb706d..37b7a72b08 100644 --- a/src/components/views/right_panel/user_info/UserInfoAdminToolsContainer.tsx +++ b/src/components/views/right_panel/user_info/UserInfoAdminToolsContainer.tsx @@ -33,8 +33,8 @@ const Container: React.FC<{ interface IBaseProps { member: RoomMember; isUpdating: boolean; - startUpdating(): void; - stopUpdating(): void; + startUpdating(this: void): void; + stopUpdating(this: void): void; } export const RoomKickButton = ({ diff --git a/src/components/views/rooms/LegacyRoomListHeader.tsx b/src/components/views/rooms/LegacyRoomListHeader.tsx index bf389c4b0c..6c9c15a778 100644 --- a/src/components/views/rooms/LegacyRoomListHeader.tsx +++ b/src/components/views/rooms/LegacyRoomListHeader.tsx @@ -114,7 +114,7 @@ const usePendingActions = (): Map> => { }; interface IProps { - onVisibilityChange?(): void; + onVisibilityChange?(this: void): void; } const LegacyRoomListHeader: React.FC = ({ onVisibilityChange }) => { diff --git a/src/components/views/rooms/LinkPreviewGroup.tsx b/src/components/views/rooms/LinkPreviewGroup.tsx index 471220bfeb..880bb3a24b 100644 --- a/src/components/views/rooms/LinkPreviewGroup.tsx +++ b/src/components/views/rooms/LinkPreviewGroup.tsx @@ -24,7 +24,7 @@ const INITIAL_NUM_PREVIEWS = 2; interface IProps { links: string[]; // the URLs to be previewed mxEvent: MatrixEvent; // the Event associated with the preview - onCancelClick(): void; // called when the preview's cancel ('hide') button is clicked + onCancelClick(this: void): void; // called when the preview's cancel ('hide') button is clicked } const LinkPreviewGroup: React.FC = ({ links, mxEvent, onCancelClick }) => { diff --git a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx index c217f71258..8db391f014 100644 --- a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx +++ b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx @@ -36,7 +36,7 @@ interface ClickableProps extends Props { /** * If specified will return an AccessibleButton instead of a div. */ - onClick(ev: ButtonEvent): void; + onClick(this: void, ev: ButtonEvent): void; tabIndex?: number; } diff --git a/src/components/views/rooms/OverflowTileView.tsx b/src/components/views/rooms/OverflowTileView.tsx index d082821dee..d1611468a2 100644 --- a/src/components/views/rooms/OverflowTileView.tsx +++ b/src/components/views/rooms/OverflowTileView.tsx @@ -14,7 +14,7 @@ import AccessibleButton from "../elements/AccessibleButton"; interface Props { // The number of remaining items remaining: number; - onClick(): void; + onClick(this: void): void; } /** diff --git a/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx b/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx index 16ef5c2234..101a1afdf9 100644 --- a/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx +++ b/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx @@ -96,7 +96,7 @@ export const CallGuestLinkButton: React.FC<{ room: Room }> = ({ room }) => { * @param onFinished Callback that is getting called if the dialog wants to close. */ export const JoinRuleDialog: React.FC<{ - onFinished(): void; + onFinished(this: void): void; room: Room; canInvite: boolean; }> = ({ onFinished, room, canInvite }) => { diff --git a/src/components/views/rooms/RoomSearchAuxPanel.tsx b/src/components/views/rooms/RoomSearchAuxPanel.tsx index be821c7de1..14c7ad511e 100644 --- a/src/components/views/rooms/RoomSearchAuxPanel.tsx +++ b/src/components/views/rooms/RoomSearchAuxPanel.tsx @@ -20,8 +20,8 @@ import InlineSpinner from "../elements/InlineSpinner"; interface Props { searchInfo?: SearchInfo; isRoomEncrypted: boolean; - onSearchScopeChange(scope: SearchScope): void; - onCancelClick(): void; + onSearchScopeChange(this: void, scope: SearchScope): void; + onCancelClick(this: void): void; } const RoomSearchAuxPanel: React.FC = ({ searchInfo, isRoomEncrypted, onSearchScopeChange, onCancelClick }) => { diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index ed3b13701c..049cb8e764 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -705,7 +705,7 @@ export default class RoomSublist extends React.Component { ); } - private onScrollPrevent(e: Event): void { + private onScrollPrevent(this: void, e: Event): void { // the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable // this fixes https://github.com/vector-im/element-web/issues/14413 (e.target as HTMLDivElement).scrollTop = 0; diff --git a/src/components/views/rooms/wysiwyg_composer/DynamicImportWysiwygComposer.tsx b/src/components/views/rooms/wysiwyg_composer/DynamicImportWysiwygComposer.tsx index 3c9e30148f..3eb405a2ff 100644 --- a/src/components/views/rooms/wysiwyg_composer/DynamicImportWysiwygComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/DynamicImportWysiwygComposer.tsx @@ -37,7 +37,7 @@ export const dynamicImportConversionFunctions = async (): Promise<{ * `false` to format it for writing to an editor element. * @returns a string of plain text that may contain markdown */ - richToPlain(rich: string, inMessageFormat: boolean): Promise; + richToPlain(this: void, rich: string, inMessageFormat: boolean): Promise; /** * Creates a rust model from plain text input (interpreted as markdown) and uses it to generate the rich text @@ -49,7 +49,7 @@ export const dynamicImportConversionFunctions = async (): Promise<{ * `false` to format it for writing to an editor element. * @returns a string of html */ - plainToRich(plain: string, inMessageFormat: boolean): Promise; + plainToRich(this: void, plain: string, inMessageFormat: boolean): Promise; }> => { const { richToPlain, plainToRich } = await import("@vector-im/matrix-wysiwyg"); diff --git a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx index aadb3c0e78..f45cc5d74f 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx @@ -22,14 +22,14 @@ import { useSettingValue } from "../../../../../hooks/useSettings"; interface PlainTextComposerProps { disabled?: boolean; - onChange?: (content: string) => void; - onSend?: () => void; + onChange?: (this: void, content: string) => void; + onSend?: (this: void) => void; placeholder?: string; initialContent?: string; className?: string; leftComponent?: ReactNode; rightComponent?: ReactNode; - children?: (ref: RefObject, composerFunctions: ComposerFunctions) => ReactNode; + children?: (this: void, ref: RefObject, composerFunctions: ComposerFunctions) => ReactNode; eventRelation?: IEventRelation; } diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useEditing.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useEditing.ts index e48b00e82a..5ba54c015d 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useEditing.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useEditing.ts @@ -20,9 +20,9 @@ export function useEditing( initialContent?: string, ): { isSaveDisabled: boolean; - onChange(content: string): void; - editMessage(): Promise; - endEditing(): void; + onChange(this: void, content: string): void; + editMessage(this: void): Promise; + endEditing(this: void): void; } { const roomContext = useScopedRoomContext("timelineRenderingType"); const mxClient = useMatrixClientContext(); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts index 57039d3599..eab9ff1db9 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -10,7 +10,7 @@ import { type FocusEvent, useCallback, useEffect, useRef, useState } from "react export function useIsFocused(): { isFocused: boolean; - onFocus(event: FocusEvent): void; + onFocus(this: void, event: FocusEvent): void; } { const [isFocused, setIsFocused] = useState(false); const timeoutIDRef = useRef(undefined); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts index be91c9ccf3..81272f8b4b 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts @@ -52,16 +52,16 @@ export function usePlainTextListeners( ref: RefObject; autocompleteRef: RefObject; content?: string; - onBeforeInput(event: SyntheticEvent): void; - onInput(event: SyntheticEvent): void; - onPaste(event: SyntheticEvent): void; - onKeyDown(event: KeyboardEvent): void; - setContent(text?: string): void; - handleMention: (link: string, text: string, attributes: AllowedMentionAttributes) => void; - handleAtRoomMention: (attributes: AllowedMentionAttributes) => void; - handleCommand: (text: string) => void; - handleEmoji: (emoji: string) => void; - onSelect: (event: SyntheticEvent) => void; + onBeforeInput(this: void, event: SyntheticEvent): void; + onInput(this: void, event: SyntheticEvent): void; + onPaste(this: void, event: SyntheticEvent): void; + onKeyDown(this: void, event: KeyboardEvent): void; + setContent(this: void, text?: string): void; + handleMention: (this: void, link: string, text: string, attributes: AllowedMentionAttributes) => void; + handleAtRoomMention: (this: void, attributes: AllowedMentionAttributes) => void; + handleCommand: (this: void, text: string) => void; + handleEmoji: (this: void, emoji: string) => void; + onSelect: (this: void, event: SyntheticEvent) => void; suggestion: MappedSuggestion | null; } { const roomContext = useScopedRoomContext("room", "timelineRenderingType", "replyToEvent"); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts index 10686f1078..23a85a32bd 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts @@ -29,7 +29,7 @@ function setSelectionContext(composerContext: ComposerContextState): void { } export function useSelection(): ReturnType[1] & { - onInput(): void; + onInput(this: void): void; } { const composerContext = useComposerContext(); const [isFocused, focusProps] = useFocus(); diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index f3189b3fcf..e5a51ca369 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -32,9 +32,9 @@ import LabelledCheckbox from "../elements/LabelledCheckbox"; export interface JoinRuleSettingsProps { room: Room; promptUpgrade?: boolean; - closeSettingsFn(): void; - onError(error: unknown): void; - beforeChange?(joinRule: JoinRule): Promise; // if returns false then aborts the change + closeSettingsFn(this: void): void; + onError(this: void, error: unknown): void; + beforeChange?(this: void, joinRule: JoinRule): Promise; // if returns false then aborts the change disabledOptions?: Set; hiddenOptions?: Set; recommendedOption?: JoinRule; diff --git a/src/components/views/spaces/SpaceBasicSettings.tsx b/src/components/views/spaces/SpaceBasicSettings.tsx index f00ab96521..ac363de115 100644 --- a/src/components/views/spaces/SpaceBasicSettings.tsx +++ b/src/components/views/spaces/SpaceBasicSettings.tsx @@ -21,9 +21,9 @@ interface IProps { nameDisabled?: boolean; topic?: string; topicDisabled?: boolean; - setAvatar(avatar?: File): void; - setName(name: string): void; - setTopic(topic: string): void; + setAvatar(this: void, avatar?: File): void; + setName(this: void, name: string): void; + setTopic(this: void, topic: string): void; } export const SpaceAvatar: React.FC> = ({ diff --git a/src/components/views/spaces/SpaceChildrenPicker.tsx b/src/components/views/spaces/SpaceChildrenPicker.tsx index 268b22d288..870108ff35 100644 --- a/src/components/views/spaces/SpaceChildrenPicker.tsx +++ b/src/components/views/spaces/SpaceChildrenPicker.tsx @@ -27,7 +27,7 @@ interface ISpecificChildrenPickerProps { filterPlaceholder: string; rooms: Room[]; selected: Set; - onChange(selected: boolean, room: Room): void; + onChange(this: void, selected: boolean, room: Room): void; } const SpecificChildrenPicker: React.FC = ({ @@ -89,7 +89,7 @@ interface IProps { noneLabel?: string; allLabel: string; specificLabel: string; - onChange(rooms: Room[]): void; + onChange(this: void, rooms: Room[]): void; } const SpaceChildrenPicker: React.FC = ({ diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 8d2d619ee4..52831d1ee1 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -113,8 +113,8 @@ interface ISpaceCreateFormProps extends BProps { aliasFieldRef: RefObject; showAliasField?: boolean; children?: ReactNode; - onSubmit(e: SyntheticEvent): void; - setAlias(alias: string): void; + onSubmit(this: void, e: SyntheticEvent): void; + setAlias(this: void, alias: string): void; } export const SpaceCreateForm: React.FC = ({ @@ -198,7 +198,7 @@ export const SpaceCreateForm: React.FC = ({ }; const SpaceCreateMenu: React.FC<{ - onFinished(): void; + onFinished(this: void): void; }> = ({ onFinished }) => { const cli = useMatrixClientContext(); const settingAllowPublicSpaces = useSettingValue(UIFeature.AllowCreatingPublicSpaces); diff --git a/src/components/views/spaces/SpacePublicShare.tsx b/src/components/views/spaces/SpacePublicShare.tsx index 18345ab230..e9203dfb57 100644 --- a/src/components/views/spaces/SpacePublicShare.tsx +++ b/src/components/views/spaces/SpacePublicShare.tsx @@ -22,7 +22,7 @@ import SpacePillButton from "../../structures/SpacePillButton.tsx"; interface IProps { space: Room; - onFinished?(): void; + onFinished?(this: void): void; } const SpacePublicShare: React.FC = ({ space, onFinished }) => { diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index 893acf20f0..8aeae9234a 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -32,7 +32,7 @@ import SettingsTab from "../settings/tabs/SettingsTab"; interface IProps { matrixClient: MatrixClient; space: Room; - closeSettingsFn(): void; + closeSettingsFn(this: void): void; } const SpaceSettingsVisibilityTab: React.FC = ({ matrixClient: cli, space, closeSettingsFn }) => { diff --git a/src/components/views/toasts/GenericExpiringToast.tsx b/src/components/views/toasts/GenericExpiringToast.tsx index abbbacc5f8..a8e4fa0e44 100644 --- a/src/components/views/toasts/GenericExpiringToast.tsx +++ b/src/components/views/toasts/GenericExpiringToast.tsx @@ -16,7 +16,7 @@ interface IProps extends IGenericToastProps { toastKey: string; numSeconds: number; dismissLabel: string; - onDismiss?(): void; + onDismiss?(this: void): void; } const SECOND = 1000; diff --git a/src/components/views/toasts/GenericToast.tsx b/src/components/views/toasts/GenericToast.tsx index d57dddc705..75149cf87c 100644 --- a/src/components/views/toasts/GenericToast.tsx +++ b/src/components/views/toasts/GenericToast.tsx @@ -17,14 +17,14 @@ export interface IProps { primaryLabel: string; PrimaryIcon?: ComponentType>; - onPrimaryClick(): void; + onPrimaryClick(this: void): void; } interface IPropsExtended extends IProps { secondaryLabel: string; SecondaryIcon?: ComponentType>; destructive?: "primary" | "secondary"; - onSecondaryClick(): void; + onSecondaryClick(this: void): void; // If set, this will override the max-width (of the description) making the toast wider or narrower than standard overrideWidth?: string; diff --git a/src/hooks/room/useRoomCall.tsx b/src/hooks/room/useRoomCall.tsx index 5d7784ed84..7c214bd019 100644 --- a/src/hooks/room/useRoomCall.tsx +++ b/src/hooks/room/useRoomCall.tsx @@ -93,9 +93,9 @@ export const useRoomCall = ( room: Room | LocalRoom, ): { voiceCallDisabledReason: string | null; - voiceCallClick(evt: React.MouseEvent | undefined, selectedType: PlatformCallType): void; + voiceCallClick(this: void, evt: React.MouseEvent | undefined, selectedType: PlatformCallType): void; videoCallDisabledReason: string | null; - videoCallClick(evt: React.MouseEvent | undefined, selectedType: PlatformCallType): void; + videoCallClick(this: void, evt: React.MouseEvent | undefined, selectedType: PlatformCallType): void; toggleCallMaximized: () => void; isViewingCall: boolean; isConnectedToCall: boolean; diff --git a/src/hooks/useProfileInfo.ts b/src/hooks/useProfileInfo.ts index 45500fac69..d0cfd4ab28 100644 --- a/src/hooks/useProfileInfo.ts +++ b/src/hooks/useProfileInfo.ts @@ -25,7 +25,7 @@ export const useProfileInfo = (): { ready: boolean; loading: boolean; profile: IProfileInfo | null; - search(opts: IProfileInfoOpts): Promise; + search(this: void, opts: IProfileInfoOpts): Promise; } => { const [profile, setProfile] = useState(null); diff --git a/src/hooks/usePublicRoomDirectory.ts b/src/hooks/usePublicRoomDirectory.ts index 530e61e070..dd6cd3382d 100644 --- a/src/hooks/usePublicRoomDirectory.ts +++ b/src/hooks/usePublicRoomDirectory.ts @@ -44,8 +44,8 @@ export const usePublicRoomDirectory = (): { publicRooms: IPublicRoomsChunkRoom[]; protocols: Protocols | null; config?: IPublicRoomDirectoryConfig | null; - setConfig(config: IPublicRoomDirectoryConfig | null): void; - search(opts: IPublicRoomsOpts): Promise; + setConfig(this: void, config: IPublicRoomDirectoryConfig | null): void; + search(this: void, opts: IPublicRoomsOpts): Promise; error?: Error | true; // true if an unknown error is encountered } => { const [publicRooms, setPublicRooms] = useState([]); diff --git a/src/hooks/useTimeoutToggle.ts b/src/hooks/useTimeoutToggle.ts index 91e0452da5..c1da8e8697 100644 --- a/src/hooks/useTimeoutToggle.ts +++ b/src/hooks/useTimeoutToggle.ts @@ -19,7 +19,7 @@ export const useTimeoutToggle = ( timeoutMs: number, ): { value: boolean; - toggle(): void; + toggle(this: void): void; } => { const timeoutId = useRef(undefined); const [value, setValue] = useState(defaultValue); diff --git a/src/hooks/useUserDirectory.ts b/src/hooks/useUserDirectory.ts index 8599b2488a..6324e06f13 100644 --- a/src/hooks/useUserDirectory.ts +++ b/src/hooks/useUserDirectory.ts @@ -21,7 +21,7 @@ export const useUserDirectory = (): { ready: boolean; loading: boolean; users: DirectoryMember[]; - search(opts: IUserDirectoryOpts): Promise; + search(this: void, opts: IUserDirectoryOpts): Promise; } => { const [users, setUsers] = useState([]); diff --git a/src/indexing/EventIndex.ts b/src/indexing/EventIndex.ts index ff06602687..97913bf845 100644 --- a/src/indexing/EventIndex.ts +++ b/src/indexing/EventIndex.ts @@ -308,7 +308,7 @@ export default class EventIndex extends EventEmitter { * @returns {bool} Returns true if the event can be indexed, false * otherwise. */ - private isValidEvent(ev: MatrixEvent): boolean { + private isValidEvent(this: void, ev: MatrixEvent): boolean { const isUsefulType = [EventType.RoomMessage, EventType.RoomName, EventType.RoomTopic].includes( ev.getType() as EventType, ); diff --git a/src/modules/Dialog.tsx b/src/modules/Dialog.tsx index 97d2839f0f..ac1f7ae442 100644 --- a/src/modules/Dialog.tsx +++ b/src/modules/Dialog.tsx @@ -20,7 +20,7 @@ const OuterDialog = ({ title: string; Dialog: ComponentType & P>; props: P; - onFinished(ok: boolean, model: M | null): void; + onFinished(this: void, ok: boolean, model: M | null): void; }): JSX.Element => { const close = useCallback(() => onFinished(false, null), [onFinished]); const submit = useCallback((model: M) => onFinished(true, model), [onFinished]); diff --git a/src/slash-commands/command.ts b/src/slash-commands/command.ts index f541f5ae98..03ce38c309 100644 --- a/src/slash-commands/command.ts +++ b/src/slash-commands/command.ts @@ -56,7 +56,7 @@ interface ICommandOpts { runFn?: RunFn; category: string; hideCompletionAfterSpace?: boolean; - isEnabled?(matrixClient: MatrixClient | null, roomId: string | null): boolean; + isEnabled?(this: void, matrixClient: MatrixClient | null, roomId: string | null): boolean; renderingTypes?: TimelineRenderingType[]; } diff --git a/src/stores/AutoRageshakeStore.ts b/src/stores/AutoRageshakeStore.ts index aeac62d507..ddac3571aa 100644 --- a/src/stores/AutoRageshakeStore.ts +++ b/src/stores/AutoRageshakeStore.ts @@ -85,7 +85,7 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient { } } - private async onDecryptionAttempt(ev: MatrixEvent): Promise { + private onDecryptionAttempt = async (ev: MatrixEvent): Promise => { if (!this.state.initialSyncCompleted) { return; } @@ -143,19 +143,19 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient { new Map([[senderUserId, new Map([[messageContent.device_id, messageContent]])]]), ); } - } + }; - private async onSyncStateChange( + private onSyncStateChange = async ( _state: SyncState, _prevState: SyncState | null, data?: SyncStateData, - ): Promise { + ): Promise => { if (!this.state.initialSyncCompleted) { await this.updateState({ initialSyncCompleted: !!data?.nextSyncToken }); } - } + }; - private async onDeviceMessage(ev: MatrixEvent): Promise { + private onDeviceMessage = async (ev: MatrixEvent): Promise => { if (ev.getType() !== AUTO_RS_REQUEST) return; const messageContent = ev.getContent(); const recipientRageshake = messageContent["recipient_rageshake"] || ""; @@ -180,7 +180,7 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient { `Not sending sender-side autorageshake for event ${messageContent["event_id"]}/session ${messageContent["session_id"]}: last rageshake was too recent`, ); } - } + }; } window.mxAutoRageshakeStore = AutoRageshakeStore.instance; diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts index 178a1ed553..443c679db8 100644 --- a/src/stores/room-list/filters/VisibilityProvider.ts +++ b/src/stores/room-list/filters/VisibilityProvider.ts @@ -38,9 +38,8 @@ export class VisibilityProvider { return false; } - const isVisibleFn = RoomListCustomisations.isRoomVisible; - if (isVisibleFn) { - return isVisibleFn(room); + if (RoomListCustomisations.isRoomVisible) { + return RoomListCustomisations.isRoomVisible(room); } return true; // default diff --git a/src/stores/spaces/flattenSpaceHierarchy.ts b/src/stores/spaces/flattenSpaceHierarchy.ts index ca393d841b..3b51032e57 100644 --- a/src/stores/spaces/flattenSpaceHierarchy.ts +++ b/src/stores/spaces/flattenSpaceHierarchy.ts @@ -43,7 +43,7 @@ export const flattenSpaceHierarchy = ( flattenedSpaceIds.forEach((id) => { const roomIds = spaceEntityMap.get(id); - roomIds?.forEach(flattenedRooms.add, flattenedRooms); + roomIds?.forEach((roomId) => flattenedRooms.add(roomId), flattenedRooms); }); return flattenedRooms; diff --git a/src/utils/exportUtils/Exporter.ts b/src/utils/exportUtils/Exporter.ts index 4085126491..185507e8cc 100644 --- a/src/utils/exportUtils/Exporter.ts +++ b/src/utils/exportUtils/Exporter.ts @@ -59,7 +59,7 @@ export default abstract class Exporter { return this.makeFileNameNoExtension(SdkConfig.get().brand) + ".zip"; } - protected onBeforeUnload(e: BeforeUnloadEvent): string { + protected onBeforeUnload(this: void, e: BeforeUnloadEvent): string { e.preventDefault(); return (e.returnValue = _t("export_chat|unload_confirm")); } diff --git a/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx b/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx index 0589e07efd..6bb482fafa 100644 --- a/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx +++ b/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx @@ -238,10 +238,10 @@ interface WidgetContextMenuProps { menuDisplayed: boolean; trigger: ReactNode; // override delete handler - onDeleteClick?(): void; + onDeleteClick?(this: void): void; // override edit handler - onEditClick?(): void; - onFinished(): void; + onEditClick?(this: void): void; + onFinished(this: void): void; } export type WidgetContextMenuViewModelProps = WidgetContextMenuProps & { diff --git a/src/workers/indexeddb.worker.ts b/src/workers/indexeddb.worker.ts index c712999d32..ebfc1b2700 100644 --- a/src/workers/indexeddb.worker.ts +++ b/src/workers/indexeddb.worker.ts @@ -10,6 +10,7 @@ import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker"; const ctx: Worker = self as any; +// eslint-disable-next-line @typescript-eslint/unbound-method const remoteWorker = new IndexedDBStoreWorker(ctx.postMessage); ctx.onmessage = remoteWorker.onMessage;