diff --git a/packages/virtual-tour-plugin/src/VirtualTourPlugin.ts b/packages/virtual-tour-plugin/src/VirtualTourPlugin.ts index b45477f2a..c5e71a784 100644 --- a/packages/virtual-tour-plugin/src/VirtualTourPlugin.ts +++ b/packages/virtual-tour-plugin/src/VirtualTourPlugin.ts @@ -445,16 +445,8 @@ export class VirtualTourPlugin extends AbstractConfigurablePlugin< if (this.isServerSide) { throw new PSVError('Cannot update node in server side mode'); } - if (!newNode.id) { - throw new PSVError('No id given for node'); - } - - const node = this.datasource.nodes[newNode.id]; - if (!node) { - throw new PSVError(`Node ${newNode.id} does not exist`); - } - Object.assign(node, newNode); + const node = (this.datasource as ClientSideDatasource).updateNode(newNode); if (newNode.name || newNode.thumbnail || newNode.panorama) { this.__setGalleryItems(); diff --git a/packages/virtual-tour-plugin/src/datasources/AbstractDataSource.ts b/packages/virtual-tour-plugin/src/datasources/AbstractDataSource.ts index 83cefaa24..b8c12e3d7 100644 --- a/packages/virtual-tour-plugin/src/datasources/AbstractDataSource.ts +++ b/packages/virtual-tour-plugin/src/datasources/AbstractDataSource.ts @@ -15,9 +15,7 @@ export abstract class AbstractDatasource { destroy() {} /** - * @summary Loads a node - * @param {string} nodeId - * @return {Promise} + * Loads a node */ abstract loadNode(nodeId: string): Promise; @@ -42,6 +40,10 @@ export abstract class AbstractDatasource { if (!this.plugin.isGps && node.markers?.some((marker) => marker.gps && !marker.position)) { throw new PSVError(`Cannot use GPS positioning for markers in manual mode`); } + if (!node.links) { + utils.logWarn(`Node ${node.id} has no links`); + node.links = []; + } } /** @@ -51,6 +53,9 @@ export abstract class AbstractDatasource { if (!link.nodeId) { throw new PSVError(`Link of node ${node.id} has no target id`); } + if (link.nodeId === node.id) { + throw new PSVError(`Node ${node.id} links to itself`); + } if (Array.isArray(link.position)) { utils.logWarn('Use the "gps" property to configure the GPS position of a virtual link'); link.gps = link.position as any; diff --git a/packages/virtual-tour-plugin/src/datasources/ClientSideDatasource.ts b/packages/virtual-tour-plugin/src/datasources/ClientSideDatasource.ts index 2555d4c03..19239de7b 100644 --- a/packages/virtual-tour-plugin/src/datasources/ClientSideDatasource.ts +++ b/packages/virtual-tour-plugin/src/datasources/ClientSideDatasource.ts @@ -3,11 +3,11 @@ import { VirtualTourNode } from '../model'; import { AbstractDatasource } from './AbstractDataSource'; export class ClientSideDatasource extends AbstractDatasource { - loadNode(nodeId: string) { + async loadNode(nodeId: string) { if (this.nodes[nodeId]) { - return Promise.resolve(this.nodes[nodeId]); + return this.nodes[nodeId]; } else { - return Promise.reject(new PSVError(`Node ${nodeId} not found`)); + throw new PSVError(`Node ${nodeId} not found`); } } @@ -25,24 +25,14 @@ export class ClientSideDatasource extends AbstractDatasource { if (nodes[node.id]) { throw new PSVError(`Duplicate node ${node.id}`); } - if (!node.links) { - utils.logWarn(`Node ${node.id} has no links`); - node.links = []; - } nodes[node.id] = node; }); rawNodes.forEach((node) => { - node.links.forEach((link) => { - if (!nodes[link.nodeId]) { - throw new PSVError(`Target node ${link.nodeId} of node ${node.id} does not exists`); - } - - link.gps = link.gps || nodes[link.nodeId].gps; - - this.checkLink(node, link); + this.__checkLinks(node, nodes); + node.links.forEach((link) => { linkedNodes[link.nodeId] = true; }); }); @@ -55,4 +45,35 @@ export class ClientSideDatasource extends AbstractDatasource { this.nodes = nodes; } + + updateNode(rawNode: Partial & { id: VirtualTourNode['id'] }) { + if (!rawNode.id) { + throw new PSVError('No id given for node'); + } + + const node = this.nodes[rawNode.id]; + if (!node) { + throw new PSVError(`Node ${rawNode.id} does not exist`); + } + + Object.assign(node, rawNode); + + this.checkNode(node); + + this.__checkLinks(node, this.nodes); + + return node; + } + + private __checkLinks(node: VirtualTourNode, nodes: Record) { + node.links.forEach((link) => { + if (!nodes[link.nodeId]) { + throw new PSVError(`Target node ${link.nodeId} of node ${node.id} does not exists`); + } + + link.gps = link.gps || nodes[link.nodeId].gps; + + this.checkLink(node, link); + }); + } } diff --git a/packages/virtual-tour-plugin/src/datasources/ServerSideDatasource.ts b/packages/virtual-tour-plugin/src/datasources/ServerSideDatasource.ts index d3bf2dbe7..26a4c2621 100644 --- a/packages/virtual-tour-plugin/src/datasources/ServerSideDatasource.ts +++ b/packages/virtual-tour-plugin/src/datasources/ServerSideDatasource.ts @@ -1,7 +1,7 @@ import type { Viewer } from '@photo-sphere-viewer/core'; -import { PSVError, utils } from '@photo-sphere-viewer/core'; +import { PSVError } from '@photo-sphere-viewer/core'; import { VirtualTourPluginConfig } from '../model'; -import { VirtualTourPlugin } from '../VirtualTourPlugin'; +import type { VirtualTourPlugin } from '../VirtualTourPlugin'; import { AbstractDatasource } from './AbstractDataSource'; export class ServerSideDatasource extends AbstractDatasource { @@ -17,28 +17,21 @@ export class ServerSideDatasource extends AbstractDatasource { this.nodeResolver = plugin.config.getNode; } - loadNode(nodeId: string) { + async loadNode(nodeId: string) { if (this.nodes[nodeId]) { - return Promise.resolve(this.nodes[nodeId]); + return this.nodes[nodeId]; } else { - return Promise.resolve(this.nodeResolver(nodeId)).then((node) => { - this.checkNode(node); - if (!node.links) { - utils.logWarn(`Node ${node.id} has no links`); - node.links = []; - } - - node.links.forEach((link) => { - if (this.nodes[link.nodeId]) { - link.gps = link.gps || this.nodes[link.nodeId].gps; - } - - this.checkLink(node, link); - }); - - this.nodes[nodeId] = node; - return node; + const node = await this.nodeResolver(nodeId); + + this.checkNode(node); + + node.links.forEach((link) => { + this.checkLink(node, link); }); + + this.nodes[nodeId] = node; + + return node; } }