Skip to content

Commit 2165fd4

Browse files
authored
✨ [Frontend] Feature: Share Tags (ITISFoundation#6899)
1 parent a0bf53a commit 2165fd4

File tree

9 files changed

+403
-99
lines changed

9 files changed

+403
-99
lines changed

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,23 @@ qx.Class.define("osparc.data.Resources", {
12661266
delete: {
12671267
method: "DELETE",
12681268
url: statics.API + "/tags/{tagId}"
1269-
}
1269+
},
1270+
getAccessRights: {
1271+
method: "GET",
1272+
url: statics.API + "/tags/{tagId}/groups"
1273+
},
1274+
putAccessRights: {
1275+
method: "PUT",
1276+
url: statics.API + "/tags/{tagId}/groups/{groupId}"
1277+
},
1278+
postAccessRights: {
1279+
method: "POST",
1280+
url: statics.API + "/tags/{tagId}/groups/{groupId}"
1281+
},
1282+
deleteAccessRights: {
1283+
method: "DELETE",
1284+
url: statics.API + "/tags/{tagId}/groups/{groupId}"
1285+
},
12701286
}
12711287
},
12721288

services/static-webserver/client/source/class/osparc/data/model/Tag.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ qx.Class.define("osparc.data.model.Tag", {
3333
name: tagData.name,
3434
description: tagData.description,
3535
color: tagData.color,
36-
accessRights: tagData.accessRights,
36+
myAccessRights: tagData.accessRights,
3737
});
3838
},
3939

@@ -65,6 +65,13 @@ qx.Class.define("osparc.data.model.Tag", {
6565
init: "#303030"
6666
},
6767

68+
myAccessRights: {
69+
check: "Object",
70+
nullable: false,
71+
init: null,
72+
event: "changeMyAccessRights"
73+
},
74+
6875
accessRights: {
6976
check: "Object",
7077
nullable: false,
@@ -73,6 +80,12 @@ qx.Class.define("osparc.data.model.Tag", {
7380
},
7481
},
7582

83+
statics: {
84+
getProperties: function() {
85+
return Object.keys(qx.util.PropertyUtil.getProperties(osparc.data.model.Tag));
86+
}
87+
},
88+
7689
members: {
7790
serialize: function() {
7891
const jsonObject = {};

services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
288288
converter: val => val ? "tab-button-selected" : "tab-button"
289289
});
290290
if (widget) {
291-
tabPage.add(widget, {
291+
const scrollView = new qx.ui.container.Scroll();
292+
scrollView.add(widget);
293+
tabPage.add(scrollView, {
292294
flex: 1
293295
});
294296
}

services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TagsPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ qx.Class.define("osparc.desktop.preferences.pages.TagsPage", {
1919
const studiesLabel = osparc.product.Utils.getStudyAlias({plural: true});
2020
const studyLabel = osparc.product.Utils.getStudyAlias();
2121
const msg = this.tr("\
22-
Tags are annotations to help users with grouping ") + studiesLabel + this.tr(" in the Dashboard. \
22+
Tags help you organize the ") + studiesLabel + this.tr(" in the Dashboard by categorizing topics, making it easier to search and filter. \
2323
Once the tags are created, they can be assigned to the ") + studyLabel + this.tr(" via 'More options...' on the ") + studyLabel + this.tr(" cards.");
2424
const intro = osparc.ui.window.TabbedView.createHelpLabel(msg);
2525
this._add(intro);

services/static-webserver/client/source/class/osparc/form/tag/TagItem.js

Lines changed: 97 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ qx.Class.define("osparc.form.tag.TagItem", {
1515
this.base(arguments);
1616
this._setLayout(new qx.ui.layout.HBox(5));
1717
this.__validationManager = new qx.ui.form.validation.Manager();
18-
this.__renderLayout();
1918
},
2019

2120
statics: {
@@ -57,18 +56,23 @@ qx.Class.define("osparc.form.tag.TagItem", {
5756
init: "#303030"
5857
},
5958

59+
myAccessRights: {
60+
check: "Object",
61+
nullable: false,
62+
event: "changeMyAccessRights",
63+
},
64+
6065
accessRights: {
6166
check: "Object",
6267
nullable: false,
6368
event: "changeAccessRights",
64-
apply: "__renderLayout",
6569
},
6670

6771
mode: {
6872
check: "String",
6973
init: "display",
7074
nullable: false,
71-
apply: "_applyMode"
75+
apply: "__applyMode"
7276
},
7377

7478
appearance: {
@@ -84,83 +88,62 @@ qx.Class.define("osparc.form.tag.TagItem", {
8488
},
8589

8690
members: {
87-
__tag: null,
88-
__description: null,
89-
__nameInput: null,
90-
__descriptionInput: null,
91-
__colorInput: null,
92-
__colorButton: null,
93-
__loadingIcon: null,
9491
__validationManager: null,
9592

9693
_createChildControlImpl: function(id) {
9794
let control;
9895
switch (id) {
9996
case "tag":
100-
// Tag sample on display mode
101-
if (this.__tag === null) {
102-
this.__tag = new osparc.ui.basic.Tag();
103-
this.bind("name", this.__tag, "value");
104-
this.bind("color", this.__tag, "color");
105-
}
106-
control = this.__tag;
97+
control = new osparc.ui.basic.Tag();
98+
this.bind("name", control, "value");
99+
this.bind("color", control, "color");
107100
break;
108101
case "description":
109-
// Description label on display mode
110-
if (this.__description === null) {
111-
this.__description = new qx.ui.basic.Label().set({
112-
rich: true
113-
});
114-
this.bind("description", this.__description, "value");
115-
}
116-
control = this.__description;
102+
control = new qx.ui.basic.Label().set({
103+
rich: true,
104+
allowGrowX: true,
105+
});
106+
this.bind("description", control, "value");
107+
break;
108+
case "shared-icon":
109+
control = new qx.ui.basic.Image().set({
110+
minWidth: 30,
111+
alignY: "middle",
112+
cursor: "pointer",
113+
});
114+
osparc.dashboard.CardBase.populateShareIcon(control, this.getAccessRights())
115+
control.addListener("tap", () => this.__openAccessRights(), this);
117116
break;
118117
case "name-input":
119-
// Tag name input in edit mode
120-
if (this.__nameInput === null) {
121-
this.__nameInput = new qx.ui.form.TextField().set({
122-
required: true
123-
});
124-
this.__validationManager.add(this.__nameInput);
125-
this.__nameInput.getContentElement().setAttribute("autocomplete", "off");
126-
}
127-
control = this.__nameInput;
118+
control = new qx.ui.form.TextField().set({
119+
required: true
120+
});
121+
this.__validationManager.add(control);
122+
control.getContentElement().setAttribute("autocomplete", "off");
128123
break;
129124
case "description-input":
130-
// Tag description input in edit mode
131-
if (this.__descriptionInput === null) {
132-
this.__descriptionInput = new qx.ui.form.TextArea().set({
133-
autoSize: true,
134-
minimalLineHeight: 1
135-
});
136-
}
137-
control = this.__descriptionInput;
125+
control = new qx.ui.form.TextArea().set({
126+
autoSize: true,
127+
minimalLineHeight: 1
128+
});
138129
break;
139130
case "color-input":
140-
// Color input in edit mode
141-
if (this.__colorInput === null) {
142-
this.__colorInput = new qx.ui.form.TextField().set({
143-
value: this.getColor(),
144-
width: 60,
145-
required: true
146-
});
147-
this.__colorInput.bind("value", this.getChildControl("color-button"), "backgroundColor");
148-
this.__colorInput.bind("value", this.getChildControl("color-button"), "textColor", {
149-
converter: value => osparc.utils.Utils.getContrastedBinaryColor(value)
150-
});
151-
this.__validationManager.add(this.__colorInput, osparc.utils.Validators.hexColor);
152-
}
153-
control = this.__colorInput;
131+
control = new qx.ui.form.TextField().set({
132+
value: this.getColor(),
133+
width: 60,
134+
required: true
135+
});
136+
control.bind("value", this.getChildControl("color-button"), "backgroundColor");
137+
control.bind("value", this.getChildControl("color-button"), "textColor", {
138+
converter: value => osparc.utils.Utils.getContrastedBinaryColor(value)
139+
});
140+
this.__validationManager.add(control, osparc.utils.Validators.hexColor);
154141
break;
155142
case "color-button":
156-
// Random color generator button in edit mode
157-
if (this.__colorButton === null) {
158-
this.__colorButton = new qx.ui.form.Button(null, "@FontAwesome5Solid/sync-alt/12");
159-
this.__colorButton.addListener("execute", () => {
160-
this.getChildControl("color-input").setValue(osparc.utils.Utils.getRandomColor());
161-
}, this);
162-
}
163-
control = this.__colorButton;
143+
control = new qx.ui.form.Button(null, "@FontAwesome5Solid/sync-alt/12");
144+
control.addListener("execute", () => {
145+
this.getChildControl("color-input").setValue(osparc.utils.Utils.getRandomColor());
146+
}, this);
164147
break;
165148
}
166149
return control || this.base(arguments, id);
@@ -171,7 +154,10 @@ qx.Class.define("osparc.form.tag.TagItem", {
171154
tag.bind("name", this, "name");
172155
tag.bind("description", this, "description");
173156
tag.bind("color", this, "color");
157+
tag.bind("myAccessRights", this, "myAccessRights");
174158
tag.bind("accessRights", this, "accessRights");
159+
160+
this.__renderLayout();
175161
},
176162

177163
/**
@@ -212,40 +198,48 @@ qx.Class.define("osparc.form.tag.TagItem", {
212198
},
213199

214200
__renderDisplayMode: function() {
215-
const tagContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox()).set({
216-
width: 100
217-
});
218-
tagContainer.add(this.getChildControl("tag"));
219-
this._add(tagContainer);
220-
const descriptionContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox());
221-
descriptionContainer.add(this.getChildControl("description"), {
222-
width: "100%"
223-
});
224-
this._add(descriptionContainer, {
201+
this._add(this.getChildControl("tag"));
202+
this._add(this.getChildControl("description"), {
225203
flex: 1
226204
});
205+
this._add(this.getChildControl("shared-icon"));
227206
this._add(this.__tagItemButtons());
228207
this.resetBackgroundColor();
229208
},
230209

210+
__openAccessRights: function() {
211+
const permissionsView = new osparc.share.CollaboratorsTag(this.getTag());
212+
const title = this.tr("Share Tag");
213+
osparc.ui.window.Window.popUpInWindow(permissionsView, title, 600, 600);
214+
215+
permissionsView.addListener("updateAccessRights", () => {
216+
const accessRights = this.getTag().getAccessRights();
217+
if (accessRights) {
218+
const sharedIcon = this.getChildControl("shared-icon");
219+
osparc.dashboard.CardBase.populateShareIcon(sharedIcon, accessRights);
220+
}
221+
}, this);
222+
},
223+
231224
/**
232225
* Generates and returns the buttons for deleting and editing an existing label (display mode)
233226
*/
234227
__tagItemButtons: function() {
228+
const canIWrite = osparc.share.CollaboratorsTag.canIWrite(this.getMyAccessRights());
229+
const canIDelete = osparc.share.CollaboratorsTag.canIDelete(this.getMyAccessRights());
230+
235231
const buttonContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox());
236232
const editButton = new qx.ui.form.Button().set({
237233
icon: "@FontAwesome5Solid/pencil-alt/12",
238-
toolTipText: this.tr("Edit")
234+
toolTipText: this.tr("Edit"),
235+
enabled: canIWrite,
239236
});
240237
const deleteButton = new osparc.ui.form.FetchButton().set({
241238
appearance: "danger-button",
242239
icon: "@FontAwesome5Solid/trash/12",
243-
toolTipText: this.tr("Delete")
240+
toolTipText: this.tr("Delete"),
241+
enabled: canIDelete,
244242
});
245-
if (this.isPropertyInitialized("accessRights")) {
246-
editButton.setEnabled(this.getAccessRights()["write"]);
247-
deleteButton.setEnabled(this.getAccessRights()["delete"]);
248-
}
249243
buttonContainer.add(editButton);
250244
buttonContainer.add(deleteButton);
251245
editButton.addListener("execute", () => this.setMode(this.self().modes.EDIT), this);
@@ -279,20 +273,31 @@ qx.Class.define("osparc.form.tag.TagItem", {
279273
if (this.__validationManager.validate()) {
280274
const data = this.__serializeData();
281275
saveButton.setFetching(true);
282-
let fetch;
276+
const tagsStore = osparc.store.Tags.getInstance();
283277
if (this.isPropertyInitialized("id")) {
284-
fetch = osparc.store.Tags.getInstance().putTag(this.getId(), data);
278+
tagsStore.putTag(this.getId(), data)
279+
.then(tag => this.setTag(tag))
280+
.catch(console.error)
281+
.finally(() => {
282+
this.fireEvent("tagSaved");
283+
this.setMode(this.self().modes.DISPLAY);
284+
saveButton.setFetching(false);
285+
});
285286
} else {
286-
fetch = osparc.store.Tags.getInstance().postTag(data);
287+
let newTag = null;
288+
tagsStore.postTag(data)
289+
.then(tag => {
290+
newTag = tag;
291+
return tagsStore.fetchAccessRights(tag);
292+
})
293+
.then(() => this.setTag(newTag))
294+
.catch(console.error)
295+
.finally(() => {
296+
this.fireEvent("tagSaved");
297+
this.setMode(this.self().modes.DISPLAY);
298+
saveButton.setFetching(false);
299+
});
287300
}
288-
fetch
289-
.then(tag => this.setTag(tag))
290-
.catch(console.error)
291-
.finally(() => {
292-
this.fireEvent("tagSaved");
293-
this.setMode(this.self().modes.DISPLAY);
294-
saveButton.setFetching(false);
295-
});
296301
}
297302
}, this);
298303
cancelButton.addListener("execute", () => {
@@ -334,7 +339,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
334339
color: color
335340
};
336341
},
337-
_applyMode: function() {
342+
__applyMode: function() {
338343
this.__renderLayout();
339344
}
340345
}

0 commit comments

Comments
 (0)