Files
cinny/src/app/components/event-readers/EventReaders.tsx
T

141 lines
5.3 KiB
TypeScript
Raw Normal View History

2023-10-06 13:44:06 +11:00
import React from 'react';
import classNames from 'classnames';
import {
Avatar,
Box,
Header,
Icon,
IconButton,
Icons,
MenuItem,
Scroll,
Text,
as,
config,
} from 'folds';
2023-10-10 17:07:15 +11:00
import { Room } from 'matrix-js-sdk';
2023-10-06 13:44:06 +11:00
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
import { getMemberDisplayName } from '../../utils/room';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
2023-10-06 13:44:06 +11:00
import * as css from './EventReaders.css';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { UserAvatar } from '../user-avatar';
2024-09-09 18:45:20 +10:00
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
2025-08-09 17:46:10 +05:30
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}`;
}
2023-10-06 13:44:06 +11:00
export type EventReadersProps = {
room: Room;
eventId: string;
requestClose: () => void;
};
export const EventReaders = as<'div', EventReadersProps>(
({ className, room, eventId, requestClose, ...props }, ref) => {
const mx = useMatrixClient();
2024-09-09 18:45:20 +10:00
const useAuthentication = useMediaAuthentication();
const myUserId = mx.getUserId();
const latestEventReaders = useRoomEventReaders(room, eventId).filter((id) => id !== myUserId);
2025-08-09 17:46:10 +05:30
const openProfile = useOpenUserRoomProfile();
const space = useSpaceOptionally();
const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
2023-10-06 13:44:06 +11:00
2023-10-10 17:07:15 +11:00
const getName = (userId: string) =>
getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId;
2023-10-06 13:44:06 +11:00
return (
<Box
className={classNames(css.EventReaders, className)}
direction="Column"
{...props}
ref={ref}
>
<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}
>
2023-10-06 13:44:06 +11:00
<Box grow="Yes">
<Text size="H3" style={lotusTerminal ? { color: '#00D4FF', textShadow: '0 0 6px rgba(0,212,255,0.45)', letterSpacing: '0.05em' } : undefined}>Seen by</Text>
2023-10-06 13:44:06 +11:00
</Box>
<IconButton size="300" onClick={requestClose} aria-label="Close">
2023-10-06 13:44:06 +11:00
<Icon src={Icons.Cross} />
</IconButton>
</Header>
<Box grow="Yes">
<Scroll visibility="Hover" hideTrack size="300">
<Box className={css.Content} direction="Column">
2023-10-10 17:07:15 +11:00
{latestEventReaders.map((readerId) => {
const name = getName(readerId);
2025-08-09 17:46:10 +05:30
const avatarMxcUrl = room.getMember(readerId)?.getMxcAvatarUrl();
const avatarUrl = avatarMxcUrl
? mxcUrlToHttp(mx, avatarMxcUrl, useAuthentication, 100, 100, 'crop') ?? undefined
2025-08-09 17:46:10 +05:30
: undefined;
const receiptTs = room.getReadReceiptForUserId(readerId)?.data.ts;
2023-10-06 13:44:06 +11:00
return (
<MenuItem
2023-10-10 17:07:15 +11:00
key={readerId}
2023-10-06 13:44:06 +11:00
style={{ padding: `0 ${config.space.S200}` }}
radii="400"
2025-08-09 17:46:10 +05:30
onClick={(event) => {
openProfile(
room.roomId,
space?.roomId,
readerId,
getMouseEventCords(event.nativeEvent),
2025-08-09 17:46:10 +05:30
'Bottom'
);
2023-10-06 13:44:06 +11:00
}}
before={
<Avatar size="200">
<UserAvatar
userId={readerId}
src={avatarUrl}
alt={name}
renderFallback={() => <Icon size="50" src={Icons.User} filled />}
/>
2023-10-06 13:44:06 +11:00
</Avatar>
}
>
<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>
2023-10-06 13:44:06 +11:00
</MenuItem>
);
})}
</Box>
</Scroll>
</Box>
</Box>
);
}
);