Commit f7069be9f61e
tarball9 files changed+5-283
@@ -77,32 +77,6 @@ pub enum Request {
7777
#[serde(default)]7878
room_id: String,7979
},80
- EventContext {81
- #[serde(default)]82
- room_id: String,83
- #[serde(default)]84
- event_id: String,85
- #[serde(default)]86
- before_limit: u32,87
- #[serde(default)]88
- after_limit: u32,89
- },90
- EventContextBefore {91
- #[serde(default)]92
- room_id: String,93
- #[serde(default)]94
- token: String,95
- #[serde(default)]96
- limit: u32,97
- },98
- EventContextAfter {99
- #[serde(default)]100
- room_id: String,101
- #[serde(default)]102
- token: String,103
- #[serde(default)]104
- limit: u32,105
- },10680
Join {10781
#[serde(default)]10882
room: String,@@ -507,41 +481,6 @@ pub async fn dispatch(
507481
handle.paginate(TimelineId::Room(room_id), 30).await?;508482
Ok(Reply::Done)509483
}510
- Request::EventContext {511
- room_id,512
- event_id,513
- before_limit,514
- after_limit,515
- } => {516
- let room_id = parse_room_id(&room_id)?;517
- let event_id = parse_event_id(&event_id)?;518
- let context = handle519
- .event_context(&room_id, &event_id, before_limit, after_limit)520
- .await?;521
- Ok(Reply::Json(json!(context)))522
- }523
- Request::EventContextBefore {524
- room_id,525
- token,526
- limit,527
- } => {528
- let room_id = parse_room_id(&room_id)?;529
- let page = handle530
- .event_context_before(&room_id, &token, limit)531
- .await?;532
- Ok(Reply::Json(json!(page)))533
- }534
- Request::EventContextAfter {535
- room_id,536
- token,537
- limit,538
- } => {539
- let room_id = parse_room_id(&room_id)?;540
- let page = handle541
- .event_context_after(&room_id, &token, limit)542
- .await?;543
- Ok(Reply::Json(json!(page)))544
- }545484
Request::Join { room } => {546485
let room = parse_room_or_alias(&room)?;547486
let room_id = handle.join_room(room, Vec::new()).await?;@@ -56,28 +56,6 @@ struct PaginateBody {
5656
}5757
5858
#[derive(Debug, Deserialize)]59
-struct EventContextBody {60
- #[serde(default)]61
- room_id: String,62
- #[serde(default)]63
- event_id: String,64
- #[serde(default)]65
- before_limit: u32,66
- #[serde(default)]67
- after_limit: u32,68
-}69
-70
-#[derive(Debug, Deserialize)]71
-struct EventContextPageBody {72
- #[serde(default)]73
- room_id: String,74
- #[serde(default)]75
- token: String,76
- #[serde(default)]77
- limit: u32,78
-}79
-80
-#[derive(Debug, Deserialize)]8159
struct JoinBody {8260
#[serde(default)]8361
room: String,@@ -682,76 +660,6 @@ async fn rooms_paginate(
682660
)683661
}684662
685
-async fn rooms_context(686
- State(state): State<AppState>,687
- headers: HeaderMap,688
- body: axum::Json<EventContextBody>,689
-) -> Response {690
- let (user, _) = match auth(&state, &headers).await {691
- Ok(pair) => pair,692
- Err(error) => return error.into_response(),693
- };694
- respond(695
- dispatch(696
- &user.handle,697
- &user.mirror,698
- Request::EventContext {699
- room_id: body.0.room_id,700
- event_id: body.0.event_id,701
- before_limit: body.0.before_limit,702
- after_limit: body.0.after_limit,703
- },704
- )705
- .await,706
- )707
-}708
-709
-async fn rooms_context_before(710
- State(state): State<AppState>,711
- headers: HeaderMap,712
- body: axum::Json<EventContextPageBody>,713
-) -> Response {714
- let (user, _) = match auth(&state, &headers).await {715
- Ok(pair) => pair,716
- Err(error) => return error.into_response(),717
- };718
- respond(719
- dispatch(720
- &user.handle,721
- &user.mirror,722
- Request::EventContextBefore {723
- room_id: body.0.room_id,724
- token: body.0.token,725
- limit: body.0.limit,726
- },727
- )728
- .await,729
- )730
-}731
-732
-async fn rooms_context_after(733
- State(state): State<AppState>,734
- headers: HeaderMap,735
- body: axum::Json<EventContextPageBody>,736
-) -> Response {737
- let (user, _) = match auth(&state, &headers).await {738
- Ok(pair) => pair,739
- Err(error) => return error.into_response(),740
- };741
- respond(742
- dispatch(743
- &user.handle,744
- &user.mirror,745
- Request::EventContextAfter {746
- room_id: body.0.room_id,747
- token: body.0.token,748
- limit: body.0.limit,749
- },750
- )751
- .await,752
- )753
-}754
-755663
async fn rooms_join(756664
State(state): State<AppState>,757665
headers: HeaderMap,@@ -1138,9 +1046,6 @@ pub fn router(state: AppState, web_dir: Option<String>) -> Router {
11381046
.route("/rooms/close", post(rooms_close))11391047
.route("/rooms/send", post(rooms_send))11401048
.route("/rooms/paginate", post(rooms_paginate))1141
- .route("/rooms/context", post(rooms_context))1142
- .route("/rooms/context/before", post(rooms_context_before))1143
- .route("/rooms/context/after", post(rooms_context_after))11441049
.route(11451050
"/rooms/upload",11461051
post(rooms_upload).layer(DefaultBodyLimit::max(21 * 1024 * 1024)),@@ -894,6 +894,7 @@ input, textarea { font: inherit; }
894894
}895895
.app > .rail, .app > .sidebar { visibility: visible !important; }896896
.account .me-names, .account-actions { display: none; }897
+ .account { padding-top: 10px; padding-bottom: 12px; }897898
.pane-resize { display: none; }898899
.app > .sidebar { transition: transform var(--pane-ease); }899900
.app > .sidebar > *, .app > .members > * { min-width: 0; }@@ -1,6 +1,6 @@
11
// Backend-agnostic API surface. Every call forwards to whichever transport22
// this build was compiled for, hosted (fetch + SSE) or on-device (worker).3
-import type { EventContext, EventContextPage, MemberSummary, RoomSummary } from './types';3
+import type { MemberSummary, RoomSummary } from './types';44
import type { Emote } from './emotes';55
import { formatBody } from './markdown';66
import { mentionedUserIds, type Mention } from './mentions';@@ -50,12 +50,6 @@ export const uploadAvatar = (file: File) => transport.uploadAvatar(file);
5050
export const uploadRoomAvatar = (room_id: string, file: File) => transport.uploadRoomAvatar(room_id, file);5151
export const uploadMedia = (file: File) => transport.uploadMedia(file);5252
export const paginateRoom = (room_id: string) => transport.paginateRoom(room_id);53
-export const eventContext = (room_id: string, event_id: string, limit?: number): Promise<EventContext> =>54
- transport.eventContext(room_id, event_id, limit);55
-export const eventContextBefore = (room_id: string, event_id: string, from: string, limit?: number): Promise<EventContextPage> =>56
- transport.eventContextBefore(room_id, event_id, from, limit);57
-export const eventContextAfter = (room_id: string, event_id: string, from: string, limit?: number): Promise<EventContextPage> =>58
- transport.eventContextAfter(room_id, event_id, from, limit);5953
export const setTyping = (room_id: string, typing: boolean) => transport.setTyping(room_id, typing);6054
export const getAccountData = (eventType: string) => transport.getAccountData(eventType);6155
export const getUrlPreview = (room_id: string, url: string) => transport.getUrlPreview(room_id, url);@@ -14,7 +14,6 @@ import {
1414
} from './types';1515
import type {1616
EncryptionState,17
- EventContext,1817
ImagePacks,1918
MemberSummary,2019
RoomSummary,@@ -29,12 +28,6 @@ export const blocked = writable<boolean>(false);
2928
export const rooms = writable<RoomSummary[]>([]);3029
export const timelines = writable<Record<string, TimelineItem[]>>({});3130
export const pinnedTimelines = writable<Record<string, TimelineItem[]>>({});32
-export interface EventContextState {33
- room_id: string;34
- request_id: string;35
- context: EventContext;36
-}37
-export const eventContexts = writable<Record<string, EventContextState>>({});3831
export const connection = writable<string>('connecting');3932
export const selectedRoomId = writable<string | null>(null);4033
export const membersByRoom = writable<Record<string, MemberSummary[]>>({});@@ -64,7 +57,6 @@ export function resetClientState(): void {
6457
rooms.set([]);6558
timelines.set({});6659
pinnedTimelines.set({});67
- eventContexts.set({});6860
connection.set('connecting');6961
selectedRoomId.set(null);7062
membersByRoom.set({});@@ -77,22 +69,6 @@ export function resetClientState(): void {
7769
resetLinkPreviews();7870
}7971
80
-export function setEventContext(state: EventContextState): void {81
- const key = `${state.room_id}:${state.request_id}`;82
- eventContexts.update((all) => ({ ...all, [key]: state }));83
-}84
-85
-export function clearEventContext(roomId: string, requestId?: string): void {86
- eventContexts.update((all) => {87
- const prefix = `${roomId}:`;88
- const next = { ...all };89
- for (const key of Object.keys(next)) {90
- if (key.startsWith(prefix) && (requestId === undefined || key === `${prefix}${requestId}`)) delete next[key];91
- }92
- return next;93
- });94
-}95
-9672
function applyTimelinePayload(roomId: string, payload: Record<string, unknown>): void {9773
if (!Array.isArray(payload['items'])) return;9874
const items = parseTimelineList(payload['items']);@@ -1,5 +1,4 @@
1
-import type { EventContext, EventContextPage, MemberSummary, RoomSummary } from './types';2
-import { parseEventContext, parseEventContextPage } from './types';1
+import type { MemberSummary, RoomSummary } from './types';32
import { ApiError, type CommandRequest, type SendKind, type SessionInfo, type Transport, type UploadExtras, type VerifyAction } from './transport';43
54
async function parseFailure(res: Response, fallback: string): Promise<never> {@@ -104,34 +103,6 @@ export class HttpTransport implements Transport {
104103
paginateRoom(room_id: string): Promise<void> {105104
return this.roomCommand(ROOM_COMMAND_PATHS.paginate, { room_id });106105
}107
- async eventContext(room_id: string, event_id: string, limit = 20): Promise<EventContext> {108
- const res = await fetch('/api/rooms/context', {109
- method: 'POST',110
- credentials: 'same-origin',111
- headers: { 'content-type': 'application/json' },112
- body: JSON.stringify({ room_id, event_id, limit })113
- });114
- const parsed = parseEventContext(await json<unknown>(res, 'Could not load event context'));115
- if (!parsed) throw new ApiError(502, 'Invalid event context response');116
- return parsed;117
- }118
- async eventContextBefore(room_id: string, event_id: string, from: string, limit = 20): Promise<EventContextPage> {119
- return this.eventContextPage('before', room_id, event_id, from, limit);120
- }121
- async eventContextAfter(room_id: string, event_id: string, from: string, limit = 20): Promise<EventContextPage> {122
- return this.eventContextPage('after', room_id, event_id, from, limit);123
- }124
- private async eventContextPage(direction: 'before' | 'after', room_id: string, event_id: string, from: string, limit: number): Promise<EventContextPage> {125
- const res = await fetch(`/api/rooms/context/${direction}`, {126
- method: 'POST',127
- credentials: 'same-origin',128
- headers: { 'content-type': 'application/json' },129
- body: JSON.stringify({ room_id, event_id, from, limit })130
- });131
- const parsed = parseEventContextPage(await json<unknown>(res, 'Could not load event context'));132
- if (!parsed) throw new ApiError(502, 'Invalid event context page response');133
- return parsed;134
- }135106
setTyping(room_id: string, typing: boolean): Promise<void> {136107
return this.roomCommand(ROOM_COMMAND_PATHS.typing, { room_id, typing });137108
}@@ -1,5 +1,4 @@
1
-import { parseEventContext, parseEventContextPage } from './types';2
-import type { EventContext, EventContextPage, MemberSummary, RoomSummary } from './types';1
+import type { MemberSummary, RoomSummary } from './types';32
import { ApiError, type CommandRequest, type SendKind, type SessionInfo, type SoftLogout, type Transport, type UploadExtras, type VerifyAction } from './transport';43
import { statusForKind } from './wire-status';54
@@ -230,25 +229,6 @@ export class WorkerTransport implements Transport {
230229
paginateRoom(room_id: string): Promise<void> {231230
return this.client.call('paginate', { room_id });232231
}233
- eventContext(room_id: string, event_id: string, limit = 20): Promise<EventContext> {234
- return this.contextCall('event_context', { room_id, event_id, limit });235
- }236
- eventContextBefore(room_id: string, event_id: string, from: string, limit = 20): Promise<EventContextPage> {237
- return this.contextPageCall('event_context_before', { room_id, event_id, from, limit });238
- }239
- eventContextAfter(room_id: string, event_id: string, from: string, limit = 20): Promise<EventContextPage> {240
- return this.contextPageCall('event_context_after', { room_id, event_id, from, limit });241
- }242
- private async contextCall(op: string, fields: Record<string, unknown>): Promise<EventContext> {243
- const parsed = parseEventContext(await this.client.call(op, fields));244
- if (!parsed) throw new ApiError(502, 'Invalid event context response');245
- return parsed;246
- }247
- private async contextPageCall(op: string, fields: Record<string, unknown>): Promise<EventContextPage> {248
- const parsed = parseEventContextPage(await this.client.call(op, fields));249
- if (!parsed) throw new ApiError(502, 'Invalid event context page response');250
- return parsed;251
- }252232
setTyping(room_id: string, typing: boolean): Promise<void> {253233
return this.client.call('typing', { room_id, typing });254234
}@@ -1,4 +1,4 @@
1
-import type { EventContext, EventContextPage, MemberSummary, PackUsage, RoomSummary } from './types';1
+import type { MemberSummary, PackUsage, RoomSummary } from './types';22
33
export class ApiError extends Error {44
status: number;@@ -98,9 +98,6 @@ export interface Transport {
9898
kind?: SendKind9999
): Promise<void>;100100
paginateRoom(room_id: string): Promise<void>;101
- eventContext(room_id: string, event_id: string, limit?: number): Promise<EventContext>;102
- eventContextBefore(room_id: string, event_id: string, from: string, limit?: number): Promise<EventContextPage>;103
- eventContextAfter(room_id: string, event_id: string, from: string, limit?: number): Promise<EventContextPage>;104101
setTyping(room_id: string, typing: boolean): Promise<void>;105102
getAccountData(eventType: string): Promise<unknown | null>;106103
getUrlPreview(room_id: string, url: string): Promise<unknown | null>;@@ -654,23 +654,6 @@ export type TimelineItem =
654654
| { kind: 'start' }655655
| { kind: 'unknown'; label: string };656656
657
-export interface EventContext {658
- events_before: TimelineItem[];659
- event: TimelineItem | null;660
- events_after: TimelineItem[];661
- start_token: string | null;662
- end_token: string | null;663
-}664
-665
-export type EventContextDirection = 'Before' | 'After';666
-667
-export interface EventContextPage {668
- direction: EventContextDirection;669
- events: TimelineItem[];670
- next_token: string | null;671
- end_reached: boolean;672
-}673
-674657
/** Normalize one TimelineItem from external-tag serde form. */675658
export function parseTimelineItem(raw: unknown): TimelineItem | null {676659
if (raw === 'ReadMarker') return { kind: 'readMarker' };@@ -697,30 +680,6 @@ export function parseTimelineList(raw: unknown): TimelineItem[] {
697680
return out;698681
}699682
700
-export function parseEventContext(raw: unknown): EventContext | null {701
- if (!isRecord(raw)) return null;702
- const eventRaw = raw['event'];703
- return {704
- events_before: parseTimelineList(raw['events_before']),705
- event: eventRaw == null ? null : parseTimelineItem(eventRaw),706
- events_after: parseTimelineList(raw['events_after']),707
- start_token: optionalString(raw, 'start_token'),708
- end_token: optionalString(raw, 'end_token')709
- };710
-}711
-712
-export function parseEventContextPage(raw: unknown): EventContextPage | null {713
- if (!isRecord(raw)) return null;714
- const direction = raw['direction'];715
- if (direction !== 'Before' && direction !== 'After') return null;716
- return {717
- direction,718
- events: parseTimelineList(raw['events']),719
- next_token: optionalString(raw, 'next_token'),720
- end_reached: raw['end_reached'] === true721
- };722
-}723
-724683
export type PackUsage = 'Emoticon' | 'Sticker';725684
export interface PackImage {726685
shortcode: string;