diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index 39d7e50a6..80a5e2e72 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -436,6 +436,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli const useAuthentication = useMediaAuthentication(); const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); const [messageLayout] = useSetting(settingsAtom, 'messageLayout'); + const [perMessageProfiles] = useSetting(settingsAtom, 'perMessageProfiles'); const [messageSpacing] = useSetting(settingsAtom, 'messageSpacing'); const [legacyUsernameColor] = useSetting(settingsAtom, 'legacyUsernameColor'); const direct = useIsDirectRoom(); @@ -1649,6 +1650,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli } const collapsed = + !perMessageProfiles && isPrevRendered && !dayDivider && (!newDivider || eventSender === mx.getUserId()) && diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx index a4f0d7af6..d96c9df02 100644 --- a/src/app/features/room/RoomView.tsx +++ b/src/app/features/room/RoomView.tsx @@ -15,6 +15,8 @@ import { RoomTombstone } from './RoomTombstone'; import { RoomInput } from './RoomInput'; import { RoomViewFollowing, RoomViewFollowingPlaceholder } from './RoomViewFollowing'; import { Page } from '../../components/page'; +import { useSetting } from '../../state/hooks/settings'; +import { settingsAtom, ChatBackground } from '../../state/settings'; import { useKeyDown } from '../../hooks/useKeyDown'; import { editableActiveElement } from '../../utils/dom'; import { settingsAtom } from '../../state/settings'; @@ -54,9 +56,31 @@ const shouldFocusMessageField = (evt: KeyboardEvent): boolean => { return true; }; + +const CHAT_BG: Record = { + none: {}, + dots: { + backgroundImage: 'radial-gradient(circle, rgba(152,0,0,0.18) 1px, transparent 1px)', + backgroundSize: '22px 22px', + }, + grid: { + backgroundImage: + 'linear-gradient(rgba(152,0,0,0.09) 1px, transparent 1px),' + + 'linear-gradient(90deg, rgba(152,0,0,0.09) 1px, transparent 1px)', + backgroundSize: '28px 28px', + }, + diagonal: { + backgroundImage: + 'repeating-linear-gradient(45deg, rgba(152,0,0,0.07) 0px, rgba(152,0,0,0.07) 1px, transparent 1px, transparent 16px)', + }, + 'solid-navy': { backgroundColor: '#0a0e1a' }, + 'solid-void': { backgroundColor: '#070709' }, +}; + export function RoomView({ eventId }: { eventId?: string }) { const roomInputRef = useRef(null); const roomViewRef = useRef(null); + const [chatBackground] = useSetting(settingsAtom, 'chatBackground'); const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); @@ -91,7 +115,7 @@ export function RoomView({ eventId }: { eventId?: string }) { ); return ( - + @@ -350,6 +351,22 @@ function Appearance() { } /> + + + } + /> + + + + } + /> + ); } @@ -741,6 +758,84 @@ function Editor() { ); } + +const BG_OPTIONS: { value: ChatBackground; label: string }[] = [ + { value: 'none', label: 'None' }, + { value: 'dots', label: 'Dots' }, + { value: 'grid', label: 'Grid' }, + { value: 'diagonal', label: 'Diagonal' }, + { value: 'solid-navy', label: 'Navy' }, + { value: 'solid-void', label: 'Void' }, +]; + +function SelectChatBackground() { + const [menuCords, setMenuCords] = useState(); + const [chatBackground, setChatBackground] = useSetting(settingsAtom, 'chatBackground'); + + const handleMenu: MouseEventHandler = (evt) => { + setMenuCords(evt.currentTarget.getBoundingClientRect()); + }; + + const handleSelect = (value: ChatBackground) => { + setChatBackground(value); + setMenuCords(undefined); + }; + + return ( + <> + + setMenuCords(undefined), + clickOutsideDeactivates: true, + isKeyForward: (evt: KeyboardEvent) => + evt.key === 'ArrowDown' || evt.key === 'ArrowRight', + isKeyBackward: (evt: KeyboardEvent) => + evt.key === 'ArrowUp' || evt.key === 'ArrowLeft', + escapeDeactivates: stopPropagation, + }} + > + + + {BG_OPTIONS.map((opt) => ( + handleSelect(opt.value)} + > + {opt.label} + + ))} + + + + } + /> + + ); +} + function SelectMessageLayout() { const [menuCords, setMenuCords] = useState(); const [messageLayout, setMessageLayout] = useSetting(settingsAtom, 'messageLayout'); diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 31ee6ccb9..a15d5ba8a 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -3,6 +3,7 @@ import { atom } from 'jotai'; const STORAGE_KEY = 'settings'; export type DateFormat = 'D MMM YYYY' | 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD' | ''; export type MessageSpacing = '0' | '100' | '200' | '300' | '400' | '500'; +export type ChatBackground = 'none' | 'dots' | 'grid' | 'diagonal' | 'solid-navy' | 'solid-void'; export enum MessageLayout { Modern = 0, Compact = 1, @@ -41,6 +42,9 @@ export interface Settings { dateFormatString: string; developerTools: boolean; + + chatBackground: ChatBackground; + perMessageProfiles: boolean; } const defaultSettings: Settings = { @@ -75,6 +79,9 @@ const defaultSettings: Settings = { dateFormatString: 'D MMM YYYY', developerTools: false, + + chatBackground: 'none', + perMessageProfiles: false, }; export const getSettings = () => {