Destilled node operations to an operation queue
This commit is contained in:
parent
1428502706
commit
4ff230007c
@ -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,17 +84,38 @@
|
|||||||
|
|
||||||
<!--- 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>
|
||||||
|
<div class="node-content node-content-long" v-else-if="type === 'restreamer'">
|
||||||
|
<b>Target: </b> {{ this.data.url }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!--- Output port --->
|
<!--- Output port --->
|
||||||
<div class="node-port node-output"
|
<div class="node-port node-output"
|
||||||
@ -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 {
|
||||||
|
@ -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.linking = false;
|
|
||||||
this.action.dragging = null;
|
this.action.dragging = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.action.dragging) {
|
||||||
|
this.$emit("nodeMoved", this.action.dragging);
|
||||||
|
this.action.dragging = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.action.linking = false;
|
||||||
this.action.scrolling = false;
|
this.action.scrolling = false;
|
||||||
},
|
},
|
||||||
handleDown(e) {
|
handleDown(e) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user