diff --git a/.changeset/add_per_room_icon_display.md b/.changeset/add_per_room_icon_display.md
new file mode 100644
index 000000000..6e20d1371
--- /dev/null
+++ b/.changeset/add_per_room_icon_display.md
@@ -0,0 +1,5 @@
+---
+default: minor
+---
+
+Add per Space setting for when to show room icons in sidebar
diff --git a/.changeset/fix-various-banner-fixes.md b/.changeset/fix-various-banner-fixes.md
new file mode 100644
index 000000000..0c012d451
--- /dev/null
+++ b/.changeset/fix-various-banner-fixes.md
@@ -0,0 +1,5 @@
+---
+default: fix
+---
+
+Various small banner changes
diff --git a/src/app/components/room-card/RoomCard.tsx b/src/app/components/room-card/RoomCard.tsx
index ed905872e..9c35f0186 100644
--- a/src/app/components/room-card/RoomCard.tsx
+++ b/src/app/components/room-card/RoomCard.tsx
@@ -18,6 +18,7 @@ import {
as,
color,
config,
+ toRem,
} from 'folds';
import classNames from 'classnames';
import FocusTrap from 'focus-trap-react';
@@ -36,6 +37,9 @@ import { KnockRoomPrompt } from '$components/knock-room-prompt';
import { RoomAvatar } from '$components/room-avatar';
import { formatCompactNumber } from '$utils/formatCompactNumber';
import * as css from './style.css';
+import type { RoomBannerContent } from '$types/matrix-sdk-events';
+import { CustomStateEvent } from '$types/matrix/room';
+import colorMXID from '$utils/colorMXID';
type GridColumnCount = '1' | '2' | '3';
const getGridColumnCount = (gridWidth: number): GridColumnCount => {
@@ -66,7 +70,6 @@ export function RoomCardGrid({ children }: { children: ReactNode }) {
export const RoomCardBase = as<'div'>(({ className, ...props }, ref) => (
(
? getRoomAvatarUrl(mx, joinedRoom, 96, useAuthentication)
: avatarUrl && mxcUrlToHttp(mx, avatarUrl, useAuthentication, 96, 96, 'crop');
+ const bannerState = joinedRoom
+ ? getStateEvent(joinedRoom, CustomStateEvent.RoomBanner)
+ : undefined;
+ const bannerMXC = bannerState?.getContent()?.url;
+ const bannerURI = mxcUrlToHttp(mx, bannerMXC ?? '', true);
const roomName = joinedRoom?.name || name || fallbackName;
const roomTopic =
(topicEvent?.getContent().topic as string) || undefined || topic || fallbackTopic;
@@ -215,11 +223,25 @@ export const RoomCard = as<'div', RoomCardProps>(
const [viewTopic, setViewTopic] = useState(false);
const closeTopic = () => setViewTopic(false);
const openTopic = () => setViewTopic(true);
-
return (
-
-
+
+ {!bannerURI && !avatar ? (
+
+ ) : (
+
+ )}
+
(
)}
/>
- {(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
-
- Space
-
- )}
-
- {roomName}
-
- {roomTopic}
-
-
- }>
-
-
- {renderTopicViewer(roomName, roomTopic, closeTopic)}
-
-
-
-
- {typeof joinedMemberCount === 'number' && (
-
-
- {`${formatCompactNumber(joinedMemberCount)} Members`}
+
+
+
+ {roomName}
+
+ {roomTopic}
+
+
+ }>
+
+
+ {renderTopicViewer(roomName, roomTopic, closeTopic)}
+
+
+
+ {(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
+
+ Space
+
+ )}
- )}
- {typeof joinedRoomId === 'string' && (
-
- )}
- {typeof joinedRoomId !== 'string' &&
- joinState.status !== AsyncStatus.Error &&
- (joinRule === JoinRule.Knock ? (
- <>
-
-
- {knocking && (
- setKnocking(false)}
- onCancel={() => setKnocking(false)}
- />
- )}
- >
- ) : (
+ {typeof joinedMemberCount === 'number' && (
+
+
+ {`${formatCompactNumber(joinedMemberCount)} Members`}
+
+ )}
+ {typeof joinedRoomId === 'string' && (
- ))}
- {typeof joinedRoomId !== 'string' && joinState.status === AsyncStatus.Error && (
-
-
-
- {(openError) => (
-
-
- )}
+
+ {knocking && (
+ setKnocking(false)}
+ onCancel={() => setKnocking(false)}
+ />
+ )}
+ >
+ ) : (
+ }
+ >
+
+ {joining ? 'Joining' : 'Join'}
+
+
+ ))}
+ {typeof joinedRoomId !== 'string' && joinState.status === AsyncStatus.Error && (
+
+
+
+ Retry
+
+
+
+ {(openError) => (
+
+
+ View Error
+
+
+ )}
+
+
+ )}
+
);
}
diff --git a/src/app/components/room-card/style.css.ts b/src/app/components/room-card/style.css.ts
index 8afe6704b..fbe4b4dce 100644
--- a/src/app/components/room-card/style.css.ts
+++ b/src/app/components/room-card/style.css.ts
@@ -1,6 +1,7 @@
import { style } from '@vanilla-extract/css';
-import { DefaultReset, config } from 'folds';
+import { DefaultReset, color, config, toRem } from 'folds';
import { ContainerColor } from '$styles/ContainerColor.css';
+import { recipe } from '@vanilla-extract/recipes';
export const CardGrid = style({
display: 'grid',
@@ -12,11 +13,15 @@ export const RoomCardBase = style([
DefaultReset,
ContainerColor({ variant: 'SurfaceVariant' }),
{
- padding: config.space.S500,
borderRadius: config.radii.R500,
+ overflow: 'hidden',
},
]);
+export const RoomCardItems = style({
+ padding: config.space.S500,
+ backgroundColor: color.SurfaceVariant.Container,
+});
export const RoomCardTopic = style({
minHeight: `calc(3 * ${config.lineHeight.T200})`,
display: '-webkit-box',
@@ -34,3 +39,27 @@ export const ActionButton = style({
flex: '1 1 0',
minWidth: 1,
});
+
+export const RoomCardBanner = recipe({
+ base: {
+ height: toRem(96),
+ minHeight: toRem(96),
+ width: '100%',
+ objectFit: 'cover',
+ objectPosition: 'center center',
+ },
+ variants: {
+ trueBanner: {
+ true: {},
+ false: {
+ filter: 'blur(10px)',
+ },
+ },
+ },
+});
+export const RoomCardAvatar = style({
+ position: 'sticky',
+ transform: 'translateY(-50%)',
+ marginLeft: config.space.S500,
+ outline: `${config.borderWidth.B600} solid ${color.Surface.Container}`,
+});
diff --git a/src/app/features/common-settings/appearance/Appearance.tsx b/src/app/features/common-settings/appearance/Appearance.tsx
new file mode 100644
index 000000000..b7bc803e0
--- /dev/null
+++ b/src/app/features/common-settings/appearance/Appearance.tsx
@@ -0,0 +1,147 @@
+import { useState, type MouseEventHandler } from 'react';
+import {
+ Box,
+ Text,
+ IconButton,
+ Icon,
+ Icons,
+ Scroll,
+ Button,
+ config,
+ Menu,
+ MenuItem,
+ PopOut,
+ type RectCords,
+} from 'folds';
+import { Page, PageContent, PageHeader } from '$components/page';
+import { SequenceCard } from '$components/sequence-card';
+import { SettingTile } from '$components/setting-tile';
+import { useRoom } from '$hooks/useRoom';
+
+import { SequenceCardStyle } from '$features/common-settings/styles.css';
+import { useShowPerRoomRoomIcon } from '$hooks/useShowRoomIcon';
+import { useSetting } from '$state/hooks/settings';
+import type { ShowRoomIcon } from '$state/settings';
+import { settingsAtom } from '$state/settings';
+import { stopPropagation } from '$utils/keyboard';
+import FocusTrap from 'focus-trap-react';
+
+export function SelectShowPerRoomRoomIcon({ roomId }: { roomId: string }) {
+ const [menuCords, setMenuCords] = useState();
+ const showRoomIconItems = useShowPerRoomRoomIcon();
+ const [showRoomIconArray, setShowRoomIconArray] = useSetting(settingsAtom, 'perRoomShowRoomIcon');
+ const showRoomIcon = showRoomIconArray?.find((item) => item.roomId === roomId)?.display;
+
+ const handleMenu: MouseEventHandler = (evt) => {
+ setMenuCords(evt.currentTarget.getBoundingClientRect());
+ };
+
+ const handleSelect = (position?: ShowRoomIcon) => {
+ let newShowRoomIconArray = showRoomIconArray.filter((item) => item.roomId !== roomId);
+ if (position) newShowRoomIconArray = [...newShowRoomIconArray, { roomId, display: position }];
+ setShowRoomIconArray(newShowRoomIconArray);
+ setMenuCords(undefined);
+ };
+
+ return (
+ <>
+ }
+ onClick={handleMenu}
+ >
+
+ {showRoomIconItems.find((i) => i.layout === showRoomIcon)?.name ?? showRoomIcon}
+
+
+ setMenuCords(undefined),
+ clickOutsideDeactivates: true,
+ isKeyForward: (evt: KeyboardEvent) =>
+ evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
+ isKeyBackward: (evt: KeyboardEvent) =>
+ evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
+ escapeDeactivates: stopPropagation,
+ }}
+ >
+
+
+ }
+ />
+ >
+ );
+}
+
+type AppearanceProps = {
+ requestClose: () => void;
+};
+export function Appearance({ requestClose }: AppearanceProps) {
+ const room = useRoom();
+
+ return (
+
+
+
+
+
+ Appearance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Visual Tweaks
+
+ }
+ />
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/features/common-settings/general/RoomProfile.tsx b/src/app/features/common-settings/general/RoomProfile.tsx
index cb49900ff..a65d7ef1a 100644
--- a/src/app/features/common-settings/general/RoomProfile.tsx
+++ b/src/app/features/common-settings/general/RoomProfile.tsx
@@ -44,7 +44,7 @@ import { createUploadAtom } from '$state/upload';
import { useFilePicker } from '$hooks/useFilePicker';
import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback';
import { useAlive } from '$hooks/useAlive';
-import type { RoomPermissionsAPI } from '$hooks/useRoomPermissions';
+import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions';
import { useSetting } from '$state/hooks/settings';
import { settingsAtom } from '$state/settings';
import { useStateEvent } from '$hooks/useStateEvent';
@@ -310,13 +310,18 @@ export function RoomProfileEdit({
}
export type ProfileProps = {
+ permissions: RoomPermissionsAPI;
bannerURI?: string;
};
-function RoomBannerEdit({ bannerURI }: Readonly) {
+function RoomBannerEdit({ bannerURI, permissions }: Readonly) {
const mx = useMatrixClient();
const [alertRemove, setAlertRemove] = useState(false);
const space = useRoom();
+
+ const userId = mx.getUserId() ?? '';
+ const canEdit = permissions.stateEvent(CustomStateEvent.RoomBanner, userId);
+
const [stagedUrl, setStagedUrl] = useState();
const [isRemoving, setIsRemoving] = useState(false);
@@ -420,6 +425,7 @@ function RoomBannerEdit({ bannerURI }: Readonly) {
fill="Soft"
outlined
radii="300"
+ disabled={!canEdit}
>
{bannerUrl ? 'Change Banner' : 'Upload Banner'}
@@ -430,6 +436,7 @@ function RoomBannerEdit({ bannerURI }: Readonly) {
fill="None"
radii="300"
onClick={() => setAlertRemove(true)}
+ disabled={!canEdit}
>
Remove
@@ -466,7 +473,7 @@ function RoomBannerEdit({ bannerURI }: Readonly) {
Are you sure you want to remove profile banner?
-
+
Remove
@@ -585,8 +592,9 @@ export function RoomProfile({ permissions }: RoomProfileProps) {
variant="SurfaceVariant"
direction="Column"
gap="400"
+ disabled={!canEdit}
>
-
+
)}
diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx
index 2641899d4..3aeb18c15 100644
--- a/src/app/features/room/MembersDrawer.tsx
+++ b/src/app/features/room/MembersDrawer.tsx
@@ -296,8 +296,6 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) {
);
const handleMemberClick: MouseEventHandler = (evt) => {
- // oxlint-disable-next-line no-console
- console.log(evt);
const btn = evt.currentTarget as HTMLButtonElement;
const userId = btn.getAttribute('data-user-id');
if (!userId) return;
diff --git a/src/app/features/settings/cosmetics/Themes.tsx b/src/app/features/settings/cosmetics/Themes.tsx
index e00e5f425..fa93a3890 100644
--- a/src/app/features/settings/cosmetics/Themes.tsx
+++ b/src/app/features/settings/cosmetics/Themes.tsx
@@ -38,6 +38,7 @@ import FocusTrap from 'focus-trap-react';
import { useShowRoomIcon } from '$hooks/useShowRoomIcon';
import type { PanelSizetItem } from '$hooks/usePanelSizes';
import { usePanelSizeItems } from '$hooks/usePanelSizes';
+import { SelectShowPerRoomRoomIcon } from '$features/common-settings/appearance/Appearance';
const clampIncomingInlineImageHeight = (n: number) => Math.max(1, Math.min(4096, n));
@@ -717,7 +718,8 @@ function SelectShowRoomIcon() {
setMenuCords(evt.currentTarget.getBoundingClientRect());
};
- const handleSelect = (position: ShowRoomIcon) => {
+ const handleSelect = (position?: ShowRoomIcon) => {
+ if (!position) return;
setShowRoomIcon(position);
setMenuCords(undefined);
};
@@ -868,12 +870,22 @@ export function Appearance({
}
/>
+ {/*THIS SHOULD BE MOVED TO A NEW SETTINGS MENU INSIDE OF THE HOME SETTINGS AS SOON AS THERE IS A REASON TO CREATE A HOME MENU SETTINGS PANEL
+ it is currently here because it would be eerie to have an entire home settings menu for just one single setting*/}
+
+ }
+ />
+
name: 'Developer Tools',
icon: Icons.Terminal,
},
+ {
+ page: SpaceSettingsPage.AppearancePage,
+ name: 'Appearance',
+ icon: Icons.Alphabet,
+ activeIcon: Icons.AlphabetUnderline,
+ },
],
[]
);
@@ -197,6 +204,9 @@ export function SpaceSettings({ initialPage, requestClose }: SpaceSettingsProps)
{activePage === SpaceSettingsPage.AbbreviationsPage && (
)}
+ {activePage === SpaceSettingsPage.AppearancePage && (
+
+ )}
);
}
diff --git a/src/app/hooks/useShowRoomIcon.ts b/src/app/hooks/useShowRoomIcon.ts
index 509f58aac..9c548c940 100644
--- a/src/app/hooks/useShowRoomIcon.ts
+++ b/src/app/hooks/useShowRoomIcon.ts
@@ -2,8 +2,8 @@ import { useMemo } from 'react';
import { ShowRoomIcon } from '$state/settings';
export type MessageLayoutItem = {
+ layout?: ShowRoomIcon;
name: string;
- layout: ShowRoomIcon;
};
export const useShowRoomIcon = (): MessageLayoutItem[] =>
@@ -24,3 +24,26 @@ export const useShowRoomIcon = (): MessageLayoutItem[] =>
],
[]
);
+
+export const useShowPerRoomRoomIcon = (): MessageLayoutItem[] =>
+ useMemo(
+ () => [
+ {
+ layout: undefined,
+ name: 'Default',
+ },
+ {
+ layout: ShowRoomIcon.Always,
+ name: 'Always',
+ },
+ {
+ layout: ShowRoomIcon.Smart,
+ name: 'Smart',
+ },
+ {
+ layout: ShowRoomIcon.Never,
+ name: 'Never',
+ },
+ ],
+ []
+ );
diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx
index dce68f104..81dc6181d 100644
--- a/src/app/pages/client/direct/Direct.tsx
+++ b/src/app/pages/client/direct/Direct.tsx
@@ -338,6 +338,7 @@ export function Direct() {
width: '100%',
aspectRatio: 1,
display: 'flex',
+ flexDirection: 'column',
}
: {}
}
diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx
index 38022a2e6..cddcefbb0 100644
--- a/src/app/pages/client/home/Home.tsx
+++ b/src/app/pages/client/home/Home.tsx
@@ -213,16 +213,20 @@ export function Home() {
const [roomSidebarWidth, setRoomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth');
const [curWidth, setCurWidth] = useState(roomSidebarWidth);
+ useEffect(() => {
+ setCurWidth(roomSidebarWidth);
+ }, [roomSidebarWidth]);
- const [showRoomIcon] = useSetting(settingsAtom, 'showRoomIcon');
+ const [showRoomIconGeneral] = useSetting(settingsAtom, 'showRoomIcon');
+ const [showRoomIconArray] = useSetting(settingsAtom, 'perRoomShowRoomIcon');
+ const showRoomIcon =
+ showRoomIconArray.find((item) => item.roomId === 'Home')?.display ?? showRoomIconGeneral;
const showIcons = () => {
if (showRoomIcon === ShowRoomIcon.Always) return true;
if (showRoomIcon === ShowRoomIcon.Never) return false;
- return curWidth < 96;
+ return curWidth < 144;
};
- useEffect(() => {
- setCurWidth(roomSidebarWidth);
- }, [roomSidebarWidth]);
+
const [joinCallOnSingleClick] = useSetting(settingsAtom, 'joinCallOnSingleClick');
const selectedRoomId = useSelectedRoom();
@@ -423,6 +427,7 @@ export function Home() {
width: '100%',
aspectRatio: 1,
display: 'flex',
+ flexDirection: 'column',
}
: {}
}
diff --git a/src/app/pages/client/sidebar/SidebarResizer.css.ts b/src/app/pages/client/sidebar/SidebarResizer.css.ts
index b37504b98..5acd9d4b9 100644
--- a/src/app/pages/client/sidebar/SidebarResizer.css.ts
+++ b/src/app/pages/client/sidebar/SidebarResizer.css.ts
@@ -1,5 +1,6 @@
import { style } from '@vanilla-extract/css';
-import { color } from 'folds';
+import { recipe } from '@vanilla-extract/recipes';
+import { color, toRem } from 'folds';
/** Out-of-flow so flex siblings (e.g. PageRoot vertical Line) stay flush with the panel edge. */
export const SidebarResizerDockRight = style({
@@ -29,17 +30,33 @@ export const SidebarResizerDockTop = style({
cursor: 'ns-resize',
});
-export const SidebarResizer = style({
- backgroundColor: 'inherit',
- transition: '0.2s',
- ':hover': {},
+export const SidebarResizer = recipe({
+ base: {
+ backgroundColor: 'inherit',
+ transition: '0.2s',
+ ':hover': {},
+ touchAction: 'none',
+ },
+ variants: {
+ topSided: {
+ true: {
+ width: '100%',
+ height: toRem(8),
+ },
+ false: {
+ width: toRem(4),
+ height: '100%',
+ },
+ },
+ },
});
export const SidebarResizerHover = style({
- zIndex: 100,
+ zIndex: 110,
});
export const SideBarResizerAnimation = style({
width: '100%',
height: '100%',
backgroundColor: color.Surface.ContainerLine,
transition: '0.5s',
+ touchAction: 'none',
});
diff --git a/src/app/pages/client/sidebar/SidebarResizer.tsx b/src/app/pages/client/sidebar/SidebarResizer.tsx
index 6b7d0144c..b54d258f0 100644
--- a/src/app/pages/client/sidebar/SidebarResizer.tsx
+++ b/src/app/pages/client/sidebar/SidebarResizer.tsx
@@ -1,6 +1,6 @@
// The disable is because the position should only update whenever the new one is updated
// oxlint-disable eslint-plugin-react-hooks/exhaustive-deps
-import { Box, toRem } from 'folds';
+import { Box } from 'folds';
import * as css from '$pages/client/sidebar/SidebarResizer.css';
import type { Dispatch, SetStateAction } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
@@ -64,6 +64,7 @@ export function SidebarResizer({
const onPointerDown = useCallback(
(e: React.PointerEvent) => {
e.preventDefault();
+ e.currentTarget.setPointerCapture(e.pointerId);
setOldPos(topSided ? e.clientY : e.clientX);
setIsPointerDown(true);
window.addEventListener('pointerup', onPointerUp);
@@ -80,14 +81,10 @@ export function SidebarResizer({
return (
setIsPointerOver(true)}
onPointerLeave={() => setIsPointerOver(false)}
onPointerDown={onPointerDown}
- style={{
- width: topSided ? '100%' : toRem(4),
- height: topSided ? toRem(4) : '100%',
- }}
shrink="No"
>
{hasBanner && (
<>
-
+
-
+
>
)}
{hasBanner && bannerViewerOpen && (
@@ -520,16 +520,19 @@ export function Space() {
const [roomSidebarWidth, setRoomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth');
const [curWidth, setCurWidth] = useState(roomSidebarWidth);
+ useEffect(() => {
+ setCurWidth(roomSidebarWidth);
+ }, [roomSidebarWidth]);
- const [showRoomIcon] = useSetting(settingsAtom, 'showRoomIcon');
+ const [showRoomIconGeneral] = useSetting(settingsAtom, 'showRoomIcon');
+ const [showRoomIconArray] = useSetting(settingsAtom, 'perRoomShowRoomIcon');
+ const showRoomIcon =
+ showRoomIconArray.find((item) => item.roomId === space.roomId)?.display ?? showRoomIconGeneral;
const showIcons = () => {
if (showRoomIcon === ShowRoomIcon.Always) return true;
if (showRoomIcon === ShowRoomIcon.Never) return false;
return curWidth < 144;
};
- useEffect(() => {
- setCurWidth(roomSidebarWidth);
- }, [roomSidebarWidth]);
const [joinCallOnSingleClick] = useSetting(settingsAtom, 'joinCallOnSingleClick');
const tombstoneEvent = useStateEvent(space, EventType.RoomTombstone);
@@ -1017,6 +1020,7 @@ export function Space() {
width: '100%',
aspectRatio: 1,
display: 'flex',
+ flexDirection: 'column',
}
: { paddingLeft }
}
diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts
index b1b744c1f..aab58f600 100644
--- a/src/app/state/settings.ts
+++ b/src/app/state/settings.ts
@@ -28,6 +28,11 @@ export enum ShowRoomIcon {
Smart = 'smart',
Never = 'never',
}
+export type PerRoomShowRoomIcon = {
+ roomId: string;
+ display: ShowRoomIcon;
+};
+
export type JumboEmojiSize = 'none' | 'extraSmall' | 'small' | 'normal' | 'large' | 'extraLarge';
export type ThemeRemoteFavorite = {
@@ -160,6 +165,7 @@ export interface Settings {
mentionInReplies: boolean;
showPersonaSetting: boolean;
closeFoldersByDefault: boolean;
+ perRoomShowRoomIcon: PerRoomShowRoomIcon[];
showRoomIcon: ShowRoomIcon;
showRoomBanners: boolean;
roomSidebarWidth: number;
@@ -292,6 +298,7 @@ export const defaultSettings: Settings = {
mentionInReplies: true,
showPersonaSetting: false,
closeFoldersByDefault: false,
+ perRoomShowRoomIcon: [],
showRoomIcon: ShowRoomIcon.Smart,
showRoomBanners: true,
roomSidebarWidth: 256,
diff --git a/src/app/state/spaceSettings.ts b/src/app/state/spaceSettings.ts
index c33b5e957..de91d6f3a 100644
--- a/src/app/state/spaceSettings.ts
+++ b/src/app/state/spaceSettings.ts
@@ -9,6 +9,7 @@ export enum SpaceSettingsPage {
// Sable pages
CosmeticsPage,
AbbreviationsPage,
+ AppearancePage,
}
export type SpaceSettingsState = {