Process events list client side
This commit is contained in:
@@ -48,7 +48,7 @@ impl APIEvent {
|
|||||||
pub struct APIEventsList {
|
pub struct APIEventsList {
|
||||||
pub start: String,
|
pub start: String,
|
||||||
pub end: Option<String>,
|
pub end: Option<String>,
|
||||||
pub messages: Vec<APIEvent>,
|
pub events: Vec<APIEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get messages for a given room
|
/// Get messages for a given room
|
||||||
@@ -65,7 +65,7 @@ pub(super) async fn get_events(
|
|||||||
Ok(APIEventsList {
|
Ok(APIEventsList {
|
||||||
start: messages.start,
|
start: messages.start,
|
||||||
end: messages.end,
|
end: messages.end,
|
||||||
messages: stream::iter(messages.chunk)
|
events: stream::iter(messages.chunk)
|
||||||
.then(async |msg| APIEvent::from_evt(msg, room.room_id()).await)
|
.then(async |msg| APIEvent::from_evt(msg, room.room_id()).await)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl APIRoomInfo {
|
|||||||
is_space: r.is_space(),
|
is_space: r.is_space(),
|
||||||
parents: parent_spaces,
|
parents: parent_spaces,
|
||||||
number_unread_messages: r.unread_notification_counts().notification_count,
|
number_unread_messages: r.unread_notification_counts().notification_count,
|
||||||
latest_event: get_events(r, 1, None).await?.messages.into_iter().next(),
|
latest_event: get_events(r, 1, None).await?.events.into_iter().next(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,70 @@
|
|||||||
|
import { APIClient } from "../ApiClient";
|
||||||
|
import type { Room } from "./MatrixApiRoom";
|
||||||
|
|
||||||
|
export interface MatrixRoomMessage {
|
||||||
|
type: "m.room.message";
|
||||||
|
content: {
|
||||||
|
body: string;
|
||||||
|
msgtype: "m.text" | "m.image" | string;
|
||||||
|
"m.relates_to"?: {
|
||||||
|
event_id: string;
|
||||||
|
rel_type: "m.replace" | string;
|
||||||
|
};
|
||||||
|
file?: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MatrixReaction {
|
||||||
|
type: "m.reaction";
|
||||||
|
content: {
|
||||||
|
"m.relates_to": {
|
||||||
|
event_id: string;
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MatrixRoomRedaction {
|
||||||
|
type: "m.room.redaction";
|
||||||
|
redacts: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MatrixEventData =
|
||||||
|
| MatrixRoomMessage
|
||||||
|
| MatrixReaction
|
||||||
|
| MatrixRoomRedaction
|
||||||
|
| { type: "other" };
|
||||||
|
|
||||||
export interface MatrixEvent {
|
export interface MatrixEvent {
|
||||||
id: string;
|
id: string;
|
||||||
time: number;
|
time: number;
|
||||||
sender: string;
|
sender: string;
|
||||||
|
data: MatrixEventData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MatrixEventsList {
|
||||||
|
start: string;
|
||||||
|
end?: string;
|
||||||
|
events: MatrixEvent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MatrixApiEvent {
|
||||||
|
/**
|
||||||
|
* Get Matrix room events
|
||||||
|
*/
|
||||||
|
static async GetRoomEvents(
|
||||||
|
room: Room,
|
||||||
|
from?: string
|
||||||
|
): Promise<MatrixEventsList> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "GET",
|
||||||
|
uri:
|
||||||
|
`/matrix/room/${encodeURIComponent(room.id)}/events` +
|
||||||
|
(from ? `?from=${from}` : ""),
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
101
matrixgw_frontend/src/utils/RoomEventsManager.ts
Normal file
101
matrixgw_frontend/src/utils/RoomEventsManager.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import type {
|
||||||
|
MatrixEvent,
|
||||||
|
MatrixEventsList,
|
||||||
|
} from "../api/matrix/MatrixApiEvent";
|
||||||
|
import type { Room } from "../api/matrix/MatrixApiRoom";
|
||||||
|
|
||||||
|
export interface MessageReaction {
|
||||||
|
event_id: string;
|
||||||
|
account: string;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Message {
|
||||||
|
event_id: string;
|
||||||
|
sent: number;
|
||||||
|
modified: boolean;
|
||||||
|
reactions: MessageReaction[];
|
||||||
|
content: string;
|
||||||
|
image?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RoomEventsManager {
|
||||||
|
readonly room: Room;
|
||||||
|
private events: MatrixEvent[];
|
||||||
|
messages: Message[];
|
||||||
|
endToken?: string;
|
||||||
|
|
||||||
|
constructor(room: Room, initialMessages: MatrixEventsList) {
|
||||||
|
this.room = room;
|
||||||
|
this.events = [];
|
||||||
|
this.messages = [];
|
||||||
|
|
||||||
|
this.processNewEvents(initialMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process events given by the API
|
||||||
|
*/
|
||||||
|
processNewEvents(evts: MatrixEventsList) {
|
||||||
|
this.endToken = evts.end;
|
||||||
|
this.events = [...this.events, ...evts.events];
|
||||||
|
this.rebuildMessagesList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private rebuildMessagesList() {
|
||||||
|
// Sorts events list to process oldest events first
|
||||||
|
this.events.sort((a, b) => a.time - b.time);
|
||||||
|
|
||||||
|
// First, process redactions to skip redacted events
|
||||||
|
let redacted = new Set(
|
||||||
|
this.events
|
||||||
|
.map((e) =>
|
||||||
|
e.data.type === "m.room.redaction" ? e.data.redacts : undefined
|
||||||
|
)
|
||||||
|
.filter((e) => e !== undefined)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const evt of this.events) {
|
||||||
|
if (redacted.has(evt.id)) continue;
|
||||||
|
|
||||||
|
const data = evt.data;
|
||||||
|
|
||||||
|
// Message
|
||||||
|
if (data.type === "m.room.message") {
|
||||||
|
// Check if this message replaces another one
|
||||||
|
if (data.content["m.relates_to"]) {
|
||||||
|
const message = this.messages.find(
|
||||||
|
(m) => m.event_id === data.content["m.relates_to"]?.event_id
|
||||||
|
);
|
||||||
|
if (!message) continue;
|
||||||
|
message.modified = true;
|
||||||
|
message.content = data.content.body;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages.push({
|
||||||
|
event_id: evt.id,
|
||||||
|
modified: false,
|
||||||
|
reactions: [],
|
||||||
|
sent: evt.time,
|
||||||
|
image: data.content.file?.url,
|
||||||
|
content: data.content.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaction
|
||||||
|
if (data.type === "m.reaction") {
|
||||||
|
const message = this.messages.find(
|
||||||
|
(m) => m.event_id === data.content["m.relates_to"].event_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!message) continue;
|
||||||
|
message.reactions.push({
|
||||||
|
account: evt.sender,
|
||||||
|
event_id: evt.id,
|
||||||
|
key: data.content["m.relates_to"].key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,30 @@
|
|||||||
|
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 { RoomEventsManager } from "../../utils/RoomEventsManager";
|
||||||
|
import { AsyncWidget } from "../AsyncWidget";
|
||||||
|
|
||||||
export function RoomWidget(p: {
|
export function RoomWidget(p: {
|
||||||
room: Room;
|
room: Room;
|
||||||
users: UsersMap;
|
users: UsersMap;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
return <>room</>;
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncWidget
|
||||||
|
loadKey={p.room.id}
|
||||||
|
ready={!!roomMgr}
|
||||||
|
load={load}
|
||||||
|
errMsg="Failed to load room!"
|
||||||
|
build={() => <>room</>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user