Skip to content

Commit d3e83ec

Browse files
committed
use a Y.Map instead of dict for message
1 parent 7a046de commit d3e83ec

File tree

7 files changed

+217
-44
lines changed

7 files changed

+217
-44
lines changed

packages/jupyter-chat/src/components/messages/header.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Box, Typography } from '@mui/material';
77
import React, { useEffect, useState } from 'react';
88

99
import { Avatar } from '../avatar';
10+
import { IChatModel } from '../../model';
1011
import { IChatMessage } from '../../types';
1112

1213
const MESSAGE_HEADER_CLASS = 'jp-chat-message-header';
@@ -20,6 +21,10 @@ type ChatMessageHeaderProps = {
2021
* The chat message.
2122
*/
2223
message: IChatMessage;
24+
/**
25+
* The chat model.
26+
*/
27+
model: IChatModel;
2328
/**
2429
* Whether this message is from the current user.
2530
*/
@@ -35,7 +40,8 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps): JSX.Element {
3540
return <></>;
3641
}
3742
const [datetime, setDatetime] = useState<Record<number, string>>({});
38-
const message = props.message;
43+
const model = props.model;
44+
const [message, setMessage] = useState<IChatMessage>(props.message);
3945
const sender = message.sender;
4046
/**
4147
* Effect: update cached datetime strings upon receiving a new message.
@@ -74,6 +80,19 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps): JSX.Element {
7480
}
7581
});
7682

83+
// Listen for changes in the current message.
84+
useEffect(() => {
85+
function messageChanged(_: any, msg: IChatMessage) {
86+
if (msg.id === message.id) {
87+
setMessage({ ...msg });
88+
}
89+
}
90+
model.messageChanged.connect(messageChanged);
91+
return () => {
92+
model.messageChanged.disconnect(messageChanged);
93+
};
94+
}, [model]);
95+
7796
const avatar = message.stacked ? null : Avatar({ user: sender });
7897

7998
const name =

packages/jupyter-chat/src/components/messages/message.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ type ChatMessageProps = BaseMessageProps & {
3737
*/
3838
export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
3939
(props, ref): JSX.Element => {
40-
const { message, model, rmRegistry } = props;
40+
const { model, rmRegistry } = props;
41+
const [message, setMessage] = useState<IChatMessage>(props.message);
4142
const [edit, setEdit] = useState<boolean>(false);
4243
const [deleted, setDeleted] = useState<boolean>(false);
4344
const [canEdit, setCanEdit] = useState<boolean>(false);
@@ -62,6 +63,19 @@ export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
6263
}
6364
}, [model, message]);
6465

66+
// Listen for changes in the current message.
67+
useEffect(() => {
68+
function messageChanged(_: any, msg: IChatMessage) {
69+
if (msg.id === message.id) {
70+
setMessage({ ...msg });
71+
}
72+
}
73+
model.messageChanged.connect(messageChanged);
74+
return () => {
75+
model.messageChanged.disconnect(messageChanged);
76+
};
77+
}, [model]);
78+
6579
// Create an input model only if the message is edited.
6680
const startEdition = (): void => {
6781
if (!canEdit) {

packages/jupyter-chat/src/components/messages/messages.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export function ChatMessages(props: BaseMessageProps): JSX.Element {
215215
>
216216
<ChatMessageHeader
217217
message={message}
218+
model={model}
218219
isCurrentUser={isCurrentUser}
219220
/>
220221
<ChatMessage

packages/jupyter-chat/src/model.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ export interface IChatModel extends IDisposable {
9191
*/
9292
readonly messagesUpdated: ISignal<IChatModel, void>;
9393

94+
/**
95+
* A signal emitting when a single message is updated.
96+
*/
97+
readonly messageChanged: ISignal<IChatModel, IChatMessage>;
98+
9499
/**
95100
* A signal emitting when the messages list is updated.
96101
*/
@@ -450,12 +455,19 @@ export abstract class AbstractChatModel implements IChatModel {
450455
}
451456

452457
/**
453-
* A signal emitting when the messages list is updated.
458+
* A signal emitting when the message list is updated.
454459
*/
455460
get messagesUpdated(): ISignal<IChatModel, void> {
456461
return this._messagesUpdated;
457462
}
458463

464+
/**
465+
* A signal emitting when a single message is updated.
466+
*/
467+
get messageChanged(): ISignal<IChatModel, IChatMessage> {
468+
return this._messageChanged;
469+
}
470+
459471
/**
460472
* A signal emitting when the messages list is updated.
461473
*/
@@ -612,6 +624,14 @@ export abstract class AbstractChatModel implements IChatModel {
612624
this._messagesUpdated.emit();
613625
}
614626

627+
/**
628+
* Function to call when a message is updated
629+
*/
630+
messageUpdated(index: number, message: IChatMessage): void {
631+
this.messages[index] = message;
632+
this._messageChanged.emit(message);
633+
}
634+
615635
/**
616636
* Update the current writers list.
617637
* This implementation only propagate the list via a signal.
@@ -731,6 +751,7 @@ export abstract class AbstractChatModel implements IChatModel {
731751
private _writers: IChatModel.IWriter[] = [];
732752
private _messageEditions = new Map<string, IInputModel>();
733753
private _messagesUpdated = new Signal<IChatModel, void>(this);
754+
private _messageChanged = new Signal<IChatModel, IChatMessage>(this);
734755
private _configChanged = new Signal<IChatModel, IConfig>(this);
735756
private _unreadChanged = new Signal<IChatModel, number[]>(this);
736757
private _viewportChanged = new Signal<IChatModel, number[]>(this);

packages/jupyterlab-chat/src/model.ts

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,8 @@ export class LabChatModel
398398
}
399399

400400
private _onchange = async (_: YChat, changes: IChatChanges) => {
401-
if (changes.messageChanges) {
402-
const msgDelta = changes.messageChanges;
401+
if (changes.messageListChanges) {
402+
const msgDelta = changes.messageListChanges;
403403
let index = 0;
404404
for (const delta of msgDelta) {
405405
if (delta.retain) {
@@ -411,7 +411,7 @@ export class LabChatModel
411411
attachments: attachmentIds,
412412
mentions: mentionsIds,
413413
...baseMessage
414-
} = ymessage;
414+
} = ymessage.toJSON() as IYmessage;
415415

416416
// Build the base message with sender.
417417
const msg: IChatMessage = {
@@ -457,6 +457,53 @@ export class LabChatModel
457457
}
458458
}
459459

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 IChatMessage];
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.attachments = attachments;
479+
} else {
480+
delete message.attachments;
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.mentions = mentions;
492+
}
493+
} else if (
494+
['body', 'time', 'raw_time', 'deleted', 'edited'].includes(key)
495+
) {
496+
(message as any)[key] = value;
497+
} else {
498+
console.error(
499+
`The attribute '${key}' of message cannot be updated`
500+
);
501+
}
502+
}
503+
this.messageUpdated(change.index, message);
504+
});
505+
}
506+
460507
if (changes.metadataChanges) {
461508
changes.metadataChanges.forEach(change => {
462509
// no need to search for update or add, if the new value contains ID, let's

0 commit comments

Comments
 (0)