entregas_app/docs/GUIA_IMPLEMENTACAO_SINCRONI...

33 KiB

Guia de Implementação - Sincronização Offline

Visão Geral

Este guia fornece instruções passo a passo para implementar a sincronização offline no aplicativo de entregas, baseado na análise completa da arquitetura existente.

Fase 1: Preparação e Configuração

1.1 Instalação de Dependências

# Dependências para sincronização
npm install lz-string crypto-js
npm install @react-native-async-storage/async-storage
npm install expo-background-fetch expo-task-manager

# Dependências para desenvolvimento
npm install --save-dev @types/lz-string @types/crypto-js

1.2 Configuração de Variáveis de Ambiente

Arquivo: .env

# Configurações de sincronização
SYNC_INTERVAL=900000
SYNC_RETRY_ATTEMPTS=3
SYNC_TIMEOUT=30000
MAX_CACHE_SIZE=100
ENCRYPTION_KEY=your-secret-encryption-key

# Configurações de API
API_BASE_URL=https://api.example.com
API_TIMEOUT=10000

1.3 Atualização do Banco de Dados

Arquivo: src/services/database.ts

// Adicionar ao setupDatabase
export const setupDatabase = async (): Promise<void> => {
  if (usingSQLite) {
    return new Promise<void>((resolve, reject) => {
      db.transaction(
        (tx: any) => {
          // Tabelas existentes...
          
          // Nova tabela de controle de sincronização
          tx.executeSql(
            `CREATE TABLE IF NOT EXISTS sync_control (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              table_name TEXT NOT NULL,
              last_sync_timestamp INTEGER,
              sync_status TEXT DEFAULT 'pending',
              created_at INTEGER DEFAULT (strftime('%s', 'now')),
              updated_at INTEGER DEFAULT (strftime('%s', 'now'))
            );`
          );

          // Nova tabela de conflitos
          tx.executeSql(
            `CREATE TABLE IF NOT EXISTS sync_conflicts (
              id TEXT PRIMARY KEY,
              table_name TEXT NOT NULL,
              record_id TEXT NOT NULL,
              local_data TEXT,
              server_data TEXT,
              conflict_fields TEXT,
              resolution TEXT,
              resolved_at INTEGER,
              created_at INTEGER DEFAULT (strftime('%s', 'now'))
            );`
          );

          // Nova tabela de log de sincronização
          tx.executeSql(
            `CREATE TABLE IF NOT EXISTS sync_log (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              sync_type TEXT NOT NULL,
              table_name TEXT,
              record_id TEXT,
              action TEXT,
              success INTEGER DEFAULT 1,
              error_message TEXT,
              duration INTEGER,
              timestamp INTEGER DEFAULT (strftime('%s', 'now'))
            );`
          );

          // Adicionar campos de controle às tabelas existentes
          tx.executeSql(
            `ALTER TABLE deliveries ADD COLUMN version INTEGER DEFAULT 1;`
          );
          tx.executeSql(
            `ALTER TABLE deliveries ADD COLUMN last_modified INTEGER DEFAULT (strftime('%s', 'now'));`
          );
          tx.executeSql(
            `ALTER TABLE deliveries ADD COLUMN sync_timestamp INTEGER;`
          );

          // Inserir configurações de sincronização
          tx.executeSql(
            `INSERT OR IGNORE INTO settings (key, value) VALUES 
             ('initial_sync_complete', 'false'),
             ('last_sync_time', '0'),
             ('sync_interval', '900000'),
             ('auto_sync_enabled', 'true');`
          );
        },
        (error: any) => reject(error),
        () => resolve()
      );
    });
  }
};

Fase 2: Implementação dos Serviços Base

2.1 Serviço de Sincronização Principal

Arquivo: src/services/syncService.ts

import { api } from './api';
import { executeQuery, saveSetting, getSetting } from './database';
import { offlineStorage } from './offlineStorage';

export enum SyncStatus {
  SYNCED = 'synced',
  PENDING = 'pending',
  CONFLICT = 'conflict',
  ERROR = 'error',
  OFFLINE = 'offline'
}

export enum SyncType {
  FULL = 'full',
  INCREMENTAL = 'incremental',
  SELECTIVE = 'selective'
}

interface SyncResult {
  success: boolean;
  totalRecords: number;
  syncedRecords: number;
  errors: Array<{ recordId: string; error: string }>;
  duration: number;
}

class SyncService {
  private isInitialized = false;

  constructor() {
    this.initializeSyncService();
  }

  private async initializeSyncService(): Promise<void> {
    try {
      // Verificar se já foi feita sincronização inicial
      const initialSyncComplete = await getSetting('initial_sync_complete');
      
      if (initialSyncComplete === 'false') {
        console.log('Sincronização inicial necessária');
        // Não iniciar automaticamente - aguardar ação do usuário
      } else {
        console.log('Sincronização inicial já foi realizada');
        this.isInitialized = true;
      }
    } catch (error) {
      console.error('Erro ao inicializar serviço de sincronização:', error);
    }
  }

  // Sincronização inicial completa
  async performInitialSync(): Promise<SyncResult> {
    try {
      console.log('=== INICIANDO SINCRONIZAÇÃO INICIAL ===');
      
      const syncResult: SyncResult = {
        success: true,
        totalRecords: 0,
        syncedRecords: 0,
        errors: [],
        duration: 0
      };

      const startTime = Date.now();

      // 1. Verificar conectividade
      const isOnline = await this.checkConnectivity();
      if (!isOnline) {
        throw new Error('Sem conexão com a internet');
      }

      // 2. Sincronizar dados de usuário
      await this.syncUserData();
      syncResult.syncedRecords += 1;

      // 3. Sincronizar entregas
      const deliveriesResult = await this.syncDeliveries();
      syncResult.syncedRecords += deliveriesResult.count;

      // 4. Sincronizar configurações
      await this.syncSettings();
      syncResult.syncedRecords += 1;

      // 5. Marcar sincronização como completa
      await saveSetting('initial_sync_complete', 'true');
      await saveSetting('last_sync_time', Date.now().toString());

      syncResult.duration = Date.now() - startTime;
      syncResult.totalRecords = syncResult.syncedRecords;

      this.isInitialized = true;

      console.log('=== SINCRONIZAÇÃO INICIAL CONCLUÍDA ===');
      return syncResult;

    } catch (error) {
      console.error('Erro na sincronização inicial:', error);
      throw new Error(`Falha na sincronização inicial: ${error.message}`);
    }
  }

  // Sincronização incremental
  async performIncrementalSync(): Promise<SyncResult> {
    try {
      console.log('=== INICIANDO SINCRONIZAÇÃO INCREMENTAL ===');
      
      const lastSyncTime = await getSetting('last_sync_time');
      const syncResult: SyncResult = {
        success: true,
        totalRecords: 0,
        syncedRecords: 0,
        errors: [],
        duration: 0
      };

      const startTime = Date.now();

      // 1. Verificar conectividade
      const isOnline = await this.checkConnectivity();
      if (!isOnline) {
        throw new Error('Sem conexão com a internet');
      }

      // 2. Buscar mudanças do servidor
      const serverChanges = await this.getServerChangesSince(parseInt(lastSyncTime || '0'));
      
      // 3. Buscar mudanças locais não sincronizadas
      const localChanges = await this.getLocalUnsyncedChanges();

      // 4. Aplicar mudanças do servidor
      await this.applyServerChanges(serverChanges);
      syncResult.syncedRecords += serverChanges.length;

      // 5. Enviar mudanças locais
      await this.sendLocalChanges(localChanges);
      syncResult.syncedRecords += localChanges.length;

      // 6. Atualizar timestamp de sincronização
      await saveSetting('last_sync_time', Date.now().toString());

      syncResult.duration = Date.now() - startTime;
      syncResult.totalRecords = syncResult.syncedRecords;

      console.log('=== SINCRONIZAÇÃO INCREMENTAL CONCLUÍDA ===');
      return syncResult;

    } catch (error) {
      console.error('Erro na sincronização incremental:', error);
      throw new Error(`Falha na sincronização incremental: ${error.message}`);
    }
  }

  // Métodos auxiliares
  private async checkConnectivity(): Promise<boolean> {
    try {
      // Implementar verificação de conectividade
      const response = await fetch('https://www.google.com', { 
        method: 'HEAD',
        timeout: 5000 
      });
      return response.ok;
    } catch {
      return false;
    }
  }

  private async syncUserData(): Promise<void> {
    // Implementar sincronização de dados do usuário
    console.log('Sincronizando dados do usuário...');
  }

  private async syncDeliveries(): Promise<{ count: number }> {
    try {
      const deliveries = await api.getDeliveries();
      let count = 0;

      for (const delivery of deliveries) {
        await this.saveDeliveryLocally(delivery);
        count += 1;
      }

      return { count };
    } catch (error) {
      console.error('Erro ao sincronizar entregas:', error);
      throw error;
    }
  }

  private async syncSettings(): Promise<void> {
    // Implementar sincronização de configurações
    console.log('Sincronizando configurações...');
  }

  private async saveDeliveryLocally(delivery: any): Promise<void> {
    try {
      await executeQuery(
        `INSERT OR REPLACE INTO deliveries (
          id, outId, customerName, street, streetNumber, neighborhood,
          city, state, zipCode, customerPhone, lat, lng, deliverySeq,
          routing, status, outDate, notes, version, last_modified, sync_timestamp
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          delivery.id, delivery.outId, delivery.customerName,
          delivery.street, delivery.streetNumber, delivery.neighborhood,
          delivery.city, delivery.state, delivery.zipCode,
          delivery.customerPhone, delivery.lat, delivery.lng,
          delivery.deliverySeq, delivery.routing, delivery.status,
          delivery.outDate, delivery.notes, 1, Date.now(), Date.now()
        ]
      );
    } catch (error) {
      console.error('Erro ao salvar entrega localmente:', error);
      throw error;
    }
  }

  private async getServerChangesSince(timestamp: number): Promise<any[]> {
    // Implementar busca de mudanças do servidor
    // Por enquanto, retornar array vazio
    return [];
  }

  private async getLocalUnsyncedChanges(): Promise<any[]> {
    try {
      const result = await executeQuery(
        "SELECT * FROM deliveries WHERE sync_timestamp IS NULL OR sync_timestamp < last_modified"
      );
      return result.rows._array;
    } catch (error) {
      console.error('Erro ao buscar mudanças locais:', error);
      return [];
    }
  }

  private async applyServerChanges(changes: any[]): Promise<void> {
    for (const change of changes) {
      await this.saveDeliveryLocally(change);
    }
  }

  private async sendLocalChanges(changes: any[]): Promise<void> {
    for (const change of changes) {
      try {
        await api.createDelivery(change);
        // Marcar como sincronizado
        await executeQuery(
          "UPDATE deliveries SET sync_timestamp = ? WHERE id = ?",
          [Date.now(), change.id]
        );
      } catch (error) {
        console.error('Erro ao enviar mudança local:', error);
      }
    }
  }

  // Verificar se sincronização inicial foi realizada
  async isInitialSyncComplete(): Promise<boolean> {
    try {
      const result = await getSetting('initial_sync_complete');
      return result === 'true';
    } catch (error) {
      console.error('Erro ao verificar sincronização inicial:', error);
      return false;
    }
  }

  // Obter estatísticas de sincronização
  async getSyncStats(): Promise<any> {
    try {
      const totalResult = await executeQuery("SELECT COUNT(*) as count FROM deliveries");
      const syncedResult = await executeQuery(
        "SELECT COUNT(*) as count FROM deliveries WHERE sync_timestamp IS NOT NULL"
      );
      const pendingResult = await executeQuery(
        "SELECT COUNT(*) as count FROM deliveries WHERE sync_timestamp IS NULL"
      );

      return {
        totalRecords: totalResult.rows._array[0].count,
        syncedRecords: syncedResult.rows._array[0].count,
        pendingRecords: pendingResult.rows._array[0].count,
        lastSyncTime: await getSetting('last_sync_time')
      };
    } catch (error) {
      console.error('Erro ao obter estatísticas de sincronização:', error);
      return null;
    }
  }
}

export const syncService = new SyncService();

2.2 Contexto de Sincronização Inicial

Arquivo: src/contexts/InitialSyncContext.tsx

import React, { createContext, useContext, useState, useEffect } from 'react';
import { syncService, SyncResult } from '../services/syncService';

interface InitialSyncContextData {
  // Estados
  isInitialSyncComplete: boolean;
  syncProgress: number;
  syncStatus: string;
  lastSyncTime: number | null;
  pendingChanges: number;
  isLoading: boolean;
  error: string | null;
  
  // Métodos
  startInitialSync: () => Promise<void>;
  retryInitialSync: () => Promise<void>;
  performIncrementalSync: () => Promise<void>;
  getSyncStats: () => Promise<any>;
}

const InitialSyncContext = createContext<InitialSyncContextData>({} as InitialSyncContextData);

export const useInitialSync = () => {
  const context = useContext(InitialSyncContext);
  if (!context) {
    throw new Error('useInitialSync deve ser usado dentro de um InitialSyncProvider');
  }
  return context;
};

export const InitialSyncProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [isInitialSyncComplete, setIsInitialSyncComplete] = useState(false);
  const [syncProgress, setSyncProgress] = useState(0);
  const [syncStatus, setSyncStatus] = useState('idle');
  const [lastSyncTime, setLastSyncTime] = useState<number | null>(null);
  const [pendingChanges, setPendingChanges] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Verificar status inicial
  useEffect(() => {
    checkInitialSyncStatus();
  }, []);

  const checkInitialSyncStatus = async () => {
    try {
      const isComplete = await syncService.isInitialSyncComplete();
      setIsInitialSyncComplete(isComplete);
      
      if (isComplete) {
        const stats = await syncService.getSyncStats();
        if (stats) {
          setLastSyncTime(parseInt(stats.lastSyncTime || '0'));
          setPendingChanges(stats.pendingRecords);
        }
      }
    } catch (error) {
      console.error('Erro ao verificar status de sincronização:', error);
    }
  };

  const startInitialSync = async () => {
    setIsLoading(true);
    setError(null);
    setSyncStatus('starting');
    setSyncProgress(0);

    try {
      // Simular progresso
      const progressInterval = setInterval(() => {
        setSyncProgress(prev => {
          if (prev >= 90) {
            clearInterval(progressInterval);
            return prev;
          }
          return prev + 10;
        });
      }, 200);

      const result = await syncService.performInitialSync();
      
      clearInterval(progressInterval);
      setSyncProgress(100);
      setSyncStatus('completed');
      setIsInitialSyncComplete(true);
      setLastSyncTime(Date.now());
      setPendingChanges(0);

      console.log('Sincronização inicial concluída:', result);

    } catch (error) {
      console.error('Erro na sincronização inicial:', error);
      setError(error.message);
      setSyncStatus('error');
    } finally {
      setIsLoading(false);
    }
  };

  const retryInitialSync = async () => {
    setError(null);
    await startInitialSync();
  };

  const performIncrementalSync = async () => {
    setIsLoading(true);
    setError(null);
    setSyncStatus('syncing');

    try {
      const result = await syncService.performIncrementalSync();
      setSyncStatus('completed');
      setLastSyncTime(Date.now());
      setPendingChanges(0);

      console.log('Sincronização incremental concluída:', result);

    } catch (error) {
      console.error('Erro na sincronização incremental:', error);
      setError(error.message);
      setSyncStatus('error');
    } finally {
      setIsLoading(false);
    }
  };

  const getSyncStats = async () => {
    try {
      return await syncService.getSyncStats();
    } catch (error) {
      console.error('Erro ao obter estatísticas:', error);
      return null;
    }
  };

  return (
    <InitialSyncContext.Provider
      value={{
        isInitialSyncComplete,
        syncProgress,
        syncStatus,
        lastSyncTime,
        pendingChanges,
        isLoading,
        error,
        startInitialSync,
        retryInitialSync,
        performIncrementalSync,
        getSyncStats,
      }}
    >
      {children}
    </InitialSyncContext.Provider>
  );
};

Fase 3: Implementação das Telas

3.1 Tela de Sincronização Inicial

Arquivo: src/screens/sync/InitialSyncScreen.tsx

import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  Alert,
  ActivityIndicator,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import { useInitialSync } from '../../contexts/InitialSyncContext';
import { COLORS, SHADOWS } from '../../constants/theme';

const InitialSyncScreen: React.FC = () => {
  const {
    syncProgress,
    syncStatus,
    isLoading,
    error,
    startInitialSync,
    retryInitialSync,
  } = useInitialSync();

  const [hasStarted, setHasStarted] = useState(false);

  const handleStartSync = async () => {
    setHasStarted(true);
    try {
      await startInitialSync();
      // Navegação será feita pelo contexto pai
    } catch (error) {
      Alert.alert('Erro', 'Falha na sincronização inicial');
    }
  };

  const handleRetry = async () => {
    try {
      await retryInitialSync();
    } catch (error) {
      Alert.alert('Erro', 'Falha ao tentar novamente');
    }
  };

  const getStatusText = (status: string) => {
    switch (status) {
      case 'starting': return 'Iniciando sincronização...';
      case 'syncing': return 'Sincronizando dados...';
      case 'completed': return 'Sincronização concluída!';
      case 'error': return 'Erro na sincronização';
      default: return 'Pronto para sincronizar';
    }
  };

  const getStatusColor = (status: string) => {
    switch (status) {
      case 'completed': return COLORS.success;
      case 'error': return COLORS.danger;
      default: return COLORS.primary;
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <LinearGradient
        colors={[COLORS.primary, '#3B82F6']}
        style={styles.header}
      >
        <View style={styles.headerContent}>
          <Ionicons name="cloud-download" size={48} color="white" />
          <Text style={styles.title}>Sincronização Inicial</Text>
          <Text style={styles.subtitle}>
            Baixando dados necessários para funcionamento offline
          </Text>
        </View>
      </LinearGradient>

      <View style={styles.content}>
        <View style={styles.progressContainer}>
          <View style={styles.progressBar}>
            <View
              style={[
                styles.progressFill,
                { width: `${syncProgress}%` }
              ]}
            />
          </View>
          <Text style={styles.progressText}>
            {Math.round(syncProgress)}% concluído
          </Text>
        </View>

        <View style={styles.statusContainer}>
          <Text style={[styles.statusText, { color: getStatusColor(syncStatus) }]}>
            {getStatusText(syncStatus)}
          </Text>
        </View>

        {error && (
          <View style={styles.errorContainer}>
            <Ionicons name="alert-circle" size={24} color={COLORS.danger} />
            <Text style={styles.errorText}>{error}</Text>
          </View>
        )}

        <View style={styles.buttonContainer}>
          {!hasStarted || syncStatus === 'error' ? (
            <TouchableOpacity
              style={[styles.syncButton, isLoading && styles.syncButtonDisabled]}
              onPress={syncStatus === 'error' ? handleRetry : handleStartSync}
              disabled={isLoading}
            >
              {isLoading ? (
                <ActivityIndicator color="white" />
              ) : (
                <Ionicons
                  name={syncStatus === 'error' ? 'refresh' : 'cloud-download'}
                  size={24}
                  color="white"
                />
              )}
              <Text style={styles.syncButtonText}>
                {syncStatus === 'error' ? 'Tentar Novamente' : 'Iniciar Sincronização'}
              </Text>
            </TouchableOpacity>
          ) : (
            <View style={styles.completedContainer}>
              <Ionicons name="checkmark-circle" size={48} color={COLORS.success} />
              <Text style={styles.completedText}>
                Sincronização concluída com sucesso!
              </Text>
            </View>
          )}
        </View>

        <View style={styles.infoContainer}>
          <Text style={styles.infoText}>
             Dados de entregas serão baixados
          </Text>
          <Text style={styles.infoText}>
             Configurações serão sincronizadas
          </Text>
          <Text style={styles.infoText}>
             Aplicativo funcionará offline após sincronização
          </Text>
        </View>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: COLORS.background,
  },
  header: {
    paddingVertical: 40,
    paddingHorizontal: 20,
    alignItems: 'center',
  },
  headerContent: {
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'white',
    marginTop: 16,
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: 'rgba(255, 255, 255, 0.8)',
    textAlign: 'center',
  },
  content: {
    flex: 1,
    padding: 20,
  },
  progressContainer: {
    marginBottom: 30,
  },
  progressBar: {
    height: 8,
    backgroundColor: COLORS.border,
    borderRadius: 4,
    overflow: 'hidden',
    marginBottom: 8,
  },
  progressFill: {
    height: '100%',
    backgroundColor: COLORS.primary,
    borderRadius: 4,
  },
  progressText: {
    fontSize: 16,
    fontWeight: '600',
    color: COLORS.text,
    textAlign: 'center',
  },
  statusContainer: {
    alignItems: 'center',
    marginBottom: 20,
  },
  statusText: {
    fontSize: 18,
    fontWeight: '600',
  },
  errorContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#FEF2F2',
    padding: 16,
    borderRadius: 12,
    marginBottom: 20,
  },
  errorText: {
    fontSize: 14,
    color: COLORS.danger,
    marginLeft: 8,
    flex: 1,
  },
  buttonContainer: {
    marginBottom: 30,
  },
  syncButton: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: COLORS.primary,
    paddingVertical: 16,
    paddingHorizontal: 24,
    borderRadius: 12,
    ...SHADOWS.medium,
  },
  syncButtonDisabled: {
    backgroundColor: COLORS.textLight,
  },
  syncButtonText: {
    fontSize: 16,
    fontWeight: '600',
    color: 'white',
    marginLeft: 8,
  },
  completedContainer: {
    alignItems: 'center',
    padding: 20,
  },
  completedText: {
    fontSize: 18,
    fontWeight: '600',
    color: COLORS.success,
    marginTop: 16,
    textAlign: 'center',
  },
  infoContainer: {
    backgroundColor: COLORS.secondary,
    padding: 20,
    borderRadius: 12,
  },
  infoText: {
    fontSize: 14,
    color: COLORS.textLight,
    marginBottom: 8,
  },
});

export default InitialSyncScreen;

3.2 Atualização da Navegação

Arquivo: src/navigation/index.tsx

// Adicionar import
import InitialSyncScreen from '../screens/sync/InitialSyncScreen';

// Adicionar ao AuthNavigator
const AuthNavigator = () => {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      <Stack.Screen name="Login" component={LoginScreen} />
      <Stack.Screen name="InitialSync" component={InitialSyncScreen} />
    </Stack.Navigator>
  );
};

// Atualizar Navigation component
const Navigation = () => {
  const { user, isLoading } = useAuth();
  const { isInitialSyncComplete } = useInitialSync();

  useEffect(() => {
    console.log('=== DEBUG: NAVIGATION STATE ===');
    console.log('isLoading:', isLoading);
    console.log('user:', user ? 'Logado' : 'Não logado');
    console.log('isInitialSyncComplete:', isInitialSyncComplete);
    
    if (!isLoading && !user) {
      console.log('Redirecionando para Auth...');
      navigationRef.current?.reset({ 
        index: 0, 
        routes: [{ name: 'Auth' }] 
      });
    } else if (!isLoading && user && !isInitialSyncComplete) {
      console.log('Redirecionando para InitialSync...');
      navigationRef.current?.reset({ 
        index: 0, 
        routes: [{ name: 'InitialSync' }] 
      });
    }
  }, [user, isLoading, isInitialSyncComplete]);

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#FFFFFF" }}>
        <Text>Carregando...</Text>
      </View>
    );
  }

  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      {user ? (
        <>
          {!isInitialSyncComplete ? (
            <Stack.Screen name="InitialSync" component={InitialSyncScreen} />
          ) : (
            <>
              <Stack.Screen 
                name="Routing" 
                component={RoutingScreen} 
                options={{ 
                  headerShown: false,
                  presentation: 'modal'
                }} 
              />
              <Stack.Screen name="Main" component={TabNavigator} />
            </>
          )}
        </>
      ) : (
        <Stack.Screen name="Auth" component={AuthNavigator} />
      )}
    </Stack.Navigator>
  );
};

3.3 Atualização do App.tsx

Arquivo: App.tsx

// Adicionar import
import { InitialSyncProvider } from "./src/contexts/InitialSyncContext";

// Atualizar estrutura de providers
return (
  <GestureHandlerRootView style={{ flex: 1 }}>
    <SafeAreaProvider onLayout={onLayoutRootView}>
      <AuthProvider>
        <InitialSyncProvider>
          <SyncProvider>
            <DeliveriesProvider>
              <OfflineProvider>
                <NavigationContainer ref={navigationRef}>
                  {Platform.OS === 'android' ? (
                    <SafeAreaView style={{ flex: 1 }} edges={['bottom']}>
                      <Navigation />
                    </SafeAreaView>
                  ) : (
                    <Navigation />
                  )}
                  <StatusBar style="light" backgroundColor={COLORS.primary} />
                </NavigationContainer>
              </OfflineProvider>
            </DeliveriesProvider>
          </SyncProvider>
        </InitialSyncProvider>
      </AuthProvider>
    </SafeAreaProvider>
  </GestureHandlerRootView>
);

Fase 4: Implementação de Sincronização Incremental

4.1 Hook para Sincronização Automática

Arquivo: src/hooks/useAutoSync.ts

import { useEffect, useRef } from 'react';
import { AppState, AppStateStatus } from 'react-native';
import { useInitialSync } from '../contexts/InitialSyncContext';
import { useSync } from '../contexts/SyncContext';

export const useAutoSync = () => {
  const { isInitialSyncComplete, performIncrementalSync } = useInitialSync();
  const { isOnline } = useSync();
  const appState = useRef(AppState.currentState);
  const syncTimeoutRef = useRef<NodeJS.Timeout>();

  useEffect(() => {
    const handleAppStateChange = (nextAppState: AppStateStatus) => {
      if (
        appState.current.match(/inactive|background/) &&
        nextAppState === 'active' &&
        isInitialSyncComplete &&
        isOnline
      ) {
        // App voltou ao foreground e está online
        console.log('App ativo - verificando sincronização');
        scheduleSync();
      }
      appState.current = nextAppState;
    };

    const subscription = AppState.addEventListener('change', handleAppStateChange);

    return () => {
      subscription?.remove();
      if (syncTimeoutRef.current) {
        clearTimeout(syncTimeoutRef.current);
      }
    };
  }, [isInitialSyncComplete, isOnline]);

  const scheduleSync = () => {
    // Aguardar 5 segundos antes de sincronizar
    syncTimeoutRef.current = setTimeout(async () => {
      try {
        console.log('Executando sincronização automática');
        await performIncrementalSync();
      } catch (error) {
        console.error('Erro na sincronização automática:', error);
      }
    }, 5000);
  };

  return {
    scheduleSync,
  };
};

4.2 Atualização do HomeScreen

Arquivo: src/screens/main/HomeScreen.tsx

// Adicionar imports
import { useInitialSync } from '../../contexts/InitialSyncContext';
import { useAutoSync } from '../../hooks/useAutoSync';

// Adicionar ao componente
const HomeScreen = () => {
  // ... código existente ...
  
  const { 
    isInitialSyncComplete, 
    pendingChanges, 
    performIncrementalSync,
    getSyncStats 
  } = useInitialSync();
  
  const { scheduleSync } = useAutoSync();

  // Usar auto sync
  useEffect(() => {
    if (isInitialSyncComplete) {
      scheduleSync();
    }
  }, [isInitialSyncComplete, scheduleSync]);

  // ... resto do código ...
};

Fase 5: Testes e Validação

5.1 Script de Teste

Arquivo: scripts/test-sync.js

const { execSync } = require('child_process');

console.log('=== TESTANDO SINCRONIZAÇÃO OFFLINE ===');

// Teste 1: Verificar se banco de dados foi criado
console.log('1. Verificando estrutura do banco...');
try {
  execSync('npx react-native run-android --variant=debug', { stdio: 'inherit' });
  console.log('✅ Banco de dados criado com sucesso');
} catch (error) {
  console.error('❌ Erro ao criar banco de dados:', error.message);
}

// Teste 2: Verificar sincronização inicial
console.log('2. Testando sincronização inicial...');
// Implementar testes específicos

// Teste 3: Verificar modo offline
console.log('3. Testando modo offline...');
// Implementar testes específicos

console.log('=== TESTES CONCLUÍDOS ===');

5.2 Checklist de Implementação

## Checklist de Implementação

### Fase 1: Preparação
- [ ] Instalar dependências necessárias
- [ ] Configurar variáveis de ambiente
- [ ] Atualizar estrutura do banco de dados
- [ ] Criar tabelas de sincronização

### Fase 2: Serviços Base
- [ ] Implementar SyncService
- [ ] Criar InitialSyncContext
- [ ] Implementar métodos de sincronização
- [ ] Adicionar tratamento de erros

### Fase 3: Interface
- [ ] Criar InitialSyncScreen
- [ ] Atualizar navegação
- [ ] Integrar com App.tsx
- [ ] Testar fluxo de navegação

### Fase 4: Sincronização Automática
- [ ] Implementar useAutoSync hook
- [ ] Adicionar sincronização incremental
- [ ] Atualizar HomeScreen
- [ ] Testar sincronização automática

### Fase 5: Testes
- [ ] Testar sincronização inicial
- [ ] Testar modo offline
- [ ] Testar sincronização incremental
- [ ] Validar performance
- [ ] Testar tratamento de erros

### Fase 6: Otimizações
- [ ] Implementar compressão de dados
- [ ] Adicionar cache inteligente
- [ ] Otimizar queries do banco
- [ ] Implementar logs de debug

Fase 6: Deploy e Monitoramento

6.1 Configuração de Build

Arquivo: eas.json

{
  "cli": {
    "version": ">= 5.9.0"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "env": {
        "API_BASE_URL": "https://api-staging.example.com"
      }
    },
    "production": {
      "env": {
        "API_BASE_URL": "https://api.example.com"
      }
    }
  },
  "submit": {
    "production": {}
  }
}

6.2 Monitoramento de Sincronização

Arquivo: src/services/syncMonitor.ts

class SyncMonitor {
  private metrics: Map<string, any> = new Map();

  recordSyncAttempt(type: string, success: boolean, duration: number) {
    const key = `${type}_${new Date().toISOString().split('T')[0]}`;
    const current = this.metrics.get(key) || {
      attempts: 0,
      successes: 0,
      failures: 0,
      totalDuration: 0,
    };

    current.attempts += 1;
    current.totalDuration += duration;

    if (success) {
      current.successes += 1;
    } else {
      current.failures += 1;
    }

    this.metrics.set(key, current);
  }

  getMetrics(): any {
    return Object.fromEntries(this.metrics);
  }

  exportMetrics(): string {
    return JSON.stringify(this.getMetrics(), null, 2);
  }
}

export const syncMonitor = new SyncMonitor();

Conclusão

Este guia fornece uma implementação completa e passo a passo da sincronização offline no aplicativo de entregas. A implementação é modular e pode ser feita incrementalmente, permitindo testes e validação em cada fase.

Próximos Passos:

  1. Implementar Fase 1 (Preparação)
  2. Testar estrutura do banco de dados
  3. Implementar Fase 2 (Serviços Base)
  4. Testar sincronização inicial
  5. Continuar com as demais fases

Considerações Importantes:

  • Sempre testar em ambiente de desenvolvimento primeiro
  • Implementar logs detalhados para debugging
  • Considerar performance e uso de bateria
  • Implementar fallbacks para cenários de erro
  • Monitorar uso de dados e armazenamento