@kispi/chat 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +68 -4
- package/README.md +65 -4
- package/dist/index.cjs +239 -17
- package/dist/index.d.cts +165 -4
- package/dist/index.d.ts +165 -4
- package/dist/index.js +239 -17
- package/dist/server/index.cjs +2 -0
- package/dist/server/index.d.cts +39 -1
- package/dist/server/index.d.ts +39 -1
- package/dist/server/index.js +2 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -142,6 +142,21 @@ type Message = {
|
|
|
142
142
|
emoji: string;
|
|
143
143
|
count: number;
|
|
144
144
|
}[];
|
|
145
|
+
/**
|
|
146
|
+
* The emoji this client's user has on the message.
|
|
147
|
+
*
|
|
148
|
+
* Filled by history reads and kept in step by the SDK afterwards: a
|
|
149
|
+
* `reaction.*` event whose `userId` is `chat.user.id` updates it, and
|
|
150
|
+
* this client's own `react()`/`unreact()` update it **before the frame
|
|
151
|
+
* goes out**, rolling back if the server refuses. Absent on a row the
|
|
152
|
+
* server rendered without a viewer (a live `message.created` has none
|
|
153
|
+
* yet, which is the same as empty).
|
|
154
|
+
*
|
|
155
|
+
* The SDK owns this field, so a consumer does not need -- and should
|
|
156
|
+
* not add -- an optimistic layer of its own on top of it. The count in
|
|
157
|
+
* `reactions` is the opposite: always the server's, never moved locally.
|
|
158
|
+
*/
|
|
159
|
+
myReactions?: string[];
|
|
145
160
|
thread?: {
|
|
146
161
|
count: number;
|
|
147
162
|
lastSeq?: number;
|
|
@@ -281,6 +296,15 @@ declare class Room extends Emitter<RoomEvents> {
|
|
|
281
296
|
id?: string;
|
|
282
297
|
key?: string;
|
|
283
298
|
});
|
|
299
|
+
/**
|
|
300
|
+
* Whether older history exists above the first message held -- what
|
|
301
|
+
* `loadOlder()` would report as `hasMore`, known before calling it.
|
|
302
|
+
*
|
|
303
|
+
* Derived from the first row's seq against the room's retention floor
|
|
304
|
+
* (the subscribe ack's `minSeq`), and false once a `loadOlder()` came
|
|
305
|
+
* back short. False while nothing is loaded. Re-read it on `messages`.
|
|
306
|
+
*/
|
|
307
|
+
get hasOlder(): boolean;
|
|
284
308
|
/** Everything this client knows about the room, in seq order. A new array whenever it changes. */
|
|
285
309
|
get messages(): Message[];
|
|
286
310
|
/**
|
|
@@ -348,9 +372,34 @@ declare class Room extends Emitter<RoomEvents> {
|
|
|
348
372
|
thread: string;
|
|
349
373
|
};
|
|
350
374
|
}): Promise<Message[]>;
|
|
351
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* Adds a reaction. Idempotent, like the frame.
|
|
377
|
+
*
|
|
378
|
+
* The row's `myReactions` moves **before the frame goes out** and rolls
|
|
379
|
+
* back if the server refuses; its count stays the server's. See
|
|
380
|
+
* `toggleReaction`.
|
|
381
|
+
*/
|
|
352
382
|
react(messageId: string, emoji: string): Promise<void>;
|
|
383
|
+
/** Removes a reaction. Idempotent, and optimistic the same way `react` is. */
|
|
353
384
|
unreact(messageId: string, emoji: string): Promise<void>;
|
|
385
|
+
/**
|
|
386
|
+
* Moves `myReactions` now, sends the frame after.
|
|
387
|
+
*
|
|
388
|
+
* 누른 값은 (사용자, 이모지)로 로컬에서 정해지고 엔진은 멱등이므로 ack를
|
|
389
|
+
* 기다릴 이유가 없다 — 기다리면 누른 느낌이 왕복 시간만큼 늦는다. 소비자가
|
|
390
|
+
* 자기 낙관 계층을 덧대면 이 필드의 주인이 둘이 되므로, SDK가 한다.
|
|
391
|
+
* **발행은 다르다**: `before_publish` 훅이 본문을 바꿀 수 있어 미리 그릴 값이
|
|
392
|
+
* 로컬에 없다. 그래서 `send()`는 낙관적이지 않다.
|
|
393
|
+
*
|
|
394
|
+
* 개수는 옮기지 않는다. ack가 오기 전에 남이 같은 이모지를 누르면 로컬
|
|
395
|
+
* +1/-1은 서버가 보낸 수와 다른 값이 되고, 그것을 고칠 이벤트는 이미
|
|
396
|
+
* 지나갔다. 또 ack와 방 프레임은 다른 길로 와 순서가 없으므로, 늦게 온 내
|
|
397
|
+
* ack(count 1)가 앞서 온 남의 이벤트(count 2)를 되돌린다.
|
|
398
|
+
*
|
|
399
|
+
* 거절·타임아웃·끊김이면 되돌리지만, 그 사이 더 새로운 값이 자리를 차지했다면
|
|
400
|
+
* 두고 나온다(`Timeline.settleReaction`).
|
|
401
|
+
*/
|
|
402
|
+
private toggleReaction;
|
|
354
403
|
/**
|
|
355
404
|
* Moves this user's read cursor.
|
|
356
405
|
*
|
|
@@ -686,6 +735,31 @@ declare class ChatClient extends Emitter<ChatClientEvents> {
|
|
|
686
735
|
registerRoomId(roomId: string, room: Room): void;
|
|
687
736
|
/** Opens the connection and resolves when `hello` arrives. */
|
|
688
737
|
connect(): Promise<void>;
|
|
738
|
+
/**
|
|
739
|
+
* Re-authenticates: calls `token()` again and replaces the socket, keeping
|
|
740
|
+
* every room handle and every subscription.
|
|
741
|
+
*
|
|
742
|
+
* For when the identity the server holds has to change mid-session --
|
|
743
|
+
* a nickname change, a new avatar, fresh claims. The server reads the
|
|
744
|
+
* token once, at `auth`, so `sender.name` on the next message is the old
|
|
745
|
+
* one until the connection is re-authenticated. `close()` + `connect()`
|
|
746
|
+
* does that too, but passes through `closed` (a consumer's "you are
|
|
747
|
+
* offline" screen) and tears down the page listeners.
|
|
748
|
+
*
|
|
749
|
+
* States: `open` → `reconnecting` → `open`, never `closed`. Rooms
|
|
750
|
+
* resubscribe from what they hold and catch up the gap, as after any
|
|
751
|
+
* drop; `messages` is kept. Frames awaiting an ack on the old socket are
|
|
752
|
+
* rejected with `closed`. `chat.user` is the new identity once this
|
|
753
|
+
* resolves. If `token()` fails the client keeps retrying in the
|
|
754
|
+
* background like any reconnect (and this rejects); a `ChatError` from
|
|
755
|
+
* `token()` stops it at `closed`, as on connect.
|
|
756
|
+
*
|
|
757
|
+
* On a client that is not connected (never connected, or `close()`d) it
|
|
758
|
+
* is `connect()`.
|
|
759
|
+
*/
|
|
760
|
+
reconnect(): Promise<void>;
|
|
761
|
+
/** Sockets `reconnect()` replaced: their late events are not this client's any more. */
|
|
762
|
+
private readonly retired;
|
|
689
763
|
/**
|
|
690
764
|
* Closes for good.
|
|
691
765
|
*
|
|
@@ -797,6 +871,21 @@ type FetchRange = (range: {
|
|
|
797
871
|
before: number;
|
|
798
872
|
limit: number;
|
|
799
873
|
}) => Promise<Message[]>;
|
|
874
|
+
/**
|
|
875
|
+
* A press of this client's own that the server has not answered yet.
|
|
876
|
+
*
|
|
877
|
+
* Carries its own sequence number, which is the whole point: by the time
|
|
878
|
+
* an answer comes back the (message, emoji) pair may belong to a newer
|
|
879
|
+
* press, and settling this one then would put a value nobody is showing
|
|
880
|
+
* any more back on screen.
|
|
881
|
+
*/
|
|
882
|
+
type PendingReaction = {
|
|
883
|
+
messageId: string;
|
|
884
|
+
emoji: string;
|
|
885
|
+
/** What the press asked for: in `myReactions`, or out of it. */
|
|
886
|
+
mine: boolean;
|
|
887
|
+
seq: number;
|
|
888
|
+
};
|
|
800
889
|
type TimelineOptions = {
|
|
801
890
|
fetchRange: FetchRange;
|
|
802
891
|
/** Called whenever the list changes. */
|
|
@@ -840,9 +929,39 @@ declare class Timeline {
|
|
|
840
929
|
private shared;
|
|
841
930
|
/** The `loadOlder` in flight, so a scroll handler firing twice asks once. */
|
|
842
931
|
private older;
|
|
932
|
+
/**
|
|
933
|
+
* The lowest seq the room still keeps (`minSeq` from the subscribe ack;
|
|
934
|
+
* 1 until told). Below it there is nothing to read, whatever seq says.
|
|
935
|
+
*/
|
|
936
|
+
private floor;
|
|
937
|
+
/** A `loadOlder` came back short: the top of what the server keeps was reached. */
|
|
938
|
+
private exhausted;
|
|
939
|
+
/**
|
|
940
|
+
* Presses this client has made and the server has not answered yet, one
|
|
941
|
+
* entry per (message, emoji) pair, each remembering the last value the
|
|
942
|
+
* **server** gave for that pair.
|
|
943
|
+
*
|
|
944
|
+
* 되돌릴 자리로 "누르기 직전 화면값"을 쓰면 안 된다. 눌렀다 곧바로 취소하고
|
|
945
|
+
* 두 거절이 차례로 오면, 두 번째 거절이 첫 번째의 낙관값 — 서버가 한 번도
|
|
946
|
+
* 가진 적 없는 값 — 을 화면에 되살린다. 마지막으로 서버가 말한 값만이
|
|
947
|
+
* 되돌릴 자리다.
|
|
948
|
+
*/
|
|
949
|
+
private readonly presses;
|
|
950
|
+
private pressSeq;
|
|
843
951
|
constructor(options: TimelineOptions);
|
|
844
952
|
/** A new array whenever the list changes; never mutated once returned. */
|
|
845
953
|
get messages(): Message[];
|
|
954
|
+
/**
|
|
955
|
+
* Whether `loadOlder()` would find anything, answered without asking.
|
|
956
|
+
*
|
|
957
|
+
* `loadOlder()`의 `hasMore`와 같은 판정이다: 맨 위 행이 방의 보존 하한
|
|
958
|
+
* (`minSeq`, seq는 1부터 구멍 없이 붙는다)보다 위에 있고, 앞선 `loadOlder`가
|
|
959
|
+
* 덜 찬 페이지로 끝을 알린 적이 없으면 더 있다. 첫 `loadOlder()` 전에 "이전
|
|
960
|
+
* 메시지 더 보기"를 그릴지 정할 수 있게 한다. 목록이 비었으면 false다.
|
|
961
|
+
*/
|
|
962
|
+
get hasOlder(): boolean;
|
|
963
|
+
/** Records the room's retention floor (`minSeq`). */
|
|
964
|
+
setFloor(minSeq: number): void;
|
|
846
965
|
/** The highest seq this timeline holds, hole or no hole. */
|
|
847
966
|
get highestSeq(): number;
|
|
848
967
|
/**
|
|
@@ -917,9 +1036,42 @@ declare class Timeline {
|
|
|
917
1036
|
*
|
|
918
1037
|
* The server sends the new `count` with the event, so this is a
|
|
919
1038
|
* replacement rather than an increment: two clients reacting at once
|
|
920
|
-
* cannot drift the way `+1`/`-1` would.
|
|
1039
|
+
* cannot drift the way `+1`/`-1` would. `count` undefined leaves the
|
|
1040
|
+
* aggregate alone (the react ack path); `mine` undefined leaves
|
|
1041
|
+
* `myReactions` alone (somebody else's event).
|
|
1042
|
+
*
|
|
1043
|
+
* This is the **authoritative** entry point: a value the server sent.
|
|
1044
|
+
* So a press still waiting on an answer for the same pair is finished
|
|
1045
|
+
* here -- a refusal landing afterwards must not push this value back
|
|
1046
|
+
* to the older one it was going to restore.
|
|
921
1047
|
*/
|
|
922
|
-
reaction(messageId: string, emoji: string, count: number): void;
|
|
1048
|
+
reaction(messageId: string, emoji: string, count: number | undefined, mine?: boolean): void;
|
|
1049
|
+
/**
|
|
1050
|
+
* Applies this client's own react/unreact to `myReactions` **before the
|
|
1051
|
+
* frame goes out**, and returns the press so its answer can settle it.
|
|
1052
|
+
*
|
|
1053
|
+
* 누른 값은 (사용자, 이모지)로 로컬에서 정해지고 엔진은 멱등이므로, 미리
|
|
1054
|
+
* 적용해도 ack가 새로 알려 줄 것이 없다. `count`는 일부러 건드리지 않는다 —
|
|
1055
|
+
* 그 사이 남이 같은 이모지를 누르면 로컬 +1/-1은 서버가 보낼 수와 다르고,
|
|
1056
|
+
* 그 차이를 고칠 이벤트는 이미 지나갔다.
|
|
1057
|
+
*
|
|
1058
|
+
* Undefined when the row is not held: there is nothing to show now and
|
|
1059
|
+
* nothing to put back later.
|
|
1060
|
+
*/
|
|
1061
|
+
pressReaction(messageId: string, emoji: string, mine: boolean): PendingReaction | undefined;
|
|
1062
|
+
/**
|
|
1063
|
+
* Settles a press with what the server said.
|
|
1064
|
+
*
|
|
1065
|
+
* `accepted` re-asserts the value the press asked for; a refusal puts
|
|
1066
|
+
* the last server-given value back. Either way it does nothing once
|
|
1067
|
+
* this press no longer owns the pair -- a newer press took it over
|
|
1068
|
+
* (and that one's answer decides), or something authoritative already
|
|
1069
|
+
* landed on it (a `reaction.*` event carrying this user's id, a
|
|
1070
|
+
* `reload()`, a reconnect's catch-up page). Reverting there would
|
|
1071
|
+
* resurrect a value older than what is on screen.
|
|
1072
|
+
*/
|
|
1073
|
+
settleReaction(press: PendingReaction, accepted: boolean): void;
|
|
1074
|
+
private applyReaction;
|
|
923
1075
|
/** Applies a `thread.updated` to the root message's aggregate. */
|
|
924
1076
|
thread(rootId: string, count: number, lastSeq?: number): void;
|
|
925
1077
|
/** Applies a `message.deleted`, remembering it if the row is not here. */
|
|
@@ -962,10 +1114,19 @@ declare class Timeline {
|
|
|
962
1114
|
/** Inserts at the seq position, replacing an existing row with that seq. */
|
|
963
1115
|
private insert;
|
|
964
1116
|
private applyDelete;
|
|
1117
|
+
/**
|
|
1118
|
+
* Forgets the presses on a row the server has just re-rendered.
|
|
1119
|
+
*
|
|
1120
|
+
* 히스토리 응답의 행은 뷰어별 `myReactions`를 싣고 오므로 그 값이 서버의
|
|
1121
|
+
* 값이다. 누름을 남겨 두면 그 뒤에 온 거절이 더 새로운 진실 위에 옛 값을
|
|
1122
|
+
* 덮어쓴다. 행 전체 단위인 것은 페이지가 그 행의 이모지 전부를 다시 그려
|
|
1123
|
+
* 주기 때문이다.
|
|
1124
|
+
*/
|
|
1125
|
+
private serverRendered;
|
|
965
1126
|
/** Makes `items` safe to mutate: a copy, if the current one was handed out. */
|
|
966
1127
|
private own;
|
|
967
1128
|
private changed;
|
|
968
1129
|
}
|
|
969
1130
|
declare function createTimeline(options: TimelineOptions): Timeline;
|
|
970
1131
|
|
|
971
|
-
export { type AuthUser, ChatClient, type ChatClientEvents, type ChatClientOptions, ChatError, type ConnectionState, type FetchRange, type Frame, type Hello, type Message, type PublishAck, type ReactionUser, type ReactionsPage, Room, type RoomEvents, type SendInput, type SendOptions, Timeline, type TimelineOptions, type WebSocketFactory, type WebSocketLike, createChatClient, createTimeline };
|
|
1132
|
+
export { type AuthUser, ChatClient, type ChatClientEvents, type ChatClientOptions, ChatError, type ConnectionState, type FetchRange, type Frame, type Hello, type Message, type PendingReaction, type PublishAck, type ReactionUser, type ReactionsPage, Room, type RoomEvents, type SendInput, type SendOptions, Timeline, type TimelineOptions, type WebSocketFactory, type WebSocketLike, createChatClient, createTimeline };
|
package/dist/index.d.ts
CHANGED
|
@@ -142,6 +142,21 @@ type Message = {
|
|
|
142
142
|
emoji: string;
|
|
143
143
|
count: number;
|
|
144
144
|
}[];
|
|
145
|
+
/**
|
|
146
|
+
* The emoji this client's user has on the message.
|
|
147
|
+
*
|
|
148
|
+
* Filled by history reads and kept in step by the SDK afterwards: a
|
|
149
|
+
* `reaction.*` event whose `userId` is `chat.user.id` updates it, and
|
|
150
|
+
* this client's own `react()`/`unreact()` update it **before the frame
|
|
151
|
+
* goes out**, rolling back if the server refuses. Absent on a row the
|
|
152
|
+
* server rendered without a viewer (a live `message.created` has none
|
|
153
|
+
* yet, which is the same as empty).
|
|
154
|
+
*
|
|
155
|
+
* The SDK owns this field, so a consumer does not need -- and should
|
|
156
|
+
* not add -- an optimistic layer of its own on top of it. The count in
|
|
157
|
+
* `reactions` is the opposite: always the server's, never moved locally.
|
|
158
|
+
*/
|
|
159
|
+
myReactions?: string[];
|
|
145
160
|
thread?: {
|
|
146
161
|
count: number;
|
|
147
162
|
lastSeq?: number;
|
|
@@ -281,6 +296,15 @@ declare class Room extends Emitter<RoomEvents> {
|
|
|
281
296
|
id?: string;
|
|
282
297
|
key?: string;
|
|
283
298
|
});
|
|
299
|
+
/**
|
|
300
|
+
* Whether older history exists above the first message held -- what
|
|
301
|
+
* `loadOlder()` would report as `hasMore`, known before calling it.
|
|
302
|
+
*
|
|
303
|
+
* Derived from the first row's seq against the room's retention floor
|
|
304
|
+
* (the subscribe ack's `minSeq`), and false once a `loadOlder()` came
|
|
305
|
+
* back short. False while nothing is loaded. Re-read it on `messages`.
|
|
306
|
+
*/
|
|
307
|
+
get hasOlder(): boolean;
|
|
284
308
|
/** Everything this client knows about the room, in seq order. A new array whenever it changes. */
|
|
285
309
|
get messages(): Message[];
|
|
286
310
|
/**
|
|
@@ -348,9 +372,34 @@ declare class Room extends Emitter<RoomEvents> {
|
|
|
348
372
|
thread: string;
|
|
349
373
|
};
|
|
350
374
|
}): Promise<Message[]>;
|
|
351
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* Adds a reaction. Idempotent, like the frame.
|
|
377
|
+
*
|
|
378
|
+
* The row's `myReactions` moves **before the frame goes out** and rolls
|
|
379
|
+
* back if the server refuses; its count stays the server's. See
|
|
380
|
+
* `toggleReaction`.
|
|
381
|
+
*/
|
|
352
382
|
react(messageId: string, emoji: string): Promise<void>;
|
|
383
|
+
/** Removes a reaction. Idempotent, and optimistic the same way `react` is. */
|
|
353
384
|
unreact(messageId: string, emoji: string): Promise<void>;
|
|
385
|
+
/**
|
|
386
|
+
* Moves `myReactions` now, sends the frame after.
|
|
387
|
+
*
|
|
388
|
+
* 누른 값은 (사용자, 이모지)로 로컬에서 정해지고 엔진은 멱등이므로 ack를
|
|
389
|
+
* 기다릴 이유가 없다 — 기다리면 누른 느낌이 왕복 시간만큼 늦는다. 소비자가
|
|
390
|
+
* 자기 낙관 계층을 덧대면 이 필드의 주인이 둘이 되므로, SDK가 한다.
|
|
391
|
+
* **발행은 다르다**: `before_publish` 훅이 본문을 바꿀 수 있어 미리 그릴 값이
|
|
392
|
+
* 로컬에 없다. 그래서 `send()`는 낙관적이지 않다.
|
|
393
|
+
*
|
|
394
|
+
* 개수는 옮기지 않는다. ack가 오기 전에 남이 같은 이모지를 누르면 로컬
|
|
395
|
+
* +1/-1은 서버가 보낸 수와 다른 값이 되고, 그것을 고칠 이벤트는 이미
|
|
396
|
+
* 지나갔다. 또 ack와 방 프레임은 다른 길로 와 순서가 없으므로, 늦게 온 내
|
|
397
|
+
* ack(count 1)가 앞서 온 남의 이벤트(count 2)를 되돌린다.
|
|
398
|
+
*
|
|
399
|
+
* 거절·타임아웃·끊김이면 되돌리지만, 그 사이 더 새로운 값이 자리를 차지했다면
|
|
400
|
+
* 두고 나온다(`Timeline.settleReaction`).
|
|
401
|
+
*/
|
|
402
|
+
private toggleReaction;
|
|
354
403
|
/**
|
|
355
404
|
* Moves this user's read cursor.
|
|
356
405
|
*
|
|
@@ -686,6 +735,31 @@ declare class ChatClient extends Emitter<ChatClientEvents> {
|
|
|
686
735
|
registerRoomId(roomId: string, room: Room): void;
|
|
687
736
|
/** Opens the connection and resolves when `hello` arrives. */
|
|
688
737
|
connect(): Promise<void>;
|
|
738
|
+
/**
|
|
739
|
+
* Re-authenticates: calls `token()` again and replaces the socket, keeping
|
|
740
|
+
* every room handle and every subscription.
|
|
741
|
+
*
|
|
742
|
+
* For when the identity the server holds has to change mid-session --
|
|
743
|
+
* a nickname change, a new avatar, fresh claims. The server reads the
|
|
744
|
+
* token once, at `auth`, so `sender.name` on the next message is the old
|
|
745
|
+
* one until the connection is re-authenticated. `close()` + `connect()`
|
|
746
|
+
* does that too, but passes through `closed` (a consumer's "you are
|
|
747
|
+
* offline" screen) and tears down the page listeners.
|
|
748
|
+
*
|
|
749
|
+
* States: `open` → `reconnecting` → `open`, never `closed`. Rooms
|
|
750
|
+
* resubscribe from what they hold and catch up the gap, as after any
|
|
751
|
+
* drop; `messages` is kept. Frames awaiting an ack on the old socket are
|
|
752
|
+
* rejected with `closed`. `chat.user` is the new identity once this
|
|
753
|
+
* resolves. If `token()` fails the client keeps retrying in the
|
|
754
|
+
* background like any reconnect (and this rejects); a `ChatError` from
|
|
755
|
+
* `token()` stops it at `closed`, as on connect.
|
|
756
|
+
*
|
|
757
|
+
* On a client that is not connected (never connected, or `close()`d) it
|
|
758
|
+
* is `connect()`.
|
|
759
|
+
*/
|
|
760
|
+
reconnect(): Promise<void>;
|
|
761
|
+
/** Sockets `reconnect()` replaced: their late events are not this client's any more. */
|
|
762
|
+
private readonly retired;
|
|
689
763
|
/**
|
|
690
764
|
* Closes for good.
|
|
691
765
|
*
|
|
@@ -797,6 +871,21 @@ type FetchRange = (range: {
|
|
|
797
871
|
before: number;
|
|
798
872
|
limit: number;
|
|
799
873
|
}) => Promise<Message[]>;
|
|
874
|
+
/**
|
|
875
|
+
* A press of this client's own that the server has not answered yet.
|
|
876
|
+
*
|
|
877
|
+
* Carries its own sequence number, which is the whole point: by the time
|
|
878
|
+
* an answer comes back the (message, emoji) pair may belong to a newer
|
|
879
|
+
* press, and settling this one then would put a value nobody is showing
|
|
880
|
+
* any more back on screen.
|
|
881
|
+
*/
|
|
882
|
+
type PendingReaction = {
|
|
883
|
+
messageId: string;
|
|
884
|
+
emoji: string;
|
|
885
|
+
/** What the press asked for: in `myReactions`, or out of it. */
|
|
886
|
+
mine: boolean;
|
|
887
|
+
seq: number;
|
|
888
|
+
};
|
|
800
889
|
type TimelineOptions = {
|
|
801
890
|
fetchRange: FetchRange;
|
|
802
891
|
/** Called whenever the list changes. */
|
|
@@ -840,9 +929,39 @@ declare class Timeline {
|
|
|
840
929
|
private shared;
|
|
841
930
|
/** The `loadOlder` in flight, so a scroll handler firing twice asks once. */
|
|
842
931
|
private older;
|
|
932
|
+
/**
|
|
933
|
+
* The lowest seq the room still keeps (`minSeq` from the subscribe ack;
|
|
934
|
+
* 1 until told). Below it there is nothing to read, whatever seq says.
|
|
935
|
+
*/
|
|
936
|
+
private floor;
|
|
937
|
+
/** A `loadOlder` came back short: the top of what the server keeps was reached. */
|
|
938
|
+
private exhausted;
|
|
939
|
+
/**
|
|
940
|
+
* Presses this client has made and the server has not answered yet, one
|
|
941
|
+
* entry per (message, emoji) pair, each remembering the last value the
|
|
942
|
+
* **server** gave for that pair.
|
|
943
|
+
*
|
|
944
|
+
* 되돌릴 자리로 "누르기 직전 화면값"을 쓰면 안 된다. 눌렀다 곧바로 취소하고
|
|
945
|
+
* 두 거절이 차례로 오면, 두 번째 거절이 첫 번째의 낙관값 — 서버가 한 번도
|
|
946
|
+
* 가진 적 없는 값 — 을 화면에 되살린다. 마지막으로 서버가 말한 값만이
|
|
947
|
+
* 되돌릴 자리다.
|
|
948
|
+
*/
|
|
949
|
+
private readonly presses;
|
|
950
|
+
private pressSeq;
|
|
843
951
|
constructor(options: TimelineOptions);
|
|
844
952
|
/** A new array whenever the list changes; never mutated once returned. */
|
|
845
953
|
get messages(): Message[];
|
|
954
|
+
/**
|
|
955
|
+
* Whether `loadOlder()` would find anything, answered without asking.
|
|
956
|
+
*
|
|
957
|
+
* `loadOlder()`의 `hasMore`와 같은 판정이다: 맨 위 행이 방의 보존 하한
|
|
958
|
+
* (`minSeq`, seq는 1부터 구멍 없이 붙는다)보다 위에 있고, 앞선 `loadOlder`가
|
|
959
|
+
* 덜 찬 페이지로 끝을 알린 적이 없으면 더 있다. 첫 `loadOlder()` 전에 "이전
|
|
960
|
+
* 메시지 더 보기"를 그릴지 정할 수 있게 한다. 목록이 비었으면 false다.
|
|
961
|
+
*/
|
|
962
|
+
get hasOlder(): boolean;
|
|
963
|
+
/** Records the room's retention floor (`minSeq`). */
|
|
964
|
+
setFloor(minSeq: number): void;
|
|
846
965
|
/** The highest seq this timeline holds, hole or no hole. */
|
|
847
966
|
get highestSeq(): number;
|
|
848
967
|
/**
|
|
@@ -917,9 +1036,42 @@ declare class Timeline {
|
|
|
917
1036
|
*
|
|
918
1037
|
* The server sends the new `count` with the event, so this is a
|
|
919
1038
|
* replacement rather than an increment: two clients reacting at once
|
|
920
|
-
* cannot drift the way `+1`/`-1` would.
|
|
1039
|
+
* cannot drift the way `+1`/`-1` would. `count` undefined leaves the
|
|
1040
|
+
* aggregate alone (the react ack path); `mine` undefined leaves
|
|
1041
|
+
* `myReactions` alone (somebody else's event).
|
|
1042
|
+
*
|
|
1043
|
+
* This is the **authoritative** entry point: a value the server sent.
|
|
1044
|
+
* So a press still waiting on an answer for the same pair is finished
|
|
1045
|
+
* here -- a refusal landing afterwards must not push this value back
|
|
1046
|
+
* to the older one it was going to restore.
|
|
921
1047
|
*/
|
|
922
|
-
reaction(messageId: string, emoji: string, count: number): void;
|
|
1048
|
+
reaction(messageId: string, emoji: string, count: number | undefined, mine?: boolean): void;
|
|
1049
|
+
/**
|
|
1050
|
+
* Applies this client's own react/unreact to `myReactions` **before the
|
|
1051
|
+
* frame goes out**, and returns the press so its answer can settle it.
|
|
1052
|
+
*
|
|
1053
|
+
* 누른 값은 (사용자, 이모지)로 로컬에서 정해지고 엔진은 멱등이므로, 미리
|
|
1054
|
+
* 적용해도 ack가 새로 알려 줄 것이 없다. `count`는 일부러 건드리지 않는다 —
|
|
1055
|
+
* 그 사이 남이 같은 이모지를 누르면 로컬 +1/-1은 서버가 보낼 수와 다르고,
|
|
1056
|
+
* 그 차이를 고칠 이벤트는 이미 지나갔다.
|
|
1057
|
+
*
|
|
1058
|
+
* Undefined when the row is not held: there is nothing to show now and
|
|
1059
|
+
* nothing to put back later.
|
|
1060
|
+
*/
|
|
1061
|
+
pressReaction(messageId: string, emoji: string, mine: boolean): PendingReaction | undefined;
|
|
1062
|
+
/**
|
|
1063
|
+
* Settles a press with what the server said.
|
|
1064
|
+
*
|
|
1065
|
+
* `accepted` re-asserts the value the press asked for; a refusal puts
|
|
1066
|
+
* the last server-given value back. Either way it does nothing once
|
|
1067
|
+
* this press no longer owns the pair -- a newer press took it over
|
|
1068
|
+
* (and that one's answer decides), or something authoritative already
|
|
1069
|
+
* landed on it (a `reaction.*` event carrying this user's id, a
|
|
1070
|
+
* `reload()`, a reconnect's catch-up page). Reverting there would
|
|
1071
|
+
* resurrect a value older than what is on screen.
|
|
1072
|
+
*/
|
|
1073
|
+
settleReaction(press: PendingReaction, accepted: boolean): void;
|
|
1074
|
+
private applyReaction;
|
|
923
1075
|
/** Applies a `thread.updated` to the root message's aggregate. */
|
|
924
1076
|
thread(rootId: string, count: number, lastSeq?: number): void;
|
|
925
1077
|
/** Applies a `message.deleted`, remembering it if the row is not here. */
|
|
@@ -962,10 +1114,19 @@ declare class Timeline {
|
|
|
962
1114
|
/** Inserts at the seq position, replacing an existing row with that seq. */
|
|
963
1115
|
private insert;
|
|
964
1116
|
private applyDelete;
|
|
1117
|
+
/**
|
|
1118
|
+
* Forgets the presses on a row the server has just re-rendered.
|
|
1119
|
+
*
|
|
1120
|
+
* 히스토리 응답의 행은 뷰어별 `myReactions`를 싣고 오므로 그 값이 서버의
|
|
1121
|
+
* 값이다. 누름을 남겨 두면 그 뒤에 온 거절이 더 새로운 진실 위에 옛 값을
|
|
1122
|
+
* 덮어쓴다. 행 전체 단위인 것은 페이지가 그 행의 이모지 전부를 다시 그려
|
|
1123
|
+
* 주기 때문이다.
|
|
1124
|
+
*/
|
|
1125
|
+
private serverRendered;
|
|
965
1126
|
/** Makes `items` safe to mutate: a copy, if the current one was handed out. */
|
|
966
1127
|
private own;
|
|
967
1128
|
private changed;
|
|
968
1129
|
}
|
|
969
1130
|
declare function createTimeline(options: TimelineOptions): Timeline;
|
|
970
1131
|
|
|
971
|
-
export { type AuthUser, ChatClient, type ChatClientEvents, type ChatClientOptions, ChatError, type ConnectionState, type FetchRange, type Frame, type Hello, type Message, type PublishAck, type ReactionUser, type ReactionsPage, Room, type RoomEvents, type SendInput, type SendOptions, Timeline, type TimelineOptions, type WebSocketFactory, type WebSocketLike, createChatClient, createTimeline };
|
|
1132
|
+
export { type AuthUser, ChatClient, type ChatClientEvents, type ChatClientOptions, ChatError, type ConnectionState, type FetchRange, type Frame, type Hello, type Message, type PendingReaction, type PublishAck, type ReactionUser, type ReactionsPage, Room, type RoomEvents, type SendInput, type SendOptions, Timeline, type TimelineOptions, type WebSocketFactory, type WebSocketLike, createChatClient, createTimeline };
|