Files
cinny/src/app/features/room/threads-menu/ThreadsMenu.tsx
T

100 lines
3.6 KiB
TypeScript
Raw Normal View History

2025-09-15 13:16:43 +05:30
/* eslint-disable react/destructuring-assignment */
2025-10-22 16:22:31 +05:30
import React, { forwardRef, useMemo } from 'react';
import { EventTimelineSet, Room } from 'matrix-js-sdk';
import { Box, config, Header, Icon, IconButton, Icons, Menu, Scroll, Text, toRem } from 'folds';
2025-09-15 13:16:43 +05:30
import * as css from './ThreadsMenu.css';
import { ContainerColor } from '../../../styles/ContainerColor.css';
2025-09-24 15:57:15 +05:30
import { useRoomMyThreads } from '../../../hooks/useRoomThreads';
2025-10-22 16:22:31 +05:30
import { AsyncStatus } from '../../../hooks/useAsyncCallback';
import { getLinkedTimelines, getTimelinesEventsCount } from '../utils';
import { ThreadsTimeline } from './ThreadsTimeline';
import { ThreadsLoading } from './ThreadsLoading';
import { ThreadsError } from './ThreadsError';
2025-09-15 13:16:43 +05:30
2025-10-22 16:22:31 +05:30
const getTimelines = (timelineSet: EventTimelineSet) => {
const liveTimeline = timelineSet.getLiveTimeline();
const linkedTimelines = getLinkedTimelines(liveTimeline);
2025-09-15 13:16:43 +05:30
2025-10-22 16:22:31 +05:30
return linkedTimelines;
};
2025-09-15 13:16:43 +05:30
2025-10-22 16:22:31 +05:30
function NoThreads() {
2025-09-15 13:16:43 +05:30
return (
2025-10-22 16:22:31 +05:30
<Box
className={ContainerColor({ variant: 'SurfaceVariant' })}
style={{
marginBottom: config.space.S200,
padding: `${config.space.S700} ${config.space.S400} ${toRem(60)}`,
borderRadius: config.radii.R300,
}}
grow="Yes"
direction="Column"
gap="400"
justifyContent="Center"
alignItems="Center"
2025-09-15 13:16:43 +05:30
>
2025-10-22 16:22:31 +05:30
<Icon src={Icons.Thread} size="600" />
<Box style={{ maxWidth: toRem(300) }} direction="Column" gap="200" alignItems="Center">
<Text size="H4" align="Center">
No Threads Yet
</Text>
<Text size="T400" align="Center">
Threads youre participating in will appear here.
</Text>
2025-09-15 13:16:43 +05:30
</Box>
2025-10-22 16:22:31 +05:30
</Box>
2025-09-15 13:16:43 +05:30
);
}
type ThreadsMenuProps = {
room: Room;
requestClose: () => void;
};
export const ThreadsMenu = forwardRef<HTMLDivElement, ThreadsMenuProps>(
({ room, requestClose }, ref) => {
2025-10-22 16:22:31 +05:30
const threadsState = useRoomMyThreads(room);
const threadsTimelineSet =
threadsState.status === AsyncStatus.Success ? threadsState.data : undefined;
2025-09-24 15:57:15 +05:30
2025-10-22 16:22:31 +05:30
const linkedTimelines = useMemo(() => {
if (!threadsTimelineSet) return undefined;
return getTimelines(threadsTimelineSet);
}, [threadsTimelineSet]);
2025-09-24 15:57:15 +05:30
2025-10-22 16:22:31 +05:30
const hasEvents = linkedTimelines && getTimelinesEventsCount(linkedTimelines) > 0;
2025-09-15 13:16:43 +05:30
return (
<Menu ref={ref} className={css.ThreadsMenu}>
<Box grow="Yes" direction="Column">
<Header className={css.ThreadsMenuHeader} size="500">
<Box grow="Yes">
2025-09-24 15:57:15 +05:30
<Text size="H5">My Threads</Text>
2025-09-15 13:16:43 +05:30
</Box>
<Box shrink="No">
<IconButton size="300" onClick={requestClose} radii="300">
<Icon src={Icons.Cross} size="400" />
</IconButton>
</Box>
</Header>
<Box grow="Yes">
2025-10-22 16:22:31 +05:30
{threadsState.status === AsyncStatus.Success && hasEvents ? (
<ThreadsTimeline timelines={linkedTimelines} requestClose={requestClose} />
) : (
<Scroll size="300" hideTrack visibility="Hover">
<Box className={css.ThreadsMenuContent} direction="Column" gap="100">
{(threadsState.status === AsyncStatus.Loading ||
threadsState.status === AsyncStatus.Idle) && <ThreadsLoading />}
{threadsState.status === AsyncStatus.Success && !hasEvents && <NoThreads />}
{threadsState.status === AsyncStatus.Error && (
<ThreadsError error={threadsState.error} />
)}
</Box>
</Scroll>
)}
2025-09-15 13:16:43 +05:30
</Box>
</Box>
</Menu>
);
}
);