Skip to content

Commit 6903354

Browse files
authored
✨ [Frontend] Conversations: notify users (#7916)
1 parent d88921c commit 6903354

File tree

17 files changed

+659
-288
lines changed

17 files changed

+659
-288
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2023 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.AddMessage", {
20+
extend: qx.ui.core.Widget,
21+
22+
/**
23+
* @param studyData {Object} serialized Study Data
24+
* @param conversationId {String} Conversation Id
25+
*/
26+
construct: function(studyData, conversationId = null) {
27+
this.base(arguments);
28+
29+
this.__studyData = studyData;
30+
this.__conversationId = conversationId;
31+
32+
this._setLayout(new qx.ui.layout.VBox(5));
33+
34+
this.__buildLayout();
35+
},
36+
37+
events: {
38+
"commentAdded": "qx.event.type.Event"
39+
},
40+
41+
members: {
42+
__studyData: null,
43+
__conversationId: null,
44+
45+
_createChildControlImpl: function(id) {
46+
let control;
47+
switch (id) {
48+
case "add-comment-label":
49+
control = new qx.ui.basic.Label().set({
50+
value: this.tr("Add comment")
51+
});
52+
this._add(control);
53+
break;
54+
case "add-comment-layout": {
55+
const grid = new qx.ui.layout.Grid(8, 5);
56+
grid.setColumnWidth(0, 32);
57+
grid.setColumnFlex(1, 1);
58+
control = new qx.ui.container.Composite(grid);
59+
this._add(control, {
60+
flex: 1
61+
});
62+
break;
63+
}
64+
case "thumbnail": {
65+
control = new qx.ui.basic.Image().set({
66+
alignY: "middle",
67+
scale: true,
68+
allowGrowX: true,
69+
allowGrowY: true,
70+
allowShrinkX: true,
71+
allowShrinkY: true,
72+
maxWidth: 32,
73+
maxHeight: 32,
74+
decorator: "rounded",
75+
});
76+
const authData = osparc.auth.Data.getInstance();
77+
const myUsername = authData.getUsername();
78+
const myEmail = authData.getEmail();
79+
control.set({
80+
source: osparc.utils.Avatar.emailToThumbnail(myEmail, myUsername, 32)
81+
});
82+
const layout = this.getChildControl("add-comment-layout");
83+
layout.add(control, {
84+
row: 0,
85+
column: 0
86+
});
87+
break;
88+
}
89+
case "comment-field":
90+
control = new osparc.editor.MarkdownEditor();
91+
control.getChildControl("buttons").exclude();
92+
const layout = this.getChildControl("add-comment-layout");
93+
layout.add(control, {
94+
row: 0,
95+
column: 1
96+
});
97+
break;
98+
case "add-comment-button":
99+
control = new qx.ui.form.Button(this.tr("Add message")).set({
100+
appearance: "form-button",
101+
allowGrowX: false,
102+
alignX: "right"
103+
});
104+
control.setEnabled(osparc.data.model.Study.canIWrite(this.__studyData["accessRights"]));
105+
this._add(control);
106+
break;
107+
case "notify-user-button":
108+
control = new qx.ui.form.Button("🔔 " + this.tr("Notify user")).set({
109+
appearance: "form-button",
110+
allowGrowX: false,
111+
alignX: "right"
112+
});
113+
control.setEnabled(osparc.data.model.Study.canIWrite(this.__studyData["accessRights"]));
114+
this._add(control);
115+
break;
116+
}
117+
118+
return control || this.base(arguments, id);
119+
},
120+
121+
__buildLayout: function() {
122+
this.getChildControl("thumbnail");
123+
this.getChildControl("comment-field");
124+
125+
const addMessageButton = this.getChildControl("add-comment-button");
126+
addMessageButton.addListener("execute", () => this.__addComment());
127+
128+
const notifyUserButton = this.getChildControl("notify-user-button");
129+
notifyUserButton.addListener("execute", () => this.__notifyUserTapped());
130+
},
131+
132+
__addComment: function() {
133+
if (this.__conversationId) {
134+
this.__postMessage();
135+
} else {
136+
// create new conversation first
137+
osparc.study.Conversations.addConversation(this.__studyData["uuid"])
138+
.then(data => {
139+
this.__conversationId = data["conversationId"];
140+
this.__postMessage();
141+
})
142+
}
143+
},
144+
145+
__notifyUserTapped: function() {
146+
const showOrganizations = false;
147+
const showAccessRights = false;
148+
const userManager = new osparc.share.NewCollaboratorsManager(this.__studyData, showOrganizations, showAccessRights).set({
149+
acceptOnlyOne: true,
150+
});
151+
userManager.setCaption(this.tr("Notify user"));
152+
userManager.getActionButton().setLabel(this.tr("Notify"));
153+
userManager.addListener("addCollaborators", e => {
154+
userManager.close();
155+
const data = e.getData();
156+
const userGids = data["selectedGids"];
157+
if (userGids && userGids.length) {
158+
const userGid = parseInt(userGids[0]);
159+
this.__notifyUser(userGid);
160+
}
161+
});
162+
},
163+
164+
__notifyUser: function(userGid) {
165+
// Note!
166+
// This check only works if the project is directly shared with the user.
167+
// If it's shared through a group, it might be a bit confusing
168+
if (userGid in this.__studyData["accessRights"]) {
169+
this.__addNotify(userGid);
170+
} else {
171+
const msg = this.tr("This user has no access to the project. Do you want to share it?");
172+
const win = new osparc.ui.window.Confirmation(msg).set({
173+
caption: this.tr("Share"),
174+
confirmText: this.tr("Share"),
175+
confirmAction: "create"
176+
});
177+
win.center();
178+
win.open();
179+
win.addListener("close", () => {
180+
if (win.getConfirmed()) {
181+
const newCollaborators = {
182+
[userGid]: osparc.data.Roles.STUDY["write"].accessRights
183+
};
184+
osparc.store.Study.addCollaborators(this.__studyData, newCollaborators)
185+
.then(() => {
186+
this.__addNotify(userGid);
187+
const potentialCollaborators = osparc.store.Groups.getInstance().getPotentialCollaborators()
188+
if (userGid in potentialCollaborators && "getUserId" in potentialCollaborators[userGid]) {
189+
const uid = potentialCollaborators[userGid].getUserId();
190+
osparc.notification.Notifications.pushStudyShared(uid, this.__studyData["uuid"]);
191+
}
192+
})
193+
.catch(err => osparc.FlashMessenger.logError(err));
194+
}
195+
}, this);
196+
}
197+
},
198+
199+
__addNotify: function(userGid) {
200+
if (this.__conversationId) {
201+
this.__postNotify(userGid);
202+
} else {
203+
// create new conversation first
204+
osparc.study.Conversations.addConversation(this.__studyData["uuid"])
205+
.then(data => {
206+
this.__conversationId = data["conversationId"];
207+
this.__postNotify(userGid);
208+
});
209+
}
210+
},
211+
212+
__postMessage: function() {
213+
const commentField = this.getChildControl("comment-field");
214+
const comment = commentField.getChildControl("text-area").getValue();
215+
if (comment) {
216+
osparc.study.Conversations.addMessage(this.__studyData["uuid"], this.__conversationId, comment)
217+
.then(data => {
218+
this.fireDataEvent("commentAdded", data);
219+
commentField.getChildControl("text-area").setValue("");
220+
osparc.FlashMessenger.logAs(this.tr("Message added"), "INFO");
221+
});
222+
}
223+
},
224+
225+
__postNotify: function(userGid) {
226+
if (userGid) {
227+
osparc.study.Conversations.notifyUser(this.__studyData["uuid"], this.__conversationId, userGid)
228+
.then(data => {
229+
this.fireDataEvent("commentAdded", data);
230+
const potentialCollaborators = osparc.store.Groups.getInstance().getPotentialCollaborators();
231+
if (userGid in potentialCollaborators) {
232+
if ("getUserId" in potentialCollaborators[userGid]) {
233+
const uid = potentialCollaborators[userGid].getUserId();
234+
osparc.notification.Notifications.pushConversationNotification(uid, this.__studyData["uuid"]);
235+
}
236+
const msg = "getLabel" in potentialCollaborators[userGid] ? potentialCollaborators[userGid].getLabel() + this.tr(" was notified") : this.tr("Notification sent");
237+
osparc.FlashMessenger.logAs(msg, "INFO");
238+
}
239+
});
240+
}
241+
},
242+
}
243+
});

services/static-webserver/client/source/class/osparc/info/Conversation.js renamed to services/static-webserver/client/source/class/osparc/conversation/Conversation.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
************************************************************************ */
1717

1818

19-
qx.Class.define("osparc.info.Conversation", {
19+
qx.Class.define("osparc.conversation.Conversation", {
2020
extend: qx.ui.tabview.Page,
2121

2222
/**
@@ -32,7 +32,7 @@ qx.Class.define("osparc.info.Conversation", {
3232
this.setConversationId(conversationId);
3333
}
3434

35-
this._setLayout(new qx.ui.layout.VBox(10));
35+
this._setLayout(new qx.ui.layout.VBox(5));
3636

3737
this.set({
3838
padding: 10,
@@ -152,7 +152,9 @@ qx.Class.define("osparc.info.Conversation", {
152152
this.__messagesList = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
153153
alignY: "middle"
154154
});
155-
this._add(this.__messagesList, {
155+
const scrollView = new qx.ui.container.Scroll();
156+
scrollView.add(this.__messagesList);
157+
this._add(scrollView, {
156158
flex: 1
157159
});
158160

@@ -161,7 +163,7 @@ qx.Class.define("osparc.info.Conversation", {
161163
this._add(this.__loadMoreMessages);
162164

163165
if (osparc.data.model.Study.canIWrite(this.__studyData["accessRights"])) {
164-
const addMessages = new osparc.info.CommentAdd(this.__studyData["uuid"], this.getConversationId());
166+
const addMessages = new osparc.conversation.AddMessage(this.__studyData, this.getConversationId());
165167
addMessages.setPaddingLeft(10);
166168
addMessages.addListener("commentAdded", e => {
167169
const data = e.getData();
@@ -223,15 +225,26 @@ qx.Class.define("osparc.info.Conversation", {
223225
},
224226

225227
__addMessages: function(messages) {
226-
if (messages.length === 1) {
228+
const nMessages = messages.filter(msg => msg["type"] === "MESSAGE").length;
229+
if (nMessages === 1) {
227230
this.__messagesTitle.setValue(this.tr("1 Message"));
228-
} else if (messages.length > 1) {
229-
this.__messagesTitle.setValue(messages.length + this.tr(" Messages"));
231+
} else if (nMessages > 1) {
232+
this.__messagesTitle.setValue(nMessages + this.tr(" Messages"));
230233
}
231234

232235
messages.forEach(message => {
233-
const messageUi = new osparc.info.CommentUI(message);
234-
this.__messagesList.add(messageUi);
236+
let control = null;
237+
switch (message["type"]) {
238+
case "MESSAGE":
239+
control = new osparc.conversation.MessageUI(message);
240+
break;
241+
case "NOTIFICATION":
242+
control = new osparc.conversation.NotificationUI(message);
243+
break;
244+
}
245+
if (control) {
246+
this.__messagesList.add(control);
247+
}
235248
});
236249
},
237250
}

0 commit comments

Comments
 (0)