diff --git a/app/lib/screens/settings_screen.dart b/app/lib/screens/settings_screen.dart index 9b99362..cb57571 100644 --- a/app/lib/screens/settings_screen.dart +++ b/app/lib/screens/settings_screen.dart @@ -2,6 +2,7 @@ // Écran de configuration — sync WebDAV NAS import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,6 +32,9 @@ class _SettingsScreenState extends State { Color _syncColor = Colors.green; Map _syncStatus = {}; Map _stats = {}; + Map _storageInfo = {}; + final _dataDirCtrl = TextEditingController(); + bool _dataDirSaved = false; // Sync auto bool _autoSync = false; @@ -41,6 +45,7 @@ class _SettingsScreenState extends State { super.initState(); _loadSettings(); _loadStats(); + _loadStorageInfo(); } Future _loadSettings() async { @@ -65,6 +70,38 @@ class _SettingsScreenState extends State { } catch (_) {} } + Future _loadStorageInfo() async { + final state = context.read(); + try { + final info = await state.client.getStorageInfo(); + final prefs = await SharedPreferences.getInstance(); + final savedDir = prefs.getString('data_dir') ?? info['data_dir'] ?? ''; + setState(() { + _storageInfo = info; + _dataDirCtrl.text = savedDir; + }); + } catch (_) {} + } + + Future _saveDataDir() async { + final prefs = await SharedPreferences.getInstance(); + final newDir = _dataDirCtrl.text.trim(); + if (newDir.isEmpty) return; + await prefs.setString('data_dir', newDir); + + // Écrire dans ~/.nexanote-config pour que nexanote.sh le lise au prochain démarrage + try { + final home = Platform.environment['HOME'] ?? '/tmp'; + final configFile = File('$home/.nexanote-config'); + await configFile.writeAsString('data_dir=$newDir\n'); + } catch (_) {} + + setState(() => _dataDirSaved = true); + Future.delayed(const Duration(seconds: 3), () { + if (mounted) setState(() => _dataDirSaved = false); + }); + } + Future _loadSyncStatus() async { final state = context.read(); try { @@ -129,6 +166,7 @@ class _SettingsScreenState extends State { _nasUrlCtrl.dispose(); _nasUserCtrl.dispose(); _nasPassCtrl.dispose(); + _dataDirCtrl.dispose(); super.dispose(); } @@ -349,6 +387,97 @@ class _SettingsScreenState extends State { const SizedBox(height: 24), + // ── Dossier de notes ─────────────────────────────────── + _SectionTitle(icon: Icons.folder_outlined, title: 'Dossier de notes'), + Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Dossier actuel (lu depuis l'API) + if (_storageInfo['data_dir'] != null) ...[ + Row(children: [ + Icon(Icons.folder, size: 16, color: const Color(0xFF6366F1)), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Actuel : ${_storageInfo['data_dir']}', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + fontFamily: 'monospace', + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (_storageInfo['db_size_mb'] != null) + Text( + '${_storageInfo['db_size_mb']} MB', + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.4), + ), + ), + ]), + const SizedBox(height: 12), + ], + + // Champ pour choisir un nouveau dossier + TextField( + controller: _dataDirCtrl, + decoration: const InputDecoration( + labelText: 'Dossier de données', + hintText: '/home/user/mes-notes', + prefixIcon: Icon(Icons.folder_open_outlined), + ), + ), + const SizedBox(height: 8), + + // Info redémarrage + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: const Color(0xFF6366F1).withOpacity(0.08), + borderRadius: BorderRadius.circular(8), + ), + child: Row(children: [ + const Icon(Icons.info_outline, size: 14, color: Color(0xFF6366F1)), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Le changement sera pris en compte au prochain démarrage via nexanote.sh', + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + ), + ]), + ), + const SizedBox(height: 12), + + // Bouton sauvegarder + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + icon: _dataDirSaved + ? const Icon(Icons.check_circle, color: Colors.green, size: 16) + : const Icon(Icons.save_outlined, size: 16), + label: Text(_dataDirSaved ? 'Sauvegardé !' : 'Sauvegarder le dossier'), + onPressed: _saveDataDir, + style: _dataDirSaved + ? OutlinedButton.styleFrom(foregroundColor: Colors.green) + : null, + ), + ), + ], + ), + ), + ), + + const SizedBox(height: 24), + // ── Backend API ──────────────────────────────────────── _SectionTitle(icon: Icons.api, title: 'Backend API'), Card( diff --git a/app/lib/services/api_client.dart b/app/lib/services/api_client.dart index 41ede74..3f4e4e0 100644 --- a/app/lib/services/api_client.dart +++ b/app/lib/services/api_client.dart @@ -305,4 +305,10 @@ class ApiClient { if (resp.statusCode == 200) return jsonDecode(resp.body); return {}; } + + Future> getStorageInfo() async { + final resp = await http.get(Uri.parse('$baseUrl/storage')); + if (resp.statusCode == 200) return jsonDecode(resp.body); + return {}; + } } diff --git a/nexanote.sh b/nexanote.sh index c5ac80d..a9ecc61 100644 --- a/nexanote.sh +++ b/nexanote.sh @@ -7,6 +7,16 @@ APP_DIR="$NEXANOTE_DIR/app" VENV_ACTIVATE="$NEXANOTE_DIR/venv/bin/activate" LOG_FILE="/tmp/nexanote_backend.log" BACKEND_PID="" +CONFIG_FILE="$HOME/.nexanote-config" + +# Lire le dossier de données depuis ~/.nexanote-config (si existant) +DATA_DIR="$HOME/.nexanote" +if [[ -f "$CONFIG_FILE" ]]; then + SAVED_DIR=$(grep -E '^data_dir=' "$CONFIG_FILE" | cut -d'=' -f2-) + if [[ -n "$SAVED_DIR" ]]; then + DATA_DIR="$SAVED_DIR" + fi +fi cleanup() { if [[ -n "${BACKEND_PID}" ]]; then @@ -17,6 +27,7 @@ trap cleanup EXIT if ! curl -s http://127.0.0.1:8766/health >/dev/null 2>&1; then echo "🚀 Démarrage du backend..." + echo "📂 Dossier de données : $DATA_DIR" if [[ -f "$VENV_ACTIVATE" ]]; then # shellcheck source=/dev/null source "$VENV_ACTIVATE" @@ -25,7 +36,7 @@ if ! curl -s http://127.0.0.1:8766/health >/dev/null 2>&1; then fi cd "$NEXANOTE_DIR" - python main.py >"$LOG_FILE" 2>&1 & + python main.py --data-dir "$DATA_DIR" >"$LOG_FILE" 2>&1 & BACKEND_PID=$! echo -n "⏳ Attente" diff --git a/nexanote/api/routes.py b/nexanote/api/routes.py index 5f7d704..5ec0d3c 100644 --- a/nexanote/api/routes.py +++ b/nexanote/api/routes.py @@ -549,7 +549,9 @@ def search(q: str = Query(..., min_length=1)): notes = db.list_notes(search_title=q) return [_note_to_schema(n) for n in notes] - return app + # ------------------------------------------------------------------ + # Stockage + # ------------------------------------------------------------------ @app.get("/storage") def get_storage_info(): @@ -561,3 +563,5 @@ def get_storage_info(): "db_path": str(db.db_path), "db_size_mb": round(db_size / 1024 / 1024, 2), } + + return app