fix bug: info elements never collapsing after being hovered over once

This commit is contained in:
bedlam343 2024-05-22 16:45:02 -07:00
parent 6d647b64c4
commit 87a1d81240
8 changed files with 42 additions and 58 deletions

View File

@ -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!, {

View File

@ -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 (

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,