This commit is contained in:
Phuoc Nguyen
2025-10-10 16:38:07 +07:00
parent e5b247d622
commit b94c158004
177 changed files with 25080 additions and 152 deletions

View File

@@ -0,0 +1,32 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../network/network_info.dart';
part 'network_info_provider.g.dart';
/// Connectivity provider - provides Connectivity instance
@Riverpod(keepAlive: true)
Connectivity connectivity(Ref ref) {
return Connectivity();
}
/// Network info provider - provides NetworkInfo implementation
@Riverpod(keepAlive: true)
NetworkInfo networkInfo(Ref ref) {
final connectivity = ref.watch(connectivityProvider);
return NetworkInfo(connectivity);
}
/// Provider to check if device is connected to internet
@riverpod
Future<bool> isConnected(Ref ref) async {
final networkInfo = ref.watch(networkInfoProvider);
return await networkInfo.isConnected;
}
/// Stream provider for connectivity changes
@riverpod
Stream<List<ConnectivityResult>> connectivityStream(Ref ref) {
final networkInfo = ref.watch(networkInfoProvider);
return networkInfo.connectivityStream;
}

View File

@@ -0,0 +1,186 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'network_info_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Connectivity provider - provides Connectivity instance
@ProviderFor(connectivity)
const connectivityProvider = ConnectivityProvider._();
/// Connectivity provider - provides Connectivity instance
final class ConnectivityProvider
extends $FunctionalProvider<Connectivity, Connectivity, Connectivity>
with $Provider<Connectivity> {
/// Connectivity provider - provides Connectivity instance
const ConnectivityProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'connectivityProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$connectivityHash();
@$internal
@override
$ProviderElement<Connectivity> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
Connectivity create(Ref ref) {
return connectivity(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(Connectivity value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<Connectivity>(value),
);
}
}
String _$connectivityHash() => r'15246627d0ae599bcd01382c80d3d25b9e9b4e18';
/// Network info provider - provides NetworkInfo implementation
@ProviderFor(networkInfo)
const networkInfoProvider = NetworkInfoProvider._();
/// Network info provider - provides NetworkInfo implementation
final class NetworkInfoProvider
extends $FunctionalProvider<NetworkInfo, NetworkInfo, NetworkInfo>
with $Provider<NetworkInfo> {
/// Network info provider - provides NetworkInfo implementation
const NetworkInfoProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'networkInfoProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$networkInfoHash();
@$internal
@override
$ProviderElement<NetworkInfo> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
NetworkInfo create(Ref ref) {
return networkInfo(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(NetworkInfo value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<NetworkInfo>(value),
);
}
}
String _$networkInfoHash() => r'7e3a8d0e6ca244de6de51bcdb699e5c0a9a3b57f';
/// Provider to check if device is connected to internet
@ProviderFor(isConnected)
const isConnectedProvider = IsConnectedProvider._();
/// Provider to check if device is connected to internet
final class IsConnectedProvider
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
with $FutureModifier<bool>, $FutureProvider<bool> {
/// Provider to check if device is connected to internet
const IsConnectedProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'isConnectedProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$isConnectedHash();
@$internal
@override
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
$FutureProviderElement(pointer);
@override
FutureOr<bool> create(Ref ref) {
return isConnected(ref);
}
}
String _$isConnectedHash() => r'c9620cadbcdee8e738f865e747dd57262236782d';
/// Stream provider for connectivity changes
@ProviderFor(connectivityStream)
const connectivityStreamProvider = ConnectivityStreamProvider._();
/// Stream provider for connectivity changes
final class ConnectivityStreamProvider
extends
$FunctionalProvider<
AsyncValue<List<ConnectivityResult>>,
List<ConnectivityResult>,
Stream<List<ConnectivityResult>>
>
with
$FutureModifier<List<ConnectivityResult>>,
$StreamProvider<List<ConnectivityResult>> {
/// Stream provider for connectivity changes
const ConnectivityStreamProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'connectivityStreamProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$connectivityStreamHash();
@$internal
@override
$StreamProviderElement<List<ConnectivityResult>> $createElement(
$ProviderPointer pointer,
) => $StreamProviderElement(pointer);
@override
Stream<List<ConnectivityResult>> create(Ref ref) {
return connectivityStream(ref);
}
}
String _$connectivityStreamHash() =>
r'7754266fc385401e595a30189ad0c31b1f926fdc';

View File

@@ -0,0 +1,3 @@
/// Export all core providers
export 'network_info_provider.dart';
export 'sync_status_provider.dart';

View File

@@ -0,0 +1,223 @@
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).syncCategories();
// Then sync products
await ref.read(productsProvider.notifier).syncProducts();
// 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).syncProducts();
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).syncCategories();
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,
);
}

View File

@@ -0,0 +1,106 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sync_status_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Sync status provider - manages data synchronization state
@ProviderFor(SyncStatus)
const syncStatusProvider = SyncStatusProvider._();
/// Sync status provider - manages data synchronization state
final class SyncStatusProvider
extends $AsyncNotifierProvider<SyncStatus, SyncResult> {
/// Sync status provider - manages data synchronization state
const SyncStatusProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'syncStatusProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$syncStatusHash();
@$internal
@override
SyncStatus create() => SyncStatus();
}
String _$syncStatusHash() => r'dc92a1b83c89af94dfe94b646aa81d9501f371d7';
/// Sync status provider - manages data synchronization state
abstract class _$SyncStatus extends $AsyncNotifier<SyncResult> {
FutureOr<SyncResult> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<SyncResult>, SyncResult>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<SyncResult>, SyncResult>,
AsyncValue<SyncResult>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// Provider for last sync time from settings
@ProviderFor(lastSyncTime)
const lastSyncTimeProvider = LastSyncTimeProvider._();
/// Provider for last sync time from settings
final class LastSyncTimeProvider
extends $FunctionalProvider<DateTime?, DateTime?, DateTime?>
with $Provider<DateTime?> {
/// Provider for last sync time from settings
const LastSyncTimeProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'lastSyncTimeProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$lastSyncTimeHash();
@$internal
@override
$ProviderElement<DateTime?> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
DateTime? create(Ref ref) {
return lastSyncTime(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(DateTime? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<DateTime?>(value),
);
}
}
String _$lastSyncTimeHash() => r'5d9bea98c58f0c838532cdf13ac1ab3fd9447051';