feat: Add refresh token mechanism and sync request handling in home screen

This commit is contained in:
2026-05-18 20:12:52 +02:00
parent 7da87843cd
commit 75513da17d
3 changed files with 75 additions and 35 deletions
+8
View File
@@ -70,6 +70,7 @@ class _NotesAppState extends State<NotesApp>
_AppSection _currentSection = _AppSection.home; _AppSection _currentSection = _AppSection.home;
SyncStatus _syncStatus = SyncStatus.idle; SyncStatus _syncStatus = SyncStatus.idle;
String? _syncErrorMessage; String? _syncErrorMessage;
int _homeRefreshToken = 0;
@override @override
void initState() { void initState() {
@@ -613,6 +614,10 @@ class _NotesAppState extends State<NotesApp>
return; return;
} }
if (_syncStatus == SyncStatus.syncing) {
return;
}
setState(() { setState(() {
_syncStatus = SyncStatus.syncing; _syncStatus = SyncStatus.syncing;
_syncErrorMessage = null; _syncErrorMessage = null;
@@ -634,6 +639,7 @@ class _NotesAppState extends State<NotesApp>
setState(() { setState(() {
_syncStatus = SyncStatus.synced; _syncStatus = SyncStatus.synced;
_syncErrorMessage = null; _syncErrorMessage = null;
_homeRefreshToken += 1;
}); });
// Reset to idle after 3 seconds // Reset to idle after 3 seconds
@@ -704,9 +710,11 @@ class _NotesAppState extends State<NotesApp>
key: const ValueKey<String>('home-screen'), key: const ValueKey<String>('home-screen'),
repository: repository, repository: repository,
onOpenSettings: _openSettings, onOpenSettings: _openSettings,
onRequestSync: _performSync,
onVaultInvalid: _resetLocalVaultData, onVaultInvalid: _resetLocalVaultData,
syncStatus: _syncStatus, syncStatus: _syncStatus,
syncErrorMessage: _syncErrorMessage, syncErrorMessage: _syncErrorMessage,
refreshToken: _homeRefreshToken,
) )
: SettingsScreen( : SettingsScreen(
key: const ValueKey<String>('settings-screen'), key: const ValueKey<String>('settings-screen'),
+20 -13
View File
@@ -17,16 +17,20 @@ class HomeScreen extends StatefulWidget {
super.key, super.key,
required this.repository, required this.repository,
required this.onOpenSettings, required this.onOpenSettings,
required this.onRequestSync,
this.onVaultInvalid, this.onVaultInvalid,
this.syncStatus = SyncStatus.idle, this.syncStatus = SyncStatus.idle,
this.syncErrorMessage, this.syncErrorMessage,
this.refreshToken = 0,
}); });
final NoteRepository repository; final NoteRepository repository;
final VoidCallback onOpenSettings; final VoidCallback onOpenSettings;
final Future<void> Function() onRequestSync;
final Future<void> Function()? onVaultInvalid; final Future<void> Function()? onVaultInvalid;
final SyncStatus syncStatus; final SyncStatus syncStatus;
final String? syncErrorMessage; final String? syncErrorMessage;
final int refreshToken;
@override @override
State<HomeScreen> createState() => _HomeScreenState(); State<HomeScreen> createState() => _HomeScreenState();
@@ -52,6 +56,14 @@ class _HomeScreenState extends State<HomeScreen> {
_loadNotes(); _loadNotes();
} }
@override
void didUpdateWidget(covariant HomeScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.refreshToken != widget.refreshToken) {
_loadNotes();
}
}
Future<void> _loadNotes() async { Future<void> _loadNotes() async {
try { try {
final List<Note> storedNotes = await widget.repository.loadNotes(); final List<Note> storedNotes = await widget.repository.loadNotes();
@@ -92,10 +104,9 @@ class _HomeScreenState extends State<HomeScreen> {
_notes = updatedNotes; _notes = updatedNotes;
}); });
// Trigger sync after creating a note and refresh local list // Trigger sync after creating a note.
try { try {
await widget.repository.performSync(); await widget.onRequestSync();
await _loadNotes();
} catch (_) {} } catch (_) {}
} }
} }
@@ -115,10 +126,9 @@ class _HomeScreenState extends State<HomeScreen> {
_notes = updatedNotes; _notes = updatedNotes;
}); });
// Trigger sync after deleting a note and refresh local list // Trigger sync after deleting a note.
try { try {
await widget.repository.performSync(); await widget.onRequestSync();
await _loadNotes();
} catch (_) {} } catch (_) {}
} }
@@ -171,10 +181,9 @@ class _HomeScreenState extends State<HomeScreen> {
setState(() { setState(() {
_notes = _normalizeNotes(updatedNotes); _notes = _normalizeNotes(updatedNotes);
}); });
// Trigger sync after editing a note and refresh local list // Trigger sync after editing a note.
try { try {
await widget.repository.performSync(); await widget.onRequestSync();
await _loadNotes();
} catch (_) {} } catch (_) {}
} }
} }
@@ -212,10 +221,7 @@ class _HomeScreenState extends State<HomeScreen> {
? const _EmptyState() ? const _EmptyState()
: RefreshIndicator( : RefreshIndicator(
onRefresh: () async { onRefresh: () async {
try { await widget.onRequestSync();
await widget.repository.performSync();
} catch (_) {}
await _loadNotes();
}, },
child: MouseRegion( child: MouseRegion(
cursor: _isDragging cursor: _isDragging
@@ -520,6 +526,7 @@ class _HomeScreenState extends State<HomeScreen> {
trailingWidget: SyncStatusIndicator( trailingWidget: SyncStatusIndicator(
status: widget.syncStatus, status: widget.syncStatus,
errorMessage: widget.syncErrorMessage, errorMessage: widget.syncErrorMessage,
onTap: widget.onRequestSync,
), ),
onSearchChanged: (String query) { onSearchChanged: (String query) {
setState(() { setState(() {
+47 -22
View File
@@ -11,57 +11,82 @@ class SyncStatusIndicator extends StatelessWidget {
const SyncStatusIndicator({ const SyncStatusIndicator({
required this.status, required this.status,
this.errorMessage, this.errorMessage,
this.onTap,
super.key, super.key,
}); });
final SyncStatus status; final SyncStatus status;
final String? errorMessage; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (status) { switch (status) {
case SyncStatus.idle: case SyncStatus.idle:
return const Tooltip( return Tooltip(
message: 'Sincronización en espera', message: 'Sincronización en espera',
child: Icon( child: _buildIndicator(
Icons.cloud_outlined, const Icon(
size: 16, Icons.cloud_outlined,
color: Colors.white38, size: 16,
color: Colors.white38,
),
), ),
); );
case SyncStatus.syncing: case SyncStatus.syncing:
return const Tooltip( return Tooltip(
message: 'Sincronizando...', message: 'Sincronizando...',
child: SizedBox( child: _buildIndicator(
width: 16, const SizedBox(
height: 16, width: 16,
child: CircularProgressIndicator( height: 16,
strokeWidth: 2, child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>( strokeWidth: 2,
Color.fromARGB(255, 150, 150, 150), valueColor: AlwaysStoppedAnimation<Color>(
Color.fromARGB(255, 150, 150, 150),
),
), ),
), ),
), ),
); );
case SyncStatus.synced: case SyncStatus.synced:
return const Tooltip( return Tooltip(
message: 'Sincronizado', message: 'Sincronizado',
child: Icon( child: _buildIndicator(
Icons.check_circle, const Icon(
size: 16, Icons.check_circle,
color: Colors.green, size: 16,
color: Colors.green,
),
), ),
); );
case SyncStatus.error: case SyncStatus.error:
return Tooltip( return Tooltip(
message: errorMessage ?? 'Error al sincronizar', message: errorMessage ?? 'Error al sincronizar',
child: const Icon( child: _buildIndicator(
Icons.error, const Icon(
size: 16, Icons.error,
color: Colors.red, size: 16,
color: Colors.red,
),
), ),
); );
} }