Add Map settings toggle to show/hide live air traffic
Add a "Map settings" popover (gear button) on the Overview Live map with a "Show live air traffic" switch, backed by a new client-side showAirTraffic preference (default on, persisted in localStorage and synced like other prefs). When off, the aircraft overlay clears and polling pauses to save OpenSky credits; the count badge and caption reflect the hidden state. Toggling on re-fetches immediately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a578555b43
commit
fee171a71f
@@ -6,8 +6,9 @@ import Icon from './Icon.vue'
|
||||
import Settings from './Settings.vue'
|
||||
import Logbook from './Logbook.vue'
|
||||
import Documents from './Documents.vue'
|
||||
import Toggle from './settings/Toggle.vue'
|
||||
import { getDevices, sendCommand, getOpenSkyStates } from '../api.js'
|
||||
import { formatTime } from '../prefs.js'
|
||||
import { formatTime, prefs } from '../prefs.js'
|
||||
|
||||
const props = defineProps({
|
||||
email: { type: String, default: '' },
|
||||
@@ -28,9 +29,11 @@ const search = ref('')
|
||||
const aircraft = ref([]) // [{ icao24, callsign, lat, lng, heading, velocity, altitude, onGround }]
|
||||
const airspace = reactive({ unavailable: false, detail: '', loaded: false })
|
||||
const airborneCount = computed(() => aircraft.value.filter((a) => !a.onGround).length)
|
||||
const mapMenu = ref(false) // "Map settings" popover open state
|
||||
let airTimer = null
|
||||
|
||||
async function refreshAirspace() {
|
||||
if (!prefs.showAirTraffic) return
|
||||
const { states, unavailable, detail } = await getOpenSkyStates()
|
||||
aircraft.value = states
|
||||
airspace.unavailable = unavailable
|
||||
@@ -38,13 +41,14 @@ async function refreshAirspace() {
|
||||
airspace.loaded = true
|
||||
}
|
||||
|
||||
// Poll OpenSky only while the Overview map is on screen, on a credit-friendly
|
||||
// cadence (state vectors refresh at most every ~10s upstream anyway).
|
||||
// Poll OpenSky only while the Overview map is on screen and the overlay is
|
||||
// enabled, on a credit-friendly cadence (state vectors refresh at most every
|
||||
// ~10s upstream anyway).
|
||||
function startAirspace() {
|
||||
if (airTimer) return
|
||||
refreshAirspace()
|
||||
airTimer = setInterval(() => {
|
||||
if (active.value === 'Overview') refreshAirspace()
|
||||
if (active.value === 'Overview' && prefs.showAirTraffic) refreshAirspace()
|
||||
}, 30000)
|
||||
}
|
||||
function stopAirspace() {
|
||||
@@ -282,6 +286,16 @@ watch(active, (v) => {
|
||||
if (v === 'Overview') refreshAirspace()
|
||||
})
|
||||
|
||||
// React to the "Show live air traffic" map toggle: fetch at once when enabled,
|
||||
// clear the overlay (and pause polling) when disabled.
|
||||
watch(
|
||||
() => prefs.showAirTraffic,
|
||||
(on) => {
|
||||
if (on) refreshAirspace()
|
||||
else aircraft.value = []
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
;(await getDevices()).forEach(upsert)
|
||||
connect()
|
||||
@@ -408,7 +422,7 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
v-if="airborneCount"
|
||||
v-if="prefs.showAirTraffic && airborneCount"
|
||||
class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold"
|
||||
:class="badgeClass.accent"
|
||||
title="Live aircraft from OpenSky Network"
|
||||
@@ -422,10 +436,36 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-current"></span>{{ flyingCount }} drones
|
||||
</span>
|
||||
<!-- Map settings -->
|
||||
<div class="relative">
|
||||
<button
|
||||
type="button"
|
||||
class="grid h-7 w-7 place-items-center rounded-md text-ink-muted transition hover:bg-surface-2 hover:text-ink"
|
||||
:class="mapMenu ? 'bg-surface-2 text-ink' : ''"
|
||||
title="Map settings"
|
||||
aria-label="Map settings"
|
||||
@click="mapMenu = !mapMenu"
|
||||
>
|
||||
<Icon name="settings" :size="16" />
|
||||
</button>
|
||||
<template v-if="mapMenu">
|
||||
<div class="fixed inset-0 z-10" @click="mapMenu = false"></div>
|
||||
<div class="panel absolute right-0 z-20 mt-1.5 w-64 p-3.5 shadow-lg">
|
||||
<div class="eyebrow mb-2.5">Map settings</div>
|
||||
<label class="flex items-center justify-between gap-3">
|
||||
<span class="text-sm text-ink-secondary">Show live air traffic</span>
|
||||
<Toggle v-model="prefs.showAirTraffic" />
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DeviceMap :position="position" :trail="trail" :aircraft="aircraft" />
|
||||
<p v-if="airspace.loaded && airspace.unavailable" class="mt-2.5 text-xs text-ink-muted">
|
||||
<DeviceMap :position="position" :trail="trail" :aircraft="prefs.showAirTraffic ? aircraft : []" />
|
||||
<p v-if="!prefs.showAirTraffic" class="mt-2.5 text-xs text-ink-muted">
|
||||
Live air traffic hidden · enable it in Map settings
|
||||
</p>
|
||||
<p v-else-if="airspace.loaded && airspace.unavailable" class="mt-2.5 text-xs text-ink-muted">
|
||||
{{ airspace.detail || 'Live air traffic is unavailable.' }}
|
||||
</p>
|
||||
<p v-else class="mt-2.5 text-xs text-ink-muted">
|
||||
|
||||
Reference in New Issue
Block a user