feat: reaction TDS styling, debounce read receipts, Escape to skip boot, type fixes
- lotus-terminal.css.ts: add reaction chip styles for dark + light TDS modes (cyan border/bg for unselected, orange accent for own/pressed reactions) - useRoomReadPositions: debounce receipt handler at 150ms (M-3) - lotus-boot.ts: Escape key skips boot animation (I-3) - RoomInput.tsx: replace (uploadRes as any) with typed assertion (M-7) - CallEmbedProvider: call mention detection, audio cleanup, display name (C-1, C-2, M-5) - EventReaders: timestamps in seen-by modal, filter self, TDS styling - ReadReceiptAvatars: StackedAvatar pill, TDS visual treatment - chatBackground: add waves/neon/aurora backgrounds - RoomView: auto-apply tactical bg when TDS active and bg is none - settings: extend ChatBackground union type
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
|
||||
import { getMemberDisplayName } from '../../utils/room';
|
||||
import { getMxIdLocalPart } from '../../utils/matrix';
|
||||
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
|
||||
import * as css from './EventReaders.css';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { UserAvatar } from '../user-avatar';
|
||||
@@ -24,6 +24,19 @@ import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||
import { useOpenUserRoomProfile } from '../../state/hooks/userRoomProfile';
|
||||
import { useSpaceOptionally } from '../../hooks/useSpace';
|
||||
import { getMouseEventCords } from '../../utils/dom';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
import { today, yesterday, timeHourMinute, timeMon, timeDay, timeYear } from '../../utils/time';
|
||||
|
||||
function formatReadTs(ts: number, hour24Clock: boolean): string {
|
||||
const timeStr = timeHourMinute(ts, hour24Clock);
|
||||
if (today(ts)) return `Today at ${timeStr}`;
|
||||
if (yesterday(ts)) return `Yesterday at ${timeStr}`;
|
||||
const sameYear = timeYear(ts) === timeYear(Date.now());
|
||||
return sameYear
|
||||
? `${timeMon(ts)} ${timeDay(ts)} at ${timeStr}`
|
||||
: `${timeMon(ts)} ${timeDay(ts)} ${timeYear(ts)} at ${timeStr}`;
|
||||
}
|
||||
|
||||
export type EventReadersProps = {
|
||||
room: Room;
|
||||
@@ -34,9 +47,12 @@ export const EventReaders = as<'div', EventReadersProps>(
|
||||
({ className, room, eventId, requestClose, ...props }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const latestEventReaders = useRoomEventReaders(room, eventId);
|
||||
const myUserId = mx.getUserId();
|
||||
const latestEventReaders = useRoomEventReaders(room, eventId).filter((id) => id !== myUserId);
|
||||
const openProfile = useOpenUserRoomProfile();
|
||||
const space = useSpaceOptionally();
|
||||
const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
|
||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||
|
||||
const getName = (userId: string) =>
|
||||
getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId;
|
||||
@@ -48,9 +64,14 @@ export const EventReaders = as<'div', EventReadersProps>(
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<Header className={css.Header} variant="Surface" size="600">
|
||||
<Header
|
||||
className={css.Header}
|
||||
variant="Surface"
|
||||
size="600"
|
||||
style={lotusTerminal ? { borderBottom: '1px solid rgba(0,212,255,0.30)', boxShadow: '0 2px 12px rgba(0,212,255,0.08)' } : undefined}
|
||||
>
|
||||
<Box grow="Yes">
|
||||
<Text size="H3">Seen by</Text>
|
||||
<Text size="H3" style={lotusTerminal ? { color: '#00D4FF', textShadow: '0 0 6px rgba(0,212,255,0.45)', letterSpacing: '0.05em' } : undefined}>Seen by</Text>
|
||||
</Box>
|
||||
<IconButton size="300" onClick={requestClose}>
|
||||
<Icon src={Icons.Cross} />
|
||||
@@ -63,16 +84,9 @@ export const EventReaders = as<'div', EventReadersProps>(
|
||||
const name = getName(readerId);
|
||||
const avatarMxcUrl = room.getMember(readerId)?.getMxcAvatarUrl();
|
||||
const avatarUrl = avatarMxcUrl
|
||||
? mx.mxcUrlToHttp(
|
||||
avatarMxcUrl,
|
||||
100,
|
||||
100,
|
||||
'crop',
|
||||
undefined,
|
||||
false,
|
||||
useAuthentication
|
||||
)
|
||||
? mxcUrlToHttp(mx, avatarMxcUrl, useAuthentication, 100, 100, 'crop') ?? undefined
|
||||
: undefined;
|
||||
const receiptTs = room.getReadReceiptForUserId(readerId)?.data.ts;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
@@ -92,16 +106,28 @@ export const EventReaders = as<'div', EventReadersProps>(
|
||||
<Avatar size="200">
|
||||
<UserAvatar
|
||||
userId={readerId}
|
||||
src={avatarUrl ?? undefined}
|
||||
src={avatarUrl}
|
||||
alt={name}
|
||||
renderFallback={() => <Icon size="50" src={Icons.User} filled />}
|
||||
/>
|
||||
</Avatar>
|
||||
}
|
||||
>
|
||||
<Text size="T400" truncate>
|
||||
{name}
|
||||
</Text>
|
||||
<Box direction="Column" grow="Yes">
|
||||
<Text size="T400" truncate>
|
||||
{name}
|
||||
</Text>
|
||||
{receiptTs !== undefined && (
|
||||
<Text
|
||||
size="T200"
|
||||
style={lotusTerminal
|
||||
? { color: '#FFB300', textShadow: '0 0 6px #FFB300, 0 0 14px rgba(255,179,0,0.40)', fontSize: '0.72rem' }
|
||||
: { opacity: 0.6 }}
|
||||
>
|
||||
{formatReadTs(receiptTs, hour24Clock)}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user