jupyterlab-chat 0.9.0 → 0.10.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/factory.d.ts +1 -1
- package/lib/factory.js +4 -4
- package/lib/model.d.ts +15 -4
- package/lib/model.js +96 -13
- package/lib/ychat.d.ts +1 -1
- package/lib/ychat.js +2 -1
- package/package.json +2 -2
- package/src/factory.ts +5 -5
- package/src/model.ts +127 -15
- package/src/ychat.ts +7 -2
package/lib/factory.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ export declare class ChatWidgetFactory extends ABCWidgetFactory<LabChatPanel, La
|
|
|
52
52
|
private _rmRegistry;
|
|
53
53
|
private _chatCommandRegistry?;
|
|
54
54
|
private _attachmentOpenerRegistry?;
|
|
55
|
-
private
|
|
55
|
+
private _inputToolbarFactory?;
|
|
56
56
|
}
|
|
57
57
|
export declare namespace ChatWidgetFactory {
|
|
58
58
|
interface IContext extends DocumentRegistry.IContext<LabChatModel> {
|
package/lib/factory.js
CHANGED
|
@@ -52,9 +52,7 @@ export class ChatWidgetFactory extends ABCWidgetFactory {
|
|
|
52
52
|
this._rmRegistry = options.rmRegistry;
|
|
53
53
|
this._chatCommandRegistry = options.chatCommandRegistry;
|
|
54
54
|
this._attachmentOpenerRegistry = options.attachmentOpenerRegistry;
|
|
55
|
-
|
|
56
|
-
this._inputToolbarRegistry = options.inputToolbarFactory.create();
|
|
57
|
-
}
|
|
55
|
+
this._inputToolbarFactory = options.inputToolbarFactory;
|
|
58
56
|
}
|
|
59
57
|
/**
|
|
60
58
|
* Create a new widget given a context.
|
|
@@ -67,7 +65,9 @@ export class ChatWidgetFactory extends ABCWidgetFactory {
|
|
|
67
65
|
context.themeManager = this._themeManager;
|
|
68
66
|
context.chatCommandRegistry = this._chatCommandRegistry;
|
|
69
67
|
context.attachmentOpenerRegistry = this._attachmentOpenerRegistry;
|
|
70
|
-
|
|
68
|
+
if (this._inputToolbarFactory) {
|
|
69
|
+
context.inputToolbarRegistry = this._inputToolbarFactory.create();
|
|
70
|
+
}
|
|
71
71
|
return new LabChatPanel({
|
|
72
72
|
context,
|
|
73
73
|
content: new ChatWidget(context)
|
package/lib/model.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractChatModel, AbstractChatContext, IChatContext, IChatMessage, IChatModel, IInputModel, 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';
|
|
@@ -10,7 +10,7 @@ import { YChat } from './ychat';
|
|
|
10
10
|
* Chat model namespace.
|
|
11
11
|
*/
|
|
12
12
|
export declare namespace LabChatModel {
|
|
13
|
-
interface IOptions extends
|
|
13
|
+
interface IOptions extends IChatModel.IOptions {
|
|
14
14
|
widgetConfig: IWidgetConfig;
|
|
15
15
|
user: User.IIdentity | null;
|
|
16
16
|
sharedModel?: YChat;
|
|
@@ -20,7 +20,7 @@ export declare namespace LabChatModel {
|
|
|
20
20
|
/**
|
|
21
21
|
* The chat model.
|
|
22
22
|
*/
|
|
23
|
-
export declare class LabChatModel extends
|
|
23
|
+
export declare class LabChatModel extends AbstractChatModel implements DocumentRegistry.IModel {
|
|
24
24
|
constructor(options: LabChatModel.IOptions);
|
|
25
25
|
readonly collaborative = true;
|
|
26
26
|
get user(): IUser;
|
|
@@ -38,7 +38,8 @@ export declare class LabChatModel extends ChatModel implements DocumentRegistry.
|
|
|
38
38
|
fromString(data: string): void;
|
|
39
39
|
toJSON(): PartialJSONObject;
|
|
40
40
|
fromJSON(data: PartialJSONObject): void;
|
|
41
|
-
|
|
41
|
+
createChatContext(): IChatContext;
|
|
42
|
+
messagesInserted(index: number, messages: IChatMessage[]): Promise<void>;
|
|
42
43
|
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
43
44
|
/**
|
|
44
45
|
* Override the clear messages method.
|
|
@@ -55,6 +56,7 @@ export declare class LabChatModel extends ChatModel implements DocumentRegistry.
|
|
|
55
56
|
* Used to populate the writers list.
|
|
56
57
|
*/
|
|
57
58
|
onAwarenessChange: () => void;
|
|
59
|
+
private _buildMentionList;
|
|
58
60
|
private _resetWritingStatus;
|
|
59
61
|
private _onchange;
|
|
60
62
|
readonly defaultKernelName: string;
|
|
@@ -69,3 +71,12 @@ export declare class LabChatModel extends ChatModel implements DocumentRegistry.
|
|
|
69
71
|
private _timeoutWriting;
|
|
70
72
|
private _user;
|
|
71
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The chat context to be sent to the input model.
|
|
76
|
+
*/
|
|
77
|
+
export declare class LabChatContext extends AbstractChatContext {
|
|
78
|
+
/**
|
|
79
|
+
* The list of users who have connected to this chat.
|
|
80
|
+
*/
|
|
81
|
+
get users(): IUser[];
|
|
82
|
+
}
|
package/lib/model.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Jupyter Development Team.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { AbstractChatModel, AbstractChatContext } from '@jupyter/chat';
|
|
6
6
|
import { PromiseDelegate, UUID } from '@lumino/coreutils';
|
|
7
7
|
import { Signal } from '@lumino/signaling';
|
|
8
8
|
import { YChat } from './ychat';
|
|
@@ -10,7 +10,7 @@ const WRITING_DELAY = 1000;
|
|
|
10
10
|
/**
|
|
11
11
|
* The chat model.
|
|
12
12
|
*/
|
|
13
|
-
export class LabChatModel extends
|
|
13
|
+
export class LabChatModel extends AbstractChatModel {
|
|
14
14
|
constructor(options) {
|
|
15
15
|
super(options);
|
|
16
16
|
this.collaborative = true;
|
|
@@ -48,17 +48,17 @@ export class LabChatModel extends ChatModel {
|
|
|
48
48
|
}
|
|
49
49
|
this.updateWriters(writers);
|
|
50
50
|
};
|
|
51
|
-
this._onchange = (_, changes) => {
|
|
51
|
+
this._onchange = async (_, changes) => {
|
|
52
52
|
if (changes.messageChanges) {
|
|
53
53
|
const msgDelta = changes.messageChanges;
|
|
54
54
|
let index = 0;
|
|
55
|
-
|
|
55
|
+
for (const delta of msgDelta) {
|
|
56
56
|
if (delta.retain) {
|
|
57
57
|
index += delta.retain;
|
|
58
58
|
}
|
|
59
59
|
else if (delta.insert) {
|
|
60
60
|
const messages = delta.insert.map(ymessage => {
|
|
61
|
-
const { sender, attachments: attachmentIds, ...baseMessage } = ymessage;
|
|
61
|
+
const { sender, attachments: attachmentIds, mentions: mentionsIds, ...baseMessage } = ymessage;
|
|
62
62
|
// Build the base message with sender.
|
|
63
63
|
const msg = {
|
|
64
64
|
...baseMessage,
|
|
@@ -79,15 +79,21 @@ export class LabChatModel extends ChatModel {
|
|
|
79
79
|
msg.attachments = attachments;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
const mentions = mentionsIds === null || mentionsIds === void 0 ? void 0 : mentionsIds.map(user => this.sharedModel.getUser(user) || {
|
|
83
|
+
username: 'User undefined'
|
|
84
|
+
});
|
|
85
|
+
if (mentions === null || mentions === void 0 ? void 0 : mentions.length) {
|
|
86
|
+
msg.mentions = mentions;
|
|
87
|
+
}
|
|
82
88
|
return msg;
|
|
83
89
|
});
|
|
84
|
-
this.messagesInserted(index, messages);
|
|
90
|
+
await this.messagesInserted(index, messages);
|
|
85
91
|
index += messages.length;
|
|
86
92
|
}
|
|
87
93
|
else if (delta.delete) {
|
|
88
94
|
this.messagesDeleted(index, delta.delete);
|
|
89
95
|
}
|
|
90
|
-
}
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
if (changes.metadataChanges) {
|
|
93
99
|
changes.metadataChanges.forEach(change => {
|
|
@@ -98,6 +104,14 @@ export class LabChatModel extends ChatModel {
|
|
|
98
104
|
}
|
|
99
105
|
});
|
|
100
106
|
}
|
|
107
|
+
if (changes.userChanges) {
|
|
108
|
+
// Update the current user if it changes (if it has been mentioned for example).
|
|
109
|
+
changes.userChanges.forEach(change => {
|
|
110
|
+
if (change.key === this._user.username && change.newValue) {
|
|
111
|
+
this._user = change.newValue;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
101
115
|
};
|
|
102
116
|
this.defaultKernelName = '';
|
|
103
117
|
this.defaultKernelLanguage = '';
|
|
@@ -178,14 +192,18 @@ export class LabChatModel extends ChatModel {
|
|
|
178
192
|
fromJSON(data) {
|
|
179
193
|
// nothing to do
|
|
180
194
|
}
|
|
181
|
-
|
|
195
|
+
createChatContext() {
|
|
196
|
+
return new LabChatContext({ model: this });
|
|
197
|
+
}
|
|
198
|
+
async messagesInserted(index, messages) {
|
|
182
199
|
// Ensure the chat has an ID before inserting the messages, to properly catch the
|
|
183
200
|
// unread messages (the last read message is saved using the chat ID).
|
|
184
|
-
this._ready.promise.then(() => {
|
|
201
|
+
return this._ready.promise.then(() => {
|
|
185
202
|
super.messagesInserted(index, messages);
|
|
186
203
|
});
|
|
187
204
|
}
|
|
188
205
|
sendMessage(message) {
|
|
206
|
+
var _a;
|
|
189
207
|
this._resetWritingStatus();
|
|
190
208
|
if (this._timeoutWriting !== null) {
|
|
191
209
|
window.clearTimeout(this._timeoutWriting);
|
|
@@ -203,11 +221,17 @@ export class LabChatModel extends ChatModel {
|
|
|
203
221
|
this.sharedModel.setUser(this._user);
|
|
204
222
|
}
|
|
205
223
|
// Add the attachments to the message.
|
|
206
|
-
|
|
207
|
-
|
|
224
|
+
const attachmentIds = (_a = this.input.attachments) === null || _a === void 0 ? void 0 : _a.map(attachment => this.sharedModel.setAttachment(attachment));
|
|
225
|
+
if (attachmentIds === null || attachmentIds === void 0 ? void 0 : attachmentIds.length) {
|
|
208
226
|
msg.attachments = attachmentIds;
|
|
209
|
-
this.input.clearAttachments();
|
|
210
227
|
}
|
|
228
|
+
this.input.clearAttachments();
|
|
229
|
+
// Add the mentioned users.
|
|
230
|
+
const mentions = this._buildMentionList(this.input.mentions, message.body);
|
|
231
|
+
if (mentions.length) {
|
|
232
|
+
msg.mentions = mentions;
|
|
233
|
+
}
|
|
234
|
+
this.input.clearMentions();
|
|
211
235
|
this.sharedModel.addMessage(msg);
|
|
212
236
|
}
|
|
213
237
|
/**
|
|
@@ -235,10 +259,22 @@ export class LabChatModel extends ChatModel {
|
|
|
235
259
|
edited: true
|
|
236
260
|
};
|
|
237
261
|
}
|
|
262
|
+
// Update the attachments.
|
|
238
263
|
const attachmentIds = (_a = updatedMessage.attachments) === null || _a === void 0 ? void 0 : _a.map(attachment => this.sharedModel.setAttachment(attachment));
|
|
239
|
-
if (attachmentIds) {
|
|
264
|
+
if (attachmentIds === null || attachmentIds === void 0 ? void 0 : attachmentIds.length) {
|
|
240
265
|
message.attachments = attachmentIds;
|
|
241
266
|
}
|
|
267
|
+
else {
|
|
268
|
+
delete message.attachments;
|
|
269
|
+
}
|
|
270
|
+
// Update the mentioned users.
|
|
271
|
+
const mentions = this._buildMentionList(updatedMessage.mentions, updatedMessage.body);
|
|
272
|
+
if (mentions.length) {
|
|
273
|
+
message.mentions = mentions;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
delete message.mentions;
|
|
277
|
+
}
|
|
242
278
|
this.sharedModel.updateMessage(index, message);
|
|
243
279
|
}
|
|
244
280
|
deleteMessage(id) {
|
|
@@ -252,6 +288,28 @@ export class LabChatModel extends ChatModel {
|
|
|
252
288
|
message.deleted = true;
|
|
253
289
|
this.sharedModel.updateMessage(index, message);
|
|
254
290
|
}
|
|
291
|
+
_buildMentionList(userMentions, body) {
|
|
292
|
+
if (!userMentions) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
const mentions = [];
|
|
296
|
+
userMentions.forEach(user => {
|
|
297
|
+
// Make sure the user is still mentioned.
|
|
298
|
+
if (!user.mention_name) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const regex = new RegExp(user.mention_name);
|
|
302
|
+
if (!regex.exec(body)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// Save the mention name if necessary.
|
|
306
|
+
if (!(this.sharedModel.getUser(user.username) === user)) {
|
|
307
|
+
this.sharedModel.setUser(user);
|
|
308
|
+
}
|
|
309
|
+
mentions.push(user.username);
|
|
310
|
+
});
|
|
311
|
+
return mentions;
|
|
312
|
+
}
|
|
255
313
|
_resetWritingStatus() {
|
|
256
314
|
const awareness = this.sharedModel.awareness;
|
|
257
315
|
const states = awareness.getLocalState();
|
|
@@ -260,3 +318,28 @@ export class LabChatModel extends ChatModel {
|
|
|
260
318
|
this._timeoutWriting = null;
|
|
261
319
|
}
|
|
262
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* The chat context to be sent to the input model.
|
|
323
|
+
*/
|
|
324
|
+
export class LabChatContext extends AbstractChatContext {
|
|
325
|
+
/**
|
|
326
|
+
* The list of users who have connected to this chat.
|
|
327
|
+
*/
|
|
328
|
+
get users() {
|
|
329
|
+
const model = this._model;
|
|
330
|
+
const users = new Set();
|
|
331
|
+
// Get the user list from the shared YChat
|
|
332
|
+
Object.values(model.sharedModel.users).forEach(userObject => {
|
|
333
|
+
users.add(userObject);
|
|
334
|
+
});
|
|
335
|
+
// Add the users connected to the chat (even if they never sent a message).
|
|
336
|
+
model.sharedModel.awareness.getStates().forEach(value => {
|
|
337
|
+
const user = value.user;
|
|
338
|
+
if (!user) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
users.add(user);
|
|
342
|
+
});
|
|
343
|
+
return Array.from(users);
|
|
344
|
+
}
|
|
345
|
+
}
|
package/lib/ychat.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare class YChat extends YDocument<IChatChanges> {
|
|
|
65
65
|
*/
|
|
66
66
|
static create(options?: YDocument.IOptions): YChat;
|
|
67
67
|
get id(): string;
|
|
68
|
-
get users():
|
|
68
|
+
get users(): Record<string, IUser>;
|
|
69
69
|
get messages(): string[];
|
|
70
70
|
get attachments(): JSONObject;
|
|
71
71
|
getSource(): JSONObject;
|
package/lib/ychat.js
CHANGED
|
@@ -140,7 +140,8 @@ export class YChat extends YDocument {
|
|
|
140
140
|
return this._metadata.get('id') || '';
|
|
141
141
|
}
|
|
142
142
|
get users() {
|
|
143
|
-
|
|
143
|
+
const userDict = JSONExt.deepCopy(this._users.toJSON());
|
|
144
|
+
return userDict;
|
|
144
145
|
}
|
|
145
146
|
get messages() {
|
|
146
147
|
return JSONExt.deepCopy(this._messages.toJSON());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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.
|
|
45
|
+
"@jupyter/chat": "^0.10.0",
|
|
46
46
|
"@jupyter/collaborative-drive": "^3.0.0",
|
|
47
47
|
"@jupyter/ydoc": "^2.0.0 || ^3.0.0",
|
|
48
48
|
"@jupyterlab/application": "^4.2.0",
|
package/src/factory.ts
CHANGED
|
@@ -83,9 +83,7 @@ export class ChatWidgetFactory extends ABCWidgetFactory<
|
|
|
83
83
|
this._rmRegistry = options.rmRegistry;
|
|
84
84
|
this._chatCommandRegistry = options.chatCommandRegistry;
|
|
85
85
|
this._attachmentOpenerRegistry = options.attachmentOpenerRegistry;
|
|
86
|
-
|
|
87
|
-
this._inputToolbarRegistry = options.inputToolbarFactory.create();
|
|
88
|
-
}
|
|
86
|
+
this._inputToolbarFactory = options.inputToolbarFactory;
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
/**
|
|
@@ -99,7 +97,9 @@ export class ChatWidgetFactory extends ABCWidgetFactory<
|
|
|
99
97
|
context.themeManager = this._themeManager;
|
|
100
98
|
context.chatCommandRegistry = this._chatCommandRegistry;
|
|
101
99
|
context.attachmentOpenerRegistry = this._attachmentOpenerRegistry;
|
|
102
|
-
|
|
100
|
+
if (this._inputToolbarFactory) {
|
|
101
|
+
context.inputToolbarRegistry = this._inputToolbarFactory.create();
|
|
102
|
+
}
|
|
103
103
|
return new LabChatPanel({
|
|
104
104
|
context,
|
|
105
105
|
content: new ChatWidget(context)
|
|
@@ -110,7 +110,7 @@ export class ChatWidgetFactory extends ABCWidgetFactory<
|
|
|
110
110
|
private _rmRegistry: IRenderMimeRegistry;
|
|
111
111
|
private _chatCommandRegistry?: IChatCommandRegistry;
|
|
112
112
|
private _attachmentOpenerRegistry?: IAttachmentOpenerRegistry;
|
|
113
|
-
private
|
|
113
|
+
private _inputToolbarFactory?: IInputToolbarRegistryFactory;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
export namespace ChatWidgetFactory {
|
package/src/model.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
AbstractChatModel,
|
|
8
|
+
AbstractChatContext,
|
|
8
9
|
IAttachment,
|
|
10
|
+
IChatContext,
|
|
9
11
|
IChatMessage,
|
|
12
|
+
IChatModel,
|
|
10
13
|
IInputModel,
|
|
11
14
|
INewMessage,
|
|
12
15
|
IUser
|
|
@@ -26,7 +29,7 @@ const WRITING_DELAY = 1000;
|
|
|
26
29
|
* Chat model namespace.
|
|
27
30
|
*/
|
|
28
31
|
export namespace LabChatModel {
|
|
29
|
-
export interface IOptions extends
|
|
32
|
+
export interface IOptions extends IChatModel.IOptions {
|
|
30
33
|
widgetConfig: IWidgetConfig;
|
|
31
34
|
user: User.IIdentity | null;
|
|
32
35
|
sharedModel?: YChat;
|
|
@@ -37,7 +40,10 @@ export namespace LabChatModel {
|
|
|
37
40
|
/**
|
|
38
41
|
* The chat model.
|
|
39
42
|
*/
|
|
40
|
-
export class LabChatModel
|
|
43
|
+
export class LabChatModel
|
|
44
|
+
extends AbstractChatModel
|
|
45
|
+
implements DocumentRegistry.IModel
|
|
46
|
+
{
|
|
41
47
|
constructor(options: LabChatModel.IOptions) {
|
|
42
48
|
super(options);
|
|
43
49
|
|
|
@@ -133,10 +139,17 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
133
139
|
// nothing to do
|
|
134
140
|
}
|
|
135
141
|
|
|
136
|
-
|
|
142
|
+
createChatContext(): IChatContext {
|
|
143
|
+
return new LabChatContext({ model: this });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async messagesInserted(
|
|
147
|
+
index: number,
|
|
148
|
+
messages: IChatMessage[]
|
|
149
|
+
): Promise<void> {
|
|
137
150
|
// Ensure the chat has an ID before inserting the messages, to properly catch the
|
|
138
151
|
// unread messages (the last read message is saved using the chat ID).
|
|
139
|
-
this._ready.promise.then(() => {
|
|
152
|
+
return this._ready.promise.then(() => {
|
|
140
153
|
super.messagesInserted(index, messages);
|
|
141
154
|
});
|
|
142
155
|
}
|
|
@@ -162,13 +175,20 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
// Add the attachments to the message.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
const attachmentIds = this.input.attachments?.map(attachment =>
|
|
179
|
+
this.sharedModel.setAttachment(attachment)
|
|
180
|
+
);
|
|
181
|
+
if (attachmentIds?.length) {
|
|
169
182
|
msg.attachments = attachmentIds;
|
|
170
|
-
this.input.clearAttachments();
|
|
171
183
|
}
|
|
184
|
+
this.input.clearAttachments();
|
|
185
|
+
|
|
186
|
+
// Add the mentioned users.
|
|
187
|
+
const mentions = this._buildMentionList(this.input.mentions, message.body);
|
|
188
|
+
if (mentions.length) {
|
|
189
|
+
msg.mentions = mentions;
|
|
190
|
+
}
|
|
191
|
+
this.input.clearMentions();
|
|
172
192
|
|
|
173
193
|
this.sharedModel.addMessage(msg);
|
|
174
194
|
}
|
|
@@ -201,12 +221,28 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
201
221
|
edited: true
|
|
202
222
|
};
|
|
203
223
|
}
|
|
224
|
+
|
|
225
|
+
// Update the attachments.
|
|
204
226
|
const attachmentIds = updatedMessage.attachments?.map(attachment =>
|
|
205
227
|
this.sharedModel.setAttachment(attachment)
|
|
206
228
|
);
|
|
207
|
-
if (attachmentIds) {
|
|
229
|
+
if (attachmentIds?.length) {
|
|
208
230
|
message.attachments = attachmentIds;
|
|
231
|
+
} else {
|
|
232
|
+
delete message.attachments;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Update the mentioned users.
|
|
236
|
+
const mentions = this._buildMentionList(
|
|
237
|
+
updatedMessage.mentions,
|
|
238
|
+
updatedMessage.body
|
|
239
|
+
);
|
|
240
|
+
if (mentions.length) {
|
|
241
|
+
message.mentions = mentions;
|
|
242
|
+
} else {
|
|
243
|
+
delete message.mentions;
|
|
209
244
|
}
|
|
245
|
+
|
|
210
246
|
this.sharedModel.updateMessage(index, message as IYmessage);
|
|
211
247
|
}
|
|
212
248
|
|
|
@@ -258,6 +294,33 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
258
294
|
this.updateWriters(writers);
|
|
259
295
|
};
|
|
260
296
|
|
|
297
|
+
private _buildMentionList(
|
|
298
|
+
userMentions: IUser[] | undefined,
|
|
299
|
+
body: string
|
|
300
|
+
): string[] {
|
|
301
|
+
if (!userMentions) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
const mentions: string[] = [];
|
|
305
|
+
userMentions.forEach(user => {
|
|
306
|
+
// Make sure the user is still mentioned.
|
|
307
|
+
if (!user.mention_name) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const regex = new RegExp(user.mention_name);
|
|
311
|
+
if (!regex.exec(body)) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Save the mention name if necessary.
|
|
316
|
+
if (!(this.sharedModel.getUser(user.username) === user)) {
|
|
317
|
+
this.sharedModel.setUser(user);
|
|
318
|
+
}
|
|
319
|
+
mentions.push(user.username);
|
|
320
|
+
});
|
|
321
|
+
return mentions;
|
|
322
|
+
}
|
|
323
|
+
|
|
261
324
|
private _resetWritingStatus() {
|
|
262
325
|
const awareness = this.sharedModel.awareness;
|
|
263
326
|
const states = awareness.getLocalState();
|
|
@@ -266,11 +329,11 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
266
329
|
this._timeoutWriting = null;
|
|
267
330
|
}
|
|
268
331
|
|
|
269
|
-
private _onchange = (_: YChat, changes: IChatChanges) => {
|
|
332
|
+
private _onchange = async (_: YChat, changes: IChatChanges) => {
|
|
270
333
|
if (changes.messageChanges) {
|
|
271
334
|
const msgDelta = changes.messageChanges;
|
|
272
335
|
let index = 0;
|
|
273
|
-
|
|
336
|
+
for (const delta of msgDelta) {
|
|
274
337
|
if (delta.retain) {
|
|
275
338
|
index += delta.retain;
|
|
276
339
|
} else if (delta.insert) {
|
|
@@ -278,6 +341,7 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
278
341
|
const {
|
|
279
342
|
sender,
|
|
280
343
|
attachments: attachmentIds,
|
|
344
|
+
mentions: mentionsIds,
|
|
281
345
|
...baseMessage
|
|
282
346
|
} = ymessage;
|
|
283
347
|
|
|
@@ -303,14 +367,24 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
303
367
|
}
|
|
304
368
|
}
|
|
305
369
|
|
|
370
|
+
const mentions = mentionsIds?.map(
|
|
371
|
+
user =>
|
|
372
|
+
this.sharedModel.getUser(user) || {
|
|
373
|
+
username: 'User undefined'
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
if (mentions?.length) {
|
|
378
|
+
msg.mentions = mentions;
|
|
379
|
+
}
|
|
306
380
|
return msg;
|
|
307
381
|
});
|
|
308
|
-
this.messagesInserted(index, messages);
|
|
382
|
+
await this.messagesInserted(index, messages);
|
|
309
383
|
index += messages.length;
|
|
310
384
|
} else if (delta.delete) {
|
|
311
385
|
this.messagesDeleted(index, delta.delete);
|
|
312
386
|
}
|
|
313
|
-
}
|
|
387
|
+
}
|
|
314
388
|
}
|
|
315
389
|
|
|
316
390
|
if (changes.metadataChanges) {
|
|
@@ -322,6 +396,15 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
322
396
|
}
|
|
323
397
|
});
|
|
324
398
|
}
|
|
399
|
+
|
|
400
|
+
if (changes.userChanges) {
|
|
401
|
+
// Update the current user if it changes (if it has been mentioned for example).
|
|
402
|
+
changes.userChanges.forEach(change => {
|
|
403
|
+
if (change.key === this._user.username && change.newValue) {
|
|
404
|
+
this._user = change.newValue;
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
325
408
|
};
|
|
326
409
|
|
|
327
410
|
readonly defaultKernelName: string = '';
|
|
@@ -339,3 +422,32 @@ export class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
|
339
422
|
|
|
340
423
|
private _user: IUser;
|
|
341
424
|
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The chat context to be sent to the input model.
|
|
428
|
+
*/
|
|
429
|
+
export class LabChatContext extends AbstractChatContext {
|
|
430
|
+
/**
|
|
431
|
+
* The list of users who have connected to this chat.
|
|
432
|
+
*/
|
|
433
|
+
get users(): IUser[] {
|
|
434
|
+
const model = this._model as LabChatModel;
|
|
435
|
+
const users = new Set<IUser>();
|
|
436
|
+
|
|
437
|
+
// Get the user list from the shared YChat
|
|
438
|
+
Object.values(model.sharedModel.users).forEach(userObject => {
|
|
439
|
+
users.add(userObject);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Add the users connected to the chat (even if they never sent a message).
|
|
443
|
+
model.sharedModel.awareness.getStates().forEach(value => {
|
|
444
|
+
const user = value.user as IUser;
|
|
445
|
+
if (!user) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
users.add(user);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
return Array.from(users);
|
|
452
|
+
}
|
|
453
|
+
}
|
package/src/ychat.ts
CHANGED
|
@@ -100,8 +100,13 @@ export class YChat extends YDocument<IChatChanges> {
|
|
|
100
100
|
return (this._metadata.get('id') as string) || '';
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
get users():
|
|
104
|
-
|
|
103
|
+
get users(): Record<string, IUser> {
|
|
104
|
+
const userDict = JSONExt.deepCopy(this._users.toJSON()) as Record<
|
|
105
|
+
string,
|
|
106
|
+
IUser
|
|
107
|
+
>;
|
|
108
|
+
|
|
109
|
+
return userDict;
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
get messages(): string[] {
|