Skip to content

Commit 932ed74

Browse files
committed
SupportCenter
1 parent c9379cd commit 932ed74

File tree

2 files changed

+369
-0
lines changed

2 files changed

+369
-0
lines changed
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
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.support.Conversations", {
20+
extend: qx.ui.core.Widget,
21+
22+
construct: function(openConversationId = null) {
23+
this.base(arguments);
24+
25+
this._setLayout(new qx.ui.layout.VBox());
26+
27+
this.__conversationsPages = [];
28+
this.__openConversationId = openConversationId;
29+
30+
this.__listenToConversationWS();
31+
},
32+
33+
statics: {
34+
TYPES: {
35+
PROJECT_STATIC: "PROJECT_STATIC",
36+
PROJECT_ANNOTATION: "PROJECT_ANNOTATION",
37+
},
38+
39+
CHANNELS: {
40+
CONVERSATION_CREATED: "conversation:created",
41+
CONVERSATION_UPDATED: "conversation:updated",
42+
CONVERSATION_DELETED: "conversation:deleted",
43+
CONVERSATION_MESSAGE_CREATED: "conversation:message:created",
44+
CONVERSATION_MESSAGE_UPDATED: "conversation:message:updated",
45+
CONVERSATION_MESSAGE_DELETED: "conversation:message:deleted",
46+
},
47+
48+
popUpInWindow: function(studyData, openConversationId = null) {
49+
const conversations = new osparc.study.Conversations(studyData, openConversationId);
50+
const title = qx.locale.Manager.tr("Conversations");
51+
const viewWidth = 600;
52+
const viewHeight = 700;
53+
const win = osparc.ui.window.Window.popUpInWindow(conversations, title, viewWidth, viewHeight).set({
54+
maxHeight: viewHeight,
55+
});
56+
win.addListener("close", () => {
57+
conversations.destroy();
58+
}, this);
59+
return win;
60+
},
61+
62+
makeButtonBlink: function(button) {
63+
const socket = osparc.wrapper.WebSocket.getInstance();
64+
Object.values(osparc.study.Conversations.CHANNELS).forEach(eventName => {
65+
socket.on(eventName, () => {
66+
if (button) {
67+
osparc.utils.Utils.makeButtonBlink(button);
68+
}
69+
});
70+
});
71+
},
72+
},
73+
74+
members: {
75+
__openConversationId: null,
76+
__conversations: null,
77+
__newConversationButton: null,
78+
__wsHandlers: null,
79+
80+
_createChildControlImpl: function(id) {
81+
let control;
82+
switch (id) {
83+
case "loading-button":
84+
control = new osparc.ui.form.FetchButton();
85+
this._add(control);
86+
break;
87+
case "conversations-layout":
88+
control = new qx.ui.tabview.TabView();
89+
this._add(control, {
90+
flex: 1
91+
});
92+
break;
93+
}
94+
95+
return control || this.base(arguments, id);
96+
},
97+
98+
__listenToConversationWS: function() {
99+
this.__wsHandlers = [];
100+
101+
const socket = osparc.wrapper.WebSocket.getInstance();
102+
103+
[
104+
this.self().CHANNELS.CONVERSATION_CREATED,
105+
this.self().CHANNELS.CONVERSATION_UPDATED,
106+
this.self().CHANNELS.CONVERSATION_DELETED,
107+
].forEach(eventName => {
108+
const eventHandler = conversation => {
109+
if (conversation) {
110+
switch (eventName) {
111+
case this.self().CHANNELS.CONVERSATION_CREATED:
112+
if (conversation["projectId"] === this.getStudyData()["uuid"]) {
113+
this.__addConversationPage(conversation);
114+
}
115+
break;
116+
case this.self().CHANNELS.CONVERSATION_UPDATED:
117+
this.__updateConversationName(conversation);
118+
break;
119+
case this.self().CHANNELS.CONVERSATION_DELETED:
120+
this.__removeConversationPage(conversation["conversationId"]);
121+
break;
122+
}
123+
}
124+
};
125+
socket.on(eventName, eventHandler, this);
126+
this.__wsHandlers.push({ eventName, handler: eventHandler });
127+
});
128+
129+
[
130+
this.self().CHANNELS.CONVERSATION_MESSAGE_CREATED,
131+
this.self().CHANNELS.CONVERSATION_MESSAGE_UPDATED,
132+
this.self().CHANNELS.CONVERSATION_MESSAGE_DELETED,
133+
].forEach(eventName => {
134+
const eventHandler = message => {
135+
if (message) {
136+
const conversationId = message["conversationId"];
137+
const conversationPage = this.__getConversationPage(conversationId);
138+
if (conversationPage) {
139+
switch (eventName) {
140+
case this.self().CHANNELS.CONVERSATION_MESSAGE_CREATED:
141+
conversationPage.addMessage(message);
142+
break;
143+
case this.self().CHANNELS.CONVERSATION_MESSAGE_UPDATED:
144+
conversationPage.updateMessage(message);
145+
break;
146+
case this.self().CHANNELS.CONVERSATION_MESSAGE_DELETED:
147+
conversationPage.deleteMessage(message);
148+
break;
149+
}
150+
}
151+
}
152+
};
153+
socket.on(eventName, eventHandler, this);
154+
this.__wsHandlers.push({ eventName, handler: eventHandler });
155+
});
156+
},
157+
158+
__getConversationPage: function(conversationId) {
159+
return this.__conversationsPages.find(conversation => conversation.getConversationId() === conversationId);
160+
},
161+
162+
__applyStudyData: function(studyData) {
163+
const loadMoreButton = this.getChildControl("loading-button");
164+
loadMoreButton.setFetching(true);
165+
166+
osparc.store.ConversationsProject.getInstance().getConversations(studyData["uuid"])
167+
.then(conversations => {
168+
if (conversations.length) {
169+
conversations.forEach(conversation => this.__addConversationPage(conversation));
170+
if (this.__openConversationId) {
171+
const conversationsLayout = this.getChildControl("conversations-layout");
172+
const conversation = conversationsLayout.getSelectables().find(c => c.getConversationId() === this.__openConversationId);
173+
if (conversation) {
174+
conversationsLayout.setSelection([conversation]);
175+
}
176+
this.__openConversationId = null; // reset it so it does not open again
177+
}
178+
} else {
179+
this.__addTempConversationPage();
180+
}
181+
})
182+
.finally(() => {
183+
loadMoreButton.setFetching(false);
184+
loadMoreButton.exclude();
185+
});
186+
},
187+
188+
__createConversationPage: function(conversationData) {
189+
const studyData = this.getStudyData();
190+
let conversationPage = null;
191+
if (conversationData) {
192+
const conversationId = conversationData["conversationId"];
193+
conversationPage = new osparc.conversation.Conversation(studyData, conversationId);
194+
conversationPage.setLabel(conversationData["name"]);
195+
osparc.store.ConversationsProject.getInstance().addListener("conversationDeleted", e => {
196+
const data = e.getData();
197+
if (conversationId === data["conversationId"]) {
198+
this.__removeConversationPage(conversationId, true);
199+
}
200+
});
201+
} else {
202+
// create a temporary conversation
203+
conversationPage = new osparc.conversation.Conversation(studyData);
204+
conversationPage.setLabel(this.tr("new"));
205+
}
206+
return conversationPage;
207+
},
208+
209+
__addTempConversationPage: function() {
210+
const temporaryConversationPage = this.__createConversationPage();
211+
this.__addToPages(temporaryConversationPage);
212+
},
213+
214+
__addConversationPage: function(conversationData) {
215+
// ignore it if it was already there
216+
const conversationId = conversationData["conversationId"];
217+
const conversationPageFound = this.__getConversationPage(conversationId);
218+
if (conversationPageFound) {
219+
return null;
220+
}
221+
222+
const conversationPage = this.__createConversationPage(conversationData);
223+
this.__addToPages(conversationPage);
224+
225+
this.__conversationsPages.push(conversationPage);
226+
227+
return conversationPage;
228+
},
229+
230+
__addToPages: function(conversationPage) {
231+
const conversationsLayout = this.getChildControl("conversations-layout");
232+
if (conversationsLayout.getChildren().length === 1) {
233+
// remove the temporary conversation page
234+
if (conversationsLayout.getChildren()[0].getConversationId() === null) {
235+
conversationsLayout.remove(conversationsLayout.getChildren()[0]);
236+
}
237+
}
238+
conversationsLayout.add(conversationPage);
239+
240+
if (this.__newConversationButton === null) {
241+
const studyData = this.getStudyData();
242+
// initialize the new button only once
243+
const newConversationButton = this.__newConversationButton = new qx.ui.form.Button().set({
244+
icon: "@FontAwesome5Solid/plus/12",
245+
toolTipText: this.tr("Add new conversation"),
246+
allowGrowX: false,
247+
backgroundColor: "transparent",
248+
enabled: osparc.data.model.Study.canIWrite(studyData["accessRights"]),
249+
});
250+
newConversationButton.addListener("execute", () => {
251+
osparc.store.ConversationsProject.getInstance().addConversation(studyData["uuid"], "new " + (this.__conversationsPages.length + 1))
252+
.then(conversationDt => {
253+
this.__addConversationPage(conversationDt);
254+
const newConversationPage = this.__getConversationPage(conversationDt["conversationId"]);
255+
if (newConversationPage) {
256+
conversationsLayout.setSelection([newConversationPage]);
257+
}
258+
});
259+
});
260+
conversationsLayout.getChildControl("bar").add(newConversationButton);
261+
}
262+
// remove and add to move to last position
263+
const bar = conversationsLayout.getChildControl("bar");
264+
if (bar.indexOf(this.__newConversationButton) > -1) {
265+
bar.remove(this.__newConversationButton);
266+
}
267+
bar.add(this.__newConversationButton);
268+
},
269+
270+
__removeConversationPage: function(conversationId, changeSelection = false) {
271+
const conversationPage = this.__getConversationPage(conversationId);
272+
if (conversationPage) {
273+
const conversationsLayout = this.getChildControl("conversations-layout");
274+
if (conversationsLayout.indexOf(conversationPage) > -1) {
275+
conversationsLayout.remove(conversationPage);
276+
}
277+
this.__conversationsPages = this.__conversationsPages.filter(c => c !== conversationPage);
278+
const conversationPages = conversationsLayout.getSelectables();
279+
if (conversationPages.length) {
280+
if (changeSelection) {
281+
// change selection to the first conversation
282+
conversationsLayout.setSelection([conversationPages[0]]);
283+
}
284+
} else {
285+
// no conversations left, add a temporary one
286+
this.__addTempConversationPage();
287+
}
288+
}
289+
},
290+
291+
// it can only be renamed, not updated
292+
__updateConversationName: function(conversationData) {
293+
const conversationId = conversationData["conversationId"];
294+
const conversationPage = this.__getConversationPage(conversationId);
295+
if (conversationPage) {
296+
conversationPage.renameConversation(conversationData["name"]);
297+
}
298+
},
299+
300+
// overridden
301+
destroy: function() {
302+
const socket = osparc.wrapper.WebSocket.getInstance();
303+
if (this.__wsHandlers) {
304+
this.__wsHandlers.forEach(({ eventName }) => {
305+
socket.removeSlot(eventName);
306+
});
307+
this.__wsHandlers = null;
308+
}
309+
310+
this.base(arguments);
311+
},
312+
},
313+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
qx.Class.define("osparc.support.SupportCenter", {
19+
extend: osparc.ui.window.SingletonWindow,
20+
21+
construct: function() {
22+
this.base(arguments);
23+
24+
this.set({
25+
layout: qx.ui.container.Stack,
26+
});
27+
28+
this.getChildControl("intro-text");
29+
this.getChildControl("conversations-list");
30+
},
31+
32+
statics: {
33+
},
34+
35+
members: {
36+
_createChildControlImpl: function(id) {
37+
let control;
38+
switch (id) {
39+
case "conversations-layout":
40+
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(10));
41+
this.add(control);
42+
case "intro-text":
43+
control = new qx.ui.basic.Label(this.tr("Welcome to the Support Center"));
44+
this.getChildControl("conversations-layout").add(control);
45+
break;
46+
case "conversations-list":
47+
const control = new osparc.support.Conversations();
48+
const scroll = new qx.ui.container.Scroll();
49+
scroll.add(control);
50+
this.getChildControl("conversations-layout").add(scroll);
51+
break;
52+
}
53+
return control || this.base(arguments, id);
54+
},
55+
}
56+
});

0 commit comments

Comments
 (0)