Workflow manager: edit and delete workflows
This CL adds the ability to view/edit and delete workflows in the
workflow manager. With this, the workflow manager initial prototype is
complete, since it allows the user to fully manage their workflows, even
if the UI might be subject to change to further improve it in the
future.
Bug: twpowertools:74
Change-Id: I4f8d1b7ab54dcc600dbd10fcb8e605bcb61d36bb
diff --git a/src/workflows/manager/components/AddDialog.js b/src/workflows/manager/components/AddDialog.js
index 32a2cae..42bfc74 100644
--- a/src/workflows/manager/components/AddDialog.js
+++ b/src/workflows/manager/components/AddDialog.js
@@ -83,7 +83,7 @@
}
_save() {
- let success = this.workflowEditorRef.value.save();
+ const success = this.workflowEditorRef.value.save();
if (success) {
this.open = false;
this._resetWorkflow();
diff --git a/src/workflows/manager/components/List.js b/src/workflows/manager/components/List.js
index a7851ee..d18571f 100644
--- a/src/workflows/manager/components/List.js
+++ b/src/workflows/manager/components/List.js
@@ -1,9 +1,13 @@
import '@material/web/list/list.js';
import '@material/web/list/list-item.js';
import '@material/web/iconbutton/standard-icon-button.js';
+import './WorkflowDialog.js';
import {css, html, LitElement, nothing} from 'lit';
import {map} from 'lit/directives/map.js';
+import {createRef, ref} from 'lit/directives/ref.js';
+
+import WorkflowsStorage from '../../workflowsStorage.js';
export default class WFList extends LitElement {
static properties = {
@@ -28,17 +32,15 @@
}
`;
+ dialogRef = createRef();
+
renderListItems() {
return map(this.workflows, w => html`
<md-list-item
headline=${w.proto?.getName?.()}
- @click=${() => this._show(w.uuid)}>
+ @click=${() => this._show(w)}>
<div slot="end" class="end">
<md-standard-icon-button
- icon="edit"
- @click=${e => this._showEdit(w.uuid, e)}>
- </md-standard-icon-button>
- <md-standard-icon-button
icon="delete"
@click=${e => this._showDelete(w.uuid, e)}>
</md-standard-icon-button>
@@ -47,7 +49,7 @@
`);
}
- render() {
+ renderList() {
if (!this.workflows) return nothing;
if (this.workflows?.length === 0)
return html`
@@ -64,14 +66,30 @@
`;
}
- _show(uuid) {}
+ renderDialog() {
+ return html`
+ <wf-workflow-dialog ${ref(this.dialogRef)}></wf-workflow-dialog>
+ `;
+ }
- _showEdit(uuid, e) {
- e.stopPropagation();
+ render() {
+ return [
+ this.renderList(),
+ this.renderDialog(),
+ ];
+ }
+
+ _show(fullWorkflow) {
+ this.dialogRef.value.uuid = fullWorkflow.uuid;
+ this.dialogRef.value.workflow = fullWorkflow.proto.cloneMessage();
+ this.dialogRef.value.open = true;
}
_showDelete(uuid, e) {
e.stopPropagation();
+ const proceed = window.confirm(
+ 'Do you really want to remove this workflow? This action is irreversible.');
+ if (proceed) WorkflowsStorage.remove(uuid);
}
}
window.customElements.define('wf-list', WFList);
diff --git a/src/workflows/manager/components/WorkflowDialog.js b/src/workflows/manager/components/WorkflowDialog.js
new file mode 100644
index 0000000..19ff796
--- /dev/null
+++ b/src/workflows/manager/components/WorkflowDialog.js
@@ -0,0 +1,68 @@
+import '@material/mwc-dialog/mwc-dialog.js';
+import '@material/web/button/text-button.js';
+import '@material/web/button/filled-button.js';
+import './WorkflowEditor.js';
+
+import {css, html, LitElement} from 'lit';
+import {createRef, ref} from 'lit/directives/ref.js';
+
+import * as pb from '../../proto/main_pb.js';
+
+export default class WFWorkflowDialog extends LitElement {
+ static properties = {
+ open: {type: Boolean},
+ uuid: {type: String},
+ workflow: {type: Object},
+ };
+
+ static styles = css`
+ :host {
+ --mdc-dialog-content-ink-color: var(--mdc-theme-on-surface, #000);
+ }
+ `;
+
+ workflowEditorRef = createRef();
+
+ constructor() {
+ super();
+ this.open = false;
+ this.workflow = new pb.workflows.Workflow();
+ }
+
+ render() {
+ return html`
+ <mwc-dialog
+ ?open=${this.open}
+ @opening=${this._openingDialog}
+ @closing=${this._closingDialog}>
+ <wf-workflow-editor ${ref(this.workflowEditorRef)}
+ .workflow=${this.workflow}>
+ </wf-workflow-editor>
+ <md-filled-button
+ slot="primaryAction"
+ label="Save"
+ @click=${this._save}>
+ </md-filled-button>
+ <md-text-button
+ slot="secondaryAction"
+ dialogAction="cancel"
+ label="Cancel">
+ </md-text-button>
+ </mwc-dialog>
+ `;
+ }
+
+ _openingDialog() {
+ this.open = true;
+ }
+
+ _closingDialog() {
+ this.open = false;
+ }
+
+ _save() {
+ const success = this.workflowEditorRef.value.save(this.uuid);
+ if (success) this.open = false;
+ }
+}
+window.customElements.define('wf-workflow-dialog', WFWorkflowDialog);
diff --git a/src/workflows/manager/components/WorkflowEditor.js b/src/workflows/manager/components/WorkflowEditor.js
index dcd38cd..33c9224 100644
--- a/src/workflows/manager/components/WorkflowEditor.js
+++ b/src/workflows/manager/components/WorkflowEditor.js
@@ -73,7 +73,7 @@
];
}
- save() {
+ checkValidity() {
let allValid = true;
// Check the workflow name is set
@@ -83,8 +83,19 @@
const actionEditors = this.renderRoot.querySelectorAll('wf-action-editor');
for (const editor of actionEditors) allValid &&= editor.checkValidity();
+ return allValid;
+ }
+
+ save(uuid) {
+ const allValid = this.checkValidity();
+
// Save the workflow if the validation checks passed
- if (allValid) WorkflowsStorage.add(this.workflow);
+ if (allValid) {
+ if (!uuid)
+ WorkflowsStorage.add(this.workflow);
+ else
+ WorkflowsStorage.update(uuid, this.workflow);
+ }
return allValid;
}
diff --git a/src/workflows/manager/index.js b/src/workflows/manager/index.js
index af0f653..01710b4 100644
--- a/src/workflows/manager/index.js
+++ b/src/workflows/manager/index.js
@@ -1,6 +1,7 @@
import '@material/web/fab/fab.js';
import './components/List.js';
import './components/AddDialog.js';
+import './components/WorkflowDialog.js';
import {css, html, LitElement} from 'lit';
import {createRef, ref} from 'lit/directives/ref.js';
@@ -32,6 +33,7 @@
];
addFabRef = createRef();
+ addDialog = createRef();
constructor() {
super();
@@ -52,7 +54,8 @@
icon="add"
@click=${this._showAddDialog}>
</md-fab>
- <wf-add-dialog></wf-add-dialog>
+ <wf-add-dialog ${ref(this.addDialog)}>
+ </wf-add-dialog>
`;
}
@@ -64,7 +67,7 @@
}
_showAddDialog() {
- this.renderRoot.querySelector('wf-add-dialog').open = true;
+ this.addDialog.value.open = true;
}
}
window.customElements.define('wf-app', WFApp);
diff --git a/src/workflows/workflowsStorage.js b/src/workflows/workflowsStorage.js
index e692209..95b3583 100644
--- a/src/workflows/workflowsStorage.js
+++ b/src/workflows/workflowsStorage.js
@@ -47,12 +47,30 @@
}
static add(workflow) {
- const binaryWorkflow = workflow.serializeBinary();
- return arrayBufferToBase64(binaryWorkflow).then(data => {
+ return this._proto2Base64(workflow).then(data => {
return this.addRaw(data);
});
}
+ static updateRaw(uuid, base64Workflow) {
+ return this.getAll().then(workflows => {
+ workflows.map(w => {
+ if (w.uuid !== uuid) return w;
+ w.data = base64Workflow;
+ return w;
+ });
+ const items = {};
+ items[kWorkflowsDataKey] = workflows;
+ chrome.storage.local.set(items);
+ });
+ }
+
+ static update(uuid, workflow) {
+ return this._proto2Base64(workflow).then(data => {
+ return this.updateRaw(uuid, data);
+ });
+ }
+
static remove(uuid) {
return this.getAll().then(workflows => {
const items = {};
@@ -60,4 +78,9 @@
chrome.storage.local.set(items);
});
}
+
+ static _proto2Base64(workflow) {
+ const binaryWorkflow = workflow.serializeBinary();
+ return arrayBufferToBase64(binaryWorkflow);
+ }
}