feat(sync): media en paralelo + visibilidad proactiva de problemas
- pushMedia sube en lotes de 3 en paralelo (Promise.all por chunk) en vez de una a una; contadores del report seguros al ser el runtime monohilo. - SyncStatusBar: fondo rojo cuando hay conflictos/errores (prioridad sobre el estado de conexión), para que el problema salte a la vista. - ProjectDetail: el auto-sync silencioso ahora avisa una vez con Alert cuando un ciclo produce conflictos/errores, con acceso directo a la cola. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,8 +16,11 @@ export function SyncStatusBar({
|
|||||||
const pendientes = counts.pending + counts.mediaPending;
|
const pendientes = counts.pending + counts.mediaPending;
|
||||||
const hasProblems = counts.conflict > 0 || counts.error > 0;
|
const hasProblems = counts.conflict > 0 || counts.error > 0;
|
||||||
|
|
||||||
|
// Prioridad de color: problemas (rojo) > sin conexión (ámbar) > al día (verde).
|
||||||
|
const bg = hasProblems ? '#b00020' : online ? '#1f6f43' : '#8a6d00';
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<View style={[styles.bar, { backgroundColor: online ? '#1f6f43' : '#8a6d00' }]}>
|
<View style={[styles.bar, { backgroundColor: bg }]}>
|
||||||
{syncing && <ActivityIndicator color="#fff" size="small" />}
|
{syncing && <ActivityIndicator color="#fff" size="small" />}
|
||||||
<Text style={styles.text}>
|
<Text style={styles.text}>
|
||||||
{online ? 'En línea' : 'Sin conexión'}
|
{online ? 'En línea' : 'Sin conexión'}
|
||||||
|
|||||||
@@ -45,8 +45,22 @@ export function ProjectDetailScreen({ route, navigation }: Props) {
|
|||||||
}, [projectId, refreshCounts]);
|
}, [projectId, refreshCounts]);
|
||||||
|
|
||||||
// Auto-sync (silencioso) al recuperar red / volver a primer plano / por intervalo.
|
// Auto-sync (silencioso) al recuperar red / volver a primer plano / por intervalo.
|
||||||
|
// Aunque es silencioso, si un ciclo produce conflictos o errores nuevos avisamos
|
||||||
|
// proactivamente una vez (cada conflicto/error solo se reporta en su ciclo).
|
||||||
useAutoSync(true, async () => {
|
useAutoSync(true, async () => {
|
||||||
await doSync();
|
const report = await doSync();
|
||||||
|
const problems = report.conflicts + report.errors;
|
||||||
|
if (problems > 0) {
|
||||||
|
Alert.alert(
|
||||||
|
'Sincronización con incidencias',
|
||||||
|
`${report.conflicts} conflicto(s) y ${report.errors} error(es) al sincronizar. ` +
|
||||||
|
'Revisa la cola para resolverlos.',
|
||||||
|
[
|
||||||
|
{ text: 'Ahora no', style: 'cancel' },
|
||||||
|
{ text: 'Revisar', onPress: () => navigation.navigate('Outbox') },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sync manual (con resumen).
|
// Sync manual (con resumen).
|
||||||
|
|||||||
+12
-1
@@ -173,10 +173,14 @@ async function processResult(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Nº de ficheros que se suben en paralelo por lote. */
|
||||||
|
const MEDIA_CONCURRENCY = 3;
|
||||||
|
|
||||||
/** Sube los ficheros pendientes a /media (solo los de padres ya sincronizados). */
|
/** Sube los ficheros pendientes a /media (solo los de padres ya sincronizados). */
|
||||||
async function pushMedia(report: SyncReport): Promise<void> {
|
async function pushMedia(report: SyncReport): Promise<void> {
|
||||||
const pending = await getPendingMedia();
|
const pending = await getPendingMedia();
|
||||||
for (const m of pending) {
|
|
||||||
|
const uploadOne = async (m: (typeof pending)[number]): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const res = await uploadMedia({
|
const res = await uploadMedia({
|
||||||
uuid: m.uuid,
|
uuid: m.uuid,
|
||||||
@@ -194,6 +198,13 @@ async function pushMedia(report: SyncReport): Promise<void> {
|
|||||||
await markMediaError(m.uuid, e instanceof Error ? e.message : String(e));
|
await markMediaError(m.uuid, e instanceof Error ? e.message : String(e));
|
||||||
report.mediaErrors += 1;
|
report.mediaErrors += 1;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lotes de MEDIA_CONCURRENCY en paralelo (el runtime es monohilo: los
|
||||||
|
// contadores del report se incrementan sin condición de carrera).
|
||||||
|
for (let i = 0; i < pending.length; i += MEDIA_CONCURRENCY) {
|
||||||
|
const batch = pending.slice(i, i + MEDIA_CONCURRENCY);
|
||||||
|
await Promise.all(batch.map(uploadOne));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user