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

Commit

Permalink
Presentation Mode - Select Multiple Beacons on Graph and Timeline (#187)
Browse files Browse the repository at this point in the history
* change presentation mode header language

* export breadcrumbLinkStyle

* shuffle Presentation mode components

* simple presentation mode beacon name header

* adding more robust PresentationItemHeader

* removing the header-like nav breadcrumbs from the CommentGroup

* forgot .map() key

* update HierarchicalGraphData.selectNodes() to accept an array

* InteractionState.changeSelected selects multiple beacons if in presentation mode

* simplify routing by removing /:currentItem from from RedEyeRoutes.CAMPAIGN_PRESENTATION_SELECTED

* repair presentation timeline beacon highlighting

* fix another issue with removing currentItemParams from router

* remove stray comment

---------

Co-authored-by: James Bradford <[email protected]>
  • Loading branch information
arniebradfo and James Bradford authored Sep 25, 2023
1 parent 0142126 commit 795e36d
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 45 deletions.
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
Expand Up @@ -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();
}
}

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

0 comments on commit 795e36d

Please sign in to comment.