Implement local vault service with encryption key management and integrate it into the app. Add settings screen for data management and enhance home screen with new features. Update database connection for encryption support and modify repository to use the new database structure. Improve UI elements across the application for better user experience.
This commit is contained in:
+194
-7
@@ -1,8 +1,17 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:notas/data/app_database.dart';
|
||||
import 'package:notas/data/local_vault_service.dart';
|
||||
import 'package:notas/data/note_repository.dart';
|
||||
import 'package:notas/platform/app_platform.dart';
|
||||
import 'package:notas/screens/home_screen.dart';
|
||||
import 'package:notas/theme/app_theme.dart';
|
||||
import 'package:notas/platform/window_state.dart';
|
||||
import 'package:notas/screens/home_screen.dart';
|
||||
import 'package:notas/screens/vault_access_screen.dart';
|
||||
import 'package:notas/theme/app_theme.dart';
|
||||
import 'package:notas/widgets/app_title_bar.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class NotesApp extends StatefulWidget {
|
||||
@@ -13,12 +22,22 @@ class NotesApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NotesAppState extends State<NotesApp> with WindowListener {
|
||||
final LocalVaultService _vaultService = LocalVaultService.instance;
|
||||
final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey =
|
||||
GlobalKey<ScaffoldMessengerState>();
|
||||
|
||||
AppDatabase? _database;
|
||||
NoteRepository? _repository;
|
||||
bool _isBootstrapping = true;
|
||||
bool _isUnlocking = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (isDesktop) {
|
||||
windowManager.addListener(this);
|
||||
}
|
||||
_bootstrapVault();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -26,9 +45,116 @@ class _NotesAppState extends State<NotesApp> with WindowListener {
|
||||
if (isDesktop) {
|
||||
windowManager.removeListener(this);
|
||||
}
|
||||
_database?.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _bootstrapVault() async {
|
||||
try {
|
||||
final String? encryptionKey = await _vaultService.readEncryptionKey();
|
||||
|
||||
if (encryptionKey != null) {
|
||||
await _openVault(encryptionKey);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isBootstrapping = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openVault(String encryptionKey) async {
|
||||
await _database?.close();
|
||||
|
||||
final AppDatabase database = AppDatabase(encryptionKey: encryptionKey);
|
||||
if (!mounted) {
|
||||
await database.close();
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_database = database;
|
||||
_repository = NoteRepository(database: database);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _resetLocalVaultData() async {
|
||||
final AppDatabase? database = _database;
|
||||
|
||||
setState(() {
|
||||
_repository = null;
|
||||
_database = null;
|
||||
_isBootstrapping = true;
|
||||
});
|
||||
|
||||
await database?.close();
|
||||
|
||||
await _vaultService.clearEncryptionKey();
|
||||
|
||||
final Directory supportDir = await getApplicationSupportDirectory();
|
||||
final String dbPath = p.join(supportDir.path, 'notes.sqlite');
|
||||
final List<String> filesToDelete = <String>[
|
||||
dbPath,
|
||||
'$dbPath-wal',
|
||||
'$dbPath-shm',
|
||||
'$dbPath-journal',
|
||||
];
|
||||
|
||||
for (final String filePath in filesToDelete) {
|
||||
final File file = File(filePath);
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isBootstrapping = false;
|
||||
_isUnlocking = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _enterWithoutAccount() async {
|
||||
if (_isUnlocking) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isUnlocking = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final String encryptionKey = await _vaultService.createEncryptionKey();
|
||||
await _openVault(encryptionKey);
|
||||
} catch (error) {
|
||||
_scaffoldMessengerKey.currentState?.showSnackBar(
|
||||
SnackBar(content: Text('No se pudo crear el vault local: $error')),
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isUnlocking = false;
|
||||
_isBootstrapping = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showAccountPlaceholder(String actionLabel) {
|
||||
_scaffoldMessengerKey.currentState?.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'$actionLabel todavía no está conectado con la API. Usa "Entrar sin cuenta" para empezar en local.',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _saveWindowSize() async {
|
||||
if (await windowManager.isFullScreen()) {
|
||||
return;
|
||||
@@ -42,6 +168,45 @@ class _NotesAppState extends State<NotesApp> with WindowListener {
|
||||
await WindowStateStore.instance.saveWindowSize(currentSize);
|
||||
}
|
||||
|
||||
Widget _buildLoadingScreen() {
|
||||
return MaterialApp(
|
||||
title: 'Mis Notas',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: AppTheme.theme,
|
||||
home: const Scaffold(
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
AppTitleBar(),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 16),
|
||||
Text('Preparando el vault local...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppShell({required Widget home}) {
|
||||
return MaterialApp(
|
||||
title: 'Mis Notas',
|
||||
debugShowCheckedModeBanner: false,
|
||||
scaffoldMessengerKey: _scaffoldMessengerKey,
|
||||
theme: AppTheme.theme,
|
||||
home: home,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowResize() {
|
||||
_saveWindowSize();
|
||||
@@ -52,12 +217,34 @@ class _NotesAppState extends State<NotesApp> with WindowListener {
|
||||
_saveWindowSize();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Mis Notas',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: AppTheme.theme,
|
||||
home: const HomeScreen(),
|
||||
if (_isBootstrapping) {
|
||||
return _buildLoadingScreen();
|
||||
}
|
||||
|
||||
final NoteRepository? repository = _repository;
|
||||
|
||||
if (repository != null) {
|
||||
return _buildAppShell(
|
||||
home: HomeScreen(
|
||||
repository: repository,
|
||||
onDeleteAllData: _resetLocalVaultData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildAppShell(
|
||||
home: VaultAccessScreen(
|
||||
isBusy: _isUnlocking,
|
||||
onCreateAccountPressed: (String email, String password) async {
|
||||
_showAccountPlaceholder('Crear cuenta');
|
||||
},
|
||||
onSignInPressed: (String email, String password) async {
|
||||
_showAccountPlaceholder('Iniciar sesión');
|
||||
},
|
||||
onContinueWithoutAccount: _enterWithoutAccount,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user