✅ fix(useRenderers): résoudre les problèmes de réactivité
- Supprimer l'import obsolète `toRaw` et simplifier les mises à jour via spread - Corriger la mutation directe de `state_changed` en créant des objets immutables + appel à trigger - Ajouter `triggerQueueReactivity()` pour déclencher la réactivité sur les événements queue - Mettre à jour le doc de revue frontend pour marquer toutes les tâches comme corrigées
This commit is contained in:
@@ -525,10 +525,15 @@ fait et ce qui reste à finir.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tâches restantes
|
## Tâches restantes — ✅ Toutes corrigées (commit 2026-04-06)
|
||||||
|
|
||||||
Trois problèmes résiduels ont été introduits ou laissés lors de l'implémentation du commit
|
| # | Problème résiduel | État |
|
||||||
ci-dessus. Ils doivent être corrigés.
|
|---|-------------------|------|
|
||||||
|
| A | `state_changed` — mutation directe sans trigger réactivité | ✅ Corrigé (spread + `triggerSnapshotReactivity()`) |
|
||||||
|
| B | `queue_refreshing`/`queue_updated` — `queueRefreshingIds` sans trigger | ✅ Corrigé (`triggerQueueReactivity()` ajoutée et appelée) |
|
||||||
|
| C | `toRaw` import obsolète, `position_changed` simplifié | ✅ Corrigé (`toRaw` supprimé de l'import, spread direct) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### A. `useRenderers.ts` — `state_changed` : mutation directe sans déclenchement de réactivité
|
### A. `useRenderers.ts` — `state_changed` : mutation directe sans déclenchement de réactivité
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* - Les snapshots complets proviennent de /renderers/{id}/full
|
* - Les snapshots complets proviennent de /renderers/{id}/full
|
||||||
* - Les événements SSE ne servent qu'à déclencher un refetch.
|
* - Les événements SSE ne servent qu'à déclencher un refetch.
|
||||||
*/
|
*/
|
||||||
import { ref, shallowRef, computed, toRaw, type Ref, onUnmounted } from "vue";
|
import { ref, shallowRef, computed, type Ref, onUnmounted } from "vue";
|
||||||
import { api } from "../services/pmocontrol/api";
|
import { api } from "../services/pmocontrol/api";
|
||||||
import { useSSE } from "./useSSE";
|
import { useSSE } from "./useSSE";
|
||||||
import { apiCache } from "./apiCache";
|
import { apiCache } from "./apiCache";
|
||||||
@@ -41,6 +41,10 @@ function triggerLoadingReactivity() {
|
|||||||
loadingIds.value = new Set(loadingIds.value);
|
loadingIds.value = new Set(loadingIds.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function triggerQueueReactivity() {
|
||||||
|
queueRefreshingIds.value = new Set(queueRefreshingIds.value);
|
||||||
|
}
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref<string | null>(null);
|
const error = ref<string | null>(null);
|
||||||
|
|
||||||
@@ -131,9 +135,13 @@ function ensureSSEInitialized() {
|
|||||||
// Sinon, mettre à jour le snapshot localement selon le type d'événement
|
// Sinon, mettre à jour le snapshot localement selon le type d'événement
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "state_changed":
|
case "state_changed":
|
||||||
// Utiliser le guard de type pour valider le transport_state
|
// Créer un nouvel objet pour déclencher la réactivité
|
||||||
if (isTransportState(event.state)) {
|
if (isTransportState(event.state)) {
|
||||||
snapshot.state.transport_state = event.state;
|
snapshots.value.set(rendererId, {
|
||||||
|
...snapshot,
|
||||||
|
state: { ...snapshot.state, transport_state: event.state },
|
||||||
|
});
|
||||||
|
triggerSnapshotReactivity();
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[useRenderers] transport_state inconnu: ${event.state}`);
|
console.warn(`[useRenderers] transport_state inconnu: ${event.state}`);
|
||||||
}
|
}
|
||||||
@@ -145,27 +153,20 @@ function ensureSSEInitialized() {
|
|||||||
|
|
||||||
// Convertir rel_time (HH:MM:SS) en millisecondes
|
// Convertir rel_time (HH:MM:SS) en millisecondes
|
||||||
const positionMs = parseTimeToMs(event.rel_time ?? null);
|
const positionMs = parseTimeToMs(event.rel_time ?? null);
|
||||||
snapshot.state.position_ms = positionMs ?? 0;
|
|
||||||
|
|
||||||
// Convertir track_duration (HH:MM:SS) en millisecondes
|
// Convertir track_duration (HH:MM:SS) en millisecondes
|
||||||
const durationMs = parseTimeToMs(event.track_duration ?? null);
|
const durationMs = parseTimeToMs(event.track_duration ?? null);
|
||||||
// Si track_duration est null/undefined (flux continu sans durée),
|
|
||||||
// mettre duration_ms à null pour afficher "--:--"
|
// Créer un nouvel objet pour déclencher la réactivité
|
||||||
snapshot.state.duration_ms = durationMs;
|
snapshots.value.set(rendererId, {
|
||||||
|
|
||||||
// Important: Trigger reactivity en réassignant l'objet complet avec deep copy
|
|
||||||
// Le shallow copy ne suffit pas car snapshot.state est partagé entre renderers
|
|
||||||
// Il faut copier state aussi pour éviter que les modifications d'un renderer
|
|
||||||
// n'affectent les autres renderers
|
|
||||||
// IMPORTANT: Utiliser toRaw() pour obtenir l'objet brut non-réactif avant de copier
|
|
||||||
// sinon Vue copie les getters réactifs qui continuent à pointer vers l'objet d'origine
|
|
||||||
const rawState = toRaw(snapshot.state);
|
|
||||||
const newState = { ...rawState };
|
|
||||||
const newSnapshot = {
|
|
||||||
...snapshot,
|
...snapshot,
|
||||||
state: newState,
|
state: {
|
||||||
};
|
...snapshot.state,
|
||||||
snapshots.value.set(rendererId, newSnapshot);
|
position_ms: positionMs ?? 0,
|
||||||
|
duration_ms: durationMs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
triggerSnapshotReactivity();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "volume_changed":
|
case "volume_changed":
|
||||||
@@ -206,11 +207,13 @@ function ensureSSEInitialized() {
|
|||||||
|
|
||||||
case "queue_refreshing":
|
case "queue_refreshing":
|
||||||
queueRefreshingIds.value.add(rendererId);
|
queueRefreshingIds.value.add(rendererId);
|
||||||
|
triggerQueueReactivity();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "queue_updated":
|
case "queue_updated":
|
||||||
snapshot.state.queue_len = event.queue_length;
|
snapshot.state.queue_len = event.queue_length;
|
||||||
queueRefreshingIds.value.delete(rendererId);
|
queueRefreshingIds.value.delete(rendererId);
|
||||||
|
triggerQueueReactivity();
|
||||||
// Pour la queue complète, on doit refetch
|
// Pour la queue complète, on doit refetch
|
||||||
void fetchRendererSnapshot(rendererId, { force: true });
|
void fetchRendererSnapshot(rendererId, { force: true });
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user