From 75513da17d62aad79075cd4abbc336a7c82a6baf Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 18 May 2026 20:12:52 +0200 Subject: [PATCH] feat: Add refresh token mechanism and sync request handling in home screen --- lib/app.dart | 8 +++ lib/screens/home_screen.dart | 33 +++++++----- lib/widgets/sync_status_indicator.dart | 69 ++++++++++++++++++-------- 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 788e654..e3cd2dd 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -70,6 +70,7 @@ class _NotesAppState extends State _AppSection _currentSection = _AppSection.home; SyncStatus _syncStatus = SyncStatus.idle; String? _syncErrorMessage; + int _homeRefreshToken = 0; @override void initState() { @@ -613,6 +614,10 @@ class _NotesAppState extends State return; } + if (_syncStatus == SyncStatus.syncing) { + return; + } + setState(() { _syncStatus = SyncStatus.syncing; _syncErrorMessage = null; @@ -634,6 +639,7 @@ class _NotesAppState extends State setState(() { _syncStatus = SyncStatus.synced; _syncErrorMessage = null; + _homeRefreshToken += 1; }); // Reset to idle after 3 seconds @@ -704,9 +710,11 @@ class _NotesAppState extends State key: const ValueKey('home-screen'), repository: repository, onOpenSettings: _openSettings, + onRequestSync: _performSync, onVaultInvalid: _resetLocalVaultData, syncStatus: _syncStatus, syncErrorMessage: _syncErrorMessage, + refreshToken: _homeRefreshToken, ) : SettingsScreen( key: const ValueKey('settings-screen'), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index f09b6cc..9f3eb7e 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -17,16 +17,20 @@ class HomeScreen extends StatefulWidget { super.key, required this.repository, required this.onOpenSettings, + required this.onRequestSync, this.onVaultInvalid, this.syncStatus = SyncStatus.idle, this.syncErrorMessage, + this.refreshToken = 0, }); final NoteRepository repository; final VoidCallback onOpenSettings; + final Future Function() onRequestSync; final Future Function()? onVaultInvalid; final SyncStatus syncStatus; final String? syncErrorMessage; + final int refreshToken; @override State createState() => _HomeScreenState(); @@ -52,6 +56,14 @@ class _HomeScreenState extends State { _loadNotes(); } + @override + void didUpdateWidget(covariant HomeScreen oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.refreshToken != widget.refreshToken) { + _loadNotes(); + } + } + Future _loadNotes() async { try { final List storedNotes = await widget.repository.loadNotes(); @@ -92,10 +104,9 @@ class _HomeScreenState extends State { _notes = updatedNotes; }); - // Trigger sync after creating a note and refresh local list + // Trigger sync after creating a note. try { - await widget.repository.performSync(); - await _loadNotes(); + await widget.onRequestSync(); } catch (_) {} } } @@ -115,10 +126,9 @@ class _HomeScreenState extends State { _notes = updatedNotes; }); - // Trigger sync after deleting a note and refresh local list + // Trigger sync after deleting a note. try { - await widget.repository.performSync(); - await _loadNotes(); + await widget.onRequestSync(); } catch (_) {} } @@ -171,10 +181,9 @@ class _HomeScreenState extends State { setState(() { _notes = _normalizeNotes(updatedNotes); }); - // Trigger sync after editing a note and refresh local list + // Trigger sync after editing a note. try { - await widget.repository.performSync(); - await _loadNotes(); + await widget.onRequestSync(); } catch (_) {} } } @@ -212,10 +221,7 @@ class _HomeScreenState extends State { ? const _EmptyState() : RefreshIndicator( onRefresh: () async { - try { - await widget.repository.performSync(); - } catch (_) {} - await _loadNotes(); + await widget.onRequestSync(); }, child: MouseRegion( cursor: _isDragging @@ -520,6 +526,7 @@ class _HomeScreenState extends State { trailingWidget: SyncStatusIndicator( status: widget.syncStatus, errorMessage: widget.syncErrorMessage, + onTap: widget.onRequestSync, ), onSearchChanged: (String query) { setState(() { diff --git a/lib/widgets/sync_status_indicator.dart b/lib/widgets/sync_status_indicator.dart index af6346d..c413fcb 100644 --- a/lib/widgets/sync_status_indicator.dart +++ b/lib/widgets/sync_status_indicator.dart @@ -11,57 +11,82 @@ class SyncStatusIndicator extends StatelessWidget { const SyncStatusIndicator({ required this.status, this.errorMessage, + this.onTap, super.key, }); final SyncStatus status; final String? errorMessage; + final VoidCallback? onTap; + + Widget _buildIndicator(Widget child) { + if (onTap == null) { + return child; + } + + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: child, + ), + ); + } @override Widget build(BuildContext context) { switch (status) { case SyncStatus.idle: - return const Tooltip( + return Tooltip( message: 'Sincronización en espera', - child: Icon( - Icons.cloud_outlined, - size: 16, - color: Colors.white38, + child: _buildIndicator( + const Icon( + Icons.cloud_outlined, + size: 16, + color: Colors.white38, + ), ), ); case SyncStatus.syncing: - return const Tooltip( + return Tooltip( message: 'Sincronizando...', - child: SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - Color.fromARGB(255, 150, 150, 150), + child: _buildIndicator( + const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + Color.fromARGB(255, 150, 150, 150), + ), ), ), ), ); case SyncStatus.synced: - return const Tooltip( + return Tooltip( message: 'Sincronizado', - child: Icon( - Icons.check_circle, - size: 16, - color: Colors.green, + child: _buildIndicator( + const Icon( + Icons.check_circle, + size: 16, + color: Colors.green, + ), ), ); case SyncStatus.error: return Tooltip( message: errorMessage ?? 'Error al sincronizar', - child: const Icon( - Icons.error, - size: 16, - color: Colors.red, + child: _buildIndicator( + const Icon( + Icons.error, + size: 16, + color: Colors.red, + ), ), ); }