@noverachat/sdk-web 0.0.3 → 0.1.0
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.ko.md +8 -8
- package/README.md +8 -8
- package/dist/index.cjs +386 -258
- package/dist/index.d.cts +163 -170
- package/dist/index.d.ts +163 -170
- package/dist/index.js +386 -258
- package/package.json +1 -1
- package/src/client.ts +50 -157
- package/src/features/room.ts +187 -344
- package/src/generated/rest.ts +480 -0
- package/src/index.ts +1 -0
- package/src/types.ts +59 -177
package/dist/index.js
CHANGED
|
@@ -1,3 +1,197 @@
|
|
|
1
|
+
// src/generated/rest.ts
|
|
2
|
+
function parseAppPolicy(j) {
|
|
3
|
+
return {
|
|
4
|
+
appId: j["app_id"],
|
|
5
|
+
allowMemberBulkDelete: j["allow_member_bulk_delete"],
|
|
6
|
+
readReceiptsEnabled: j["read_receipts_enabled"],
|
|
7
|
+
typingIndicatorsEnabled: j["typing_indicators_enabled"],
|
|
8
|
+
allowMemberDeleteAnyMessage: j["allow_member_delete_any_message"]
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function parseUnreadSummary(j) {
|
|
12
|
+
return {
|
|
13
|
+
total: j["total"],
|
|
14
|
+
rooms: j["rooms"].map((e) => parseUnreadRoomItem(e))
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function parseUnreadRoomItem(j) {
|
|
18
|
+
return {
|
|
19
|
+
roomId: j["room_id"],
|
|
20
|
+
name: j["name"],
|
|
21
|
+
roomType: j["room_type"],
|
|
22
|
+
unreadCount: j["unread_count"],
|
|
23
|
+
lastMessageId: j["last_message_id"],
|
|
24
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
25
|
+
lastMessagePreview: j["last_message_preview"],
|
|
26
|
+
memberCount: j["member_count"],
|
|
27
|
+
membersPreview: j["members_preview"] == null ? void 0 : j["members_preview"].map((e) => parseMemberPreview(e))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function parseMemberPreview(j) {
|
|
31
|
+
return {
|
|
32
|
+
userId: j["user_id"],
|
|
33
|
+
nickname: j["nickname"],
|
|
34
|
+
profileImageUrl: j["profile_image_url"],
|
|
35
|
+
isOnline: j["is_online"]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parseRoomUnread(j) {
|
|
39
|
+
return {
|
|
40
|
+
roomId: j["room_id"],
|
|
41
|
+
unreadCount: j["unread_count"],
|
|
42
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
43
|
+
lastMessageId: j["last_message_id"]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function parseMessageOut(j) {
|
|
47
|
+
return {
|
|
48
|
+
appId: j["app_id"],
|
|
49
|
+
roomId: j["room_id"],
|
|
50
|
+
messageId: j["message_id"],
|
|
51
|
+
sender: j["sender"] == null ? null : parseSenderMini(j["sender"]),
|
|
52
|
+
senderId: j["sender_id"],
|
|
53
|
+
messageType: j["message_type"],
|
|
54
|
+
customType: j["custom_type"],
|
|
55
|
+
content: j["content"],
|
|
56
|
+
data: j["data"],
|
|
57
|
+
meta: j["meta"],
|
|
58
|
+
fileId: j["file_id"],
|
|
59
|
+
file: j["file"],
|
|
60
|
+
files: j["files"],
|
|
61
|
+
replyToId: j["reply_to_id"],
|
|
62
|
+
reactions: j["reactions"] == null ? void 0 : j["reactions"].map((e) => parseReactionEntry(e)),
|
|
63
|
+
createdAt: j["created_at"],
|
|
64
|
+
updatedAt: j["updated_at"],
|
|
65
|
+
deletedAt: j["deleted_at"],
|
|
66
|
+
deletedByUserId: j["deleted_by_user_id"],
|
|
67
|
+
deleteScope: j["delete_scope"],
|
|
68
|
+
editedCount: j["edited_count"],
|
|
69
|
+
createdAtMs: j["created_at_ms"],
|
|
70
|
+
updatedAtMs: j["updated_at_ms"]
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function parseAnnouncementOut(j) {
|
|
74
|
+
return {
|
|
75
|
+
id: j["id"],
|
|
76
|
+
roomId: j["room_id"],
|
|
77
|
+
content: j["content"],
|
|
78
|
+
createdBy: j["created_by"],
|
|
79
|
+
createdAt: j["created_at"],
|
|
80
|
+
updatedAt: j["updated_at"]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function parseInviteTokenOut(j) {
|
|
84
|
+
return {
|
|
85
|
+
token: j["token"],
|
|
86
|
+
roomId: j["room_id"],
|
|
87
|
+
createdBy: j["created_by"],
|
|
88
|
+
createdAt: j["created_at"],
|
|
89
|
+
expiresAt: j["expires_at"],
|
|
90
|
+
maxUses: j["max_uses"],
|
|
91
|
+
usedCount: j["used_count"],
|
|
92
|
+
revokedAt: j["revoked_at"]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function parseInviteAcceptResponse(j) {
|
|
96
|
+
return {
|
|
97
|
+
roomId: j["room_id"],
|
|
98
|
+
roomName: j["room_name"],
|
|
99
|
+
wasAlreadyMember: j["was_already_member"]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function parseDeviceOut(j) {
|
|
103
|
+
return {
|
|
104
|
+
deviceId: j["device_id"],
|
|
105
|
+
tokenType: j["token_type"],
|
|
106
|
+
tokenPreview: j["token_preview"],
|
|
107
|
+
os: j["os"],
|
|
108
|
+
appVersion: j["app_version"],
|
|
109
|
+
pushEnabled: j["push_enabled"],
|
|
110
|
+
lastActiveAt: j["last_active_at"],
|
|
111
|
+
createdAt: j["created_at"]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseDeviceListResponse(j) {
|
|
115
|
+
return {
|
|
116
|
+
items: j["items"].map((e) => parseDeviceOut(e)),
|
|
117
|
+
total: j["total"]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function parsePushPreferencesOut(j) {
|
|
121
|
+
return {
|
|
122
|
+
pushEnabled: j["push_enabled"]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function parseFileOut(j) {
|
|
126
|
+
return {
|
|
127
|
+
id: j["id"],
|
|
128
|
+
kind: j["kind"],
|
|
129
|
+
mime: j["mime"],
|
|
130
|
+
sizeBytes: j["size_bytes"],
|
|
131
|
+
originalName: j["original_name"],
|
|
132
|
+
width: j["width"],
|
|
133
|
+
height: j["height"],
|
|
134
|
+
durationSec: j["duration_sec"],
|
|
135
|
+
thumbnailStatus: j["thumbnail_status"],
|
|
136
|
+
uploadStatus: j["upload_status"],
|
|
137
|
+
createdAt: j["created_at"],
|
|
138
|
+
committedAt: j["committed_at"],
|
|
139
|
+
forwardBlocked: j["forward_blocked"]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function parseFileCreateResponse(j) {
|
|
143
|
+
return {
|
|
144
|
+
fileId: j["file_id"],
|
|
145
|
+
uploadUrl: j["upload_url"],
|
|
146
|
+
headers: j["headers"],
|
|
147
|
+
expiresAtMs: j["expires_at_ms"]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function parseBlockOut(j) {
|
|
151
|
+
return {
|
|
152
|
+
blockedUserId: j["blocked_user_id"],
|
|
153
|
+
reason: j["reason"],
|
|
154
|
+
createdAt: j["created_at"]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parseRoomMemberOut(j) {
|
|
158
|
+
return {
|
|
159
|
+
userId: j["user_id"],
|
|
160
|
+
nickname: j["nickname"],
|
|
161
|
+
role: j["role"],
|
|
162
|
+
pushTrigger: j["push_trigger"],
|
|
163
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
164
|
+
joinedAt: j["joined_at"],
|
|
165
|
+
isOnline: j["is_online"],
|
|
166
|
+
mutedUntil: j["muted_until"]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function parseBulkDeleteResult(j) {
|
|
170
|
+
return {
|
|
171
|
+
cleared: j["cleared"],
|
|
172
|
+
upToMessageId: j["up_to_message_id"]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function parseBulkDeleteBySenderResult(j) {
|
|
176
|
+
return {
|
|
177
|
+
cleared: j["cleared"]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function parseSenderMini(j) {
|
|
181
|
+
return {
|
|
182
|
+
userId: j["user_id"],
|
|
183
|
+
nickname: j["nickname"],
|
|
184
|
+
profileImageUrl: j["profile_image_url"]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function parseReactionEntry(j) {
|
|
188
|
+
return {
|
|
189
|
+
key: j["key"],
|
|
190
|
+
userIds: j["user_ids"],
|
|
191
|
+
updatedAt: j["updated_at"]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
1
195
|
// src/errors.ts
|
|
2
196
|
var ErrorCode = {
|
|
3
197
|
INTERNAL: "NC-GEN-001",
|
|
@@ -293,64 +487,31 @@ function tempId() {
|
|
|
293
487
|
}
|
|
294
488
|
|
|
295
489
|
// src/features/room.ts
|
|
296
|
-
function
|
|
297
|
-
|
|
490
|
+
function toFileInline(f) {
|
|
491
|
+
return {
|
|
492
|
+
id: f.id,
|
|
493
|
+
kind: f.kind,
|
|
494
|
+
mime: f.mime,
|
|
495
|
+
size: f.size,
|
|
496
|
+
name: f.name ?? null,
|
|
497
|
+
width: f.width ?? null,
|
|
498
|
+
height: f.height ?? null,
|
|
499
|
+
durationSec: f.duration_sec ?? null,
|
|
500
|
+
thumbnailStatus: f.thumbnail_status,
|
|
501
|
+
forwardBlocked: Boolean(f.forward_blocked)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function toMessageOut(j) {
|
|
505
|
+
const base = parseMessageOut(j);
|
|
506
|
+
const raw = j;
|
|
507
|
+
const dataObj = base.data ?? null;
|
|
298
508
|
const customMeta = dataObj && typeof dataObj === "object" && "_customMeta" in dataObj ? dataObj._customMeta : null;
|
|
299
509
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
nickname: r.sender.nickname ?? null,
|
|
306
|
-
profileImageUrl: r.sender.profile_image_url ?? null
|
|
307
|
-
} : null,
|
|
308
|
-
senderId: r.sender_id ?? null,
|
|
309
|
-
messageType: r.message_type,
|
|
310
|
-
customType: r.custom_type ?? null,
|
|
311
|
-
content: r.content ?? null,
|
|
312
|
-
data: dataObj,
|
|
313
|
-
meta: r.meta ?? null,
|
|
314
|
-
customMeta,
|
|
315
|
-
fileId: r.file_id ?? null,
|
|
316
|
-
file: r.file ? {
|
|
317
|
-
id: r.file.id,
|
|
318
|
-
kind: r.file.kind,
|
|
319
|
-
mime: r.file.mime,
|
|
320
|
-
size: r.file.size,
|
|
321
|
-
name: r.file.name ?? null,
|
|
322
|
-
width: r.file.width ?? null,
|
|
323
|
-
height: r.file.height ?? null,
|
|
324
|
-
durationSec: r.file.duration_sec ?? null,
|
|
325
|
-
thumbnailStatus: r.file.thumbnail_status,
|
|
326
|
-
forwardBlocked: Boolean(r.file.forward_blocked)
|
|
327
|
-
} : null,
|
|
328
|
-
files: r.files && r.files.length > 0 ? r.files.map((f) => ({
|
|
329
|
-
id: f.id,
|
|
330
|
-
kind: f.kind,
|
|
331
|
-
mime: f.mime,
|
|
332
|
-
size: f.size,
|
|
333
|
-
name: f.name ?? null,
|
|
334
|
-
width: f.width ?? null,
|
|
335
|
-
height: f.height ?? null,
|
|
336
|
-
durationSec: f.duration_sec ?? null,
|
|
337
|
-
thumbnailStatus: f.thumbnail_status,
|
|
338
|
-
forwardBlocked: Boolean(f.forward_blocked)
|
|
339
|
-
})) : null,
|
|
340
|
-
replyToId: r.reply_to_id ?? null,
|
|
341
|
-
reactions: (r.reactions ?? []).map((x) => ({
|
|
342
|
-
key: x.key,
|
|
343
|
-
userIds: x.user_ids,
|
|
344
|
-
updatedAt: x.updated_at
|
|
345
|
-
})),
|
|
346
|
-
createdAt: r.created_at,
|
|
347
|
-
createdAtMs: r.created_at_ms,
|
|
348
|
-
updatedAt: r.updated_at ?? null,
|
|
349
|
-
updatedAtMs: r.updated_at_ms ?? null,
|
|
350
|
-
deletedAt: r.deleted_at ?? null,
|
|
351
|
-
deletedByUserId: r.deleted_by_user_id ?? null,
|
|
352
|
-
deleteScope: r.delete_scope ?? null,
|
|
353
|
-
editedCount: r.edited_count ?? 0
|
|
510
|
+
...base,
|
|
511
|
+
file: raw.file ? toFileInline(raw.file) : null,
|
|
512
|
+
files: raw.files && raw.files.length > 0 ? raw.files.map(toFileInline) : null,
|
|
513
|
+
reactions: base.reactions ?? [],
|
|
514
|
+
customMeta
|
|
354
515
|
};
|
|
355
516
|
}
|
|
356
517
|
var Room = class _Room {
|
|
@@ -536,20 +697,24 @@ var Room = class _Room {
|
|
|
536
697
|
async sendFile(file, opts = {}) {
|
|
537
698
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
538
699
|
const name = opts.name ?? file.name;
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
700
|
+
const created = parseFileCreateResponse(
|
|
701
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
702
|
+
kind,
|
|
703
|
+
mime: file.type || "application/octet-stream",
|
|
704
|
+
size: file.size,
|
|
705
|
+
original_name: name,
|
|
706
|
+
// 파일 전달 제한 — uploader-set at presign time. When true,
|
|
707
|
+
// other users can't forward this file to a different room.
|
|
708
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
709
|
+
})
|
|
710
|
+
);
|
|
548
711
|
const presigned = {
|
|
549
|
-
fileId:
|
|
550
|
-
uploadUrl:
|
|
551
|
-
headers
|
|
552
|
-
|
|
712
|
+
fileId: created.fileId,
|
|
713
|
+
uploadUrl: created.uploadUrl,
|
|
714
|
+
// Presigned S3 headers are always string-valued; the generated
|
|
715
|
+
// model types the JSON object loosely as Record<string, unknown>.
|
|
716
|
+
headers: created.headers,
|
|
717
|
+
expiresAtMs: created.expiresAtMs
|
|
553
718
|
};
|
|
554
719
|
await new Promise((resolve, reject) => {
|
|
555
720
|
const xhr = new XMLHttpRequest();
|
|
@@ -604,17 +769,19 @@ var Room = class _Room {
|
|
|
604
769
|
async uploadFileOnly(file, opts = {}) {
|
|
605
770
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
606
771
|
const name = opts.name ?? file.name;
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
772
|
+
const created = parseFileCreateResponse(
|
|
773
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
774
|
+
kind,
|
|
775
|
+
mime: file.type || "application/octet-stream",
|
|
776
|
+
size: file.size,
|
|
777
|
+
original_name: name,
|
|
778
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
779
|
+
})
|
|
780
|
+
);
|
|
614
781
|
await new Promise((resolve, reject) => {
|
|
615
782
|
const xhr = new XMLHttpRequest();
|
|
616
|
-
xhr.open("PUT",
|
|
617
|
-
for (const [k, v] of Object.entries(
|
|
783
|
+
xhr.open("PUT", created.uploadUrl);
|
|
784
|
+
for (const [k, v] of Object.entries(created.headers)) {
|
|
618
785
|
xhr.setRequestHeader(k, v);
|
|
619
786
|
}
|
|
620
787
|
if (opts.onProgress) {
|
|
@@ -641,10 +808,10 @@ var Room = class _Room {
|
|
|
641
808
|
});
|
|
642
809
|
await this.http.request(
|
|
643
810
|
"POST",
|
|
644
|
-
`/api/v1/files/${
|
|
811
|
+
`/api/v1/files/${created.fileId}/commit`,
|
|
645
812
|
{}
|
|
646
813
|
);
|
|
647
|
-
return
|
|
814
|
+
return created.fileId;
|
|
648
815
|
}
|
|
649
816
|
/**
|
|
650
817
|
* List files shared in this room — newest-first, paginated.
|
|
@@ -669,23 +836,26 @@ var Room = class _Room {
|
|
|
669
836
|
}
|
|
670
837
|
);
|
|
671
838
|
return {
|
|
672
|
-
items: raw.items.map((r) =>
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
839
|
+
items: raw.items.map((r) => {
|
|
840
|
+
const f = parseFileOut(r.file);
|
|
841
|
+
return {
|
|
842
|
+
messageId: r.message_id,
|
|
843
|
+
senderId: r.sender_id,
|
|
844
|
+
createdAtMs: r.created_at_ms,
|
|
845
|
+
file: {
|
|
846
|
+
id: f.id,
|
|
847
|
+
kind: f.kind,
|
|
848
|
+
mime: f.mime,
|
|
849
|
+
size: f.sizeBytes,
|
|
850
|
+
name: f.originalName ?? null,
|
|
851
|
+
width: f.width ?? null,
|
|
852
|
+
height: f.height ?? null,
|
|
853
|
+
durationSec: f.durationSec ?? null,
|
|
854
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
855
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
}),
|
|
689
859
|
nextCursor: raw.next_cursor,
|
|
690
860
|
hasMore: raw.has_more
|
|
691
861
|
};
|
|
@@ -723,11 +893,12 @@ var Room = class _Room {
|
|
|
723
893
|
this.ws.send({ type: "typing", room_id: this.roomId, is_typing: isTyping });
|
|
724
894
|
}
|
|
725
895
|
async edit(messageId, patch) {
|
|
726
|
-
|
|
896
|
+
const raw = await this.http.request(
|
|
727
897
|
"PUT",
|
|
728
898
|
`/api/v1/rooms/${this.roomId}/messages/${messageId}`,
|
|
729
899
|
patch
|
|
730
900
|
);
|
|
901
|
+
return toMessageOut(raw);
|
|
731
902
|
}
|
|
732
903
|
async delete(messageId, scope = "ALL") {
|
|
733
904
|
await this.http.request(
|
|
@@ -752,12 +923,39 @@ var Room = class _Room {
|
|
|
752
923
|
* Other members get a `MEMBER_LEFT` [`roomEvent`](../reference/noverachat.md#events).
|
|
753
924
|
* Idempotent if already a non-member; throws 409 if the caller was KICKED
|
|
754
925
|
* (ask an operator to restore first). For GROUP/SUPER_GROUP, re-entry needs
|
|
755
|
-
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
756
|
-
|
|
926
|
+
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
927
|
+
*
|
|
928
|
+
* Optional `deleteMessages` — atomically clean up chat history as part
|
|
929
|
+
* of leaving. Default is `"none"` (messages survive so the room's
|
|
930
|
+
* history keeps making sense to remaining members).
|
|
931
|
+
* - `"none"` — leave only.
|
|
932
|
+
* - `"mine"` — soft-delete every message YOU sent, then leave. Other
|
|
933
|
+
* members' messages are untouched. Broadcasts
|
|
934
|
+
* `messages_cleared_by_sender` so connected clients hide
|
|
935
|
+
* them locally.
|
|
936
|
+
* - `"all"` — wipe every message in the room, then leave. Subject to
|
|
937
|
+
* the app's `allow_member_bulk_delete` gate and per-room
|
|
938
|
+
* OPERATOR check — a plain member gets 403 back. */
|
|
939
|
+
async leave(options) {
|
|
940
|
+
const mode = options?.deleteMessages ?? "none";
|
|
941
|
+
const query = mode === "none" ? "" : `?delete_messages=${encodeURIComponent(mode)}`;
|
|
757
942
|
await this.http.request(
|
|
758
943
|
"POST",
|
|
759
|
-
`/api/v1/rooms/${this.roomId}/leave`
|
|
944
|
+
`/api/v1/rooms/${this.roomId}/leave${query}`
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
/** Delete every message YOU sent in this room. Standalone version of
|
|
948
|
+
* `leave({ deleteMessages: "mine" })` — useful when a user wants to
|
|
949
|
+
* scrub their history without actually leaving. Other members'
|
|
950
|
+
* messages are untouched; broadcasts `messages_cleared_by_sender`
|
|
951
|
+
* so connected clients hide the vanished ones locally.
|
|
952
|
+
* No OPERATOR gate — you own your messages. */
|
|
953
|
+
async deleteMyMessages() {
|
|
954
|
+
const raw = await this.http.request(
|
|
955
|
+
"DELETE",
|
|
956
|
+
`/api/v1/rooms/${this.roomId}/messages/mine`
|
|
760
957
|
);
|
|
958
|
+
return parseBulkDeleteBySenderResult(raw);
|
|
761
959
|
}
|
|
762
960
|
/**
|
|
763
961
|
* Set the caller's per-room push preference — like long-pressing a
|
|
@@ -783,11 +981,11 @@ var Room = class _Room {
|
|
|
783
981
|
* hide rendered messages up to (and including) that id.
|
|
784
982
|
*/
|
|
785
983
|
async deleteAllMessages() {
|
|
786
|
-
const raw = await this.http.request(
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
984
|
+
const raw = await this.http.request(
|
|
985
|
+
"DELETE",
|
|
986
|
+
`/api/v1/rooms/${this.roomId}/messages`
|
|
987
|
+
);
|
|
988
|
+
return parseBulkDeleteResult(raw);
|
|
791
989
|
}
|
|
792
990
|
async react(messageId, key) {
|
|
793
991
|
await this.http.request(
|
|
@@ -874,17 +1072,11 @@ var Room = class _Room {
|
|
|
874
1072
|
* rooms, so the caller must already be a member to read this.
|
|
875
1073
|
*/
|
|
876
1074
|
async listMembers() {
|
|
877
|
-
const raw = await this.http.request(
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
pushTrigger: m.push_trigger,
|
|
883
|
-
lastReadMessageId: m.last_read_message_id,
|
|
884
|
-
joinedAt: m.joined_at,
|
|
885
|
-
isOnline: m.is_online ?? null,
|
|
886
|
-
mutedUntil: m.muted_until ?? null
|
|
887
|
-
}));
|
|
1075
|
+
const raw = await this.http.request(
|
|
1076
|
+
"GET",
|
|
1077
|
+
`/api/v1/rooms/${this.roomId}/members`
|
|
1078
|
+
);
|
|
1079
|
+
return raw.map((m) => parseRoomMemberOut(m));
|
|
888
1080
|
}
|
|
889
1081
|
// -------- mute --------
|
|
890
1082
|
/** OPERATOR-only: mute a member so they can't send messages in this
|
|
@@ -912,13 +1104,11 @@ var Room = class _Room {
|
|
|
912
1104
|
* newer than the caller's `read_watermark`, plus the room's
|
|
913
1105
|
* `last_message_id` so the UI can show a "•" dot regardless of count. */
|
|
914
1106
|
async unread() {
|
|
915
|
-
const raw = await this.http.request(
|
|
916
|
-
|
|
917
|
-
roomId
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
lastMessageId: raw.last_message_id
|
|
921
|
-
};
|
|
1107
|
+
const raw = await this.http.request(
|
|
1108
|
+
"GET",
|
|
1109
|
+
`/api/v1/rooms/${this.roomId}/unread`
|
|
1110
|
+
);
|
|
1111
|
+
return parseRoomUnread(raw);
|
|
922
1112
|
}
|
|
923
1113
|
// -------- freeze --------
|
|
924
1114
|
/** OPERATOR-only moderation: freeze this room. Non-admin messages are
|
|
@@ -953,49 +1143,35 @@ var Room = class _Room {
|
|
|
953
1143
|
}
|
|
954
1144
|
// -------- announcements --------
|
|
955
1145
|
async listAnnouncements() {
|
|
956
|
-
const raw = await this.http.request(
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
createdBy: a.created_by,
|
|
962
|
-
createdAt: a.created_at,
|
|
963
|
-
updatedAt: a.updated_at
|
|
964
|
-
}));
|
|
1146
|
+
const raw = await this.http.request(
|
|
1147
|
+
"GET",
|
|
1148
|
+
`/api/v1/rooms/${this.roomId}/announcements`
|
|
1149
|
+
);
|
|
1150
|
+
return raw.map((a) => parseAnnouncementOut(a));
|
|
965
1151
|
}
|
|
966
1152
|
async getCurrentAnnouncement() {
|
|
967
|
-
const raw = await this.http.request(
|
|
1153
|
+
const raw = await this.http.request(
|
|
1154
|
+
"GET",
|
|
1155
|
+
`/api/v1/rooms/${this.roomId}/announcements/current`
|
|
1156
|
+
);
|
|
968
1157
|
if (!raw) return null;
|
|
969
|
-
return
|
|
970
|
-
id: raw.id,
|
|
971
|
-
roomId: raw.room_id,
|
|
972
|
-
content: raw.content,
|
|
973
|
-
createdBy: raw.created_by,
|
|
974
|
-
createdAt: raw.created_at,
|
|
975
|
-
updatedAt: raw.updated_at
|
|
976
|
-
};
|
|
1158
|
+
return parseAnnouncementOut(raw);
|
|
977
1159
|
}
|
|
978
1160
|
async postAnnouncement(content) {
|
|
979
|
-
const raw = await this.http.request(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
createdAt: raw.created_at,
|
|
986
|
-
updatedAt: raw.updated_at
|
|
987
|
-
};
|
|
1161
|
+
const raw = await this.http.request(
|
|
1162
|
+
"POST",
|
|
1163
|
+
`/api/v1/rooms/${this.roomId}/announcements`,
|
|
1164
|
+
{ content }
|
|
1165
|
+
);
|
|
1166
|
+
return parseAnnouncementOut(raw);
|
|
988
1167
|
}
|
|
989
1168
|
async updateAnnouncement(id, content) {
|
|
990
|
-
const raw = await this.http.request(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
createdAt: raw.created_at,
|
|
997
|
-
updatedAt: raw.updated_at
|
|
998
|
-
};
|
|
1169
|
+
const raw = await this.http.request(
|
|
1170
|
+
"PATCH",
|
|
1171
|
+
`/api/v1/rooms/${this.roomId}/announcements/${id}`,
|
|
1172
|
+
{ content }
|
|
1173
|
+
);
|
|
1174
|
+
return parseAnnouncementOut(raw);
|
|
999
1175
|
}
|
|
1000
1176
|
async deleteAnnouncement(id) {
|
|
1001
1177
|
await this.http.request(
|
|
@@ -1017,32 +1193,21 @@ var Room = class _Room {
|
|
|
1017
1193
|
const body = {};
|
|
1018
1194
|
if (opts && "expiresInHours" in opts) body.expires_in_hours = opts.expiresInHours;
|
|
1019
1195
|
if (opts && "maxUses" in opts) body.max_uses = opts.maxUses;
|
|
1020
|
-
const raw = await this.http.request(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
expiresAt: raw.expires_at,
|
|
1027
|
-
maxUses: raw.max_uses,
|
|
1028
|
-
usedCount: raw.used_count,
|
|
1029
|
-
revokedAt: raw.revoked_at
|
|
1030
|
-
};
|
|
1196
|
+
const raw = await this.http.request(
|
|
1197
|
+
"POST",
|
|
1198
|
+
`/api/v1/rooms/${this.roomId}/invites`,
|
|
1199
|
+
body
|
|
1200
|
+
);
|
|
1201
|
+
return parseInviteTokenOut(raw);
|
|
1031
1202
|
}
|
|
1032
1203
|
/** OPERATOR-only: list all invite tokens for this room (including
|
|
1033
1204
|
* revoked / expired / exhausted), newest first. */
|
|
1034
1205
|
async listInvites() {
|
|
1035
|
-
const raw = await this.http.request(
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
createdAt: r.created_at,
|
|
1041
|
-
expiresAt: r.expires_at,
|
|
1042
|
-
maxUses: r.max_uses,
|
|
1043
|
-
usedCount: r.used_count,
|
|
1044
|
-
revokedAt: r.revoked_at
|
|
1045
|
-
}));
|
|
1206
|
+
const raw = await this.http.request(
|
|
1207
|
+
"GET",
|
|
1208
|
+
`/api/v1/rooms/${this.roomId}/invites`
|
|
1209
|
+
);
|
|
1210
|
+
return raw.map((r) => parseInviteTokenOut(r));
|
|
1046
1211
|
}
|
|
1047
1212
|
/** OPERATOR-only: revoke a token immediately. Idempotent — revoking
|
|
1048
1213
|
* an already-revoked token returns 204 as well. */
|
|
@@ -1300,13 +1465,7 @@ var NoveraChat = class {
|
|
|
1300
1465
|
* destructive buttons to expose. */
|
|
1301
1466
|
async appPolicy() {
|
|
1302
1467
|
const raw = await this.http.request("GET", "/api/v1/app/policy");
|
|
1303
|
-
return
|
|
1304
|
-
appId: raw.app_id,
|
|
1305
|
-
allowMemberBulkDelete: raw.allow_member_bulk_delete,
|
|
1306
|
-
readReceiptsEnabled: raw.read_receipts_enabled ?? true,
|
|
1307
|
-
typingIndicatorsEnabled: raw.typing_indicators_enabled ?? true,
|
|
1308
|
-
deleteAnyMessageEnabled: raw.allow_member_delete_any_message ?? false
|
|
1309
|
-
};
|
|
1468
|
+
return parseAppPolicy(raw);
|
|
1310
1469
|
}
|
|
1311
1470
|
// ---- device tokens (push) ----
|
|
1312
1471
|
/**
|
|
@@ -1316,19 +1475,23 @@ var NoveraChat = class {
|
|
|
1316
1475
|
* safe. Returns the stored device record (token shown only as a prefix).
|
|
1317
1476
|
*/
|
|
1318
1477
|
async registerDevice(params) {
|
|
1319
|
-
const raw = await this.http.request(
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1478
|
+
const raw = await this.http.request(
|
|
1479
|
+
"POST",
|
|
1480
|
+
"/api/v1/users/me/devices",
|
|
1481
|
+
{
|
|
1482
|
+
device_id: params.deviceId,
|
|
1483
|
+
token: params.token,
|
|
1484
|
+
token_type: params.tokenType,
|
|
1485
|
+
os: params.os ?? null,
|
|
1486
|
+
app_version: params.appVersion ?? null
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
return parseDeviceOut(raw);
|
|
1327
1490
|
}
|
|
1328
1491
|
/** List the devices registered for the authenticated user. */
|
|
1329
1492
|
async listDevices() {
|
|
1330
1493
|
const raw = await this.http.request("GET", "/api/v1/users/me/devices");
|
|
1331
|
-
return raw.items
|
|
1494
|
+
return parseDeviceListResponse(raw).items;
|
|
1332
1495
|
}
|
|
1333
1496
|
/**
|
|
1334
1497
|
* Turn push delivery on/off for one device without unregistering it —
|
|
@@ -1366,7 +1529,7 @@ var NoveraChat = class {
|
|
|
1366
1529
|
"GET",
|
|
1367
1530
|
"/api/v1/users/me/push-preferences"
|
|
1368
1531
|
);
|
|
1369
|
-
return
|
|
1532
|
+
return parsePushPreferencesOut(raw);
|
|
1370
1533
|
}
|
|
1371
1534
|
/**
|
|
1372
1535
|
* Flip the caller's global push kill switch. Pass `false` to mute
|
|
@@ -1379,7 +1542,7 @@ var NoveraChat = class {
|
|
|
1379
1542
|
"/api/v1/users/me/push-preferences",
|
|
1380
1543
|
{ push_enabled: enabled }
|
|
1381
1544
|
);
|
|
1382
|
-
return
|
|
1545
|
+
return parsePushPreferencesOut(raw);
|
|
1383
1546
|
}
|
|
1384
1547
|
// -------------------------------------------------------------------------
|
|
1385
1548
|
// User blocks — KakaoTalk-style one-way block.
|
|
@@ -1403,11 +1566,7 @@ var NoveraChat = class {
|
|
|
1403
1566
|
`/api/v1/users/me/blocks/${encodeURIComponent(userId)}`,
|
|
1404
1567
|
{ reason: reason ?? null }
|
|
1405
1568
|
);
|
|
1406
|
-
return
|
|
1407
|
-
blockedUserId: raw.blocked_user_id,
|
|
1408
|
-
reason: raw.reason,
|
|
1409
|
-
createdAt: raw.created_at
|
|
1410
|
-
};
|
|
1569
|
+
return parseBlockOut(raw);
|
|
1411
1570
|
}
|
|
1412
1571
|
/** Unblock a user. Idempotent — resolves either way. */
|
|
1413
1572
|
async unblockUser(userId) {
|
|
@@ -1419,23 +1578,7 @@ var NoveraChat = class {
|
|
|
1419
1578
|
/** List every user the caller has blocked, newest first. */
|
|
1420
1579
|
async listBlocks() {
|
|
1421
1580
|
const raw = await this.http.request("GET", "/api/v1/users/me/blocks");
|
|
1422
|
-
return raw.map((r) => (
|
|
1423
|
-
blockedUserId: r.blocked_user_id,
|
|
1424
|
-
reason: r.reason,
|
|
1425
|
-
createdAt: r.created_at
|
|
1426
|
-
}));
|
|
1427
|
-
}
|
|
1428
|
-
_toDeviceInfo(raw) {
|
|
1429
|
-
return {
|
|
1430
|
-
deviceId: raw.device_id,
|
|
1431
|
-
tokenType: raw.token_type,
|
|
1432
|
-
tokenPreview: raw.token_preview,
|
|
1433
|
-
os: raw.os,
|
|
1434
|
-
appVersion: raw.app_version,
|
|
1435
|
-
pushEnabled: raw.push_enabled,
|
|
1436
|
-
lastActiveAt: raw.last_active_at,
|
|
1437
|
-
createdAt: raw.created_at
|
|
1438
|
-
};
|
|
1581
|
+
return raw.map((r) => parseBlockOut(r));
|
|
1439
1582
|
}
|
|
1440
1583
|
/**
|
|
1441
1584
|
* Authorization-gated URL for downloading a file attachment.
|
|
@@ -1493,41 +1636,27 @@ var NoveraChat = class {
|
|
|
1493
1636
|
* shipping date.
|
|
1494
1637
|
*/
|
|
1495
1638
|
async getFileMeta(fileId) {
|
|
1496
|
-
const raw = await this.http.request(
|
|
1639
|
+
const raw = await this.http.request(
|
|
1640
|
+
"GET",
|
|
1641
|
+
`/api/v1/files/${encodeURIComponent(fileId)}/meta`
|
|
1642
|
+
);
|
|
1643
|
+
const f = parseFileOut(raw);
|
|
1497
1644
|
return {
|
|
1498
|
-
id:
|
|
1499
|
-
kind:
|
|
1500
|
-
mime:
|
|
1501
|
-
size:
|
|
1502
|
-
name:
|
|
1503
|
-
width:
|
|
1504
|
-
height:
|
|
1505
|
-
durationSec:
|
|
1506
|
-
thumbnailStatus:
|
|
1507
|
-
forwardBlocked: Boolean(
|
|
1645
|
+
id: f.id,
|
|
1646
|
+
kind: f.kind,
|
|
1647
|
+
mime: f.mime,
|
|
1648
|
+
size: f.sizeBytes,
|
|
1649
|
+
name: f.originalName ?? null,
|
|
1650
|
+
width: f.width ?? null,
|
|
1651
|
+
height: f.height ?? null,
|
|
1652
|
+
durationSec: f.durationSec ?? null,
|
|
1653
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
1654
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
1508
1655
|
};
|
|
1509
1656
|
}
|
|
1510
1657
|
async unreadSummary() {
|
|
1511
1658
|
const raw = await this.http.request("GET", "/api/v1/users/me/unread");
|
|
1512
|
-
return
|
|
1513
|
-
total: raw.total,
|
|
1514
|
-
rooms: raw.rooms.map((r) => ({
|
|
1515
|
-
roomId: r.room_id,
|
|
1516
|
-
name: r.name,
|
|
1517
|
-
roomType: r.room_type ?? null,
|
|
1518
|
-
unreadCount: r.unread_count,
|
|
1519
|
-
lastMessageId: r.last_message_id,
|
|
1520
|
-
lastMessagePreview: r.last_message_preview ?? null,
|
|
1521
|
-
lastReadMessageId: r.last_read_message_id,
|
|
1522
|
-
memberCount: r.member_count ?? 0,
|
|
1523
|
-
members: (r.members_preview ?? []).map((m) => ({
|
|
1524
|
-
userId: m.user_id,
|
|
1525
|
-
nickname: m.nickname ?? null,
|
|
1526
|
-
profileImageUrl: m.profile_image_url ?? null,
|
|
1527
|
-
isOnline: m.is_online ?? null
|
|
1528
|
-
}))
|
|
1529
|
-
}))
|
|
1530
|
-
};
|
|
1659
|
+
return parseUnreadSummary(raw);
|
|
1531
1660
|
}
|
|
1532
1661
|
/**
|
|
1533
1662
|
* Accept an invite link — call this in flows where you receive a
|
|
@@ -1545,12 +1674,11 @@ var NoveraChat = class {
|
|
|
1545
1674
|
* kicked from the target room.
|
|
1546
1675
|
*/
|
|
1547
1676
|
async acceptInvite(token) {
|
|
1548
|
-
const raw = await this.http.request(
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
};
|
|
1677
|
+
const raw = await this.http.request(
|
|
1678
|
+
"POST",
|
|
1679
|
+
`/api/v1/invites/${encodeURIComponent(token)}/accept`
|
|
1680
|
+
);
|
|
1681
|
+
return parseInviteAcceptResponse(raw);
|
|
1554
1682
|
}
|
|
1555
1683
|
// ---- internal ----
|
|
1556
1684
|
dispatch(msg) {
|