main
  1import 'dart:collection';
  2import 'dart:convert';
  3
  4import 'package:blood_pressure_app/model/export_import/column.dart';
  5import 'package:flutter/material.dart';
  6
  7/// Class for managing columns available to the user.
  8class ExportColumnsManager extends ChangeNotifier {
  9  /// Create a instance from a map created by [toMap].
 10  factory ExportColumnsManager.fromMap(Map<String, dynamic> map) {
 11    final List<dynamic> jsonUserColumns = map['userColumns'];
 12    final manager = ExportColumnsManager();
 13    for (final Map<String, dynamic> c in jsonUserColumns) {
 14      switch (c['t']) {
 15        case 0:
 16          manager.addOrUpdate(UserColumn.explicit(c['id'], c['csvTitle'], c['formatString']));
 17        case 1:
 18          manager.addOrUpdate(TimeColumn.explicit(c['id'], c['csvTitle'], c['formatPattern']));
 19        default:
 20          assert(false, 'Unexpected column type ${c['t']}.');
 21      }
 22    }
 23    return manager;
 24  }
 25
 26  /// Create a instance from a [String] created by [toJson].
 27  factory ExportColumnsManager.fromJson(String jsonString) {
 28    try {
 29      return ExportColumnsManager.fromMap(jsonDecode(jsonString));
 30    } catch (e) {
 31      assert(e is FormatException || e is TypeError);
 32      return ExportColumnsManager();
 33    }
 34  }
 35  /// Create a new manager for export columns.
 36  ///
 37  /// It will be filled with the default columns but won't contain initial user columns.
 38  ExportColumnsManager();
 39
 40  /// Reset all fields to their default values.
 41  void reset() {
 42    _userColumns.clear();
 43    notifyListeners();
 44  }
 45  void copyFrom(ExportColumnsManager other) {
 46    _userColumns.clear();
 47    _userColumns.addAll(other._userColumns);
 48    notifyListeners();
 49  }
 50
 51  /// Namespaces that may not lead a user columns internal identifier.
 52  static const List<String> reservedNamespaces = ['buildIn', 'myHeart'];
 53
 54  /// Map between all [ExportColumn.internalIdentifier]s and [ExportColumn]s
 55  /// added by a user.
 56  final Map<String, ExportColumn> _userColumns = {};
 57
 58  /// View of map between all [ExportColumn.internalIdentifier]s and columns
 59  /// added by a user.
 60  UnmodifiableMapView<String, ExportColumn> get userColumns => UnmodifiableMapView(_userColumns);
 61
 62  /// Tries to save the column to the map with the [ExportColumn.internalIdentifier]
 63  /// key.
 64  ///
 65  /// This method fails and returns false when there is a default [ExportColumn] with the same internal name is
 66  /// available.
 67  bool addOrUpdate(ExportColumn column) {
 68    if (reservedNamespaces.any((element) => column.internalIdentifier.startsWith(element))) return false;
 69    _userColumns[column.internalIdentifier] = column;
 70    notifyListeners();
 71    return true;
 72  }
 73
 74  /// Deletes a [ExportColumn] the user added.
 75  ///
 76  /// Calling this with the [ExportColumnI.internalIdentifier] of build-in columns
 77  /// or undefined columns will have no effect.
 78  void deleteUserColumn(String identifier) {
 79    assert(_userColumns.containsKey(identifier), "Don't call deleteUserColumn for non-existent or non-user columns");
 80    _userColumns.remove(identifier);
 81    notifyListeners();
 82  }
 83
 84  /// Get any defined column (user or build in) by identifier.
 85  ExportColumn? getColumn(String identifier) => 
 86      firstWhere((c) => c.internalIdentifier == identifier);
 87
 88  /// Get the first of column that satisfies [test].
 89  /// 
 90  /// Checks in the order: 
 91  /// 1. userColumns 
 92  /// 2. NativeColumn 
 93  /// 3. BuildInColumn
 94  ExportColumn? firstWhere(bool Function(ExportColumn) test) =>
 95    userColumns.values.where(test).firstOrNull
 96      ?? NativeColumn.allColumns.where(test).firstOrNull
 97      ?? BuildInColumn.allColumns.where(test).firstOrNull;
 98      // ?? ...
 99
100  /// Returns a list of all userColumns, NativeColumns and BuildInColumns defined.
101  ///
102  /// Prefer using other methods like [firstWhere] when possible.
103  UnmodifiableListView<ExportColumn> getAllColumns() {
104    final columns = <ExportColumn>[];
105    columns.addAll(NativeColumn.allColumns);
106    columns.addAll(userColumns.values);
107    columns.addAll(BuildInColumn.allColumns);
108    return UnmodifiableListView(columns);
109  }
110
111  /// Returns a list of all NativeColumns and BuildInColumns defined.
112  UnmodifiableListView<ExportColumn> getAllUnmodifiableColumns() {
113    final columns = <ExportColumn>[];
114    columns.addAll(NativeColumn.allColumns);
115    columns.addAll(BuildInColumn.allColumns);
116    return UnmodifiableListView(columns);
117  }
118
119  /// Serialize the object to a restoreable map.
120  Map<String, dynamic> toMap() {
121    final columns = [];
122    for (final c in _userColumns.values) {
123      switch (c) {
124        case UserColumn():
125          columns.add({
126            't': 0, // class type
127            'id': c.internalIdentifier,
128            'csvTitle': c.csvTitle,
129            'formatString': c.formatPattern,
130          });
131          break;
132        case BuildInColumn():
133        case NativeColumn():
134          assert(false, 'User is currently not able to create these columns.');
135          break;
136        case TimeColumn():
137          columns.add({
138            't': 1, // class type
139            'id': c.internalIdentifier,
140            'csvTitle': c.csvTitle,
141            'formatPattern': c.formatPattern,
142          });
143          break;
144      }
145    }
146    return {
147      'userColumns': columns,
148    };
149  }
150
151  /// Serialize the object to a restoreable string.
152  String toJson() => jsonEncode(toMap());
153}
154