mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 10:33:41 -05:00
web/types: split in-memory and wire event types
This commit is contained in:
parent
7afa2d48c4
commit
15b7380b29
7 changed files with 52 additions and 37 deletions
|
@ -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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue