Skip to content

Commit 3679d6b

Browse files
Merge pull request #42 from sebastiankrll/feature/toastify
Feature/toastify
2 parents d5f18c3 + 7f36ea4 commit 3679d6b

9 files changed

Lines changed: 169 additions & 32 deletions

File tree

apps/web/components/Header/Header.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ header {
5252
#header-search {
5353
line-height: 2rem;
5454
border: none;
55-
width: 17rem;
55+
width: 20rem;
5656
height: 2rem;
5757
box-sizing: border-box;
5858
background-color: transparent;

apps/web/components/Map/Map.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import { useEffect } from "react";
44
import "./Map.css";
55
import { useRouter } from "next/navigation";
6+
import { ToastContainer } from "react-toastify";
7+
import { MessageBoxCloseButton } from "../MessageBox/MessageBox";
68
import { onClick, onMoveEnd, onPointerMove, setNavigator } from "./utils/events";
79
import { initMap } from "./utils/init";
810
import { animatePilotFeatures } from "./utils/pilotFeatures";
@@ -34,5 +36,10 @@ export default function OMap() {
3436
};
3537
}, [router]);
3638

37-
return <div id="map" />;
39+
return (
40+
<>
41+
<ToastContainer closeButton={MessageBoxCloseButton} icon={false} theme="colored" />
42+
<div id="map" />
43+
</>
44+
);
3845
}

apps/web/components/Map/utils/controllerFeatures.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import { fromLonLat } from "ol/proj";
88
import Fill from "ol/style/Fill";
99
import Style from "ol/style/Style";
1010
import Text from "ol/style/Text";
11+
import { toast } from "react-toastify";
12+
import MessageBox from "@/components/MessageBox/MessageBox";
1113
import { getCachedAirport, getCachedFir, getCachedTracon } from "@/storage/cache";
1214
import type { AirportLabelProperties, ControllerLabelProperties } from "@/types/ol";
1315
import { getAirportSize } from "./airportFeatures";
1416
import { airportLabelSource, controllerLabelSource, firSource, traconSource } from "./dataLayers";
17+
import { resetMap } from "./events";
1518
import { getMapView } from "./init";
1619

1720
export function getControllerLabelStyle(feature: FeatureLike, resolution: number): Style {
@@ -207,6 +210,12 @@ export async function updateControllerFeatures(delta: ControllerDelta): Promise<
207210
}
208211
}
209212
}
213+
214+
if (highlightedController && !controllerList.has(highlightedController)) {
215+
toast.info(MessageBox, { data: { title: "Controller Disconnected", message: `The viewed controller has disconnected.` } });
216+
highlightedController = null;
217+
resetMap(true);
218+
}
210219
}
211220

212221
function createControllerLabel(lon: number, lat: number, label: string, type: "tracon" | "fir"): void {
@@ -285,7 +294,6 @@ function getAirportLabelStations(controllerMerged: ControllerMerged): number[] {
285294
}
286295

287296
export function moveToSectorFeature(id: string): Feature<Point> | null {
288-
console.log(controllerLabelSource.getFeatures());
289297
const labelFeature = controllerLabelSource.getFeatureById(`sector_${id}`) as Feature<Point> | null;
290298

291299
const view = getMapView();
@@ -299,5 +307,17 @@ export function moveToSectorFeature(id: string): Feature<Point> | null {
299307
zoom: 7,
300308
});
301309

310+
addHighlightedController(id);
311+
302312
return labelFeature;
303313
}
314+
315+
let highlightedController: string | null = null;
316+
317+
export function addHighlightedController(id: string | null): void {
318+
highlightedController = id;
319+
}
320+
321+
export function clearHighlightedController(): void {
322+
highlightedController = null;
323+
}

apps/web/components/Map/utils/events.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createRoot, type Root } from "react-dom/client";
88
import { getAirportShort, getCachedAirline, getCachedAirport, getCachedFir, getCachedTracon, getControllerMerged } from "@/storage/cache";
99
import { AirportOverlay, PilotOverlay, SectorOverlay } from "../components/Overlay/Overlays";
1010
import { addHighlightedAirport, clearHighlightedAirport, moveToAirportFeature } from "./airportFeatures";
11-
import { moveToSectorFeature } from "./controllerFeatures";
11+
import { addHighlightedController, clearHighlightedController, moveToSectorFeature } from "./controllerFeatures";
1212
import { firSource, pilotMainSource, setFeatures, trackSource, traconSource } from "./dataLayers";
1313
import { getMap, getMapView } from "./init";
1414
import { addHighlightedPilot, clearHighlightedPilot, moveToPilotFeature } from "./pilotFeatures";
@@ -147,6 +147,7 @@ export async function onClick(evt: MapBrowserEvent): Promise<void> {
147147
if ((type === "tracon" || type === "fir") && id) {
148148
const strippedId = id.toString().replace(/^(sector)_/, "");
149149
navigate?.(`/sector/${strippedId}`);
150+
addHighlightedController(strippedId);
150151
}
151152
}
152153

@@ -226,11 +227,6 @@ export function updateOverlays(): void {
226227
}
227228

228229
async function updateOverlay(feature: Feature<Point>, overlay: Overlay): Promise<void> {
229-
if (!feature || !overlay) {
230-
resetMap(true);
231-
return;
232-
}
233-
234230
const geom = feature.getGeometry();
235231
const coords = geom?.getCoordinates();
236232
overlay.setPosition(coords);
@@ -397,6 +393,7 @@ function clearMap(): void {
397393
toggleControllerSectorHover(clickedFeature, false, "clicked");
398394
clearHighlightedAirport();
399395
clearHighlightedPilot();
396+
clearHighlightedController();
400397
if (followInterval) {
401398
clearInterval(followInterval);
402399
followInterval = null;

apps/web/components/Map/utils/pilotFeatures.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { Extent } from "ol/extent";
44
import { Point } from "ol/geom";
55
import { fromLonLat, transformExtent } from "ol/proj";
66
import RBush from "rbush";
7+
import { toast } from "react-toastify";
8+
import MessageBox from "@/components/MessageBox/MessageBox";
79
import type { PilotProperties } from "@/types/ol";
810
import { pilotMainSource } from "./dataLayers";
911
import { animateOverlays, resetMap } from "./events";
@@ -50,7 +52,6 @@ export function initPilotFeatures(data: WsAll): void {
5052
}
5153

5254
export function updatePilotFeatures(delta: PilotDelta): void {
53-
const items: RBushPilotFeature[] = [];
5455
const pilotsInDelta = new Set<string>();
5556

5657
for (const p of delta.updated) {
@@ -71,15 +72,6 @@ export function updatePilotFeatures(delta: PilotDelta): void {
7172
}
7273

7374
pilotMap.set(p.id, feature);
74-
75-
const item: RBushPilotFeature = {
76-
minX: p.longitude ?? props.longitude,
77-
minY: p.latitude ?? props.latitude,
78-
maxX: p.longitude ?? props.longitude,
79-
maxY: p.latitude ?? props.latitude,
80-
feature,
81-
};
82-
items.push(item);
8375
}
8476

8577
for (const p of delta.added) {
@@ -98,15 +90,6 @@ export function updatePilotFeatures(delta: PilotDelta): void {
9890
feature.setId(`pilot_${p.id}`);
9991

10092
pilotMap.set(p.id, feature);
101-
102-
const item: RBushPilotFeature = {
103-
minX: p.longitude,
104-
minY: p.latitude,
105-
maxX: p.longitude,
106-
maxY: p.latitude,
107-
feature,
108-
};
109-
items.push(item);
11093
}
11194

11295
for (const id of pilotMap.keys()) {
@@ -115,13 +98,26 @@ export function updatePilotFeatures(delta: PilotDelta): void {
11598
}
11699
}
117100

118-
if (highlightedPilot && !pilotMap.has(highlightedPilot)) {
119-
highlightedPilot = null;
120-
resetMap(true);
101+
const items: RBushPilotFeature[] = [];
102+
for (const [_id, feature] of pilotMap.entries()) {
103+
const props = feature.getProperties() as PilotProperties;
104+
items.push({
105+
minX: props.longitude,
106+
minY: props.latitude,
107+
maxX: props.longitude,
108+
maxY: props.latitude,
109+
feature,
110+
});
121111
}
122112

123113
pilotRBush.clear();
124114
pilotRBush.load(items);
115+
116+
if (highlightedPilot && !pilotMap.has(highlightedPilot)) {
117+
toast.info(MessageBox, { data: { title: "Pilot Disconnected", message: `The viewed pilot has disconnected.` } });
118+
highlightedPilot = null;
119+
resetMap(true);
120+
}
125121
}
126122

127123
let highlightedPilot: string | null = null;
@@ -134,8 +130,10 @@ export function clearHighlightedPilot(): void {
134130
highlightedPilot = null;
135131
}
136132

133+
let initialized = false;
137134
export function setPilotFeatures(extent: Extent, zoom: number): void {
138-
if (zoom > 10) {
135+
if (zoom > 12 && !initialized) {
136+
initialized = true;
139137
return;
140138
}
141139

@@ -203,6 +201,8 @@ export function animatePilotFeatures(map: OlMap) {
203201

204202
features.forEach((feature) => {
205203
const groundspeed = (feature.get("groundspeed") as number) || 0;
204+
if (groundspeed <= 0) return;
205+
206206
const heading = (feature.get("heading") as number) || 0;
207207
const latitude = (feature.get("latitude") as number) || 0;
208208
const longitude = (feature.get("longitude") as number) || 0;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
:root {
2+
--toastify-color-light: var(--color-blue);
3+
--toastify-color-dark: var(--color-blue);
4+
--toastify-color-info: var(--color-blue);
5+
--toastify-color-success: var(--color-green);
6+
--toastify-color-warning: var(--color-orange);
7+
--toastify-color-error: var(--color-red);
8+
--toastify-color-transparent: rgba(255, 255, 255, 0.7);
9+
10+
--toastify-icon-color-info: white;
11+
--toastify-icon-color-success: white;
12+
--toastify-icon-color-warning: white;
13+
--toastify-icon-color-error: white;
14+
15+
--toastify-toast-top: 4.5rem;
16+
--toastify-toast-right: 1rem;
17+
--toastify-toast-padding: 0.5rem;
18+
--toastify-toast-min-height: inhert;
19+
--toastify-toast-bd-radius: 0px;
20+
--toastify-toast-shadow: 1px 1px 5px 1px var(--color-box-shadow);
21+
--toastify-font-family: inherit;
22+
--toastify-text-color-light: var(--color-light-text);
23+
--toastify-text-color-dark: var(--color-main-text);
24+
25+
--toastify-color-progress-light: white;
26+
--toastify-color-progress-dark: white;
27+
--toastify-color-progress-info: white;
28+
--toastify-color-progress-success: white;
29+
--toastify-color-progress-warning: white;
30+
--toastify-color-progress-error: white;
31+
}
32+
33+
.message-box * {
34+
color: white;
35+
margin-bottom: 4px;
36+
}
37+
38+
.message-box p:first-child {
39+
font-weight: 700;
40+
text-transform: uppercase;
41+
}
42+
43+
.message-box p:last-child {
44+
font-size: 12px;
45+
}
46+
47+
.message-close {
48+
position: absolute;
49+
top: 0rem;
50+
right: 0rem;
51+
background-color: var(--color-red);
52+
width: 1.5rem;
53+
height: 1.5rem;
54+
padding: 0.25rem;
55+
margin-left: auto;
56+
}
57+
58+
.message-close svg {
59+
fill: white;
60+
}
61+
62+
.message-close:hover {
63+
background-color: white;
64+
}
65+
66+
.message-close:hover svg {
67+
fill: var(--color-red);
68+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import "./MessageBox.css";
2+
3+
interface MessageBoxData {
4+
title: string;
5+
message: string;
6+
}
7+
8+
export default function MessageBox({ data }: { data: MessageBoxData }) {
9+
return (
10+
<div className="message-box">
11+
<p>{data.title}</p>
12+
<p>{data.message}</p>
13+
</div>
14+
);
15+
}
16+
17+
export function MessageBoxCloseButton() {
18+
return (
19+
<button className="message-close" type="button">
20+
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
21+
<title>Close panel</title>
22+
<path
23+
fillRule="evenodd"
24+
d="M23.763 22.658 13.106 12 23.68 1.42a.781.781 0 0 0-1.1-1.1L12 10.894 1.42.237a.78.78 0 0 0-1.1 1.105L10.894 12 .237 22.658a.763.763 0 0 0 0 1.105.76.76 0 0 0 1.105 0L12 13.106l10.658 10.657a.76.76 0 0 0 1.105 0 .76.76 0 0 0 0-1.105"
25+
clipRule="evenodd"
26+
></path>
27+
</svg>
28+
</button>
29+
);
30+
}

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"react": "19.2.0",
2424
"react-dom": "19.2.0",
2525
"react-intersection-observer": "^10.0.0",
26+
"react-toastify": "^11.0.5",
2627
"recharts": "^3.5.1",
2728
"swr": "^2.3.7"
2829
},

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)