forked from platypush/platypush
More performance improvements for the entities page.
- Don't recalculate entity groups every time. Instead, keep them in sync every time an entity is added or removed. - Removed `computedChildren` from the entity component - no null nodes are guaranteed to be passed now, so there's no need for another iteration on the list of children. - `childrenByParentId` now only looks in the scope of the entity's children instead of searching all the entities.
This commit is contained in:
parent
0fc0a22cd7
commit
68359b88a9
2 changed files with 72 additions and 76 deletions
|
@ -8,7 +8,7 @@
|
||||||
:is="component"
|
:is="component"
|
||||||
:value="value"
|
:value="value"
|
||||||
:parent="parent"
|
:parent="parent"
|
||||||
:children="computedChildren"
|
:children="children"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
ref="instance"
|
ref="instance"
|
||||||
:error="error || value?.reachable == false"
|
:error="error || value?.reachable == false"
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="children fade-in" v-if="hasChildren && !isCollapsed">
|
<div class="children fade-in" v-if="hasChildren && !isCollapsed">
|
||||||
<div class="child" v-for="entity in computedChildren" :key="entity.id">
|
<div class="child" v-for="entity in children" :key="entity.id">
|
||||||
<Entity
|
<Entity
|
||||||
:value="entity"
|
:value="entity"
|
||||||
:parent="value"
|
:parent="value"
|
||||||
|
@ -57,19 +57,12 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedChildren() {
|
|
||||||
return Object.values(this.children || {}).filter((child) => child)
|
|
||||||
},
|
|
||||||
|
|
||||||
hasChildren() {
|
hasChildren() {
|
||||||
return !!this.computedChildren.length
|
return !!Object.keys(this.children).length
|
||||||
},
|
},
|
||||||
|
|
||||||
isCollapsed() {
|
isCollapsed() {
|
||||||
if (!this.hasChildren)
|
return !this.hasChildren ? true : this.collapsed
|
||||||
return true
|
|
||||||
|
|
||||||
return this.collapsed
|
|
||||||
},
|
},
|
||||||
|
|
||||||
instance() {
|
instance() {
|
||||||
|
@ -90,13 +83,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
childrenByParentId(parentId) {
|
childrenByParentId(parentId) {
|
||||||
return Object.values(this.allEntities || {}).
|
const parentEntity = this.allEntities?.[parentId]
|
||||||
filter(
|
if (!parentEntity)
|
||||||
(entity) => entity
|
return {}
|
||||||
&& entity.parent_id === parentId
|
|
||||||
&& !entity.is_configuration
|
return (parentEntity.children_ids || []).reduce((obj, entityId) => {
|
||||||
).
|
const entity = this.allEntities[entityId]
|
||||||
reduce((obj, entity) => {
|
if (entity && !entity.is_configuration)
|
||||||
obj[entity.id] = entity
|
obj[entity.id] = entity
|
||||||
return obj
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
|
@ -131,7 +124,7 @@ export default {
|
||||||
if (!isChildUpdate)
|
if (!isChildUpdate)
|
||||||
return
|
return
|
||||||
|
|
||||||
this.setJustUpdated()
|
this.notifyUpdate()
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleCollapsed() {
|
toggleCollapsed() {
|
||||||
|
@ -141,7 +134,7 @@ export default {
|
||||||
this.instance.collapsed = !this.instance.collapsed
|
this.instance.collapsed = !this.instance.collapsed
|
||||||
},
|
},
|
||||||
|
|
||||||
setJustUpdated() {
|
notifyUpdate() {
|
||||||
this.justUpdated = true
|
this.justUpdated = true
|
||||||
const self = this;
|
const self = this;
|
||||||
setTimeout(() => self.justUpdated = false, 1000)
|
setTimeout(() => self.justUpdated = false, 1000)
|
||||||
|
@ -160,7 +153,7 @@ export default {
|
||||||
if (this.valuesEqual(oldValue, newValue))
|
if (this.valuesEqual(oldValue, newValue))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
this.setJustUpdated()
|
this.notifyUpdate()
|
||||||
this.$emit('update', {value: newValue})
|
this.$emit('update', {value: newValue})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="entity-frame"
|
<div class="entity-frame"
|
||||||
v-for="entity in group.entities"
|
v-for="entity in Object.values(group.entities).sort((a, b) => a.name.localeCompare(b.name))"
|
||||||
:key="entity.id">
|
:key="entity.id">
|
||||||
<Entity
|
<Entity
|
||||||
:value="entity"
|
:value="entity"
|
||||||
|
@ -64,6 +64,7 @@
|
||||||
@show-modal="onEntityModal($event)"
|
@show-modal="onEntityModal($event)"
|
||||||
@input="onEntityInput(entity)"
|
@input="onEntityInput(entity)"
|
||||||
:error="!!errorEntities[entity.id]"
|
:error="!!errorEntities[entity.id]"
|
||||||
|
:key="entity.id"
|
||||||
:loading="!!loadingEntities[entity.id]"
|
:loading="!!loadingEntities[entity.id]"
|
||||||
@loading="loadingEntities[entity.id] = $event"
|
@loading="loadingEntities[entity.id] = $event"
|
||||||
v-if="!entity.parent_id"
|
v-if="!entity.parent_id"
|
||||||
|
@ -122,6 +123,12 @@ export default {
|
||||||
errorEntities: {},
|
errorEntities: {},
|
||||||
entityTimeouts: {},
|
entityTimeouts: {},
|
||||||
entities: {},
|
entities: {},
|
||||||
|
entityGroups: {
|
||||||
|
id: {},
|
||||||
|
category: {},
|
||||||
|
plugin: {},
|
||||||
|
type: {},
|
||||||
|
},
|
||||||
modalEntityId: null,
|
modalEntityId: null,
|
||||||
modalVisible: false,
|
modalVisible: false,
|
||||||
variableModalVisible: false,
|
variableModalVisible: false,
|
||||||
|
@ -141,10 +148,6 @@ export default {
|
||||||
return icons
|
return icons
|
||||||
},
|
},
|
||||||
|
|
||||||
entityTypes() {
|
|
||||||
return this.groupEntities('type')
|
|
||||||
},
|
|
||||||
|
|
||||||
typesByCategory() {
|
typesByCategory() {
|
||||||
return Object.entries(meta).reduce((obj, [type, meta]) => {
|
return Object.entries(meta).reduce((obj, [type, meta]) => {
|
||||||
obj[meta.name_plural] = type
|
obj[meta.name_plural] = type
|
||||||
|
@ -152,21 +155,10 @@ export default {
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
|
|
||||||
entityGroups() {
|
|
||||||
return {
|
|
||||||
'id': Object.entries(this.groupEntities('id')).reduce((obj, [id, entities]) => {
|
|
||||||
obj[id] = entities[0]
|
|
||||||
return obj
|
|
||||||
}, {}),
|
|
||||||
'category': this.groupEntities('category'),
|
|
||||||
'plugin': this.groupEntities('plugin'),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
displayGroups() {
|
displayGroups() {
|
||||||
return Object.entries(this.entityGroups[this.selector.grouping]).
|
return Object.entries(this.entityGroups[this.selector.grouping]).
|
||||||
filter(
|
filter(
|
||||||
(entry) => entry[1].filter(
|
(entry) => Object.values(entry[1]).filter(
|
||||||
(e) =>
|
(e) =>
|
||||||
!!this.selector.selectedEntities[e.id] && e.parent_id == null
|
!!this.selector.selectedEntities[e.id] && e.parent_id == null
|
||||||
).length > 0
|
).length > 0
|
||||||
|
@ -175,7 +167,7 @@ export default {
|
||||||
([grouping, entities]) => {
|
([grouping, entities]) => {
|
||||||
return {
|
return {
|
||||||
name: grouping,
|
name: grouping,
|
||||||
entities: entities.filter(
|
entities: Object.values(entities).filter(
|
||||||
(e) => e.id in this.selector.selectedEntities
|
(e) => e.id in this.selector.selectedEntities
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -186,19 +178,32 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
groupEntities(attr) {
|
addEntity(entity) {
|
||||||
return Object.values(this.entities).
|
if (entity.parent_id != null)
|
||||||
filter((entity) => entity.parent_id == null).
|
return // Only group entities that have no parent
|
||||||
reduce((obj, entity) => {
|
|
||||||
const entities = obj[entity[attr]] || {}
|
|
||||||
entities[entity.id] = entity
|
|
||||||
|
|
||||||
obj[entity[attr]] = Object.values(entities).sort((a, b) => {
|
this.entities[entity.id] = entity;
|
||||||
return a.name.localeCompare(b.name)
|
['id', 'type', 'category', 'plugin'].forEach((attr) => {
|
||||||
|
if (entity[attr] == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!this.entityGroups[attr][entity[attr]])
|
||||||
|
this.entityGroups[attr][entity[attr]] = {}
|
||||||
|
this.entityGroups[attr][entity[attr]][entity.id] = entity
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
removeEntity(entity) {
|
||||||
|
if (entity.parent_id != null)
|
||||||
|
return // Only group entities that have no parent
|
||||||
|
|
||||||
|
['id', 'type', 'category', 'plugin'].forEach((attr) => {
|
||||||
|
if (this.entityGroups[attr][entity[attr]][entity.id])
|
||||||
|
delete this.entityGroups[attr][entity[attr]][entity.id]
|
||||||
})
|
})
|
||||||
|
|
||||||
return obj
|
if (this.entities[entity.id])
|
||||||
}, {})
|
delete this.entities[entity.id]
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldSkipLoading(entity) {
|
_shouldSkipLoading(entity) {
|
||||||
|
@ -236,6 +241,7 @@ export default {
|
||||||
if (this.entityTimeouts[id])
|
if (this.entityTimeouts[id])
|
||||||
clearTimeout(this.entityTimeouts[id])
|
clearTimeout(this.entityTimeouts[id])
|
||||||
|
|
||||||
|
this.addEntity(entity)
|
||||||
this.entityTimeouts[id] = setTimeout(() => {
|
this.entityTimeouts[id] = setTimeout(() => {
|
||||||
if (self.loadingEntities[id])
|
if (self.loadingEntities[id])
|
||||||
delete self.loadingEntities[id]
|
delete self.loadingEntities[id]
|
||||||
|
@ -266,6 +272,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
obj[entity.id] = entity
|
obj[entity.id] = entity
|
||||||
|
this.addEntity(entity)
|
||||||
return obj
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
|
@ -275,30 +282,26 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
childrenByParentId(parentId) {
|
childrenByParentId(parentId, selectConfig) {
|
||||||
return Object.values(this.entities).
|
const entity = this.entities?.[parentId]
|
||||||
filter(
|
if (!entity?.children_ids?.length)
|
||||||
(entity) => entity
|
return {}
|
||||||
&& entity.parent_id === parentId
|
|
||||||
&& !entity.is_configuration
|
return entity.children_ids.reduce((obj, id) => {
|
||||||
).
|
const child = this.entities[id]
|
||||||
reduce((obj, entity) => {
|
if (
|
||||||
obj[entity.id] = entity
|
child && (
|
||||||
|
(!selectConfig && !child.is_configuration) ||
|
||||||
|
(selectConfig && child.is_configuration)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
obj[id] = this.entities[id]
|
||||||
return obj
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
|
|
||||||
configValuesByParentId(parentId) {
|
configValuesByParentId(parentId) {
|
||||||
return Object.values(this.entities).
|
return this.childrenByParentId(parentId, true)
|
||||||
filter(
|
|
||||||
(entity) => entity
|
|
||||||
&& entity.parent_id === parentId
|
|
||||||
&& entity.is_configuration
|
|
||||||
).
|
|
||||||
reduce((obj, entity) => {
|
|
||||||
obj[entity.id] = entity
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clearEntityTimeouts(entityId) {
|
clearEntityTimeouts(entityId) {
|
||||||
|
@ -343,7 +346,7 @@ export default {
|
||||||
...(event.entity?.meta || {}),
|
...(event.entity?.meta || {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.entities[entityId] = entity
|
this.addEntity(entity)
|
||||||
bus.publishEntity(entity)
|
bus.publishEntity(entity)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -354,7 +357,7 @@ export default {
|
||||||
if (entityId === this.modalEntityId)
|
if (entityId === this.modalEntityId)
|
||||||
this.modalEntityId = null
|
this.modalEntityId = null
|
||||||
if (this.entities[entityId])
|
if (this.entities[entityId])
|
||||||
delete this.entities[entityId]
|
this.removeEntity(this.entities[entityId])
|
||||||
},
|
},
|
||||||
|
|
||||||
onEntityModal(entityId) {
|
onEntityModal(entityId) {
|
||||||
|
|
Loading…
Reference in a new issue