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 you’re 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>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|