224 lines
5.7 KiB
Dart
224 lines
5.7 KiB
Dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
import '../../features/products/presentation/providers/products_provider.dart';
|
|
import '../../features/categories/presentation/providers/categories_provider.dart';
|
|
import '../../features/settings/presentation/providers/settings_provider.dart';
|
|
import 'network_info_provider.dart';
|
|
|
|
part 'sync_status_provider.g.dart';
|
|
|
|
/// Sync status provider - manages data synchronization state
|
|
@riverpod
|
|
class SyncStatus extends _$SyncStatus {
|
|
@override
|
|
Future<SyncResult> build() async {
|
|
// Initialize with idle state
|
|
return const SyncResult(
|
|
status: SyncState.idle,
|
|
lastSyncTime: null,
|
|
message: 'Ready to sync',
|
|
);
|
|
}
|
|
|
|
/// Perform full sync of all data
|
|
Future<void> syncAll() async {
|
|
// Check network connectivity first
|
|
final isConnected = await ref.read(isConnectedProvider.future);
|
|
if (!isConnected) {
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.offline,
|
|
lastSyncTime: null,
|
|
message: 'No internet connection',
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Start sync
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.syncing,
|
|
lastSyncTime: null,
|
|
message: 'Syncing data...',
|
|
),
|
|
);
|
|
|
|
try {
|
|
// Sync categories first (products depend on categories)
|
|
await ref.read(categoriesProvider.notifier).refresh();
|
|
|
|
// Then sync products
|
|
await ref.read(productsProvider.notifier).refresh();
|
|
|
|
// Update last sync time in settings
|
|
await ref.read(settingsProvider.notifier).updateLastSyncTime();
|
|
|
|
// Sync completed successfully
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.success,
|
|
lastSyncTime: DateTime.now(),
|
|
message: 'Sync completed successfully',
|
|
),
|
|
);
|
|
} catch (error, stackTrace) {
|
|
// Sync failed
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.failed,
|
|
lastSyncTime: null,
|
|
message: 'Sync failed: ${error.toString()}',
|
|
error: error,
|
|
),
|
|
);
|
|
|
|
// Also set error state for proper error handling
|
|
state = AsyncValue.error(error, stackTrace);
|
|
}
|
|
}
|
|
|
|
/// Sync only products
|
|
Future<void> syncProducts() async {
|
|
final isConnected = await ref.read(isConnectedProvider.future);
|
|
if (!isConnected) {
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.offline,
|
|
lastSyncTime: null,
|
|
message: 'No internet connection',
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.syncing,
|
|
lastSyncTime: null,
|
|
message: 'Syncing products...',
|
|
),
|
|
);
|
|
|
|
try {
|
|
await ref.read(productsProvider.notifier).refresh();
|
|
await ref.read(settingsProvider.notifier).updateLastSyncTime();
|
|
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.success,
|
|
lastSyncTime: DateTime.now(),
|
|
message: 'Products synced successfully',
|
|
),
|
|
);
|
|
} catch (error, stackTrace) {
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.failed,
|
|
lastSyncTime: null,
|
|
message: 'Product sync failed: ${error.toString()}',
|
|
error: error,
|
|
),
|
|
);
|
|
state = AsyncValue.error(error, stackTrace);
|
|
}
|
|
}
|
|
|
|
/// Sync only categories
|
|
Future<void> syncCategories() async {
|
|
final isConnected = await ref.read(isConnectedProvider.future);
|
|
if (!isConnected) {
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.offline,
|
|
lastSyncTime: null,
|
|
message: 'No internet connection',
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.syncing,
|
|
lastSyncTime: null,
|
|
message: 'Syncing categories...',
|
|
),
|
|
);
|
|
|
|
try {
|
|
await ref.read(categoriesProvider.notifier).refresh();
|
|
await ref.read(settingsProvider.notifier).updateLastSyncTime();
|
|
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.success,
|
|
lastSyncTime: DateTime.now(),
|
|
message: 'Categories synced successfully',
|
|
),
|
|
);
|
|
} catch (error, stackTrace) {
|
|
state = AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.failed,
|
|
lastSyncTime: null,
|
|
message: 'Category sync failed: ${error.toString()}',
|
|
error: error,
|
|
),
|
|
);
|
|
state = AsyncValue.error(error, stackTrace);
|
|
}
|
|
}
|
|
|
|
/// Reset sync status to idle
|
|
void resetStatus() {
|
|
state = const AsyncValue.data(
|
|
SyncResult(
|
|
status: SyncState.idle,
|
|
lastSyncTime: null,
|
|
message: 'Ready to sync',
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Sync state enum
|
|
enum SyncState {
|
|
idle,
|
|
syncing,
|
|
success,
|
|
failed,
|
|
offline,
|
|
}
|
|
|
|
/// Sync result model
|
|
class SyncResult {
|
|
final SyncState status;
|
|
final DateTime? lastSyncTime;
|
|
final String message;
|
|
final Object? error;
|
|
|
|
const SyncResult({
|
|
required this.status,
|
|
required this.lastSyncTime,
|
|
required this.message,
|
|
this.error,
|
|
});
|
|
|
|
bool get isSyncing => status == SyncState.syncing;
|
|
bool get isSuccess => status == SyncState.success;
|
|
bool get isFailed => status == SyncState.failed;
|
|
bool get isOffline => status == SyncState.offline;
|
|
bool get isIdle => status == SyncState.idle;
|
|
}
|
|
|
|
/// Provider for last sync time from settings
|
|
@riverpod
|
|
DateTime? lastSyncTime(Ref ref) {
|
|
final settingsAsync = ref.watch(settingsProvider);
|
|
return settingsAsync.when(
|
|
data: (settings) => settings.lastSyncAt,
|
|
loading: () => null,
|
|
error: (_, __) => null,
|
|
);
|
|
}
|