Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ qx.Class.define("osparc.conversation.AddMessage", {
});
break;
}
case "thumbnail": {
case "avatar": {
control = osparc.utils.Utils.createThumbnail(32);
const authStore = osparc.auth.Data.getInstance();
control.set({
Expand All @@ -95,6 +95,7 @@ qx.Class.define("osparc.conversation.AddMessage", {
break;
case "add-comment-button":
control = new qx.ui.form.Button(null, "@FontAwesome5Solid/arrow-up/16").set({
toolTipText: this.tr("Ctrl+Enter"),
backgroundColor: "input_background",
allowGrowX: false,
alignX: "right",
Expand Down Expand Up @@ -135,7 +136,7 @@ qx.Class.define("osparc.conversation.AddMessage", {
},

__buildLayout: function() {
this.getChildControl("thumbnail");
this.getChildControl("avatar");
this.getChildControl("comment-field");
this.getChildControl("add-comment-button");
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/* ************************************************************************

osparc - the simcore frontend

https://osparc.io

Copyright:
2025 IT'IS Foundation, https://itis.swiss

License:
MIT: https://opensource.org/licenses/MIT

Authors:
* Odei Maiz (odeimaiz)

************************************************************************ */


qx.Class.define("osparc.conversation.Conversation", {
extend: qx.ui.core.Widget,

/**
* @param conversation {osparc.data.model.Conversation} Conversation
*/
construct: function(conversation) {
this.base(arguments);

this._messages = [];

this._setLayout(new qx.ui.layout.VBox(5));

this._buildLayout();

if (conversation) {
this.setConversation(conversation);
}
},

properties: {
conversation: {
check: "osparc.data.model.Conversation",
init: null,
nullable: true,
event: "changeConversation",
apply: "_applyConversation",
},
},

events: {
"messagesChanged": "qx.event.type.Event",
},

members: {
_messages: null,

_createChildControlImpl: function(id) {
let control;
switch (id) {
case "spacer-top":
control = new qx.ui.core.Spacer();
this._addAt(control, 0, {
flex: 100 // high number to keep even a one message list at the bottom
});
break;
case "messages-container-scroll":
control = new qx.ui.container.Scroll();
this._addAt(control, 1, {
flex: 1
});
break;
case "messages-container":
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
alignY: "middle"
});
this.getChildControl("messages-container-scroll").add(control);
break;
case "load-more-button":
control = new osparc.ui.form.FetchButton(this.tr("Load more messages..."));
control.addListener("execute", () => this.__reloadMessages(false));
this._addAt(control, 2);
break;
case "add-message":
control = new osparc.conversation.AddMessage().set({
padding: 5,
});
this.bind("conversation", control, "conversationId", {
converter: conversation => conversation ? conversation.getConversationId() : null
});
this._addAt(control, 3);
break;
}
return control || this.base(arguments, id);
},

_buildLayout: function() {
this.getChildControl("spacer-top");
this.getChildControl("messages-container");
this.getChildControl("add-message");
},

_applyConversation: function(conversation) {
this.__reloadMessages(true);

if (conversation) {
conversation.addListener("messageAdded", e => {
const data = e.getData();
this.addMessage(data);
});
conversation.addListener("messageUpdated", e => {
const data = e.getData();
this.updateMessage(data);
});
conversation.addListener("messageDeleted", e => {
const data = e.getData();
this.deleteMessage(data);
});
}
},

__reloadMessages: function(removeMessages = true) {
if (removeMessages) {
this.clearAllMessages();
}

const loadMoreMessages = this.getChildControl("load-more-button");
if (this.getConversation() === null) {
loadMoreMessages.hide();
return;
}

loadMoreMessages.show();
loadMoreMessages.setFetching(true);
this.getConversation().getNextMessages()
.then(resp => {
const messages = resp["data"];
messages.forEach(message => this.addMessage(message));
if (resp["_links"]["next"] === null && loadMoreMessages) {
loadMoreMessages.exclude();
}
})
.finally(() => loadMoreMessages.setFetching(false));
},

_createMessageUI: function(message) {
return new osparc.conversation.MessageUI(message);
},

getMessages: function() {
return this._messages;
},

clearAllMessages: function() {
this._messages = [];
this.getChildControl("messages-container").removeAll();

this.fireEvent("messagesChanged");
},

addMessage: function(message) {
// ignore it if it was already there
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
if (messageIndex !== -1) {
return;
}

// determine insertion index for latest‐first order
const newTime = new Date(message["created"]);
let insertAt = this._messages.findIndex(m => new Date(m["created"]) > newTime);
if (insertAt === -1) {
insertAt = this._messages.length;
}

// Insert the message in the messages array
this._messages.splice(insertAt, 0, message);

// Add the UI element to the messages list
let control = null;
switch (message["type"]) {
case "MESSAGE":
control = this._createMessageUI(message);
control.addListener("messageUpdated", e => this.updateMessage(e.getData()));
control.addListener("messageDeleted", e => this.deleteMessage(e.getData()));
break;
case "NOTIFICATION":
control = new osparc.conversation.NotificationUI(message);
break;
}
if (control) {
// insert into the UI at the same position
const messagesContainer = this.getChildControl("messages-container");
messagesContainer.addAt(control, insertAt);
}

// scroll to bottom
// add timeout to ensure the scroll happens after the UI is updated
setTimeout(() => {
const messagesScroll = this.getChildControl("messages-container-scroll");
messagesScroll.scrollToY(messagesScroll.getChildControl("pane").getScrollMaxY());
}, 50);

this.fireEvent("messagesChanged");
},

deleteMessage: function(message) {
// remove it from the messages array
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
if (messageIndex === -1) {
return;
}
this._messages.splice(messageIndex, 1);

// Remove the UI element from the messages list
const messagesContainer = this.getChildControl("messages-container");
const children = messagesContainer.getChildren();
const controlIndex = children.findIndex(
ctrl => ("getMessage" in ctrl && ctrl.getMessage()["messageId"] === message["messageId"])
);
if (controlIndex > -1) {
messagesContainer.remove(children[controlIndex]);
}

this.fireEvent("messagesChanged");
},

updateMessage: function(message) {
// Replace the message in the messages array
const messageIndex = this._messages.findIndex(msg => msg["messageId"] === message["messageId"]);
if (messageIndex === -1) {
return;
}
this._messages[messageIndex] = message;

// Update the UI element from the messages list
const messagesContainer = this.getChildControl("messages-container");
const messageUI = messagesContainer.getChildren().find(control => {
return "getMessage" in control && control.getMessage()["messageId"] === message["messageId"];
});
if (messageUI) {
// Force a new reference
messageUI.setMessage(Object.assign({}, message));
}
},
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ qx.Class.define("osparc.conversation.MessageUI", {
const isMyMessage = this.self().isMyMessage(this.getMessage());
let control;
switch (id) {
case "thumbnail":
case "avatar":
control = new osparc.ui.basic.UserThumbnail(32).set({
marginTop: 4,
alignY: "top",
Expand Down Expand Up @@ -149,18 +149,18 @@ qx.Class.define("osparc.conversation.MessageUI", {
const messageContent = this.getChildControl("message-content");
messageContent.setValue(message["content"]);

const thumbnail = this.getChildControl("thumbnail");
const avatar = this.getChildControl("avatar");
const userName = this.getChildControl("user-name");
if (message["userGroupId"] === "system") {
userName.setValue("Support");
} else {
osparc.store.Users.getInstance().getUser(message["userGroupId"])
.then(user => {
thumbnail.setUser(user);
avatar.setUser(user);
userName.setValue(user ? user.getLabel() : "Unknown user");
})
.catch(() => {
thumbnail.setSource(osparc.utils.Avatar.emailToThumbnail());
avatar.setSource(osparc.utils.Avatar.emailToThumbnail());
userName.setValue("Unknown user");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ qx.Class.define("osparc.data.model.Conversation", {

/**
* @param conversationData {Object} Object containing the serialized Conversation Data
* @param studyId {String} ID of the Study
* */
construct: function(conversationData) {
construct: function(conversationData, studyId) {
this.base(arguments);

this.set({
Expand All @@ -37,6 +38,7 @@ qx.Class.define("osparc.data.model.Conversation", {
modified: new Date(conversationData.modified),
projectId: conversationData.projectUuid || null,
extraContext: conversationData.extraContext || null,
studyId: studyId || null,
});

this.__messages = [];
Expand Down Expand Up @@ -134,6 +136,12 @@ qx.Class.define("osparc.data.model.Conversation", {
event: "changeLastMessage",
apply: "__applyLastMessage",
},

studyId: {
check: "String",
nullable: true,
init: null,
},
},

events: {
Expand Down Expand Up @@ -221,6 +229,10 @@ qx.Class.define("osparc.data.model.Conversation", {
limit: 42
}
};
if (this.getStudyId()) {
params.url.studyId = this.getStudyId();
}

const nextRequestParams = this.__nextRequestParams;
if (nextRequestParams) {
params.url.offset = nextRequestParams.offset;
Expand All @@ -229,7 +241,10 @@ qx.Class.define("osparc.data.model.Conversation", {
const options = {
resolveWResponse: true
};
return osparc.data.Resources.fetch("conversationsSupport", "getMessagesPage", params, options)
const promise = this.getStudyId() ?
osparc.data.Resources.fetch("conversationsStudies", "getMessagesPage", params, options) :
osparc.data.Resources.fetch("conversationsSupport", "getMessagesPage", params, options);
return promise
.then(resp => {
const messages = resp["data"];
messages.forEach(message => this.addMessage(message));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ qx.Class.define("osparc.desktop.StudyEditor", {
this.__listenToNodeUpdated();
this.__listenToNodeProgress();
this.__listenToNoMoreCreditsEvents();
this.__listenToEvent();
this.__listenToServiceCustomEvents();
this.__listenToServiceStatus();
this.__listenToStatePorts();

Expand Down Expand Up @@ -537,7 +537,7 @@ qx.Class.define("osparc.desktop.StudyEditor", {
}
},

__listenToEvent: function() {
__listenToServiceCustomEvents: function() {
const socket = osparc.wrapper.WebSocket.getInstance();

// callback for events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,7 @@ qx.Class.define("osparc.desktop.organizations.MembersList", {
},
configureItem: item => {
item.subscribeToFilterGroup("organizationMembersList");
item.getChildControl("thumbnail").getContentElement()
.setStyles({
"border-radius": "16px"
});
item.getChildControl("thumbnail").setDecorator("circled");
item.addListener("promoteToMember", e => {
const listedMember = e.getData();
this.__promoteToUser(listedMember);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,7 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", {
configureItem: item => {
item.subscribeToFilterGroup("organizationsList");
osparc.utils.Utils.setIdToWidget(item, "organizationListItem");
const thumbnail = item.getChildControl("thumbnail");
thumbnail.getContentElement()
.setStyles({
"border-radius": "16px"
});
item.getChildControl("thumbnail").setDecorator("circled");

item.addListener("openEditOrganization", e => {
const orgKey = e.getData();
Expand Down
Loading
Loading