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