110 lines
2.9 KiB
Vue
110 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
type TrackableHunt,
|
|
useGeoTracking
|
|
} from '~/composables/useGeoTracking';
|
|
|
|
const props = defineProps<{
|
|
hunt: TrackableHunt;
|
|
seedDistance?: number;
|
|
}>();
|
|
|
|
const { t, locale } = useI18n();
|
|
const { formatDistance } = useFormatDistance();
|
|
|
|
const {
|
|
distance,
|
|
lastFix,
|
|
paused,
|
|
consented,
|
|
eligible,
|
|
gate,
|
|
isSupported,
|
|
start,
|
|
stop,
|
|
toggleConsent
|
|
} = useGeoTracking(props.hunt, props.seedDistance ?? 0);
|
|
|
|
// Ticking clock so the "last fix … ago" label stays live while the card is open.
|
|
const nowTs = ref(Date.now());
|
|
let clock: ReturnType<typeof setInterval> | undefined;
|
|
|
|
onMounted(() => {
|
|
clock = setInterval(() => (nowTs.value = Date.now()), 1000);
|
|
});
|
|
|
|
onScopeDispose(() => {
|
|
if (clock !== undefined) clearInterval(clock);
|
|
});
|
|
|
|
const lastFixAgo = computed(() => {
|
|
if (!lastFix.value) return '';
|
|
const seconds = Math.max(
|
|
0,
|
|
Math.round((nowTs.value - lastFix.value.getTime()) / 1000)
|
|
);
|
|
return new Intl.RelativeTimeFormat(locale.value || 'en', {
|
|
numeric: 'auto'
|
|
}).format(-seconds, 'second');
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<VaCard
|
|
v-if="eligible"
|
|
stripe
|
|
:stripe-color="gate && !paused ? 'success' : 'primary'"
|
|
>
|
|
<VaCardTitle>{{ t('page.hunt.tracking.title') }}</VaCardTitle>
|
|
<VaCardContent>
|
|
<div class="flex flex-col gap-3">
|
|
<VaAlert v-if="!isSupported" color="warning">
|
|
{{ t('page.hunt.tracking.unsupported') }}
|
|
</VaAlert>
|
|
<VaSwitch
|
|
:model-value="consented"
|
|
:disabled="!isSupported"
|
|
:label="t('page.hunt.tracking.consent')"
|
|
@update:model-value="toggleConsent"
|
|
/>
|
|
<p class="text-sm opacity-70">
|
|
{{ t('page.hunt.tracking.consent_explain') }}
|
|
</p>
|
|
<template v-if="consented">
|
|
<div class="grid grid-cols-2 gap-2 text-lg md:grid-cols-3">
|
|
<VaChip :color="paused ? 'secondary' : 'success'">
|
|
{{
|
|
t(
|
|
paused
|
|
? 'page.hunt.tracking.status_paused'
|
|
: 'page.hunt.tracking.status_tracking'
|
|
)
|
|
}}
|
|
</VaChip>
|
|
<span class="font-bold">{{ formatDistance(distance) }}</span>
|
|
<span
|
|
v-if="lastFixAgo"
|
|
class="col-span-2 text-sm opacity-70 md:col-span-1"
|
|
>
|
|
{{ t('page.hunt.tracking.last_fix', { ago: lastFixAgo }) }}
|
|
</span>
|
|
</div>
|
|
<VaButton
|
|
:disabled="!isSupported"
|
|
:icon="paused ? 'play_arrow' : 'pause'"
|
|
@click="paused ? start() : stop()"
|
|
>
|
|
{{
|
|
t(
|
|
paused
|
|
? 'page.hunt.tracking.resume'
|
|
: 'page.hunt.tracking.pause'
|
|
)
|
|
}}
|
|
</VaButton>
|
|
</template>
|
|
</div>
|
|
</VaCardContent>
|
|
</VaCard>
|
|
</template>
|