Line data Source code
1 : import 'dart:math'; 2 : 3 : import 'package:event_db/event_db.dart'; 4 : import 'package:tuple/tuple.dart'; 5 : 6 : /// A [GenericModel] that has an [ordinal] parameter. When used with a 7 : /// [ReorderableMap], by default greater ordinal values should be shown first. 8 : /// 9 : /// This is to make it convenient to add a custom sort feature. 10 : mixin OrdereableModel on GenericModel { 11 : /// Compared with other instances of [OrdereableModel] to set the custom sort. 12 : int get ordinal; 13 : set ordinal(int value); 14 : 15 : /// Add this to your implementation of the 16 : /// [getGetterSetterMap] function of [GenericModel] 17 0 : Tuple2<Getter<dynamic>, Setter<dynamic>> get ordinalGetterSetter => 18 4 : Tuple2(() => ordinal, (val) => ordinal = val as int? ?? 0); 19 : } 20 : 21 : /// Records a movement of an [OrdereableModel]. 22 : class ListMovement<T extends OrdereableModel> { 23 : /// Records a movement of an [OrdereableModel] 24 0 : ListMovement(this.moved, this.to); 25 : 26 : /// The [OrdereableModel] that was moved. 27 : final T moved; 28 : 29 : /// The location in the list that [moved] was moved to. 30 : final int to; 31 : } 32 : 33 : /// Adds functions to [GenericModelMap] specifically for [OrdereableModel] 34 : /// 35 : /// This is to make it more convenient to work with [OrdereableModel]s 36 : extension ReorderableMap<T extends OrdereableModel> on GenericModelMap<T> { 37 : /// Automatically sets the ordinal of the given [model] based on the existing 38 : /// models in [map] 39 1 : T setOrdinalOfNewEntry(T model) { 40 4 : return model..ordinal = map.entries.length; 41 : } 42 : 43 : /// Returns a [List] of keys sorted based on the ordinal value of the 44 : /// [OrdereableModel]s in [map] 45 4 : List<String> get defaultOrderedKeyList => map.keys.toList() 46 9 : ..sort((a, b) => map[b]!.ordinal.compareTo(map[a]!.ordinal)); 47 : 48 : /// Returns a [List] of [T] sorted based on the ordinal value of the 49 : /// [OrdereableModel]s in [map] 50 1 : List<T> get defaultOrderedList => 51 8 : map.values.toList()..sort((a, b) => b.ordinal.compareTo(a.ordinal)); 52 : 53 : /// Reorders the [model] to have the [newOrdinal] value for ordinal 54 : /// 55 : /// This will automatically change the ordinal value for the other [T] in 56 : /// [map]. This will also automatically update [repository] 57 1 : Future<Iterable<T>> reorder( 58 : T model, 59 : int newOrdinal, { 60 : String? databaseName, 61 : }) async { 62 1 : final list = defaultOrderedKeyList; 63 : 64 2 : final checkedOrdinal = min(newOrdinal, list.length); 65 2 : final initialOrdinal = list.indexOf(model.id!); 66 : 67 1 : if (checkedOrdinal == initialOrdinal) { 68 1 : return []; 69 : } 70 : 71 : final updatedModels = <T>{}; 72 : 73 3 : for (var i = 0; i < list.length; i++) { 74 1 : final pastOldIndex = i >= initialOrdinal; 75 1 : final pastNewIndex = i >= checkedOrdinal; 76 : 77 : if (!pastOldIndex && !pastNewIndex) { 78 : continue; 79 : } 80 : if (pastNewIndex && pastOldIndex) { 81 : break; 82 : } 83 3 : final updatedModel = map[list[i]]!; 84 1 : updatedModels.add(updatedModel); 85 : if (pastNewIndex) { 86 4 : updatedModel.ordinal = list.length - i - 2; 87 : } 88 : if (pastOldIndex) { 89 3 : updatedModel.ordinal = list.length - i; 90 : } 91 : } 92 1 : updatedModels.add(model); 93 3 : model.ordinal = list.length - 94 1 : checkedOrdinal - 95 1 : (checkedOrdinal > initialOrdinal ? 0 : 1); 96 : 97 1 : return Future.wait( 98 3 : updatedModels.map((e) => updateModel(e, databaseName: databaseName)), 99 : ); 100 : } 101 : }