30101c83e8
The previous fix (7f329e3b) made Page transparent so the body background
would show through. But PageRoot sits between Page and body with an opaque
Background.Container color, so the body background was blocked — it only
showed through the glassmorphism sidebar (which is a sibling of PageRoot,
not a child).
Revert to applying getChatBg() directly to Page via inline style, which
overrides PageRoot's class-level background-color by CSS specificity rules.
SidebarNav continues to mirror the same background to document.body so the
glassmorphism sidebar can blur through it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
157 lines
5.5 KiB
TypeScript
157 lines
5.5 KiB
TypeScript
import React, { useCallback, useMemo, useRef } from 'react';
|
|
import { Box, Text, config } from 'folds';
|
|
import { EventType } from 'matrix-js-sdk';
|
|
import { ReactEditor } from 'slate-react';
|
|
import { isKeyHotkey } from 'is-hotkey';
|
|
import { useStateEvent } from '../../hooks/useStateEvent';
|
|
import { StateEvent } from '../../../types/matrix/room';
|
|
import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
|
import { useEditor } from '../../components/editor';
|
|
import { RoomInputPlaceholder } from './RoomInputPlaceholder';
|
|
import { RoomTimeline } from './RoomTimeline';
|
|
import { RoomViewTyping } from './RoomViewTyping';
|
|
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 } from '../../state/settings';
|
|
import { useTheme, ThemeKind } from '../../hooks/useTheme';
|
|
import { getChatBg } from '../lotus/chatBackground';
|
|
import { useKeyDown } from '../../hooks/useKeyDown';
|
|
import { editableActiveElement } from '../../utils/dom';
|
|
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
|
|
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
|
import { useRoom } from '../../hooks/useRoom';
|
|
|
|
const FN_KEYS_REGEX = /^F\d+$/;
|
|
const shouldFocusMessageField = (evt: KeyboardEvent): boolean => {
|
|
const { code } = evt;
|
|
if (evt.metaKey || evt.altKey || evt.ctrlKey) {
|
|
return false;
|
|
}
|
|
|
|
if (FN_KEYS_REGEX.test(code)) return false;
|
|
|
|
if (
|
|
code.startsWith('OS') ||
|
|
code.startsWith('Meta') ||
|
|
code.startsWith('Shift') ||
|
|
code.startsWith('Alt') ||
|
|
code.startsWith('Control') ||
|
|
code.startsWith('Arrow') ||
|
|
code.startsWith('Page') ||
|
|
code.startsWith('End') ||
|
|
code.startsWith('Home') ||
|
|
code === 'Tab' ||
|
|
code === 'Space' ||
|
|
code === 'Enter' ||
|
|
code === 'NumLock' ||
|
|
code === 'ScrollLock'
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
export function RoomView({ eventId }: { eventId?: string }) {
|
|
const roomInputRef = useRef<HTMLDivElement>(null) as React.RefObject<HTMLDivElement>;
|
|
const roomViewRef = useRef<HTMLDivElement>(null) as React.RefObject<HTMLDivElement>;
|
|
const [chatBackground] = useSetting(settingsAtom, 'chatBackground');
|
|
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
|
const [pauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
|
|
const theme = useTheme();
|
|
const isDark = theme.kind === ThemeKind.Dark;
|
|
|
|
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
|
|
|
const room = useRoom();
|
|
const { roomId } = room;
|
|
const editor = useEditor();
|
|
|
|
const mx = useMatrixClient();
|
|
|
|
const tombstoneEvent = useStateEvent(room, StateEvent.RoomTombstone);
|
|
const powerLevels = usePowerLevelsContext();
|
|
const creators = useRoomCreators(room);
|
|
|
|
const permissions = useRoomPermissions(creators, powerLevels);
|
|
const canMessage = permissions.event(EventType.RoomMessage, mx.getSafeUserId());
|
|
|
|
useKeyDown(
|
|
window,
|
|
useCallback(
|
|
(evt) => {
|
|
if (editableActiveElement()) return;
|
|
const portalContainer = document.getElementById('portalContainer');
|
|
if (portalContainer && portalContainer.children.length > 0) {
|
|
return;
|
|
}
|
|
if (shouldFocusMessageField(evt) || isKeyHotkey('mod+v', evt)) {
|
|
ReactEditor.focus(editor);
|
|
}
|
|
},
|
|
[editor],
|
|
),
|
|
);
|
|
|
|
// Apply the background directly to Page so it overrides PageRoot's opaque
|
|
// Background.Container color. SidebarNav mirrors it onto document.body separately
|
|
// so the glassmorphism sidebar can blur through it.
|
|
const chatBgStyle = useMemo(() => {
|
|
if (chatBackground !== 'none') return getChatBg(chatBackground, isDark, pauseAnimations);
|
|
if (lotusTerminal) return getChatBg('tactical', isDark, pauseAnimations);
|
|
return {};
|
|
}, [chatBackground, lotusTerminal, isDark, pauseAnimations]);
|
|
|
|
return (
|
|
<Page ref={roomViewRef} style={chatBgStyle}>
|
|
<Box grow="Yes" direction="Column">
|
|
<RoomTimeline
|
|
key={roomId}
|
|
room={room}
|
|
eventId={eventId}
|
|
roomInputRef={roomInputRef}
|
|
editor={editor}
|
|
/>
|
|
<RoomViewTyping room={room} />
|
|
</Box>
|
|
<Box shrink="No" direction="Column">
|
|
<div style={{ padding: `0 ${config.space.S400}` }}>
|
|
{tombstoneEvent ? (
|
|
<RoomTombstone
|
|
roomId={roomId}
|
|
body={tombstoneEvent.getContent().body}
|
|
replacementRoomId={tombstoneEvent.getContent().replacement_room}
|
|
/>
|
|
) : (
|
|
<>
|
|
{canMessage && (
|
|
<RoomInput
|
|
room={room}
|
|
editor={editor}
|
|
roomId={roomId}
|
|
fileDropContainerRef={roomViewRef}
|
|
ref={roomInputRef}
|
|
/>
|
|
)}
|
|
{!canMessage && (
|
|
<RoomInputPlaceholder
|
|
style={{ padding: config.space.S200 }}
|
|
alignItems="Center"
|
|
justifyContent="Center"
|
|
>
|
|
<Text align="Center">You do not have permission to post in this room</Text>
|
|
</RoomInputPlaceholder>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
{hideActivity ? <RoomViewFollowingPlaceholder /> : <RoomViewFollowing room={room} />}
|
|
</Box>
|
|
</Page>
|
|
);
|
|
}
|