main
  1import 'dart:convert';
  2
  3import 'package:blood_pressure_app/model/export_import/column.dart';
  4import 'package:blood_pressure_app/model/storage/convert_util.dart';
  5import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
  6import 'package:collection/collection.dart';
  7import 'package:flutter/widgets.dart';
  8import 'package:blood_pressure_app/l10n/app_localizations.dart';
  9
 10/// Class for managing columns currently used for ex- and import.
 11class ActiveExportColumnConfiguration extends ChangeNotifier {
 12  /// Create a manager of the currently relevant [ExportColumn]s.
 13  ///
 14  /// The default configuration is guaranteed to be restoreable.
 15  ActiveExportColumnConfiguration({
 16    ExportImportPreset? activePreset,
 17    List<String>? userSelectedColumnIds,
 18  }) :
 19      _activePreset = activePreset ?? ExportImportPreset.bloodPressureApp,
 20      _userSelectedColumns = userSelectedColumnIds ?? [];
 21
 22  /// Create a instance from a [String] created by [toJson].
 23  factory ActiveExportColumnConfiguration.fromJson(String jsonString) {
 24    try {
 25      final json = jsonDecode(jsonString);
 26      return ActiveExportColumnConfiguration(
 27          activePreset: ExportImportPreset.decode(json['preset']) ?? ExportImportPreset.bloodPressureApp,
 28          userSelectedColumnIds: ConvertUtil.parseList<String>(json['columns']),
 29      );
 30    } on FormatException {
 31      return ActiveExportColumnConfiguration();
 32    }
 33
 34  }
 35
 36  /// Serialize the object to a restoreable string.
 37  String toJson() => jsonEncode({
 38    'columns': _userSelectedColumns,
 39    'preset': _activePreset.encode(),
 40  });
 41
 42  /// The [UserColumn.internalIdentifier] of columns currently selected by user.
 43  ///
 44  /// Note that this is persistent different from [getActiveColumns].
 45  final List<String> _userSelectedColumns;
 46
 47  ExportImportPreset _activePreset;
 48
 49  /// The current selection on what set of values will be exported.
 50  ExportImportPreset get activePreset => _activePreset;
 51
 52  set activePreset(ExportImportPreset value) {
 53    _activePreset = value;
 54    notifyListeners();
 55  }
 56
 57  /// Put the user column at [oldIndex] to [newIndex].
 58  void reorderUserColumns(int oldIndex, int newIndex) {
 59    assert(_activePreset == ExportImportPreset.none, 'user columns are not modifiable while another configuration is active');
 60    assert(oldIndex >= 0 && oldIndex < _userSelectedColumns.length);
 61    assert(newIndex >= 0 && newIndex < _userSelectedColumns.length);
 62    if (oldIndex < newIndex) {
 63      newIndex -= 1;
 64    }
 65    final item = _userSelectedColumns.removeAt(oldIndex);
 66    _userSelectedColumns.insert(newIndex, item);
 67    notifyListeners();
 68  }
 69
 70  /// Add a export column to the end of user columns.
 71  void addUserColumn(ExportColumn column) {
 72    assert(_activePreset == ExportImportPreset.none, 'user columns are not modifiable while another configuration is active');
 73    _userSelectedColumns.add(column.internalIdentifier);
 74    notifyListeners();
 75  }
 76
 77  /// Removes the first export column from user columns where
 78  /// [ExportColumn.internalIdentifier] matches [identifier].
 79  void removeUserColumn(String identifier) {
 80    assert(_activePreset == ExportImportPreset.none, 'user columns are not modifiable while another configuration is active');
 81    _userSelectedColumns.removeWhere((c) => c == identifier);
 82    notifyListeners();
 83  }
 84
 85  /// Columns to respect for export.
 86  UnmodifiableListView<ExportColumn> getActiveColumns(ExportColumnsManager availableColumns) => UnmodifiableListView(
 87    switch (_activePreset) {
 88      ExportImportPreset.none => _userSelectedColumns.map((e) => availableColumns.getColumn(e)).whereNotNull(),
 89      ExportImportPreset.bloodPressureApp => [
 90        NativeColumn.timestampUnixMs,
 91        NativeColumn.systolic,
 92        NativeColumn.diastolic,
 93        NativeColumn.pulse,
 94        NativeColumn.notes,
 95        NativeColumn.color,
 96        NativeColumn.intakes,
 97        NativeColumn.bodyweight,
 98      ],
 99      ExportImportPreset.myHeart => [
100        BuildInColumn.mhDate,
101        BuildInColumn.mhSys,
102        BuildInColumn.mhDia,
103        BuildInColumn.mhPul,
104        BuildInColumn.mhDesc,
105        BuildInColumn.mhTags,
106        BuildInColumn.mhWeight,
107        BuildInColumn.mhOxygen,
108      ],
109      ExportImportPreset.bloodPressureAppPdf => [
110        BuildInColumn.formattedTime,
111        NativeColumn.systolic,
112        NativeColumn.diastolic,
113        NativeColumn.pulse,
114      ]
115    },);
116}
117
118
119/// Common export configurations that have specific associated columns.
120enum ExportImportPreset {
121  /// Custom export configuration.
122  none,
123
124  /// Default preset, that ensures working exports and restoration.
125  ///
126  /// All [NativeColumn]s.
127  bloodPressureApp,
128
129  /// Default preset for pdf exports.
130  ///
131  /// Includes formatted time, sys, dia and pulse.
132  bloodPressureAppPdf,
133
134  /// Preset for exporting data to the myHeart app.
135  myHeart;
136
137  /// Selection of a displayable string from [localizations].
138  String localize(AppLocalizations localizations) => switch (this) {
139    ExportImportPreset.none => localizations.custom,
140    ExportImportPreset.bloodPressureApp => localizations.default_,
141    ExportImportPreset.bloodPressureAppPdf => localizations.pdf,
142    ExportImportPreset.myHeart => '"My Heart" export'
143  };
144
145  /// Turn the value into a [decode]able integer for serialization purposes.
146  int encode() => switch (this) {
147    ExportImportPreset.none => 0,
148    ExportImportPreset.bloodPressureApp => 1,
149    ExportImportPreset.myHeart => 2,
150    ExportImportPreset.bloodPressureAppPdf => 3,
151  };
152
153  /// Create a enum value form a number returned by [encode].
154  static ExportImportPreset? decode(Object? e) => switch(e) {
155      0 => ExportImportPreset.none,
156      1 => ExportImportPreset.bloodPressureApp,
157      2 => ExportImportPreset.myHeart,
158      3 => ExportImportPreset.bloodPressureAppPdf,
159      _ => (){
160        assert(e is! int, 'non ints can happen through bad user values, other ints can happen as well, but should developers should be notified.');
161        return null;
162      }(),
163    };
164}