Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Presentation Mode - Select Multiple Beacons on Graph and Timeline #187

Merged
merged 14 commits into from
Sep 25, 2023
Merged
48 changes: 31 additions & 17 deletions applications/client/src/store/campaign/interaction-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
},
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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 {
Expand Down
29 changes: 10 additions & 19 deletions applications/client/src/store/campaign/presentation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CampaignViews } from '@redeye/client/types';
import type { UUID } from '@redeye/client/types/uuid';

Check warning on line 2 in applications/client/src/store/campaign/presentation.ts

View workflow job for this annotation

GitHub Actions / RedTeamChrome (1, 18.16.1)

'UUID' is defined but never used

Check warning on line 2 in applications/client/src/store/campaign/presentation.ts

View workflow job for this annotation

GitHub Actions / BlueTeamChrome (1, 18.16.1)

'UUID' is defined but never used

Check warning on line 2 in applications/client/src/store/campaign/presentation.ts

View workflow job for this annotation

GitHub Actions / RedTeamChrome (2, 18.16.1)

'UUID' is defined but never used

Check warning on line 2 in applications/client/src/store/campaign/presentation.ts

View workflow job for this annotation

GitHub Actions / BlueTeamChrome (2, 18.16.1)

'UUID' is defined but never used
import { computed } from 'mobx';
import { ExtendedModel, model, modelAction } from 'mobx-keystone';
import type { PresentationCommandGroupModel, PresentationItemModel } from '../graphql';
Expand Down Expand Up @@ -30,25 +30,16 @@

@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();
}
}

Expand Down
4 changes: 2 additions & 2 deletions applications/client/src/store/routing/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
};

Expand All @@ -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})`,
};

Expand Down
2 changes: 1 addition & 1 deletion applications/redeye-e2e/src/support/beacon.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
});
});

Expand Down
13 changes: 8 additions & 5 deletions packages/graph/src/GraphData/HierarchicalGraphData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
3 changes: 2 additions & 1 deletion packages/graph/src/GraphHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading