feat(app): mode street 'An lari a' - visionneuse mapillary-js lazy, /api/round?mode=street, i18n FR/GCF (ADR-003)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { t } from '$lib/i18n/store.svelte';
|
||||
import type { Viewer } from 'mapillary-js';
|
||||
|
||||
interface Props {
|
||||
mapillaryId: string;
|
||||
credit?: string;
|
||||
}
|
||||
|
||||
let { mapillaryId, credit }: Props = $props();
|
||||
|
||||
let conteneur: HTMLDivElement;
|
||||
let viewer: Viewer | null = null;
|
||||
let etat = $state<'chargement' | 'pret' | 'erreur' | 'token'>('chargement');
|
||||
|
||||
onMount(() => {
|
||||
let detruit = false;
|
||||
// Lazy load : mapillary-js (JS + CSS) n'est chargé que sur l'écran de jeu street.
|
||||
void (async () => {
|
||||
const token = env.PUBLIC_MAPILLARY_TOKEN;
|
||||
if (!token) {
|
||||
etat = 'token';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [mod] = await Promise.all([
|
||||
import('mapillary-js'),
|
||||
import('mapillary-js/dist/mapillary.css')
|
||||
]);
|
||||
if (detruit) return;
|
||||
// cover désactivé : la photo s'affiche directement, sans écran d'intro.
|
||||
viewer = new mod.Viewer({
|
||||
accessToken: token,
|
||||
container: conteneur,
|
||||
component: { cover: false }
|
||||
});
|
||||
// moveTo rejette si l'image a été retirée de Mapillary entre-temps.
|
||||
await viewer.moveTo(mapillaryId);
|
||||
if (detruit) return;
|
||||
etat = 'pret';
|
||||
} catch {
|
||||
if (!detruit) etat = 'erreur';
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
detruit = true;
|
||||
viewer?.remove();
|
||||
viewer = null;
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<figure class="street-panel">
|
||||
<div
|
||||
bind:this={conteneur}
|
||||
class="visionneuse"
|
||||
role="application"
|
||||
aria-label={t().jeu.streetAriaLabel}
|
||||
></div>
|
||||
{#if etat !== 'pret'}
|
||||
<div class="voile">
|
||||
<p>
|
||||
{#if etat === 'chargement'}{t().jeu.streetChargement}
|
||||
{:else if etat === 'token'}{t().jeu.streetToken}
|
||||
{:else}{t().jeu.streetErreur}{/if}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if credit && etat === 'pret'}
|
||||
<figcaption>{credit}</figcaption>
|
||||
{/if}
|
||||
</figure>
|
||||
|
||||
<style>
|
||||
.street-panel {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--oki-vert);
|
||||
border-radius: var(--oki-radius);
|
||||
}
|
||||
|
||||
.visionneuse {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.voile {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
color: var(--oki-blanc);
|
||||
background: var(--oki-vert);
|
||||
}
|
||||
|
||||
figcaption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.4rem 0.8rem;
|
||||
font-size: 0.72rem;
|
||||
color: #fff;
|
||||
background: linear-gradient(transparent, rgba(11, 61, 46, 0.85));
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user