Skip to content

Commit b3c85fb

Browse files
committed
refactor
1 parent ae08b5d commit b3c85fb

File tree

2 files changed

+240
-196
lines changed

2 files changed

+240
-196
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2025 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
19+
qx.Class.define("osparc.conversation.Conversation", {
20+
extend: qx.ui.core.Widget,
21+
22+
/**
23+
* @param conversation {osparc.data.model.Conversation} Conversation
24+
*/
25+
construct: function(conversation) {
26+
this.base(arguments);
27+
28+
this._messages = [];
29+
30+
this._setLayout(new qx.ui.layout.VBox(5));
31+
32+
this._buildLayout();
33+
34+
if (conversation) {
35+
this.setConversation(conversation);
36+
}
37+
},
38+
39+
properties: {
40+
conversation: {
41+
check: "osparc.data.model.Conversation",
42+
init: null,
43+
nullable: true,
44+
event: "changeConversation",
45+
apply: "_applyConversation",
46+
},
47+
},
48+
49+
members: {
50+
_messages: null,
51+
52+
_createChildControlImpl: function(id) {
53+
let control;
54+
switch (id) {
55+
case "spacer-top":
56+
control = new qx.ui.core.Spacer();
57+
this._addAt(control, 0, {
58+
flex: 100 // high number to keep even a one message list at the bottom
59+
});
60+
break;
61+
case "messages-container-scroll":
62+
control = new qx.ui.container.Scroll();
63+
this._addAt(control, 1, {
64+
flex: 1
65+
});
66+
break;
67+
case "messages-container":
68+
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
69+
alignY: "middle"
70+
});
71+
this.getChildControl("messages-container-scroll").add(control);
72+
break;
73+
case "load-more-button":
74+
control = new osparc.ui.form.FetchButton(this.tr("Load more messages..."));
75+
control.addListener("execute", () => this.__reloadMessages(false));
76+
this._addAt(control, 2);
77+
break;
78+
case "add-message":
79+
control = new osparc.conversation.AddMessage().set({
80+
padding: 5,
81+
});
82+
this.bind("conversation", control, "conversationId", {
83+
converter: conversation => conversation ? conversation.getConversationId() : null
84+
});
85+
this._addAt(control, 3);
86+
break;
87+
}
88+
return control || this.base(arguments, id);
89+
},
90+
91+
_buildLayout: function() {
92+
this.getChildControl("spacer-top");
93+
this.getChildControl("messages-container");
94+
this.getChildControl("add-message");
95+
},
96+
97+
_applyConversation: function(conversation) {
98+
this.__reloadMessages(true);
99+
100+
if (conversation) {
101+
conversation.addListener("messageAdded", e => {
102+
const data = e.getData();
103+
this.addMessage(data);
104+
});
105+
conversation.addListener("messageUpdated", e => {
106+
const data = e.getData();
107+
this.updateMessage(data);
108+
});
109+
conversation.addListener("messageDeleted", e => {
110+
const data = e.getData();
111+
this.deleteMessage(data);
112+
});
113+
}
114+
},
115+
116+
__reloadMessages: function(removeMessages = true) {
117+
if (removeMessages) {
118+
this.clearAllMessages();
119+
}
120+
121+
const loadMoreMessages = this.getChildControl("load-more-button");
122+
if (this.getConversation() === null) {
123+
loadMoreMessages.hide();
124+
return;
125+
}
126+
127+
loadMoreMessages.show();
128+
loadMoreMessages.setFetching(true);
129+
this.getConversation().getNextMessages()
130+
.then(resp => {
131+
const messages = resp["data"];
132+
messages.forEach(message => this.addMessage(message));
133+
if (resp["_links"]["next"] === null && loadMoreMessages) {
134+
loadMoreMessages.exclude();
135+
}
136+
})
137+
.finally(() => loadMoreMessages.setFetching(false));
138+
},
139+
140+
clearAllMessages: function() {
141+
this._messages = [];
142+
this.getChildControl("messages-container").removeAll();
143+
},
144+
145+
addMessage: function(message) {
146+
// ignore it if it was already there
147+
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
148+
if (messageIndex !== -1) {
149+
return;
150+
}
151+
152+
// determine insertion index for latest‐first order
153+
const newTime = new Date(message["created"]);
154+
let insertAt = this._messages.findIndex(m => new Date(m["created"]) > newTime);
155+
if (insertAt === -1) {
156+
insertAt = this._messages.length;
157+
}
158+
159+
// Insert the message in the messages array
160+
this._messages.splice(insertAt, 0, message);
161+
162+
// Add the UI element to the messages list
163+
let control = null;
164+
switch (message["type"]) {
165+
case "MESSAGE":
166+
control = new osparc.conversation.MessageUI(message);
167+
control.addListener("messageUpdated", e => this.updateMessage(e.getData()));
168+
control.addListener("messageDeleted", e => this.deleteMessage(e.getData()));
169+
break;
170+
case "NOTIFICATION":
171+
control = new osparc.conversation.NotificationUI(message);
172+
break;
173+
}
174+
if (control) {
175+
// insert into the UI at the same position
176+
const messagesContainer = this.getChildControl("messages-container");
177+
messagesContainer.addAt(control, insertAt);
178+
}
179+
180+
// scroll to bottom
181+
// add timeout to ensure the scroll happens after the UI is updated
182+
setTimeout(() => {
183+
const messagesScroll = this.getChildControl("messages-container-scroll");
184+
messagesScroll.scrollToY(messagesScroll.getChildControl("pane").getScrollMaxY());
185+
}, 50);
186+
},
187+
188+
deleteMessage: function(message) {
189+
// remove it from the messages array
190+
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
191+
if (messageIndex === -1) {
192+
return;
193+
}
194+
this._messages.splice(messageIndex, 1);
195+
196+
// Remove the UI element from the messages list
197+
const messagesContainer = this.getChildControl("messages-container");
198+
const children = messagesContainer.getChildren();
199+
const controlIndex = children.findIndex(
200+
ctrl => ("getMessage" in ctrl && ctrl.getMessage()["messageId"] === message["messageId"])
201+
);
202+
if (controlIndex > -1) {
203+
messagesContainer.remove(children[controlIndex]);
204+
}
205+
},
206+
207+
updateMessage: function(message) {
208+
// Replace the message in the messages array
209+
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
210+
if (messageIndex === -1) {
211+
return;
212+
}
213+
this._messages[messageIndex] = message;
214+
215+
// Update the UI element from the messages list
216+
const messagesContainer = this.getChildControl("messages-container");
217+
const messageUI = messagesContainer.getChildren().find(control => {
218+
return "getMessage" in control && control.getMessage()["messageId"] === message["messageId"];
219+
});
220+
if (messageUI) {
221+
// Force a new reference
222+
messageUI.setMessage(Object.assign({}, message));
223+
}
224+
},
225+
}
226+
});

0 commit comments

Comments
 (0)