Apply `strictNullChecks` to `src/components/views/spaces/*` (#10517)
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>pull/28788/head^2
parent
209f5bdf33
commit
c0db739d81
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -33,7 +33,6 @@ import { Icon as PinUprightIcon } from "../../../../res/img/element-icons/room/p
|
|||
import { Icon as EllipsisIcon } from "../../../../res/img/element-icons/room/ellipsis.svg";
|
||||
import { Icon as MembersIcon } from "../../../../res/img/element-icons/room/members.svg";
|
||||
import { Icon as FavoriteIcon } from "../../../../res/img/element-icons/roomlist/favorite.svg";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import Modal from "../../../Modal";
|
||||
import DevtoolsDialog from "../dialogs/DevtoolsDialog";
|
||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||
|
@ -46,6 +45,9 @@ const QuickSettingsButton: React.FC<{
|
|||
const { [MetaSpace.Favourites]: favouritesEnabled, [MetaSpace.People]: peopleEnabled } =
|
||||
useSettingValue<Record<MetaSpace, boolean>>("Spaces.enabledMetaSpaces");
|
||||
|
||||
const currentRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
const developerModeEnabled = useSettingValue("developerMode");
|
||||
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed && handle.current) {
|
||||
contextMenu = (
|
||||
|
@ -68,14 +70,14 @@ const QuickSettingsButton: React.FC<{
|
|||
{_t("All settings")}
|
||||
</AccessibleButton>
|
||||
|
||||
{SettingsStore.getValue("developerMode") && SdkContextClass.instance.roomViewStore.getRoomId() && (
|
||||
{currentRoomId && developerModeEnabled && (
|
||||
<AccessibleButton
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
Modal.createDialog(
|
||||
DevtoolsDialog,
|
||||
{
|
||||
roomId: SdkContextClass.instance.roomViewStore.getRoomId()!,
|
||||
roomId: currentRoomId,
|
||||
},
|
||||
"mx_DevtoolsDialog_wrapper",
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -95,9 +95,9 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
}
|
||||
|
||||
let notifBadge;
|
||||
if (notificationState) {
|
||||
if (space && notificationState) {
|
||||
let ariaLabel = _t("Jump to first unread room.");
|
||||
if (space?.getMyMembership() === "invite") {
|
||||
if (space.getMyMembership() === "invite") {
|
||||
ariaLabel = _t("Jump to first invite.");
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,9 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
}
|
||||
|
||||
const viewSpaceHome = (): void =>
|
||||
defaultDispatcher.dispatch({ action: Action.ViewRoom, room_id: space.roomId });
|
||||
const activateSpace = (): void => SpaceStore.instance.setActiveSpace(spaceKey ?? space.roomId);
|
||||
// space is set here because of the assignment condition of onClick
|
||||
defaultDispatcher.dispatch({ action: Action.ViewRoom, room_id: space!.roomId });
|
||||
const activateSpace = (): void => SpaceStore.instance.setActiveSpace(spaceKey ?? space?.roomId ?? "");
|
||||
const onClick = props.onClick ?? (selected && space ? viewSpaceHome : activateSpace);
|
||||
|
||||
return (
|
||||
|
|
|
@ -46,6 +46,7 @@ import { FontWatcher } from "./watchers/FontWatcher";
|
|||
import RustCryptoSdkController from "./controllers/RustCryptoSdkController";
|
||||
import ServerSupportUnstableFeatureController from "./controllers/ServerSupportUnstableFeatureController";
|
||||
import { WatchManager } from "./WatchManager";
|
||||
import { CustomTheme } from "../theme";
|
||||
|
||||
export const defaultWatchManager = new WatchManager();
|
||||
|
||||
|
@ -111,7 +112,15 @@ export const labGroupNames: Record<LabGroup, string> = {
|
|||
[LabGroup.Developer]: _td("Developer"),
|
||||
};
|
||||
|
||||
export type SettingValueType = boolean | number | string | number[] | string[] | Record<string, unknown> | null;
|
||||
export type SettingValueType =
|
||||
| boolean
|
||||
| number
|
||||
| string
|
||||
| number[]
|
||||
| string[]
|
||||
| Record<string, unknown>
|
||||
| Record<string, unknown>[]
|
||||
| null;
|
||||
|
||||
export interface IBaseSetting<T extends SettingValueType = SettingValueType> {
|
||||
isFeature?: false | undefined;
|
||||
|
@ -653,7 +662,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
|||
},
|
||||
"custom_themes": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: [],
|
||||
default: [] as CustomTheme[],
|
||||
},
|
||||
"use_system_theme": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
|
|
25
src/theme.ts
25
src/theme.ts
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { compare } from "matrix-js-sdk/src/utils";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from "./languageHandler";
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
|
@ -34,7 +35,8 @@ interface IFontFaces extends Omit<Record<(typeof allowedFontFaceProps)[number],
|
|||
}[];
|
||||
}
|
||||
|
||||
interface ICustomTheme {
|
||||
export type CustomTheme = {
|
||||
name: string;
|
||||
colors: {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
@ -44,7 +46,7 @@ interface ICustomTheme {
|
|||
monospace: string;
|
||||
};
|
||||
is_dark?: boolean; // eslint-disable-line camelcase
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a non-high-contrast theme, find the corresponding high-contrast one
|
||||
|
@ -79,11 +81,20 @@ export function enumerateThemes(): { [key: string]: string } {
|
|||
"light-high-contrast": _t("Light high contrast"),
|
||||
"dark": _t("Dark"),
|
||||
};
|
||||
const customThemes = SettingsStore.getValue("custom_themes");
|
||||
const customThemes = SettingsStore.getValue("custom_themes") || [];
|
||||
const customThemeNames: Record<string, string> = {};
|
||||
for (const { name } of customThemes) {
|
||||
customThemeNames[`custom-${name}`] = name;
|
||||
|
||||
try {
|
||||
for (const { name } of customThemes) {
|
||||
customThemeNames[`custom-${name}`] = name;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn("Error loading custom themes", {
|
||||
err,
|
||||
customThemes,
|
||||
});
|
||||
}
|
||||
|
||||
return Object.assign({}, customThemeNames, BUILTIN_THEMES);
|
||||
}
|
||||
|
||||
|
@ -166,7 +177,7 @@ function generateCustomFontFaceCSS(faces: IFontFaces[]): string {
|
|||
.join("\n");
|
||||
}
|
||||
|
||||
function setCustomThemeVars(customTheme: ICustomTheme): void {
|
||||
function setCustomThemeVars(customTheme: CustomTheme): void {
|
||||
const { style } = document.body;
|
||||
|
||||
function setCSSColorVariable(name: string, hexColor: string, doPct = true): void {
|
||||
|
@ -209,7 +220,7 @@ function setCustomThemeVars(customTheme: ICustomTheme): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function getCustomTheme(themeName: string): ICustomTheme {
|
||||
export function getCustomTheme(themeName: string): CustomTheme {
|
||||
// set css variables
|
||||
const customThemes = SettingsStore.getValue("custom_themes");
|
||||
if (!customThemes) {
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import QuickSettingsButton from "../../../../src/components/views/spaces/QuickSettingsButton";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||
|
||||
describe("QuickSettingsButton", () => {
|
||||
const roomId = "!room:example.com";
|
||||
|
||||
const renderQuickSettingsButton = () => {
|
||||
render(<QuickSettingsButton isPanelCollapsed={true} />);
|
||||
};
|
||||
|
||||
const getQuickSettingsButton = () => {
|
||||
return screen.getByRole("button", { name: "Quick settings" });
|
||||
};
|
||||
|
||||
const openQuickSettings = async () => {
|
||||
await userEvent.click(getQuickSettingsButton());
|
||||
await screen.findByText("Quick settings");
|
||||
};
|
||||
|
||||
it("should render the quick settings button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(getQuickSettingsButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("when the quick settings are open", () => {
|
||||
beforeEach(async () => {
|
||||
renderQuickSettingsButton();
|
||||
await openQuickSettings();
|
||||
});
|
||||
|
||||
it("should not render the »Developer tools« button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when developer mode is enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "developerMode");
|
||||
renderQuickSettingsButton();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocked(SettingsStore.getValue).mockRestore();
|
||||
});
|
||||
|
||||
describe("and no room is viewed", () => {
|
||||
it("should not render the »Developer tools« button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and a room is viewed", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(roomId);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocked(SdkContextClass.instance.roomViewStore.getRoomId).mockRestore();
|
||||
});
|
||||
|
||||
describe("and the quick settings are open", () => {
|
||||
beforeEach(async () => {
|
||||
await openQuickSettings();
|
||||
});
|
||||
|
||||
it("should render the »Developer tools« button", () => {
|
||||
expect(screen.getByRole("button", { name: "Developer tools" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { setTheme } from "../src/theme";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import { enumerateThemes, setTheme } from "../src/theme";
|
||||
|
||||
describe("theme", () => {
|
||||
describe("setTheme", () => {
|
||||
|
@ -124,4 +125,25 @@ describe("theme", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("enumerateThemes", () => {
|
||||
it("should return a list of themes", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue([{ name: "pink" }]);
|
||||
expect(enumerateThemes()).toEqual({
|
||||
"light": "Light",
|
||||
"light-high-contrast": "Light high contrast",
|
||||
"dark": "Dark",
|
||||
"custom-pink": "pink",
|
||||
});
|
||||
});
|
||||
|
||||
it("should be robust to malformed custom_themes values", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue([23]);
|
||||
expect(enumerateThemes()).toEqual({
|
||||
"light": "Light",
|
||||
"light-high-contrast": "Light high contrast",
|
||||
"dark": "Dark",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue