diff --git a/src/components/Element/Complex/MapThreatInfoElement.tsx b/src/components/Element/Complex/MapThreatInfoElement.tsx index b84aa0a..d924c09 100644 --- a/src/components/Element/Complex/MapThreatInfoElement.tsx +++ b/src/components/Element/Complex/MapThreatInfoElement.tsx @@ -26,23 +26,7 @@ const MapThreatInfoElement = ({ element, inGaze }: Props) => { } useEffect(() => { - // set its own expiration time in the beginning as soon as it is rendered - if (!collapsed && !element.expiration && element.expirationInterval) { - const expireAt = new Date(); - expireAt.setMilliseconds( - expireAt.getMilliseconds() + element.expirationInterval, - ); - - dispatch( - updateElement(element.widgetId, { - ...element, - expiration: expireAt.toISOString(), - }), - ); - } - }, [collapsed, element, dispatch]); - - useEffect(() => { + // react to deescalation if (deescalate) { dispatch( updateElement(element.widgetId!, { diff --git a/src/components/Widget/MapWarningWidget.tsx b/src/components/Widget/MapWarningWidget.tsx index 0714bce..d4311dc 100644 --- a/src/components/Widget/MapWarningWidget.tsx +++ b/src/components/Widget/MapWarningWidget.tsx @@ -1,7 +1,10 @@ import type { MapWarningWidget as MapWarningWidgetType } from 'src/types/widget'; import { useAppDispatch, useAppSelector } from 'src/redux/hooks'; import { getElementsInGaze } from 'src/redux/slices/gazeSlice'; -import { updateElement } from 'src/redux/slices/minimapSlice'; +import { + updateElement, + updateElementExpiration, +} from 'src/redux/slices/minimapSlice'; import { useEffect } from 'react'; import type { IconElement as IconElementType, @@ -15,10 +18,6 @@ type MapWarningWidgetProps = { }; const MapWarningWidget = ({ widget }: MapWarningWidgetProps) => { - /** We have made widgets too generic, I think. Severely restricts our ability to design different widgets differently. - * It would be very useful for different widgets types to specify in detail the number of elements they will have - * as well as the type of each of those elements - Jagjit. - */ const [iconElement, threatInfoElement] = widget.elements; const elementsInGaze = useAppSelector(getElementsInGaze); @@ -28,13 +27,19 @@ const MapWarningWidget = ({ widget }: MapWarningWidgetProps) => { (element) => element.id === iconElement.id, ); - // show threat info element if icon element is in gaze useEffect(() => { + // show threat info element if icon element is in gaze if (inGaze && threatInfoElement.collapsed) { dispatch( updateElement(widget.id, { ...threatInfoElement, collapsed: false }), ); } + + if (inGaze && threatInfoElement.expirationIntervalMs) { + // update expiration even if only icon element is in gaze + // keep displaying threat info element while we hover over the icon + dispatch(updateElementExpiration(widget.id, threatInfoElement.id)); + } }, [inGaze, dispatch, iconElement, threatInfoElement, widget.id]); return ( diff --git a/src/prototype/lpd/stress/highLPD.ts b/src/prototype/lpd/stress/highLPD.ts index 485a456..71b0c76 100644 --- a/src/prototype/lpd/stress/highLPD.ts +++ b/src/prototype/lpd/stress/highLPD.ts @@ -67,7 +67,7 @@ const requestApprovalToAttackMessageHigh = ( message, size: 'M', // size L when stress is low collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, @@ -196,7 +196,7 @@ const missileToOwnshipDetectedMessageHigh = ( w: 128, widgetId: minimapWidgetId1, src: mapTargetTypeToWarningIcon('missile'), - expirationInterval: 5000, + expirationIntervalMs: 5000, onExpiration: 'escalate', } satisfies IconElement, { @@ -208,7 +208,7 @@ const missileToOwnshipDetectedMessageHigh = ( message, size: 'M', // size L when stress is low collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, diff --git a/src/prototype/lpd/stress/lowLPD.ts b/src/prototype/lpd/stress/lowLPD.ts index 43f5aeb..431522e 100644 --- a/src/prototype/lpd/stress/lowLPD.ts +++ b/src/prototype/lpd/stress/lowLPD.ts @@ -50,7 +50,7 @@ const requestApprovalToAttackMessageLow = ( w: 80, widgetId: minimapWidgetId1, src: mapTargetTypeToWarningIcon(message.data.target.type), - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'escalate', } satisfies IconElement, { @@ -62,7 +62,7 @@ const requestApprovalToAttackMessageLow = ( message, size: 'L', // size L when stress is low collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, @@ -183,7 +183,7 @@ const missileToOwnshipDetectedMessageLow = ( message, size: 'L', // size L when stress is low collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, diff --git a/src/prototype/lpd/stress/mediumLPD.ts b/src/prototype/lpd/stress/mediumLPD.ts index 48a5181..a26dd64 100644 --- a/src/prototype/lpd/stress/mediumLPD.ts +++ b/src/prototype/lpd/stress/mediumLPD.ts @@ -60,7 +60,7 @@ const requestApprovalToAttackMessageMedium = ( message, size: 'M', // size L when stress is low collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, @@ -199,7 +199,7 @@ const missileToOwnshipDetectedMessageMedium = ( message, size: 'M', // size L when stress is medium collapsed: true, // initially, the information elemnt is not displayed - expirationInterval: 3000, + expirationIntervalMs: 3000, onExpiration: 'deescalate', widgetId: minimapWidgetId1, } satisfies InformationElement, diff --git a/src/redux/slices/minimapSlice.ts b/src/redux/slices/minimapSlice.ts index e784d4a..0a3e266 100644 --- a/src/redux/slices/minimapSlice.ts +++ b/src/redux/slices/minimapSlice.ts @@ -1,7 +1,7 @@ import { createSlice, createSelector } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit'; import type { Widget, VehicleWidget, WidgetMap } from 'src/types/widget'; -import type { Screen, WidgetChannel } from 'src/types/support-types'; +import type { Screen } from 'src/types/support-types'; import type { Message } from 'src/types/schema-types'; import type { Element, ElementMap } from 'src/types/element'; import type { LinkedSectionWidget, Section } from 'src/types/support-types'; @@ -10,11 +10,6 @@ export type InitialMinimapState = { visualComplexity: number; audioComplexity: number; - // for sharing data between widgets - widgetChannels: { - [key in WidgetChannel]: any; - }; - // read-only ownship: VehicleWidget | null; drones: VehicleWidget[]; @@ -29,11 +24,6 @@ export type InitialMinimapState = { const initialState: InitialMinimapState = { visualComplexity: 0, audioComplexity: 0, - widgetChannels: { - 'list-history': { - activeMessageId: '', - }, - }, ownship: null, drones: [], messages: [], @@ -210,21 +200,26 @@ export const minimapSlice = createSlice({ // if widget exists if (widget) { - const tempElements = state.widgets[widgetId].elements; - tempElements.forEach(function (element, elementIndex) { - if (element.id === elementId && element.expirationInterval) { - const newExpiration = new Date(); - newExpiration.setSeconds( - newExpiration.getSeconds() + element.expirationInterval, + widget.elements.forEach((element) => { + if (element.id === elementId) { + // if element does not have an expiration interval, log an error + if (!element.expirationIntervalMs) { + console.error( + `Element with id ${elementId} does not have an expiration interval`, + ); + return; + } + + const expiration = new Date(); + expiration.setMilliseconds( + expiration.getMilliseconds() + element.expirationIntervalMs, ); - tempElements[elementIndex].expiration = - newExpiration.toISOString(); + + element.expiration = expiration.toISOString(); } }); - state.widgets[widgetId] = { - ...widget, - elements: tempElements, - }; + + state.widgets[widgetId] = widget; } else { console.error(`Widget with id ${widgetId} not found`); } @@ -495,7 +490,7 @@ export const { removeWidget, deleteElementFromWidget, - toggleElementInteraction, + // toggleElementInteraction, setStressLevel, } = minimapSlice.actions; diff --git a/src/types/element.ts b/src/types/element.ts index 107333d..d3204ab 100644 --- a/src/types/element.ts +++ b/src/types/element.ts @@ -20,7 +20,7 @@ export type BaseElement = { priority?: number; collapsed?: boolean; - expirationInterval?: number; + expirationIntervalMs?: number; // should be in ms expiration?: string; onExpiration?: 'delete' | 'escalate' | 'deescalate'; escalate?: boolean; diff --git a/src/utils/lpdHelper.ts b/src/utils/lpdHelper.ts index a056da4..238c088 100644 --- a/src/utils/lpdHelper.ts +++ b/src/utils/lpdHelper.ts @@ -122,7 +122,7 @@ const generateBaseElement = ( priority?: number, widgetId?: string, collapsed?: boolean, - expirationInterval?: number, + expirationIntervalMs?: number, expiration?: string, onExpiration?: 'delete' | 'escalate' | 'deescalate', interacted?: boolean, @@ -136,7 +136,7 @@ const generateBaseElement = ( widgetId, priority, collapsed, - expirationInterval, + expirationIntervalMs, expiration, onExpiration, interacted,