web/types: split in-memory and wire event types

This commit is contained in:
Tulir Asokan 2024-10-11 22:37:51 +03:00
parent 7afa2d48c4
commit 15b7380b29
7 changed files with 52 additions and 37 deletions

View file

@ -17,13 +17,13 @@ import { CachedEventDispatcher, EventDispatcher } from "../util/eventdispatcher.
import { CancellablePromise } from "../util/promise.ts" import { CancellablePromise } from "../util/promise.ts"
import type { import type {
ClientWellKnown, ClientWellKnown,
DBEvent,
EventID, EventID,
EventRowID, EventRowID,
EventType, EventType,
PaginationResponse, PaginationResponse,
RPCCommand, RPCCommand,
RPCEvent, RPCEvent,
RawDBEvent,
RoomID, RoomID,
TimelineRowID, TimelineRowID,
UserID, UserID,
@ -105,7 +105,7 @@ export default abstract class RPCClient {
}, this.cancelRequest.bind(this, request_id)) }, this.cancelRequest.bind(this, request_id))
} }
sendMessage(room_id: RoomID, type: EventType, content: Record<string, unknown>): Promise<DBEvent> { sendMessage(room_id: RoomID, type: EventType, content: Record<string, unknown>): Promise<RawDBEvent> {
return this.request("send_message", { room_id, type, content }) return this.request("send_message", { room_id, type, content })
} }
@ -113,15 +113,15 @@ export default abstract class RPCClient {
return this.request("ensure_group_session_shared", { room_id }) return this.request("ensure_group_session_shared", { room_id })
} }
getRoomState(room_id: RoomID, fetch_members = false, refetch = false): Promise<DBEvent[]> { getRoomState(room_id: RoomID, fetch_members = false, refetch = false): Promise<RawDBEvent[]> {
return this.request("get_room_state", { room_id, fetch_members, refetch }) return this.request("get_room_state", { room_id, fetch_members, refetch })
} }
getEvent(room_id: RoomID, event_id: EventID): Promise<DBEvent> { getEvent(room_id: RoomID, event_id: EventID): Promise<RawDBEvent> {
return this.request("get_event", { room_id, event_id }) return this.request("get_event", { room_id, event_id })
} }
getEventsByRowIDs(row_ids: EventRowID[]): Promise<DBEvent[]> { getEventsByRowIDs(row_ids: EventRowID[]): Promise<RawDBEvent[]> {
return this.request("get_events_by_row_ids", { row_ids }) return this.request("get_events_by_row_ids", { row_ids })
} }

View file

@ -16,7 +16,6 @@
import { NonNullCachedEventDispatcher } from "../util/eventdispatcher.ts" import { NonNullCachedEventDispatcher } from "../util/eventdispatcher.ts"
import type { import type {
ContentURI, ContentURI,
DBEvent,
DBRoom, DBRoom,
EncryptedEventContent, EncryptedEventContent,
EventID, EventID,
@ -24,6 +23,8 @@ import type {
EventType, EventType,
EventsDecryptedData, EventsDecryptedData,
LazyLoadSummary, LazyLoadSummary,
MemDBEvent,
RawDBEvent,
RoomID, RoomID,
SyncCompleteData, SyncCompleteData,
SyncRoom, SyncRoom,
@ -67,15 +68,15 @@ export class RoomStateStore {
readonly timeline = new NonNullCachedEventDispatcher<TimelineRowTuple[]>([]) readonly timeline = new NonNullCachedEventDispatcher<TimelineRowTuple[]>([])
state: Map<EventType, Map<string, EventRowID>> = new Map() state: Map<EventType, Map<string, EventRowID>> = new Map()
stateLoaded = false stateLoaded = false
readonly eventsByRowID: Map<EventRowID, DBEvent> = new Map() readonly eventsByRowID: Map<EventRowID, MemDBEvent> = new Map()
readonly eventsByID: Map<EventID, DBEvent> = new Map() readonly eventsByID: Map<EventID, MemDBEvent> = new Map()
constructor(meta: DBRoom) { constructor(meta: DBRoom) {
this.roomID = meta.room_id this.roomID = meta.room_id
this.meta = new NonNullCachedEventDispatcher(meta) this.meta = new NonNullCachedEventDispatcher(meta)
} }
getStateEvent(type: EventType, stateKey: string): DBEvent | undefined { getStateEvent(type: EventType, stateKey: string): MemDBEvent | undefined {
const rowID = this.state.get(type)?.get(stateKey) const rowID = this.state.get(type)?.get(stateKey)
if (!rowID) { if (!rowID) {
return return
@ -83,7 +84,7 @@ export class RoomStateStore {
return this.eventsByRowID.get(rowID) return this.eventsByRowID.get(rowID)
} }
applyPagination(history: DBEvent[]) { applyPagination(history: RawDBEvent[]) {
// Pagination comes in newest to oldest, timeline is in the opposite order // Pagination comes in newest to oldest, timeline is in the opposite order
history.reverse() history.reverse()
const newTimeline = history.map(evt => { const newTimeline = history.map(evt => {
@ -93,14 +94,18 @@ export class RoomStateStore {
this.timeline.emit([...newTimeline, ...this.timeline.current]) this.timeline.emit([...newTimeline, ...this.timeline.current])
} }
applyEvent(evt: DBEvent) { applyEvent(evt: RawDBEvent) {
const memEvt = evt as MemDBEvent
memEvt.mem = true
if (evt.type === "m.room.encrypted" && evt.decrypted && evt.decrypted_type) { if (evt.type === "m.room.encrypted" && evt.decrypted && evt.decrypted_type) {
evt.type = evt.decrypted_type memEvt.type = evt.decrypted_type
evt.encrypted = evt.content as EncryptedEventContent memEvt.encrypted = evt.content as EncryptedEventContent
evt.content = evt.decrypted memEvt.content = evt.decrypted
} }
this.eventsByRowID.set(evt.rowid, evt) delete evt.decrypted
this.eventsByID.set(evt.event_id, evt) delete evt.decrypted_type
this.eventsByRowID.set(memEvt.rowid, memEvt)
this.eventsByID.set(memEvt.event_id, memEvt)
} }
applySync(sync: SyncRoom) { applySync(sync: SyncRoom) {
@ -133,8 +138,7 @@ export class RoomStateStore {
let timelineChanged = false let timelineChanged = false
for (const evt of decrypted.events) { for (const evt of decrypted.events) {
timelineChanged = timelineChanged || !!this.timeline.current.find(rt => rt.event_rowid === evt.rowid) timelineChanged = timelineChanged || !!this.timeline.current.find(rt => rt.event_rowid === evt.rowid)
this.eventsByRowID.set(evt.rowid, evt) this.applyEvent(evt)
this.eventsByID.set(evt.event_id, evt)
} }
if (timelineChanged) { if (timelineChanged) {
this.timeline.emit([...this.timeline.current]) this.timeline.emit([...this.timeline.current])
@ -148,7 +152,7 @@ export class RoomStateStore {
export interface RoomListEntry { export interface RoomListEntry {
room_id: RoomID room_id: RoomID
sorting_timestamp: number sorting_timestamp: number
preview_event?: DBEvent preview_event?: MemDBEvent
name: string name: string
avatar?: ContentURI avatar?: ContentURI
} }

View file

@ -14,9 +14,9 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { import {
DBEvent,
DBRoom, DBRoom,
EventRowID, EventRowID,
RawDBEvent,
TimelineRowTuple, TimelineRowTuple,
} from "./hitypes.ts" } from "./hitypes.ts"
import { import {
@ -42,7 +42,7 @@ export interface TypingEvent extends RPCCommand<TypingEventData> {
} }
export interface SendCompleteData { export interface SendCompleteData {
event: DBEvent event: RawDBEvent
error: string | null error: string | null
} }
@ -53,7 +53,7 @@ export interface SendCompleteEvent extends RPCCommand<SendCompleteData> {
export interface EventsDecryptedData { export interface EventsDecryptedData {
room_id: RoomID room_id: RoomID
preview_event_rowid?: EventRowID preview_event_rowid?: EventRowID
events: DBEvent[] events: RawDBEvent[]
} }
export interface EventsDecryptedEvent extends RPCCommand<EventsDecryptedData> { export interface EventsDecryptedEvent extends RPCCommand<EventsDecryptedData> {
@ -63,7 +63,7 @@ export interface EventsDecryptedEvent extends RPCCommand<EventsDecryptedData> {
export interface SyncRoom { export interface SyncRoom {
meta: DBRoom meta: DBRoom
timeline: TimelineRowTuple[] timeline: TimelineRowTuple[]
events: DBEvent[] events: RawDBEvent[]
state: Record<EventType, Record<string, EventRowID>> state: Record<EventType, Record<string, EventRowID>>
reset: boolean reset: boolean
} }

View file

@ -62,7 +62,7 @@ export interface DBRoom {
prev_batch: string prev_batch: string
} }
export interface DBEvent { export interface BaseDBEvent {
rowid: EventRowID rowid: EventRowID
timeline_rowid: TimelineRowID timeline_rowid: TimelineRowID
@ -73,10 +73,8 @@ export interface DBEvent {
state_key?: string state_key?: string
timestamp: number timestamp: number
content: unknown //eslint-disable-next-line @typescript-eslint/no-explicit-any
decrypted?: unknown content: Record<string, any>
decrypted_type?: EventType
encrypted?: EncryptedEventContent
unsigned: EventUnsigned unsigned: EventUnsigned
transaction_id?: string transaction_id?: string
@ -91,6 +89,17 @@ export interface DBEvent {
last_edit_rowid?: EventRowID last_edit_rowid?: EventRowID
} }
export interface RawDBEvent extends BaseDBEvent {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
decrypted?: Record<string, any>
decrypted_type?: EventType
}
export interface MemDBEvent extends BaseDBEvent {
mem: true
encrypted?: EncryptedEventContent
}
export interface DBAccountData { export interface DBAccountData {
user_id: UserID user_id: UserID
room_id?: RoomID room_id?: RoomID
@ -99,7 +108,7 @@ export interface DBAccountData {
} }
export interface PaginationResponse { export interface PaginationResponse {
events: DBEvent[] events: RawDBEvent[]
has_more: boolean has_more: boolean
} }

View file

@ -15,19 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { getMediaURL } from "../../api/media.ts" import { getMediaURL } from "../../api/media.ts"
import type { RoomListEntry } from "../../api/statestore.ts" import type { RoomListEntry } from "../../api/statestore.ts"
import type { DBEvent } from "../../api/types/hitypes.ts" import type { MemDBEvent } from "../../api/types"
export interface RoomListEntryProps { export interface RoomListEntryProps {
room: RoomListEntry room: RoomListEntry
setActiveRoom: (evt: React.MouseEvent) => void setActiveRoom: (evt: React.MouseEvent) => void
} }
function makePreviewText(evt?: DBEvent): string { function makePreviewText(evt?: MemDBEvent): string {
if (!evt) { if (!evt) {
return "" return ""
} }
if (evt.type === "m.room.message" || evt.type === "m.sticker") { if ((evt.type === "m.room.message" || evt.type === "m.sticker") && typeof evt.content.body === "string") {
// @ts-expect-error TODO add content types
return evt.content.body return evt.content.body
} }
return "" return ""

View file

@ -16,7 +16,7 @@
import React from "react" import React from "react"
import { getMediaURL } from "../../api/media.ts" import { getMediaURL } from "../../api/media.ts"
import { RoomStateStore } from "../../api/statestore.ts" import { RoomStateStore } from "../../api/statestore.ts"
import { DBEvent, MemberEventContent } from "../../api/types" import { MemDBEvent, MemberEventContent } from "../../api/types"
import HiddenEvent from "./content/HiddenEvent.tsx" import HiddenEvent from "./content/HiddenEvent.tsx"
import MessageBody from "./content/MessageBody.tsx" import MessageBody from "./content/MessageBody.tsx"
import { EventContentProps } from "./content/props.ts" import { EventContentProps } from "./content/props.ts"
@ -27,7 +27,10 @@ export interface TimelineEventProps {
eventRowID: number eventRowID: number
} }
function getBodyType(evt: DBEvent): React.FunctionComponent<EventContentProps> { function getBodyType(evt: MemDBEvent): React.FunctionComponent<EventContentProps> {
if (evt.content["m.relates_to"]?.relation_type === "m.replace") {
return HiddenEvent
}
switch (evt.type) { switch (evt.type) {
case "m.room.message": case "m.room.message":
case "m.sticker": case "m.sticker":

View file

@ -14,9 +14,9 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { RoomStateStore } from "../../../api/statestore.ts" import { RoomStateStore } from "../../../api/statestore.ts"
import { DBEvent } from "../../../api/types/hitypes.ts" import { MemDBEvent } from "../../../api/types"
export interface EventContentProps { export interface EventContentProps {
room: RoomStateStore room: RoomStateStore
event: DBEvent event: MemDBEvent
} }