main
  1import 'dart:convert';
  2import 'dart:io';
  3
  4import 'package:archive/archive_io.dart';
  5import 'package:blood_pressure_app/model/storage/db/settings_loader.dart';
  6import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
  7import 'package:blood_pressure_app/model/storage/export_csv_settings_store.dart';
  8import 'package:blood_pressure_app/model/storage/export_pdf_settings_store.dart';
  9import 'package:blood_pressure_app/model/storage/export_settings_store.dart';
 10import 'package:blood_pressure_app/model/storage/export_xsl_settings_store.dart';
 11import 'package:blood_pressure_app/model/storage/interval_store.dart';
 12import 'package:blood_pressure_app/model/storage/settings_store.dart';
 13import 'package:flutter/widgets.dart';
 14import 'package:path/path.dart';
 15import 'package:sqflite/sqflite.dart';
 16
 17/// Store settings in a directory format on disk.
 18class FileSettingsLoader implements SettingsLoader {
 19  FileSettingsLoader._create(this._path);
 20
 21  /// Creates setting loader from relative directory [path] or the default
 22  /// settings path.
 23  static Future<FileSettingsLoader> load([String? path]) async {
 24    path ??= join(await getDatabasesPath(), 'settings');
 25    Directory(path).createSync(recursive: true);
 26    return FileSettingsLoader._create(path);
 27  }
 28
 29  final String _path;
 30
 31  /// Instantiates a settings file relative to [_path] and writes changes.
 32  ///
 33  /// If the is read successfully [build] is called else [createNew] is called.
 34  T _loadFile<T extends ChangeNotifier>(
 35    String fileName,
 36    T Function(String) build,
 37    T Function() createNew,
 38    String Function(T) serialize,
 39  ) {
 40    final f = File(join(_path, fileName));
 41    T? obj;
 42    try {
 43      obj = build(f.readAsStringSync());
 44    } on FileSystemException {}
 45    obj ??= createNew();
 46
 47    obj.addListener(() => f.writeAsStringSync(serialize(obj!)));
 48    f.writeAsStringSync(serialize(obj));
 49    return obj;
 50  }
 51
 52  @override
 53  Future<CsvExportSettings> loadCsvExportSettings() async => _loadFile(
 54    'csv-export',
 55    CsvExportSettings.fromJson,
 56    CsvExportSettings.new,
 57    (e) => e.toJson(),
 58  );
 59
 60  @override
 61  Future<ExportColumnsManager> loadExportColumnsManager() async => _loadFile(
 62    'export-columns',
 63    ExportColumnsManager.fromJson,
 64    ExportColumnsManager.new,
 65    (e) => e.toJson(),
 66  );
 67
 68  @override
 69  Future<ExportSettings> loadExportSettings() async => _loadFile(
 70    'export',
 71    ExportSettings.fromJson,
 72    ExportSettings.new,
 73    (e) => e.toJson(),
 74  );
 75
 76  @override
 77  Future<IntervalStoreManager> loadIntervalStorageManager() async => _loadFile(
 78    'intervall-store',
 79    (String jsonStr) {
 80      final json = jsonDecode(jsonStr);
 81      if (json is Map<String, dynamic>) {
 82        return IntervalStoreManager(
 83          json['main'] is! String ? IntervalStorage() : IntervalStorage.fromJson(json['main']!),
 84          json['export'] is! String ? IntervalStorage() : IntervalStorage.fromJson(json['export']!),
 85          json['stats'] is! String ? IntervalStorage() : IntervalStorage.fromJson(json['stats']!),
 86        );
 87      }
 88      return IntervalStoreManager(IntervalStorage(), IntervalStorage(), IntervalStorage());
 89    },
 90    () => IntervalStoreManager(IntervalStorage(), IntervalStorage(), IntervalStorage()),
 91    (e) => jsonEncode({
 92      'main': e.mainPage.toJson(),
 93      'export': e.exportPage.toJson(),
 94      'stats': e.statsPage.toJson(),
 95    }),
 96  );
 97
 98  @override
 99  Future<PdfExportSettings> loadPdfExportSettings() async => _loadFile(
100    'pdf-export',
101    PdfExportSettings.fromJson,
102    PdfExportSettings.new,
103    (e) => e.toJson(),
104  );
105
106  @override
107  Future<ExcelExportSettings> loadXslExportSettings() async => _loadFile(
108    'xsl-export',
109    ExcelExportSettings.fromJson,
110    ExcelExportSettings.new,
111    (e) => e.toJson(),
112  );
113
114  @override
115  Future<Settings> loadSettings() async => _loadFile(
116    'general',
117    Settings.fromJson,
118    Settings.new,
119    (e) => e.toJson(),
120  );
121
122  /// Attempt to backup all stored data to archive.
123  Archive? createArchive() {
124    try {
125      final archive = Archive();
126      _backupFile(archive, 'general');
127      _backupFile(archive, 'export');
128      _backupFile(archive, 'csv-export');
129      _backupFile(archive, 'pdf-export');
130      _backupFile(archive, 'export-columns');
131      _backupFile(archive, 'intervall-store');
132      return archive;
133    } on FileSystemException {
134      return null;
135    }
136  }
137
138  void _backupFile(Archive archive, String fileName) {
139    final data = File(join(_path, fileName)).readAsStringSync();
140    archive.addFile(ArchiveFile.string(fileName, data));
141  }
142}