jupyterlab-chat 0.19.0-alpha.2 → 0.19.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/lib/model.d.ts +3 -3
- package/lib/model.js +48 -3
- package/lib/ychat.d.ts +17 -6
- package/lib/ychat.js +73 -13
- package/package.json +2 -2
- package/src/model.ts +55 -7
- package/src/ychat.ts +92 -23
package/lib/model.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractChatModel, AbstractChatContext, IChatContext,
|
|
1
|
+
import { AbstractChatModel, AbstractChatContext, IChatContext, IChatModel, IMessageContent, INewMessage, IUser } from '@jupyter/chat';
|
|
2
2
|
import { IChangedArgs } from '@jupyterlab/coreutils';
|
|
3
3
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
4
|
import { User } from '@jupyterlab/services';
|
|
@@ -65,13 +65,13 @@ export declare class LabChatModel extends AbstractChatModel implements DocumentR
|
|
|
65
65
|
toJSON(): PartialJSONObject;
|
|
66
66
|
fromJSON(data: PartialJSONObject): void;
|
|
67
67
|
createChatContext(): IChatContext;
|
|
68
|
-
messagesInserted(index: number, messages:
|
|
68
|
+
messagesInserted(index: number, messages: IMessageContent[]): Promise<void>;
|
|
69
69
|
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
70
70
|
/**
|
|
71
71
|
* Override the clear messages method.
|
|
72
72
|
*/
|
|
73
73
|
clearMessages(): void;
|
|
74
|
-
updateMessage(id: string, updatedMessage:
|
|
74
|
+
updateMessage(id: string, updatedMessage: IMessageContent): Promise<boolean | void> | boolean | void;
|
|
75
75
|
deleteMessage(id: string): Promise<boolean | void> | boolean | void;
|
|
76
76
|
/**
|
|
77
77
|
* Function called when the input content changed.
|
package/lib/model.js
CHANGED
|
@@ -104,8 +104,8 @@ export class LabChatModel extends AbstractChatModel {
|
|
|
104
104
|
this.updateWriters(writers);
|
|
105
105
|
};
|
|
106
106
|
this._onchange = async (_, changes) => {
|
|
107
|
-
if (changes.
|
|
108
|
-
const msgDelta = changes.
|
|
107
|
+
if (changes.messageListChanges) {
|
|
108
|
+
const msgDelta = changes.messageListChanges;
|
|
109
109
|
let index = 0;
|
|
110
110
|
for (const delta of msgDelta) {
|
|
111
111
|
if (delta.retain) {
|
|
@@ -113,7 +113,7 @@ export class LabChatModel extends AbstractChatModel {
|
|
|
113
113
|
}
|
|
114
114
|
else if (delta.insert) {
|
|
115
115
|
const messages = delta.insert.map(ymessage => {
|
|
116
|
-
const { sender, attachments: attachmentIds, mentions: mentionsIds, ...baseMessage } = ymessage;
|
|
116
|
+
const { sender, attachments: attachmentIds, mentions: mentionsIds, ...baseMessage } = ymessage.toJSON();
|
|
117
117
|
// Build the base message with sender.
|
|
118
118
|
const msg = {
|
|
119
119
|
...baseMessage,
|
|
@@ -152,6 +152,51 @@ export class LabChatModel extends AbstractChatModel {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
+
if (changes.messageChanges) {
|
|
156
|
+
// Update change in the message.
|
|
157
|
+
changes.messageChanges.forEach(change => {
|
|
158
|
+
const message = this.messages[change.index];
|
|
159
|
+
if (change.type === 'remove') {
|
|
160
|
+
delete message[change.key];
|
|
161
|
+
}
|
|
162
|
+
else if (change.newValue !== undefined) {
|
|
163
|
+
const key = change.key;
|
|
164
|
+
const value = change.newValue;
|
|
165
|
+
if (key === 'attachments') {
|
|
166
|
+
const attachments = [];
|
|
167
|
+
value.forEach(attachmentId => {
|
|
168
|
+
const attachment = this.sharedModel.getAttachment(attachmentId);
|
|
169
|
+
if (attachment) {
|
|
170
|
+
attachments.push(attachment);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
if (attachments.length) {
|
|
174
|
+
message.update({ attachments });
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
message.update({ attachments: undefined });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else if (key === 'mentions') {
|
|
181
|
+
const mentions = value.map(user => this.sharedModel.getUser(user) || {
|
|
182
|
+
username: 'User undefined',
|
|
183
|
+
mention_name: 'User-undefined'
|
|
184
|
+
});
|
|
185
|
+
if (mentions === null || mentions === void 0 ? void 0 : mentions.length) {
|
|
186
|
+
message.update({ mentions });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else if (['body', 'time', 'raw_time', 'deleted', 'edited'].includes(key)) {
|
|
190
|
+
const update = {};
|
|
191
|
+
update[key] = value;
|
|
192
|
+
message.update(update);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.error(`The attribute '${key}' of message cannot be updated`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
155
200
|
if (changes.metadataChanges) {
|
|
156
201
|
changes.metadataChanges.forEach(change => {
|
|
157
202
|
// no need to search for update or add, if the new value contains ID, let's
|
package/lib/ychat.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { IAttachment,
|
|
1
|
+
import { IAttachment, IMessageContent, IUser } from '@jupyter/chat';
|
|
2
2
|
import { Delta, DocumentChange, IMapChange, YDocument } from '@jupyter/ydoc';
|
|
3
3
|
import { JSONObject, PartialJSONValue } from '@lumino/coreutils';
|
|
4
|
+
import * as Y from 'yjs';
|
|
4
5
|
/**
|
|
5
6
|
* The type for a YMessage.
|
|
6
7
|
*/
|
|
7
|
-
export type IYmessage =
|
|
8
|
+
export type IYmessage = IMessageContent<string, string>;
|
|
8
9
|
/**
|
|
9
10
|
* The type for a YMessage.
|
|
10
11
|
*/
|
|
@@ -16,7 +17,11 @@ export interface IChatChanges extends DocumentChange {
|
|
|
16
17
|
/**
|
|
17
18
|
* Changes in messages.
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
+
messageListChanges?: MessageListChange;
|
|
21
|
+
/**
|
|
22
|
+
* Changes of one message.
|
|
23
|
+
*/
|
|
24
|
+
messageChanges?: MessageChange[];
|
|
20
25
|
/**
|
|
21
26
|
* Changes in users.
|
|
22
27
|
*/
|
|
@@ -30,10 +35,16 @@ export interface IChatChanges extends DocumentChange {
|
|
|
30
35
|
*/
|
|
31
36
|
metadataChanges?: MetadataChange[];
|
|
32
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The message list change type.
|
|
40
|
+
*/
|
|
41
|
+
export type MessageListChange = Delta<Y.Map<any>[]>;
|
|
33
42
|
/**
|
|
34
43
|
* The message change type.
|
|
35
44
|
*/
|
|
36
|
-
export type MessageChange =
|
|
45
|
+
export type MessageChange = IMapChange<any> & {
|
|
46
|
+
index: number;
|
|
47
|
+
};
|
|
37
48
|
/**
|
|
38
49
|
* The user change type.
|
|
39
50
|
*/
|
|
@@ -73,8 +84,8 @@ export declare class YChat extends YDocument<IChatChanges> {
|
|
|
73
84
|
getUser(username: string | undefined): IUser | undefined;
|
|
74
85
|
setUser(user: IUser): void;
|
|
75
86
|
getMessage(index: number): IYmessage | undefined;
|
|
76
|
-
addMessage(
|
|
77
|
-
updateMessage(index: number,
|
|
87
|
+
addMessage(msg: IYmessage): void;
|
|
88
|
+
updateMessage(index: number, msg: IYmessage): void;
|
|
78
89
|
getMessageIndex(id: string): number;
|
|
79
90
|
deleteMessage(index: number): void;
|
|
80
91
|
getAttachment(id: string): IAttachment | undefined;
|
package/lib/ychat.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { YDocument } from '@jupyter/ydoc';
|
|
6
6
|
import { JSONExt, UUID } from '@lumino/coreutils';
|
|
7
|
+
import * as Y from 'yjs';
|
|
7
8
|
/**
|
|
8
9
|
* The jupyterlab chat shared document.
|
|
9
10
|
*/
|
|
@@ -50,10 +51,55 @@ export class YChat extends YDocument {
|
|
|
50
51
|
});
|
|
51
52
|
this._changed.emit({ userChanges });
|
|
52
53
|
};
|
|
53
|
-
this._messagesObserver = (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
this._messagesObserver = (events) => {
|
|
55
|
+
events.forEach(event => {
|
|
56
|
+
if (event instanceof Y.YArrayEvent) {
|
|
57
|
+
// Change on the message list.
|
|
58
|
+
const messageListChanges = event.delta;
|
|
59
|
+
this._changed.emit({
|
|
60
|
+
messageListChanges
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (event instanceof Y.YMapEvent) {
|
|
64
|
+
// Change on existing message(s), let's update the whole message.
|
|
65
|
+
const messageChanges = new Array();
|
|
66
|
+
event.keysChanged.forEach(key => {
|
|
67
|
+
const change = event.changes.keys.get(key);
|
|
68
|
+
if (change) {
|
|
69
|
+
const index = this.getMessageIndex(event.target.get('id'));
|
|
70
|
+
switch (change.action) {
|
|
71
|
+
case 'add':
|
|
72
|
+
messageChanges.push({
|
|
73
|
+
index,
|
|
74
|
+
key,
|
|
75
|
+
newValue: event.target.get(key),
|
|
76
|
+
type: 'add'
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
case 'delete':
|
|
80
|
+
messageChanges.push({
|
|
81
|
+
index,
|
|
82
|
+
key,
|
|
83
|
+
oldValue: change.oldValue,
|
|
84
|
+
type: 'remove'
|
|
85
|
+
});
|
|
86
|
+
break;
|
|
87
|
+
case 'update':
|
|
88
|
+
messageChanges.push({
|
|
89
|
+
index,
|
|
90
|
+
key: key,
|
|
91
|
+
oldValue: change.oldValue,
|
|
92
|
+
newValue: event.target.get(key),
|
|
93
|
+
type: 'change'
|
|
94
|
+
});
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
this._changed.emit({
|
|
100
|
+
messageChanges
|
|
101
|
+
});
|
|
102
|
+
}
|
|
57
103
|
});
|
|
58
104
|
};
|
|
59
105
|
this._attachmentsObserver = (event) => {
|
|
@@ -122,7 +168,7 @@ export class YChat extends YDocument {
|
|
|
122
168
|
this._users = this.ydoc.getMap('users');
|
|
123
169
|
this._users.observe(this._usersObserver);
|
|
124
170
|
this._messages = this.ydoc.getArray('messages');
|
|
125
|
-
this._messages.
|
|
171
|
+
this._messages.observeDeep(this._messagesObserver);
|
|
126
172
|
this._attachments = this.ydoc.getMap('attachments');
|
|
127
173
|
this._attachments.observe(this._attachmentsObserver);
|
|
128
174
|
this._metadata = this.ydoc.getMap('metadata');
|
|
@@ -162,9 +208,15 @@ export class YChat extends YDocument {
|
|
|
162
208
|
this.transact(() => {
|
|
163
209
|
var _a, _b, _c;
|
|
164
210
|
const messages = (_a = value['messages']) !== null && _a !== void 0 ? _a : [];
|
|
211
|
+
const ymessages = [];
|
|
165
212
|
messages.forEach(message => {
|
|
166
|
-
|
|
213
|
+
const ymessage = new Y.Map();
|
|
214
|
+
for (const [key, value] of Object.entries(message)) {
|
|
215
|
+
ymessage.set(key, value);
|
|
216
|
+
}
|
|
217
|
+
ymessages.push(ymessage);
|
|
167
218
|
});
|
|
219
|
+
this._messages.push(ymessages);
|
|
168
220
|
const users = (_b = value['users']) !== null && _b !== void 0 ? _b : {};
|
|
169
221
|
Object.entries(users).forEach(([key, val]) => this._users.set(key, val));
|
|
170
222
|
const metadata = (_c = value['metadata']) !== null && _c !== void 0 ? _c : {};
|
|
@@ -187,21 +239,29 @@ export class YChat extends YDocument {
|
|
|
187
239
|
});
|
|
188
240
|
}
|
|
189
241
|
getMessage(index) {
|
|
190
|
-
return this._messages.get(index);
|
|
242
|
+
return this._messages.get(index).toJSON();
|
|
191
243
|
}
|
|
192
|
-
addMessage(
|
|
244
|
+
addMessage(msg) {
|
|
193
245
|
this.transact(() => {
|
|
194
|
-
|
|
246
|
+
const ymessage = new Y.Map();
|
|
247
|
+
for (const [key, value] of Object.entries(msg)) {
|
|
248
|
+
ymessage.set(key, value);
|
|
249
|
+
}
|
|
250
|
+
this._messages.push([ymessage]);
|
|
195
251
|
});
|
|
196
252
|
}
|
|
197
|
-
updateMessage(index,
|
|
253
|
+
updateMessage(index, msg) {
|
|
198
254
|
this.transact(() => {
|
|
199
|
-
this._messages.
|
|
200
|
-
|
|
255
|
+
const original = this._messages.get(index);
|
|
256
|
+
for (const [key, value] of Object.entries(msg)) {
|
|
257
|
+
if (original.get(key) !== value) {
|
|
258
|
+
original.set(key, value);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
201
261
|
});
|
|
202
262
|
}
|
|
203
263
|
getMessageIndex(id) {
|
|
204
|
-
return this._messages.toArray().findIndex(msg => msg.id === id);
|
|
264
|
+
return this._messages.toArray().findIndex(msg => msg.get('id') === id);
|
|
205
265
|
}
|
|
206
266
|
deleteMessage(index) {
|
|
207
267
|
this.transact(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab-chat",
|
|
3
|
-
"version": "0.19.0
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "The library to build a chat based on shared document",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"watch:src": "tsc -w --sourceMap"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@jupyter/chat": "^0.19.0
|
|
45
|
+
"@jupyter/chat": "^0.19.0",
|
|
46
46
|
"@jupyter/collaborative-drive": "^4.0.2",
|
|
47
47
|
"@jupyter/ydoc": "^2.0.0 || ^3.0.0",
|
|
48
48
|
"@jupyterlab/application": "^4.2.0",
|
package/src/model.ts
CHANGED
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
AbstractChatContext,
|
|
9
9
|
IAttachment,
|
|
10
10
|
IChatContext,
|
|
11
|
-
IChatMessage,
|
|
12
11
|
IChatModel,
|
|
13
12
|
IInputModel,
|
|
13
|
+
IMessageContent,
|
|
14
14
|
INewMessage,
|
|
15
15
|
IUser
|
|
16
16
|
} from '@jupyter/chat';
|
|
@@ -189,7 +189,7 @@ export class LabChatModel
|
|
|
189
189
|
|
|
190
190
|
async messagesInserted(
|
|
191
191
|
index: number,
|
|
192
|
-
messages:
|
|
192
|
+
messages: IMessageContent[]
|
|
193
193
|
): Promise<void> {
|
|
194
194
|
// Ensure the chat has an ID before inserting the messages, to properly catch the
|
|
195
195
|
// unread messages (the last read message is saved using the chat ID).
|
|
@@ -246,7 +246,7 @@ export class LabChatModel
|
|
|
246
246
|
|
|
247
247
|
updateMessage(
|
|
248
248
|
id: string,
|
|
249
|
-
updatedMessage:
|
|
249
|
+
updatedMessage: IMessageContent
|
|
250
250
|
): Promise<boolean | void> | boolean | void {
|
|
251
251
|
const index = this.sharedModel.getMessageIndex(id);
|
|
252
252
|
let message = this.sharedModel.getMessage(index);
|
|
@@ -398,8 +398,8 @@ export class LabChatModel
|
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
private _onchange = async (_: YChat, changes: IChatChanges) => {
|
|
401
|
-
if (changes.
|
|
402
|
-
const msgDelta = changes.
|
|
401
|
+
if (changes.messageListChanges) {
|
|
402
|
+
const msgDelta = changes.messageListChanges;
|
|
403
403
|
let index = 0;
|
|
404
404
|
for (const delta of msgDelta) {
|
|
405
405
|
if (delta.retain) {
|
|
@@ -411,10 +411,10 @@ export class LabChatModel
|
|
|
411
411
|
attachments: attachmentIds,
|
|
412
412
|
mentions: mentionsIds,
|
|
413
413
|
...baseMessage
|
|
414
|
-
} = ymessage;
|
|
414
|
+
} = ymessage.toJSON() as IYmessage;
|
|
415
415
|
|
|
416
416
|
// Build the base message with sender.
|
|
417
|
-
const msg:
|
|
417
|
+
const msg: IMessageContent = {
|
|
418
418
|
...baseMessage,
|
|
419
419
|
sender: this.sharedModel.getUser(sender) || {
|
|
420
420
|
username: 'User undefined',
|
|
@@ -457,6 +457,54 @@ export class LabChatModel
|
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
if (changes.messageChanges) {
|
|
461
|
+
// Update change in the message.
|
|
462
|
+
changes.messageChanges.forEach(change => {
|
|
463
|
+
const message = this.messages[change.index];
|
|
464
|
+
if (change.type === 'remove') {
|
|
465
|
+
delete message[change.key as keyof IMessageContent];
|
|
466
|
+
} else if (change.newValue !== undefined) {
|
|
467
|
+
const key = change.key;
|
|
468
|
+
const value = change.newValue;
|
|
469
|
+
if (key === 'attachments') {
|
|
470
|
+
const attachments: IAttachment[] = [];
|
|
471
|
+
(value as string[]).forEach(attachmentId => {
|
|
472
|
+
const attachment = this.sharedModel.getAttachment(attachmentId);
|
|
473
|
+
if (attachment) {
|
|
474
|
+
attachments.push(attachment);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
if (attachments.length) {
|
|
478
|
+
message.update({ attachments });
|
|
479
|
+
} else {
|
|
480
|
+
message.update({ attachments: undefined });
|
|
481
|
+
}
|
|
482
|
+
} else if (key === 'mentions') {
|
|
483
|
+
const mentions: IUser[] = (value as string[]).map(
|
|
484
|
+
user =>
|
|
485
|
+
this.sharedModel.getUser(user) || {
|
|
486
|
+
username: 'User undefined',
|
|
487
|
+
mention_name: 'User-undefined'
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
if (mentions?.length) {
|
|
491
|
+
message.update({ mentions });
|
|
492
|
+
}
|
|
493
|
+
} else if (
|
|
494
|
+
['body', 'time', 'raw_time', 'deleted', 'edited'].includes(key)
|
|
495
|
+
) {
|
|
496
|
+
const update: Partial<IMessageContent> = {};
|
|
497
|
+
update[key as keyof IMessageContent] = value;
|
|
498
|
+
message.update(update);
|
|
499
|
+
} else {
|
|
500
|
+
console.error(
|
|
501
|
+
`The attribute '${key}' of message cannot be updated`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
460
508
|
if (changes.metadataChanges) {
|
|
461
509
|
changes.metadataChanges.forEach(change => {
|
|
462
510
|
// no need to search for update or add, if the new value contains ID, let's
|
package/src/ychat.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IAttachment,
|
|
6
|
+
import { IAttachment, IMessageContent, IUser } from '@jupyter/chat';
|
|
7
7
|
import { Delta, DocumentChange, IMapChange, YDocument } from '@jupyter/ydoc';
|
|
8
8
|
import { JSONExt, JSONObject, PartialJSONValue, UUID } from '@lumino/coreutils';
|
|
9
9
|
import * as Y from 'yjs';
|
|
@@ -11,7 +11,7 @@ import * as Y from 'yjs';
|
|
|
11
11
|
/**
|
|
12
12
|
* The type for a YMessage.
|
|
13
13
|
*/
|
|
14
|
-
export type IYmessage =
|
|
14
|
+
export type IYmessage = IMessageContent<string, string>;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* The type for a YMessage.
|
|
@@ -25,7 +25,11 @@ export interface IChatChanges extends DocumentChange {
|
|
|
25
25
|
/**
|
|
26
26
|
* Changes in messages.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
messageListChanges?: MessageListChange;
|
|
29
|
+
/**
|
|
30
|
+
* Changes of one message.
|
|
31
|
+
*/
|
|
32
|
+
messageChanges?: MessageChange[];
|
|
29
33
|
/**
|
|
30
34
|
* Changes in users.
|
|
31
35
|
*/
|
|
@@ -40,10 +44,15 @@ export interface IChatChanges extends DocumentChange {
|
|
|
40
44
|
metadataChanges?: MetadataChange[];
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
/**
|
|
48
|
+
* The message list change type.
|
|
49
|
+
*/
|
|
50
|
+
export type MessageListChange = Delta<Y.Map<any>[]>;
|
|
51
|
+
|
|
43
52
|
/**
|
|
44
53
|
* The message change type.
|
|
45
54
|
*/
|
|
46
|
-
export type MessageChange =
|
|
55
|
+
export type MessageChange = IMapChange<any> & { index: number };
|
|
47
56
|
|
|
48
57
|
/**
|
|
49
58
|
* The user change type.
|
|
@@ -72,8 +81,8 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
72
81
|
this._users = this.ydoc.getMap<IUser>('users');
|
|
73
82
|
this._users.observe(this._usersObserver);
|
|
74
83
|
|
|
75
|
-
this._messages = this.ydoc.getArray<
|
|
76
|
-
this._messages.
|
|
84
|
+
this._messages = this.ydoc.getArray<Y.Map<any>>('messages');
|
|
85
|
+
this._messages.observeDeep(this._messagesObserver);
|
|
77
86
|
|
|
78
87
|
this._attachments = this.ydoc.getMap<IAttachment>('attachments');
|
|
79
88
|
this._attachments.observe(this._attachmentsObserver);
|
|
@@ -132,9 +141,15 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
132
141
|
|
|
133
142
|
this.transact(() => {
|
|
134
143
|
const messages = (value['messages'] as unknown as Array<IYmessage>) ?? [];
|
|
144
|
+
const ymessages: Y.Map<any>[] = [];
|
|
135
145
|
messages.forEach(message => {
|
|
136
|
-
|
|
146
|
+
const ymessage = new Y.Map<any>();
|
|
147
|
+
for (const [key, value] of Object.entries(message)) {
|
|
148
|
+
ymessage.set(key, value);
|
|
149
|
+
}
|
|
150
|
+
ymessages.push(ymessage);
|
|
137
151
|
});
|
|
152
|
+
this._messages.push(ymessages);
|
|
138
153
|
|
|
139
154
|
const users = value['users'] ?? {};
|
|
140
155
|
Object.entries(users).forEach(([key, val]) =>
|
|
@@ -167,24 +182,32 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
getMessage(index: number): IYmessage | undefined {
|
|
170
|
-
return this._messages.get(index);
|
|
185
|
+
return this._messages.get(index).toJSON() as IYmessage;
|
|
171
186
|
}
|
|
172
187
|
|
|
173
|
-
addMessage(
|
|
188
|
+
addMessage(msg: IYmessage): void {
|
|
174
189
|
this.transact(() => {
|
|
175
|
-
|
|
190
|
+
const ymessage = new Y.Map<any>();
|
|
191
|
+
for (const [key, value] of Object.entries(msg)) {
|
|
192
|
+
ymessage.set(key, value);
|
|
193
|
+
}
|
|
194
|
+
this._messages.push([ymessage]);
|
|
176
195
|
});
|
|
177
196
|
}
|
|
178
197
|
|
|
179
|
-
updateMessage(index: number,
|
|
198
|
+
updateMessage(index: number, msg: IYmessage): void {
|
|
180
199
|
this.transact(() => {
|
|
181
|
-
this._messages.
|
|
182
|
-
|
|
200
|
+
const original = this._messages.get(index);
|
|
201
|
+
for (const [key, value] of Object.entries(msg)) {
|
|
202
|
+
if (original.get(key) !== value) {
|
|
203
|
+
original.set(key, value);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
183
206
|
});
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
getMessageIndex(id: string): number {
|
|
187
|
-
return this._messages.toArray().findIndex(msg => msg.id === id);
|
|
210
|
+
return this._messages.toArray().findIndex(msg => msg.get('id') === id);
|
|
188
211
|
}
|
|
189
212
|
|
|
190
213
|
deleteMessage(index: number): void {
|
|
@@ -258,14 +281,60 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
258
281
|
}
|
|
259
282
|
});
|
|
260
283
|
|
|
261
|
-
this._changed.emit({ userChanges }
|
|
284
|
+
this._changed.emit({ userChanges });
|
|
262
285
|
};
|
|
263
286
|
|
|
264
|
-
private _messagesObserver = (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
287
|
+
private _messagesObserver = (
|
|
288
|
+
events: (Y.YArrayEvent<Y.Map<any>> | Y.YMapEvent<any>)[]
|
|
289
|
+
): void => {
|
|
290
|
+
events.forEach(event => {
|
|
291
|
+
if (event instanceof Y.YArrayEvent) {
|
|
292
|
+
// Change on the message list.
|
|
293
|
+
const messageListChanges = event.delta;
|
|
294
|
+
this._changed.emit({
|
|
295
|
+
messageListChanges
|
|
296
|
+
} as Partial<IChatChanges>);
|
|
297
|
+
} else if (event instanceof Y.YMapEvent) {
|
|
298
|
+
// Change on existing message(s), let's update the whole message.
|
|
299
|
+
const messageChanges = new Array<MessageChange>();
|
|
300
|
+
event.keysChanged.forEach(key => {
|
|
301
|
+
const change = event.changes.keys.get(key);
|
|
302
|
+
if (change) {
|
|
303
|
+
const index = this.getMessageIndex(event.target.get('id'));
|
|
304
|
+
switch (change.action) {
|
|
305
|
+
case 'add':
|
|
306
|
+
messageChanges.push({
|
|
307
|
+
index,
|
|
308
|
+
key,
|
|
309
|
+
newValue: event.target.get(key),
|
|
310
|
+
type: 'add'
|
|
311
|
+
});
|
|
312
|
+
break;
|
|
313
|
+
case 'delete':
|
|
314
|
+
messageChanges.push({
|
|
315
|
+
index,
|
|
316
|
+
key,
|
|
317
|
+
oldValue: change.oldValue,
|
|
318
|
+
type: 'remove'
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
case 'update':
|
|
322
|
+
messageChanges.push({
|
|
323
|
+
index,
|
|
324
|
+
key: key,
|
|
325
|
+
oldValue: change.oldValue,
|
|
326
|
+
newValue: event.target.get(key),
|
|
327
|
+
type: 'change'
|
|
328
|
+
});
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
this._changed.emit({
|
|
334
|
+
messageChanges
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
269
338
|
};
|
|
270
339
|
|
|
271
340
|
private _attachmentsObserver = (event: Y.YMapEvent<IAttachment>): void => {
|
|
@@ -300,7 +369,7 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
300
369
|
}
|
|
301
370
|
});
|
|
302
371
|
|
|
303
|
-
this._changed.emit({ attachmentChanges }
|
|
372
|
+
this._changed.emit({ attachmentChanges });
|
|
304
373
|
};
|
|
305
374
|
|
|
306
375
|
private _metadataObserver = (event: Y.YMapEvent<IMetadata>): void => {
|
|
@@ -332,11 +401,11 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
332
401
|
}
|
|
333
402
|
});
|
|
334
403
|
|
|
335
|
-
this._changed.emit({ metadataChanges }
|
|
404
|
+
this._changed.emit({ metadataChanges });
|
|
336
405
|
};
|
|
337
406
|
|
|
338
407
|
private _users: Y.Map<IUser>;
|
|
339
|
-
private _messages: Y.Array<
|
|
408
|
+
private _messages: Y.Array<Y.Map<any>>;
|
|
340
409
|
private _attachments: Y.Map<IAttachment>;
|
|
341
410
|
private _metadata: Y.Map<IMetadata>;
|
|
342
411
|
}
|