176 lines
5.0 KiB
TypeScript
176 lines
5.0 KiB
TypeScript
import { Divider } from "@mui/material";
|
|
import React from "react";
|
|
import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent";
|
|
import {
|
|
MatrixApiProfile,
|
|
type UsersMap,
|
|
} from "../../api/matrix/MatrixApiProfile";
|
|
import { MatrixApiRoom, type Room } from "../../api/matrix/MatrixApiRoom";
|
|
import { MatrixSyncApi } from "../../api/MatrixSyncApi";
|
|
import type { WsMessage } from "../../api/WsApi";
|
|
import { RoomEventsManager } from "../../utils/RoomEventsManager";
|
|
import { AsyncWidget } from "../AsyncWidget";
|
|
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
|
import { AppIconModifier } from "./AppIconModifier";
|
|
import { MatrixWS } from "./MatrixWS";
|
|
import { RoomSelector } from "./RoomSelector";
|
|
import { RoomWidget } from "./RoomWidget";
|
|
import { SpaceSelector } from "./SpaceSelector";
|
|
|
|
export function MainMessageWidget(): React.ReactElement {
|
|
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
|
const [users, setUsers] = React.useState<UsersMap | undefined>();
|
|
|
|
const loadRoomsList = async () => {
|
|
await MatrixSyncApi.Start();
|
|
|
|
const rooms = await MatrixApiRoom.ListJoined();
|
|
setRooms(rooms);
|
|
|
|
// Get the list of users in rooms
|
|
const users = rooms.reduce((prev, r) => {
|
|
r.members.forEach((m) => prev.add(m));
|
|
return prev;
|
|
}, new Set<string>());
|
|
|
|
setUsers(await MatrixApiProfile.GetMultiple([...users]));
|
|
};
|
|
|
|
return (
|
|
<AsyncWidget
|
|
loadKey={1}
|
|
load={loadRoomsList}
|
|
ready={!!rooms && !!users}
|
|
errMsg="Failed to initialize messaging component!"
|
|
build={() => (
|
|
<_MainMessageWidget
|
|
rooms={rooms!}
|
|
users={users!}
|
|
onRoomsListUpdate={(cb) => setRooms((r) => cb(r!))}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function _MainMessageWidget(p: {
|
|
rooms: Room[];
|
|
users: UsersMap;
|
|
onRoomsListUpdate: (cb: (a: Room[]) => Room[]) => void;
|
|
}): React.ReactElement {
|
|
const user = useUserInfo();
|
|
|
|
const [space, setSpace] = React.useState<string | undefined>();
|
|
const [currentRoom, setCurrentRoom] = React.useState<Room | undefined>();
|
|
|
|
const spaceRooms = React.useMemo(() => {
|
|
return p.rooms
|
|
.filter((r) => !r.is_space && (!space || r.parents.includes(space)))
|
|
.sort(
|
|
(a, b) => (b.latest_event?.time ?? 0) - (a.latest_event?.time ?? 0)
|
|
);
|
|
}, [space, p.rooms]);
|
|
|
|
const unreadRooms = React.useMemo(
|
|
() => p.rooms.filter((r) => r.number_unread_messages > 0).length,
|
|
[p.rooms]
|
|
);
|
|
|
|
const [_refreshCount, setRefreshCount] = React.useState(0);
|
|
const [roomMgr, setRoomMgr] = React.useState<undefined | RoomEventsManager>();
|
|
|
|
const loadRoom = async () => {
|
|
setRoomMgr(undefined);
|
|
if (!currentRoom) {
|
|
console.warn("Cannot load manager for no room!");
|
|
return;
|
|
}
|
|
const messages = await MatrixApiEvent.GetRoomEvents(currentRoom);
|
|
const mgr = new RoomEventsManager(currentRoom!, messages);
|
|
setRoomMgr(mgr);
|
|
};
|
|
|
|
const [wsState, setWsState] = React.useState("");
|
|
const handleWsEvent = (m: WsMessage) => {
|
|
// Process messages for current room
|
|
if (roomMgr?.processWsMessage(m)) {
|
|
console.info("Current room updated!");
|
|
setRefreshCount((c) => c + 1);
|
|
}
|
|
|
|
// Add a new unread message on left sidebar
|
|
if (
|
|
m.type === "RoomMessageEvent" &&
|
|
!m.data["m.new_content"] &&
|
|
m.sender !== user.info.matrix_user_id
|
|
) {
|
|
p.onRoomsListUpdate((r) => {
|
|
const n = [...r];
|
|
const idx = r.findIndex((el) => el.id === m.room_id);
|
|
if (idx)
|
|
n[idx] = {
|
|
...n[idx],
|
|
number_unread_messages: n[idx].number_unread_messages + 1,
|
|
};
|
|
return n;
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div style={{ display: "flex", height: "100%" }}>
|
|
{/* Websocket */}
|
|
<div style={{ position: "absolute", right: "0px", padding: "10px" }}>
|
|
<MatrixWS onMessage={handleWsEvent} onStateChange={setWsState} />
|
|
</div>
|
|
|
|
{/** Application icon modifier */}
|
|
<AppIconModifier numberUnread={unreadRooms} state={wsState} />
|
|
|
|
{/* Space selector */}
|
|
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
|
|
|
{/* Separator */}
|
|
<Divider orientation="vertical" />
|
|
|
|
{/* Room selector */}
|
|
<RoomSelector
|
|
{...p}
|
|
rooms={spaceRooms}
|
|
currRoom={currentRoom}
|
|
onChange={setCurrentRoom}
|
|
/>
|
|
|
|
{/* Separator */}
|
|
<Divider orientation="vertical" />
|
|
|
|
{/* If no room is selected */}
|
|
{currentRoom === undefined && (
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
flex: 1,
|
|
}}
|
|
>
|
|
No room selected.
|
|
</div>
|
|
)}
|
|
|
|
{/* In case of room */}
|
|
{currentRoom && (
|
|
<AsyncWidget
|
|
loadKey={currentRoom.id}
|
|
ready={!!roomMgr}
|
|
load={loadRoom}
|
|
errMsg="Failed to load room!"
|
|
build={() => (
|
|
<RoomWidget {...p} manager={roomMgr!} room={currentRoom} />
|
|
)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|