fix bug: info elements never collapsing after being hovered over once
This commit is contained in:
parent
6d647b64c4
commit
87a1d81240
@ -26,23 +26,7 @@ const MapThreatInfoElement = ({ element, inGaze }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// set its own expiration time in the beginning as soon as it is rendered
|
// react to deescalation
|
||||||
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(() => {
|
|
||||||
if (deescalate) {
|
if (deescalate) {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateElement(element.widgetId!, {
|
updateElement(element.widgetId!, {
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import type { MapWarningWidget as MapWarningWidgetType } from 'src/types/widget';
|
import type { MapWarningWidget as MapWarningWidgetType } from 'src/types/widget';
|
||||||
import { useAppDispatch, useAppSelector } from 'src/redux/hooks';
|
import { useAppDispatch, useAppSelector } from 'src/redux/hooks';
|
||||||
import { getElementsInGaze } from 'src/redux/slices/gazeSlice';
|
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 { useEffect } from 'react';
|
||||||
import type {
|
import type {
|
||||||
IconElement as IconElementType,
|
IconElement as IconElementType,
|
||||||
@ -15,10 +18,6 @@ type MapWarningWidgetProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MapWarningWidget = ({ widget }: 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 [iconElement, threatInfoElement] = widget.elements;
|
||||||
|
|
||||||
const elementsInGaze = useAppSelector(getElementsInGaze);
|
const elementsInGaze = useAppSelector(getElementsInGaze);
|
||||||
@ -28,13 +27,19 @@ const MapWarningWidget = ({ widget }: MapWarningWidgetProps) => {
|
|||||||
(element) => element.id === iconElement.id,
|
(element) => element.id === iconElement.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// show threat info element if icon element is in gaze
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// show threat info element if icon element is in gaze
|
||||||
if (inGaze && threatInfoElement.collapsed) {
|
if (inGaze && threatInfoElement.collapsed) {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateElement(widget.id, { ...threatInfoElement, collapsed: false }),
|
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]);
|
}, [inGaze, dispatch, iconElement, threatInfoElement, widget.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -67,7 +67,7 @@ const requestApprovalToAttackMessageHigh = (
|
|||||||
message,
|
message,
|
||||||
size: 'M', // size L when stress is low
|
size: 'M', // size L when stress is low
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
@ -196,7 +196,7 @@ const missileToOwnshipDetectedMessageHigh = (
|
|||||||
w: 128,
|
w: 128,
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
src: mapTargetTypeToWarningIcon('missile'),
|
src: mapTargetTypeToWarningIcon('missile'),
|
||||||
expirationInterval: 5000,
|
expirationIntervalMs: 5000,
|
||||||
onExpiration: 'escalate',
|
onExpiration: 'escalate',
|
||||||
} satisfies IconElement,
|
} satisfies IconElement,
|
||||||
{
|
{
|
||||||
@ -208,7 +208,7 @@ const missileToOwnshipDetectedMessageHigh = (
|
|||||||
message,
|
message,
|
||||||
size: 'M', // size L when stress is low
|
size: 'M', // size L when stress is low
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
|
@ -50,7 +50,7 @@ const requestApprovalToAttackMessageLow = (
|
|||||||
w: 80,
|
w: 80,
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
src: mapTargetTypeToWarningIcon(message.data.target.type),
|
src: mapTargetTypeToWarningIcon(message.data.target.type),
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'escalate',
|
onExpiration: 'escalate',
|
||||||
} satisfies IconElement,
|
} satisfies IconElement,
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ const requestApprovalToAttackMessageLow = (
|
|||||||
message,
|
message,
|
||||||
size: 'L', // size L when stress is low
|
size: 'L', // size L when stress is low
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
@ -183,7 +183,7 @@ const missileToOwnshipDetectedMessageLow = (
|
|||||||
message,
|
message,
|
||||||
size: 'L', // size L when stress is low
|
size: 'L', // size L when stress is low
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
|
@ -60,7 +60,7 @@ const requestApprovalToAttackMessageMedium = (
|
|||||||
message,
|
message,
|
||||||
size: 'M', // size L when stress is low
|
size: 'M', // size L when stress is low
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
@ -199,7 +199,7 @@ const missileToOwnshipDetectedMessageMedium = (
|
|||||||
message,
|
message,
|
||||||
size: 'M', // size L when stress is medium
|
size: 'M', // size L when stress is medium
|
||||||
collapsed: true, // initially, the information elemnt is not displayed
|
collapsed: true, // initially, the information elemnt is not displayed
|
||||||
expirationInterval: 3000,
|
expirationIntervalMs: 3000,
|
||||||
onExpiration: 'deescalate',
|
onExpiration: 'deescalate',
|
||||||
widgetId: minimapWidgetId1,
|
widgetId: minimapWidgetId1,
|
||||||
} satisfies InformationElement,
|
} satisfies InformationElement,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createSlice, createSelector } from '@reduxjs/toolkit';
|
import { createSlice, createSelector } from '@reduxjs/toolkit';
|
||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import type { Widget, VehicleWidget, WidgetMap } from 'src/types/widget';
|
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 { Message } from 'src/types/schema-types';
|
||||||
import type { Element, ElementMap } from 'src/types/element';
|
import type { Element, ElementMap } from 'src/types/element';
|
||||||
import type { LinkedSectionWidget, Section } from 'src/types/support-types';
|
import type { LinkedSectionWidget, Section } from 'src/types/support-types';
|
||||||
@ -10,11 +10,6 @@ export type InitialMinimapState = {
|
|||||||
visualComplexity: number;
|
visualComplexity: number;
|
||||||
audioComplexity: number;
|
audioComplexity: number;
|
||||||
|
|
||||||
// for sharing data between widgets
|
|
||||||
widgetChannels: {
|
|
||||||
[key in WidgetChannel]: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
// read-only
|
// read-only
|
||||||
ownship: VehicleWidget | null;
|
ownship: VehicleWidget | null;
|
||||||
drones: VehicleWidget[];
|
drones: VehicleWidget[];
|
||||||
@ -29,11 +24,6 @@ export type InitialMinimapState = {
|
|||||||
const initialState: InitialMinimapState = {
|
const initialState: InitialMinimapState = {
|
||||||
visualComplexity: 0,
|
visualComplexity: 0,
|
||||||
audioComplexity: 0,
|
audioComplexity: 0,
|
||||||
widgetChannels: {
|
|
||||||
'list-history': {
|
|
||||||
activeMessageId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ownship: null,
|
ownship: null,
|
||||||
drones: [],
|
drones: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
@ -210,21 +200,26 @@ export const minimapSlice = createSlice({
|
|||||||
|
|
||||||
// if widget exists
|
// if widget exists
|
||||||
if (widget) {
|
if (widget) {
|
||||||
const tempElements = state.widgets[widgetId].elements;
|
widget.elements.forEach((element) => {
|
||||||
tempElements.forEach(function (element, elementIndex) {
|
if (element.id === elementId) {
|
||||||
if (element.id === elementId && element.expirationInterval) {
|
// if element does not have an expiration interval, log an error
|
||||||
const newExpiration = new Date();
|
if (!element.expirationIntervalMs) {
|
||||||
newExpiration.setSeconds(
|
console.error(
|
||||||
newExpiration.getSeconds() + element.expirationInterval,
|
`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,
|
state.widgets[widgetId] = widget;
|
||||||
elements: tempElements,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
console.error(`Widget with id ${widgetId} not found`);
|
console.error(`Widget with id ${widgetId} not found`);
|
||||||
}
|
}
|
||||||
@ -495,7 +490,7 @@ export const {
|
|||||||
removeWidget,
|
removeWidget,
|
||||||
deleteElementFromWidget,
|
deleteElementFromWidget,
|
||||||
|
|
||||||
toggleElementInteraction,
|
// toggleElementInteraction,
|
||||||
|
|
||||||
setStressLevel,
|
setStressLevel,
|
||||||
} = minimapSlice.actions;
|
} = minimapSlice.actions;
|
||||||
|
@ -20,7 +20,7 @@ export type BaseElement = {
|
|||||||
priority?: number;
|
priority?: number;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
|
|
||||||
expirationInterval?: number;
|
expirationIntervalMs?: number; // should be in ms
|
||||||
expiration?: string;
|
expiration?: string;
|
||||||
onExpiration?: 'delete' | 'escalate' | 'deescalate';
|
onExpiration?: 'delete' | 'escalate' | 'deescalate';
|
||||||
escalate?: boolean;
|
escalate?: boolean;
|
||||||
|
@ -122,7 +122,7 @@ const generateBaseElement = (
|
|||||||
priority?: number,
|
priority?: number,
|
||||||
widgetId?: string,
|
widgetId?: string,
|
||||||
collapsed?: boolean,
|
collapsed?: boolean,
|
||||||
expirationInterval?: number,
|
expirationIntervalMs?: number,
|
||||||
expiration?: string,
|
expiration?: string,
|
||||||
onExpiration?: 'delete' | 'escalate' | 'deescalate',
|
onExpiration?: 'delete' | 'escalate' | 'deescalate',
|
||||||
interacted?: boolean,
|
interacted?: boolean,
|
||||||
@ -136,7 +136,7 @@ const generateBaseElement = (
|
|||||||
widgetId,
|
widgetId,
|
||||||
priority,
|
priority,
|
||||||
collapsed,
|
collapsed,
|
||||||
expirationInterval,
|
expirationIntervalMs,
|
||||||
expiration,
|
expiration,
|
||||||
onExpiration,
|
onExpiration,
|
||||||
interacted,
|
interacted,
|
||||||
|
Loading…
Reference in New Issue
Block a user