diff --git a/applications/client/src/store/campaign/interaction-state.ts b/applications/client/src/store/campaign/interaction-state.ts index afe01dbd..f34e3619 100644 --- a/applications/client/src/store/campaign/interaction-state.ts +++ b/applications/client/src/store/campaign/interaction-state.ts @@ -44,7 +44,7 @@ export class InteractionState extends ExtendedModel(() => ({ })) { protected onAttachedToRootStore(rootStore: any): (() => void) | void { return reaction( - () => [rootStore.router.params.currentItemId, !!rootStore.campaign.graph], + () => [rootStore.router.params.currentItemId, rootStore.router.params.slide, !!rootStore.campaign.graph], () => { this.changeSelected(); }, @@ -155,27 +155,41 @@ export class InteractionState extends ExtendedModel(() => ({ @modelAction changeSelected() { if (this.appStore) { - const { server, host, operator, beacon, commandType } = this.currentItem?.items || {}; this.appStore.campaign.timeline?.selectedBeacons.clear(); this.appStore.campaign.timeline?.selectedChainedBeacons.clear(); - if (beacon) { - this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(beacon)); - } else if (host) { - const comp = this.currentHosts.get(host); - if (comp) { - for (const hostBeaconId of comp.beaconIds) { - this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(hostBeaconId)); + const isPresentation = this.appStore?.router.params.view === CampaignViews.PRESENTATION; + if (isPresentation) { + const beacons = this.appStore.campaign.presentation.currentSlide?.beacons; + const beaconIds = beacons?.map((beacon) => beacon.id); + if (beaconIds) { + for (const beaconId of beaconIds) { + this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(beaconId)); } + this.appStore.campaign.graph?.graphData.selectNodes(beaconIds); + } else { + this.appStore.campaign.graph?.graphData.clearSelection(); } - } else if (server) { - const comp = this.appStore.graphqlStore.servers.get(server); - if (comp) { - for (const serverBeacon of comp.beacons) { - this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(serverBeacon.id)); + } else { + const { server, host, operator, beacon, commandType } = this.currentItem?.items || {}; + if (beacon) { + this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(beacon)); + } else if (host) { + const comp = this.currentHosts.get(host); + if (comp) { + for (const hostBeaconId of comp.beaconIds) { + this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(hostBeaconId)); + } + } + } else if (server) { + const comp = this.appStore.graphqlStore.servers.get(server); + if (comp) { + for (const serverBeacon of comp.beacons) { + this.appStore.campaign.timeline?.setBeacon(this.currentBeacons.get(serverBeacon.id)); + } } } + this.setSelectedModels(beacon, host, server, operator, commandType); } - this.setSelectedModels(beacon, host, server, operator, commandType); } } @@ -206,8 +220,8 @@ export class InteractionState extends ExtendedModel(() => ({ else this.selectedCommandType = undefined; if (beaconId || hostId || serverId) { - this.appStore?.campaign.graph?.graphData.selectNode( - (beaconId || hostId || this.selectedServer?.maybeCurrent?.serverHost?.id)!, + this.appStore?.campaign.graph?.graphData.selectNodes( + [(beaconId || hostId || this.selectedServer?.maybeCurrent?.serverHost?.id)!], false ); } else { diff --git a/applications/client/src/store/campaign/presentation.ts b/applications/client/src/store/campaign/presentation.ts index 3f9daddb..b9ebbe13 100644 --- a/applications/client/src/store/campaign/presentation.ts +++ b/applications/client/src/store/campaign/presentation.ts @@ -30,25 +30,16 @@ export class PresentationStore extends ExtendedModel(RedEyeModel, {}) { @modelAction async changeIndex(index: number, presentation?: string) { if (this.appStore?.router.params.presentation || presentation) { - const currentSlide = presentation - ? this.appStore?.graphqlStore.presentationItems.get(presentation)?.commandGroups?.[index]?.current - : this.selectedItem?.commandGroups?.[index]?.current; - const beaconId = currentSlide?.beaconIds?.[(currentSlide?.beaconIds?.length || 1) - 1]; - const beacon = beaconId && this.appStore?.graphqlStore.beacons.get(beaconId); - if (beacon) { - this.appStore?.router.updateRoute({ - path: routes[CampaignViews.PRESENTATION], - params: { - presentation: presentation || this.selectedItem?.id, - slide: `${index + 1}`, - currentItem: 'beacon', - currentItemId: beacon.id as UUID, - activeItem: undefined, - activeItemId: undefined, - }, - }); - this.updateTimeline(); - } + this.appStore?.router.updateRoute({ + path: routes[CampaignViews.PRESENTATION], + params: { + presentation: presentation || this.selectedItem?.id, + slide: `${index + 1}`, + activeItem: undefined, + activeItemId: undefined, + }, + }); + this.updateTimeline(); } } diff --git a/applications/client/src/store/routing/router.ts b/applications/client/src/store/routing/router.ts index da5d5f80..83885758 100644 --- a/applications/client/src/store/routing/router.ts +++ b/applications/client/src/store/routing/router.ts @@ -26,7 +26,7 @@ export const RedEyeRoutes = { CAMPAIGN: '/campaign/:id', CAMPAIGN_EXPLORE: `${CampaignViews.EXPLORE}/:currentItem/:tab`, CAMPAIGN_PRESENTATION: `${CampaignViews.PRESENTATION}`, - CAMPAIGN_PRESENTATION_SELECTED: ':presentation/:slide/:currentItem', + CAMPAIGN_PRESENTATION_SELECTED: ':presentation/:slide', CAMPAIGN_SEARCH: `${CampaignViews.SEARCH}`, }; @@ -37,7 +37,7 @@ export const routes = { [Views.CAMPAIGN]: RedEyeRoutes.CAMPAIGN, [Views.CAMPAIGNS_LIST]: RedEyeRoutes.CAMPAIGNS_LIST, [CampaignViews.EXPLORE]: `${RedEyeRoutes.CAMPAIGN}/:view(${CampaignViews.EXPLORE})/${currentItemParams}/:tab(${tabs})?/${activeItemParams}`, - [CampaignViews.PRESENTATION]: `${RedEyeRoutes.CAMPAIGN}/:view(${CampaignViews.PRESENTATION})/:presentation?/:slide?/${currentItemParams}/${activeItemParams}`, + [CampaignViews.PRESENTATION]: `${RedEyeRoutes.CAMPAIGN}/:view(${CampaignViews.PRESENTATION})/:presentation?/:slide?/${activeItemParams}`, [CampaignViews.SEARCH]: `${RedEyeRoutes.CAMPAIGN}/:view(${CampaignViews.SEARCH})`, }; diff --git a/applications/redeye-e2e/src/support/beacon.js b/applications/redeye-e2e/src/support/beacon.js index fb193759..15f82cf8 100644 --- a/applications/redeye-e2e/src/support/beacon.js +++ b/applications/redeye-e2e/src/support/beacon.js @@ -71,7 +71,7 @@ Cypress.Commands.add('addMultiCommandComment', () => { Cypress.Commands.add('beaconClick', (id) => { cy.wait(1000); return cy.window().then((win) => { - win.graph.graphData.selectNode(id); + win.graph.graphData.selectNode([id]); }); }); diff --git a/packages/graph/src/GraphData/HierarchicalGraphData.ts b/packages/graph/src/GraphData/HierarchicalGraphData.ts index 922f1a24..d1e4d37b 100644 --- a/packages/graph/src/GraphData/HierarchicalGraphData.ts +++ b/packages/graph/src/GraphData/HierarchicalGraphData.ts @@ -335,12 +335,15 @@ export class HierarchicalGraphData { } } - selectNode(node: HierarchicalGraphNode | string, fireEvent = true) { - const _node = typeof node === 'string' ? this.allNodes.get(node) : node; - if (!_node) return; + selectNodes(nodes: (HierarchicalGraphNode | string)[], fireEvent = true) { this.clearSelection(false); - this.addNodeToSelection(_node, false); - if (fireEvent) this.onSelectionChange(_node, Array.from(this.selectionSet)); + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + const _node = typeof node === 'string' ? this.allNodes.get(node) : node; + if (!_node) return; + this.addNodeToSelection(_node, false); + if (fireEvent) this.onSelectionChange(_node, Array.from(this.selectionSet)); + } } addNodeToSelection(node: HierarchicalGraphNode, fireEvent = true) { this.addToSet(node, 'selection'); diff --git a/packages/graph/src/GraphHandler.ts b/packages/graph/src/GraphHandler.ts index bd748ec6..dd48f473 100644 --- a/packages/graph/src/GraphHandler.ts +++ b/packages/graph/src/GraphHandler.ts @@ -292,7 +292,8 @@ export class GraphHandler { clickNode(_event: PointerEvent, node: HierarchicalGraphNode) { if (node.selectedFocus) this.graphData.clearSelection(); - else this.graphData.selectNode(node); + // else if (_event.metaKey) this.graphData.addNodeToSelection(node); + else this.graphData.selectNodes([node]); } mouseOverNode(_event: PointerEvent, node: HierarchicalGraphNode) { if (!this.dragState.isDragging) this.graphData.previewNode(node);