Refactor rooms management
This commit is contained in:
@@ -50,7 +50,10 @@ export class RoomEventsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
processWsMessage(m: WsMessage) {
|
processWsMessage(m: WsMessage) {
|
||||||
if (m.room_id !== this.room.id) return false;
|
if (m.room_id !== this.room.id) {
|
||||||
|
console.debug("Not an event for current room.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let data: MatrixEventData;
|
let data: MatrixEventData;
|
||||||
if (m.type === "RoomReactionEvent") {
|
if (m.type === "RoomReactionEvent") {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Divider } from "@mui/material";
|
import { Divider } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent";
|
||||||
import {
|
import {
|
||||||
MatrixApiProfile,
|
MatrixApiProfile,
|
||||||
type UsersMap,
|
type UsersMap,
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
import { MatrixApiRoom, type Room } from "../../api/matrix/MatrixApiRoom";
|
import { MatrixApiRoom, type Room } from "../../api/matrix/MatrixApiRoom";
|
||||||
import { MatrixSyncApi } from "../../api/MatrixSyncApi";
|
import { MatrixSyncApi } from "../../api/MatrixSyncApi";
|
||||||
import type { WsMessage } from "../../api/WsApi";
|
import type { WsMessage } from "../../api/WsApi";
|
||||||
|
import { RoomEventsManager } from "../../utils/RoomEventsManager";
|
||||||
import { AsyncWidget } from "../AsyncWidget";
|
import { AsyncWidget } from "../AsyncWidget";
|
||||||
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
||||||
import { MatrixWS } from "./MatrixWS";
|
import { MatrixWS } from "./MatrixWS";
|
||||||
@@ -17,9 +19,8 @@ import { SpaceSelector } from "./SpaceSelector";
|
|||||||
export function MainMessageWidget(): React.ReactElement {
|
export function MainMessageWidget(): React.ReactElement {
|
||||||
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
||||||
const [users, setUsers] = React.useState<UsersMap | undefined>();
|
const [users, setUsers] = React.useState<UsersMap | undefined>();
|
||||||
const user = useUserInfo();
|
|
||||||
|
|
||||||
const load = async () => {
|
const loadRoomsList = async () => {
|
||||||
await MatrixSyncApi.Start();
|
await MatrixSyncApi.Start();
|
||||||
|
|
||||||
const rooms = await MatrixApiRoom.ListJoined();
|
const rooms = await MatrixApiRoom.ListJoined();
|
||||||
@@ -34,37 +35,17 @@ export function MainMessageWidget(): React.ReactElement {
|
|||||||
setUsers(await MatrixApiProfile.GetMultiple([...users]));
|
setUsers(await MatrixApiProfile.GetMultiple([...users]));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEvent = (m: WsMessage) => {
|
|
||||||
// Add a new unread message
|
|
||||||
if (
|
|
||||||
m.type === "RoomMessageEvent" &&
|
|
||||||
!m.data["m.new_content"] &&
|
|
||||||
m.sender !== user.info.matrix_user_id
|
|
||||||
) {
|
|
||||||
setRooms((r) => {
|
|
||||||
const n = r ? [...r] : undefined;
|
|
||||||
const idx = n?.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 (
|
return (
|
||||||
<AsyncWidget
|
<AsyncWidget
|
||||||
loadKey={1}
|
loadKey={1}
|
||||||
load={load}
|
load={loadRoomsList}
|
||||||
ready={!!rooms && !!users}
|
ready={!!rooms && !!users}
|
||||||
errMsg="Failed to initialize messaging component!"
|
errMsg="Failed to initialize messaging component!"
|
||||||
build={() => (
|
build={() => (
|
||||||
<_MainMessageWidget
|
<_MainMessageWidget
|
||||||
rooms={rooms!}
|
rooms={rooms!}
|
||||||
users={users!}
|
users={users!}
|
||||||
onEvent={handleEvent}
|
onRoomsListUpdate={(cb) => setRooms((r) => cb(r!))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -74,10 +55,12 @@ export function MainMessageWidget(): React.ReactElement {
|
|||||||
function _MainMessageWidget(p: {
|
function _MainMessageWidget(p: {
|
||||||
rooms: Room[];
|
rooms: Room[];
|
||||||
users: UsersMap;
|
users: UsersMap;
|
||||||
onEvent: (m: WsMessage) => void;
|
onRoomsListUpdate: (cb: (a: Room[]) => Room[]) => void;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
|
const user = useUserInfo();
|
||||||
|
|
||||||
const [space, setSpace] = React.useState<string | undefined>();
|
const [space, setSpace] = React.useState<string | undefined>();
|
||||||
const [room, setRoom] = React.useState<Room | undefined>();
|
const [currentRoom, setCurrentRoom] = React.useState<Room | undefined>();
|
||||||
|
|
||||||
const spaceRooms = React.useMemo(() => {
|
const spaceRooms = React.useMemo(() => {
|
||||||
return p.rooms
|
return p.rooms
|
||||||
@@ -87,33 +70,96 @@ function _MainMessageWidget(p: {
|
|||||||
);
|
);
|
||||||
}, [space, p.rooms]);
|
}, [space, 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 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 (
|
return (
|
||||||
<div style={{ display: "flex", height: "100%" }}>
|
<div style={{ display: "flex", height: "100%" }}>
|
||||||
|
{/* Websocket */}
|
||||||
|
<div style={{ position: "absolute", right: "0px", padding: "10px" }}>
|
||||||
|
<MatrixWS onMessage={handleWsEvent} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Space selector */}
|
||||||
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
|
|
||||||
|
{/* Room selector */}
|
||||||
<RoomSelector
|
<RoomSelector
|
||||||
{...p}
|
{...p}
|
||||||
rooms={spaceRooms}
|
rooms={spaceRooms}
|
||||||
currRoom={room}
|
currRoom={currentRoom}
|
||||||
onChange={setRoom}
|
onChange={setCurrentRoom}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
{room === undefined && (
|
|
||||||
<>
|
{/* If no room is selected */}
|
||||||
<MatrixWS onMessage={p.onEvent} />
|
{currentRoom === undefined && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
No room selected.
|
No room selected.
|
||||||
</div>
|
</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} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{room && <RoomWidget {...p} room={room} />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ export function MatrixWS(p: {
|
|||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
const snackbar = useSnackbar();
|
const snackbar = useSnackbar();
|
||||||
|
|
||||||
|
// Keep only the latest version of onMessage
|
||||||
|
const cbRef = React.useRef(p.onMessage);
|
||||||
|
React.useEffect(() => {
|
||||||
|
cbRef.current = p.onMessage;
|
||||||
|
}, [p.onMessage]);
|
||||||
|
|
||||||
const [state, setState] = React.useState<string>(State.Closed);
|
const [state, setState] = React.useState<string>(State.Closed);
|
||||||
const wsId = React.useRef<number | undefined>(undefined);
|
const wsId = React.useRef<number | undefined>(undefined);
|
||||||
const [connCount, setConnCount] = React.useState(0);
|
const [connCount, setConnCount] = React.useState(0);
|
||||||
@@ -46,7 +52,7 @@ export function MatrixWS(p: {
|
|||||||
|
|
||||||
const dec = JSON.parse(msg.data);
|
const dec = JSON.parse(msg.data);
|
||||||
console.info("WS message", dec);
|
console.info("WS message", dec);
|
||||||
p.onMessage(dec);
|
cbRef.current(dec);
|
||||||
};
|
};
|
||||||
|
|
||||||
return () => ws.close();
|
return () => ws.close();
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { AccountIcon } from "./AccountIcon";
|
|||||||
export function RoomMessagesList(p: {
|
export function RoomMessagesList(p: {
|
||||||
room: Room;
|
room: Room;
|
||||||
users: UsersMap;
|
users: UsersMap;
|
||||||
mgr: RoomEventsManager;
|
manager: RoomEventsManager;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
const messagesEndRef = React.createRef<HTMLDivElement>();
|
const messagesEndRef = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ export function RoomMessagesList(p: {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (messagesEndRef)
|
if (messagesEndRef)
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
|
messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
|
||||||
}, [p.mgr.messages.length]);
|
}, [p.manager.messages.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -58,20 +58,20 @@ export function RoomMessagesList(p: {
|
|||||||
paddingLeft: "20px",
|
paddingLeft: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{p.mgr.messages.map((m, idx) => (
|
{p.manager.messages.map((m, idx) => (
|
||||||
<RoomMessage
|
<RoomMessage
|
||||||
key={m.event_id}
|
key={m.event_id}
|
||||||
{...p}
|
{...p}
|
||||||
message={m}
|
message={m}
|
||||||
previousFromSamePerson={
|
previousFromSamePerson={
|
||||||
idx > 0 &&
|
idx > 0 &&
|
||||||
p.mgr.messages[idx - 1].account === m.account &&
|
p.manager.messages[idx - 1].account === m.account &&
|
||||||
m.time_sent - p.mgr.messages[idx - 1].time_sent < 60 * 3 * 1000
|
m.time_sent - p.manager.messages[idx - 1].time_sent < 60 * 3 * 1000
|
||||||
}
|
}
|
||||||
firstMessageOfDay={
|
firstMessageOfDay={
|
||||||
idx === 0 ||
|
idx === 0 ||
|
||||||
m.time_sent_dayjs.startOf("day").unix() !=
|
m.time_sent_dayjs.startOf("day").unix() !=
|
||||||
p.mgr.messages[idx - 1].time_sent_dayjs.startOf("day").unix()
|
p.manager.messages[idx - 1].time_sent_dayjs.startOf("day").unix()
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,49 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent";
|
|
||||||
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
|
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
|
||||||
import type { Room } from "../../api/matrix/MatrixApiRoom";
|
import type { Room } from "../../api/matrix/MatrixApiRoom";
|
||||||
import type { WsMessage } from "../../api/WsApi";
|
|
||||||
import { RoomEventsManager } from "../../utils/RoomEventsManager";
|
import { RoomEventsManager } from "../../utils/RoomEventsManager";
|
||||||
import { AsyncWidget } from "../AsyncWidget";
|
|
||||||
import { MatrixWS } from "./MatrixWS";
|
|
||||||
import { RoomMessagesList } from "./RoomMessagesList";
|
import { RoomMessagesList } from "./RoomMessagesList";
|
||||||
import { SendMessageForm } from "./SendMessageForm";
|
import { SendMessageForm } from "./SendMessageForm";
|
||||||
|
|
||||||
export function RoomWidget(p: {
|
export function RoomWidget(p: {
|
||||||
room: Room;
|
room: Room;
|
||||||
users: UsersMap;
|
users: UsersMap;
|
||||||
onEvent: (m: WsMessage) => void;
|
manager: RoomEventsManager;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
const [_count, setCount] = React.useState(0);
|
|
||||||
const [roomMgr, setRoomMgr] = React.useState<undefined | RoomEventsManager>();
|
|
||||||
|
|
||||||
const load = async () => {
|
|
||||||
setRoomMgr(undefined);
|
|
||||||
const messages = await MatrixApiEvent.GetRoomEvents(p.room);
|
|
||||||
const mgr = new RoomEventsManager(p.room, messages);
|
|
||||||
setRoomMgr(mgr);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNewMessage = (m: WsMessage) => {
|
|
||||||
if (roomMgr?.processWsMessage(m)) setCount((c) => c + 1);
|
|
||||||
p.onEvent(m);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncWidget
|
<div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
|
||||||
loadKey={p.room.id}
|
<RoomMessagesList {...p} />
|
||||||
ready={!!roomMgr}
|
<SendMessageForm {...p} />
|
||||||
load={load}
|
</div>
|
||||||
errMsg="Failed to load room!"
|
|
||||||
build={() => (
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
|
|
||||||
<div style={{ position: "absolute", right: "0px", padding: "10px" }}>
|
|
||||||
<MatrixWS onMessage={handleNewMessage} />
|
|
||||||
</div>
|
|
||||||
<RoomMessagesList mgr={roomMgr!} {...p} />
|
|
||||||
<SendMessageForm {...p} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user