Destilled node operations to an operation queue

This commit is contained in:
Pünkösd Marcell 2020-12-05 03:41:44 +01:00
parent 1428502706
commit 4ff230007c
3 changed files with 164 additions and 31 deletions

View File

@ -6,13 +6,24 @@
<form class="editor-form"> <form class="editor-form">
<div v-if="type === 'ingest'"> <div v-if="type === 'ingest' && apiId !== null">
<md-field> <md-field>
<label>Url</label> <label>URL</label>
<md-input readonly="true" v-model="editorData.url"></md-input> <md-input readonly="true" v-model="editorData.url"></md-input>
</md-field> </md-field>
<md-field>
<label>Stream Key</label>
<md-input readonly="true" type="password" v-model="editorData.streamkey"></md-input>
</md-field>
</div>
<div v-if="type === 'ingest' && apiId === null">
<b>Press APPLY first, to generate your connection details!</b>
</div> </div>
<div v-else-if="type === 'encoder'"> <div v-else-if="type === 'encoder'">
@ -36,7 +47,7 @@
<div v-else-if="type === 'restreamer'"> <div v-else-if="type === 'restreamer'">
<md-field> <md-field>
<label>Url</label> <label>URL</label>
<md-input v-model="editorData.url"></md-input> <md-input v-model="editorData.url"></md-input>
</md-field> </md-field>
@ -52,8 +63,8 @@
<md-dialog-actions> <md-dialog-actions>
<md-button @click="show.editor = false">Discard</md-button> <md-button @click="show.editor = false">{{ discardButtonLabel }}</md-button>
<md-button class="md-primary" @click="saveEdit">Save</md-button> <md-button class="md-primary" @click="saveEdit" v-if="type !== 'ingest'">Save</md-button>
</md-dialog-actions> </md-dialog-actions>
</md-dialog> </md-dialog>
@ -73,18 +84,39 @@
<!--- The box itself ---> <!--- The box itself --->
<div class="node-main"> <div class="node-main">
<div v-text="capitalizedType" class="node-type"></div> <div class="node-type">{{ capitalizedType }}<span v-if="apiId === null">*</span></div>
<div class="node-content"> <div class="node-content node-content-long" v-if="type === 'ingest'">
<table>
<tr :key="k" v-for="(v, k) in data"> <span v-if="this.apiId === null">Apply first!</span>
<th>{{ k }}</th> <span v-else><b>URL:</b> {{this.url}}</span>
<td>{{ v }}</td>
</tr>
</table>
</div> </div>
<div class="node-content" v-else-if="type === 'encoder'">
<table>
<tr>
<th>width</th>
<td>{{ this.data.width }}</td>
<td>px</td>
</tr>
<tr>
<th>height</th>
<td>{{ this.data.height }}</td>
<td>px</td>
</tr>
<tr>
<th>bitrate</th>
<td>{{ this.data.bitrate }}</td>
<td>kbps</td>
</tr>
</table>
</div>
<div class="node-content node-content-long" v-else-if="type === 'restreamer'">
<b>Target: </b> {{ this.data.url }}
</div>
</div> </div>
<!--- Output port ---> <!--- Output port --->
<div class="node-port node-output" <div class="node-port node-output"
v-if="haveOutput" v-if="haveOutput"
@ -134,6 +166,10 @@ export default {
default: () => { default: () => {
} }
}, },
apiId: {
type: String,
default: null
},
options: { options: {
type: Object, type: Object,
default() { default() {
@ -155,6 +191,9 @@ export default {
} }
}, },
mounted() { mounted() {
if (this.apiId === null && this.type !== 'ingest') {
this.$nextTick(() => this.show.editor = true);
}
}, },
computed: { computed: {
nodeStyle() { nodeStyle() {
@ -172,8 +211,16 @@ export default {
}, },
capitalizedType() { capitalizedType() {
return capitalize(this.type); return capitalize(this.type);
},
discardButtonLabel() {
if (this.type === 'ingest') {
return "CLOSE";
} else {
return "DISCARD";
}
} }
}, },
methods: { methods: {
handleMousedown(e) { handleMousedown(e) {
const target = e.target || e.srcElement; const target = e.target || e.srcElement;
@ -240,12 +287,18 @@ $portSize: 14;
.node-content { .node-content {
font-size: 13px; font-size: 13px;
padding: 0.5em;
th { th {
width: 50%; width: 50%;
} }
} }
.node-content-long {
word-break: break-all;
}
} }
.node-port { .node-port {

View File

@ -152,7 +152,11 @@ export default {
const existed = this.scene.links.find((link) => { const existed = this.scene.links.find((link) => {
return link.from === this.draggingLink.from && link.to === index; return link.from === this.draggingLink.from && link.to === index;
}) })
if (!existed) { // check for conflicting
const conflicting = this.scene.links.find((link) => { // This makes sure that only one link can go towards a node
return link.to === index;
})
if (!existed && !conflicting) {
let maxID = Math.max(0, ...this.scene.links.map((link) => { let maxID = Math.max(0, ...this.scene.links.map((link) => {
return link.id return link.id
})) }))
@ -223,10 +227,16 @@ export default {
if (typeof target.className === 'string' && target.className.indexOf('node-delete') > -1) { if (typeof target.className === 'string' && target.className.indexOf('node-delete') > -1) {
// console.log('delete2', this.action.dragging); // console.log('delete2', this.action.dragging);
this.nodeDelete(this.action.dragging); this.nodeDelete(this.action.dragging);
this.action.dragging = null;
} }
} }
if (this.action.dragging) {
this.$emit("nodeMoved", this.action.dragging);
this.action.dragging = null;
}
this.action.linking = false; this.action.linking = false;
this.action.dragging = null;
this.action.scrolling = false; this.action.scrolling = false;
}, },
handleDown(e) { handleDown(e) {

View File

@ -2,7 +2,8 @@
<div class="page-container md-layout-column" id="dashboard-container"> <div class="page-container md-layout-column" id="dashboard-container">
<!-- toolbar --> <!-- toolbar -->
<toolbar @showDrawerClicked="showDrawer = true" :processing="true" :model-changed="true"/> <toolbar @showDrawerClicked="showDrawer = true" @applyClicked="performApply" :processing="false"
:model-changed="modelChanged"/>
<!-- drawer --> <!-- drawer -->
<md-drawer :md-active.sync="showDrawer" md-swipeable> <md-drawer :md-active.sync="showDrawer" md-swipeable>
@ -11,7 +12,14 @@
<!-- workspace --> <!-- workspace -->
<md-content id="diagram-container"> <md-content id="diagram-container">
<simple-flowchart :scene.sync="diagram"></simple-flowchart> <simple-flowchart
:scene.sync="model"
@nodeDelete="enqueuePendingNodeDeletion"
@nodeDataEdited="enqueuePendingNodeUpdate"
@linkBreak="handleLinkBreak"
@linkAdded="handleLinkAdd"
@nodeMoved="handleNodeMove"
/>
<element-adder @addElement="newElement"/> <element-adder @addElement="newElement"/>
</md-content> </md-content>
@ -24,7 +32,8 @@ import SimpleFlowchart from '@/simple-flowchart';
import ElementAdder from "@/components/ElementAdder"; import ElementAdder from "@/components/ElementAdder";
import WorkspaceDrawerContent from "@/components/WorkspaceDrawerContent"; import WorkspaceDrawerContent from "@/components/WorkspaceDrawerContent";
import Toolbar from "@/components/Toolbar"; import Toolbar from "@/components/Toolbar";
import {maxBy} from 'lodash';
import {some} from 'lodash';
export default { export default {
name: 'Dashboard', name: 'Dashboard',
@ -36,7 +45,13 @@ export default {
}, },
data: () => ({ data: () => ({
showDrawer: false, showDrawer: false,
diagram: { idGenerator: 1,
pendingChanges: {
created: [],
updated: [],
deleted: []
},
model: {
centerX: 0, centerX: 0,
centerY: 0, centerY: 0,
scale: 1, scale: 1,
@ -51,40 +66,95 @@ export default {
let newNode = { let newNode = {
x: 10, x: 10,
y: 10, y: 10,
type type,
apiId: null
} }
switch (type) { switch (type) {
case "ingest": case "ingest":
newNode.data = { newNode.data = {
"url": "https://videon.test.kmlabz.com/live" url: "",
streamkey: "demokey"
} }
break; break;
case "encoder": case "encoder":
newNode.data = { newNode.data = {
"bitrate": 8000, bitrate: 0,
"width": 600, width: 0,
"height": 800 height: 0
} }
break; break;
case "restreamer": case "restreamer":
newNode.data = { newNode.data = {
"url": "https://youtube.com/live", url: "",
"streamkey": "testkey" streamkey: ""
} }
break; break;
} }
if (this.diagram.nodes.length > 0) { newNode.id = this.idGenerator;
const max_id = maxBy(this.diagram.nodes, 'id').id this.idGenerator++;
newNode.id = max_id + 1; this.model.nodes.push(newNode);
//this.pendingChanges.created.add(newNode.id);
this.enqueuePendingNodeCreation(newNode.id);
},
// Vuejs can not track changes of a set, so we do this magic hack to fix it
enqueuePendingNodeDeletion(id) {
let pending_deletes = new Set(this.pendingChanges.deleted);
let pending_updates = new Set(this.pendingChanges.updated);
let pending_creations = new Set(this.pendingChanges.created);
// This stuff is here to prevent the creation and deletion of an object in the same transaction
if (pending_creations.has(id)) {
pending_creations.delete(id);
} else { } else {
newNode.id = 1; pending_deletes.add(id);
} }
this.diagram.nodes.push(newNode) pending_updates.delete(id);
this.pendingChanges.deleted = Array.from(pending_deletes);
this.pendingChanges.updated = Array.from(pending_updates);
this.pendingChanges.created = Array.from(pending_creations);
},
enqueuePendingNodeUpdate(id) {
let pending_updates = new Set(this.pendingChanges.updated);
pending_updates.add(id);
this.pendingChanges.updated = Array.from(pending_updates);
},
enqueuePendingNodeCreation(id) {
let pending_creations = new Set(this.pendingChanges.created);
pending_creations.add(id);
this.pendingChanges.created = Array.from(pending_creations);
},
// --- end of magic hack
handleNodeMove() {
// TODO
},
handleLinkBreak({from,to}) {
this.enqueuePendingNodeUpdate(from);
this.enqueuePendingNodeUpdate(to);
},
handleLinkAdd({from, to}) {
this.enqueuePendingNodeUpdate(from);
this.enqueuePendingNodeUpdate(to);
},
performApply() {
this.pendingChanges.created.forEach((id) => {
this.model.nodes.find((n) => n.id === id).apiId = 'a';
});
this.pendingChanges = {
deleted: [],
created: [],
updated: []
}
}
},
computed: {
modelChanged() {
return some(this.pendingChanges, (o) => o.length > 0);
} }
} }