test fix, tuple record migration wip
This commit is contained in:
parent
7afbdfaa84
commit
1117da068b
13 changed files with 61 additions and 63 deletions
|
@ -13,7 +13,6 @@ import 'package:collection/collection.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
final Covers covers = Covers._private();
|
||||
|
||||
|
@ -40,11 +39,11 @@ class Covers {
|
|||
|
||||
Set<CoverRow> get all => Set.unmodifiable(_rows);
|
||||
|
||||
Tuple3<int?, String?, Color?>? of(CollectionFilter filter) {
|
||||
(int? entryId, String? packageName, Color? color)? of(CollectionFilter filter) {
|
||||
if (filter is AlbumFilter && vaults.isLocked(filter.album)) return null;
|
||||
|
||||
final row = _rows.firstWhereOrNull((row) => row.filter == filter);
|
||||
return row != null ? Tuple3(row.entryId, row.packageName, row.color) : null;
|
||||
return row != null ? (row.entryId, row.packageName, row.color) : null;
|
||||
}
|
||||
|
||||
Future<void> set({
|
||||
|
@ -113,7 +112,7 @@ class Covers {
|
|||
}
|
||||
|
||||
AlbumType effectiveAlbumType(String albumPath) {
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.item2;
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.$2;
|
||||
if (filterPackage != null) {
|
||||
return filterPackage.isEmpty ? AlbumType.regular : AlbumType.app;
|
||||
} else {
|
||||
|
@ -122,7 +121,7 @@ class Covers {
|
|||
}
|
||||
|
||||
String? effectiveAlbumPackage(String albumPath) {
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.item2;
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.$2;
|
||||
return filterPackage ?? appInventory.getAlbumAppPackageName(albumPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ class AlbumFilter extends CoveredCollectionFilter {
|
|||
@override
|
||||
Future<Color> color(BuildContext context) {
|
||||
// custom color has precedence over others, even custom app color
|
||||
final customColor = covers.of(this)?.item3;
|
||||
final customColor = covers.of(this)?.$3;
|
||||
if (customColor != null) return SynchronousFuture(customColor);
|
||||
|
||||
final colors = context.read<AvesColorsData>();
|
||||
|
|
|
@ -157,7 +157,7 @@ abstract class CoveredCollectionFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
Future<Color> color(BuildContext context) {
|
||||
final customColor = covers.of(this)?.item3;
|
||||
final customColor = covers.of(this)?.$3;
|
||||
if (customColor != null) {
|
||||
return SynchronousFuture(customColor);
|
||||
}
|
||||
|
|
|
@ -274,9 +274,9 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
final existingCover = covers.of(oldFilter);
|
||||
await covers.set(
|
||||
filter: newFilter,
|
||||
entryId: existingCover?.item1,
|
||||
packageName: existingCover?.item2,
|
||||
color: existingCover?.item3,
|
||||
entryId: existingCover?.$1,
|
||||
packageName: existingCover?.$2,
|
||||
color: existingCover?.$3,
|
||||
);
|
||||
|
||||
renameNewAlbum(sourceAlbum, destinationAlbum);
|
||||
|
@ -547,7 +547,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
}
|
||||
|
||||
AvesEntry? coverEntry(CollectionFilter filter) {
|
||||
final id = covers.of(filter)?.item1;
|
||||
final id = covers.of(filter)?.$1;
|
||||
if (id != null) {
|
||||
final entry = visibleEntries.firstWhereOrNull((entry) => entry.id == id);
|
||||
if (entry != null) return entry;
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:aves/services/common/services.dart';
|
|||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
mixin LocationMixin on CountryMixin, StateMixin {
|
||||
static const commitCountThreshold = 200;
|
||||
|
@ -96,16 +95,16 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
// - 652 calls (22%) when approximating to 2 decimal places (~1km - town or village)
|
||||
// cf https://en.wikipedia.org/wiki/Decimal_degrees#Precision
|
||||
final latLngFactor = pow(10, 2);
|
||||
Tuple2<int, int> approximateLatLng(AvesEntry entry) {
|
||||
(int latitude, int longitude) approximateLatLng(AvesEntry entry) {
|
||||
// entry has coordinates
|
||||
final catalogMetadata = entry.catalogMetadata!;
|
||||
final lat = catalogMetadata.latitude!;
|
||||
final lng = catalogMetadata.longitude!;
|
||||
return Tuple2<int, int>((lat * latLngFactor).round(), (lng * latLngFactor).round());
|
||||
return ((lat * latLngFactor).round(), (lng * latLngFactor).round());
|
||||
}
|
||||
|
||||
final located = visibleEntries.where((entry) => entry.hasGps).toSet().difference(todo);
|
||||
final knownLocations = <Tuple2<int, int>, AddressDetails?>{};
|
||||
final knownLocations = <(int, int), AddressDetails?>{};
|
||||
located.forEach((entry) {
|
||||
knownLocations.putIfAbsent(approximateLatLng(entry), () => entry.addressDetails);
|
||||
});
|
||||
|
|
|
@ -3,13 +3,12 @@ import 'dart:collection';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
final ServicePolicy servicePolicy = ServicePolicy._private();
|
||||
|
||||
class ServicePolicy {
|
||||
final StreamController<QueueState> _queueStreamController = StreamController.broadcast();
|
||||
final Map<Object, Tuple2<int, _Task>> _paused = {};
|
||||
final Map<Object, (int, _Task)> _paused = {};
|
||||
final SplayTreeMap<int, LinkedHashMap<Object, _Task>> _queues = SplayTreeMap();
|
||||
final LinkedHashMap<Object, _Task> _runningQueue = LinkedHashMap();
|
||||
|
||||
|
@ -30,8 +29,8 @@ class ServicePolicy {
|
|||
key ??= platformCall.hashCode;
|
||||
final toResume = _paused.remove(key);
|
||||
if (toResume != null) {
|
||||
priority = toResume.item1;
|
||||
task = toResume.item2 as _Task<T>;
|
||||
priority = toResume.$1;
|
||||
task = toResume.$2 as _Task<T>;
|
||||
completer = task.completer;
|
||||
} else {
|
||||
completer = Completer<T>();
|
||||
|
@ -56,8 +55,8 @@ class ServicePolicy {
|
|||
Future<T>? resume<T>(Object key) {
|
||||
final toResume = _paused.remove(key);
|
||||
if (toResume != null) {
|
||||
final priority = toResume.item1;
|
||||
final task = toResume.item2 as _Task<T>;
|
||||
final priority = toResume.$1;
|
||||
final task = toResume.$2 as _Task<T>;
|
||||
_getQueue(priority)[key] = task;
|
||||
_pickNext();
|
||||
return task.completer.future;
|
||||
|
@ -97,7 +96,7 @@ class ServicePolicy {
|
|||
}
|
||||
|
||||
bool pause(Object key, Iterable<int> priorities) {
|
||||
return _takeOut(key, priorities, (priority, task) => _paused.putIfAbsent(key, () => Tuple2(priority, task)));
|
||||
return _takeOut(key, priorities, (priority, task) => _paused.putIfAbsent(key, () => (priority, task)));
|
||||
}
|
||||
|
||||
bool isPaused(Object key) => _paused.containsKey(key);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
int highestPowerOf2(num x) => x < 1 ? 0 : pow(2, (log(x) / ln2).floor()).toInt();
|
||||
|
||||
int smallestPowerOf2(num x) => x < 1 ? 1 : pow(2, (log(x) / ln2).ceil()).toInt();
|
||||
|
@ -10,16 +8,16 @@ int smallestPowerOf2(num x) => x < 1 ? 1 : pow(2, (log(x) / ln2).ceil()).toInt()
|
|||
double roundToPrecision(final double value, {required final int decimals}) => (value * pow(10, decimals)).round() / pow(10, decimals);
|
||||
|
||||
// cf https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
|
||||
Offset? segmentIntersection(Tuple2<Offset, Offset> s1, Tuple2<Offset, Offset> s2) {
|
||||
final x1 = s1.item1.dx;
|
||||
final y1 = s1.item1.dy;
|
||||
final x2 = s1.item2.dx;
|
||||
final y2 = s1.item2.dy;
|
||||
Offset? segmentIntersection((Offset, Offset) s1, (Offset, Offset) s2) {
|
||||
final x1 = s1.$1.dx;
|
||||
final y1 = s1.$1.dy;
|
||||
final x2 = s1.$2.dx;
|
||||
final y2 = s1.$2.dy;
|
||||
|
||||
final x3 = s2.item1.dx;
|
||||
final y3 = s2.item1.dy;
|
||||
final x4 = s2.item2.dx;
|
||||
final y4 = s2.item2.dy;
|
||||
final x3 = s2.$1.dx;
|
||||
final y3 = s2.$1.dy;
|
||||
final x4 = s2.$2.dx;
|
||||
final y4 = s2.$2.dy;
|
||||
|
||||
final a1 = x2 - x1;
|
||||
final b1 = -(x4 - x3);
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'dart:math';
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class TileExtentController {
|
||||
final String settingsRouteKey;
|
||||
|
@ -102,7 +101,7 @@ class TileExtentController {
|
|||
|
||||
double get effectiveExtentMax => _extentForColumnCount(_effectiveColumnCountMin());
|
||||
|
||||
Tuple2<int, int> get effectiveColumnRange => Tuple2(_effectiveColumnCountMin(), _effectiveColumnCountMax());
|
||||
(int min, int max) get effectiveColumnRange => (_effectiveColumnCountMin(), _effectiveColumnCountMax());
|
||||
|
||||
int get columnCount => _effectiveColumnCountForExtent(extentNotifier.value);
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class _AddShortcutDialogState extends State<AddShortcutDialog> {
|
|||
if (_collection != null) {
|
||||
final entries = _collection.sortedEntries;
|
||||
if (entries.isNotEmpty) {
|
||||
final coverEntries = _collection.filters.map((filter) => covers.of(filter)?.item1).whereNotNull().map((id) => entries.firstWhereOrNull((entry) => entry.id == id)).whereNotNull();
|
||||
final coverEntries = _collection.filters.map((filter) => covers.of(filter)?.$1).whereNotNull().map((id) => entries.firstWhereOrNull((entry) => entry.id == id)).whereNotNull();
|
||||
_coverEntry = coverEntries.firstOrNull ?? entries.first;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,8 +70,8 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
|
|||
|
||||
final extentController = tileExtentController;
|
||||
final columnRange = extentController.effectiveColumnRange;
|
||||
_columnMin = columnRange.item1;
|
||||
_columnMax = columnRange.item2;
|
||||
_columnMin = columnRange.$1;
|
||||
_columnMax = columnRange.$2;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -326,15 +326,15 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
|||
if (!await unlockFilter(context, filter)) return;
|
||||
|
||||
final existingCover = covers.of(filter);
|
||||
final entryId = existingCover?.item1;
|
||||
final entryId = existingCover?.$1;
|
||||
final customEntry = entryId != null ? context.read<CollectionSource>().visibleEntries.firstWhereOrNull((entry) => entry.id == entryId) : null;
|
||||
final selectedCover = await showDialog<Tuple3<AvesEntry?, String?, Color?>>(
|
||||
context: context,
|
||||
builder: (context) => CoverSelectionDialog(
|
||||
filter: filter,
|
||||
customEntry: customEntry,
|
||||
customPackage: existingCover?.item2,
|
||||
customColor: existingCover?.item3,
|
||||
customPackage: existingCover?.$2,
|
||||
customColor: existingCover?.$3,
|
||||
),
|
||||
routeSettings: const RouteSettings(name: CoverSelectionDialog.routeName),
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/l10n/l10n.dart';
|
||||
import 'package:aves/model/availability.dart';
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
|
@ -201,7 +202,7 @@ void main() {
|
|||
|
||||
await covers.set(filter: albumFilter, entryId: image1.id, packageName: null, color: null);
|
||||
expect(covers.count, 1);
|
||||
expect(covers.of(albumFilter)?.item1, image1.id);
|
||||
expect(covers.of(albumFilter)?.$1, image1.id);
|
||||
|
||||
await covers.set(filter: albumFilter, entryId: null, packageName: null, color: null);
|
||||
expect(covers.count, 0);
|
||||
|
@ -229,7 +230,7 @@ void main() {
|
|||
expect(favourites.count, 1);
|
||||
expect(image1.isFavourite, true);
|
||||
expect(covers.count, 1);
|
||||
expect(covers.of(albumFilter)?.item1, image1.id);
|
||||
expect(covers.of(albumFilter)?.$1, image1.id);
|
||||
});
|
||||
|
||||
test('favourites and covers are cleared when removing entries', () async {
|
||||
|
@ -348,7 +349,7 @@ void main() {
|
|||
expect(favourites.count, 1);
|
||||
expect(image1.isFavourite, true);
|
||||
expect(covers.count, 1);
|
||||
expect(covers.of(albumFilter)?.item1, image1.id);
|
||||
expect(covers.of(albumFilter)?.$1, image1.id);
|
||||
});
|
||||
|
||||
testWidgets('unique album names', (tester) async {
|
||||
|
@ -369,22 +370,26 @@ void main() {
|
|||
|
||||
final source = await _initSource();
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (context) {
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Elea/Zeno'), 'Elea/Zeno');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Citium/Zeno'), 'Citium/Zeno');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Cleanthes'), 'Cleanthes');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Chrysippus'), 'Chrysippus');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Chrysippus'), 'Chrysippus (${FakeStorageService.removableDescription})');
|
||||
expect(source.getAlbumDisplayName(context, FakeStorageService.primaryRootAlbum), FakeStorageService.primaryDescription);
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Seneca'), 'Pictures/Seneca');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Seneca'), 'Seneca');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Cicero'), 'Cicero');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Marcus Aurelius'), 'Marcus Aurelius');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Hannah Arendt'), 'Hannah Arendt');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Arendt'), 'Arendt');
|
||||
return const Placeholder();
|
||||
},
|
||||
Localizations(
|
||||
locale: AppLocalizations.supportedLocales.first,
|
||||
delegates: AppLocalizations.localizationsDelegates,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Elea/Zeno'), 'Elea/Zeno');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Citium/Zeno'), 'Citium/Zeno');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Cleanthes'), 'Cleanthes');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Chrysippus'), 'Chrysippus');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Chrysippus'), 'Chrysippus (${FakeStorageService.removableDescription})');
|
||||
expect(source.getAlbumDisplayName(context, FakeStorageService.primaryRootAlbum), FakeStorageService.primaryDescription);
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Seneca'), 'Pictures/Seneca');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Seneca'), 'Seneca');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Cicero'), 'Cicero');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Marcus Aurelius'), 'Marcus Aurelius');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Hannah Arendt'), 'Hannah Arendt');
|
||||
expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Arendt'), 'Arendt');
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:ui';
|
|||
|
||||
import 'package:aves/utils/math_utils.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
void main() {
|
||||
test('highest power of 2 that is smaller than or equal to the number', () {
|
||||
|
@ -29,8 +28,8 @@ void main() {
|
|||
});
|
||||
|
||||
test('segment intersection', () {
|
||||
const s1 = Tuple2(Offset(1, 1), Offset(3, 2));
|
||||
const s2 = Tuple2(Offset(1, 4), Offset(2, -1));
|
||||
const s1 = (Offset(1, 1), Offset(3, 2));
|
||||
const s2 = (Offset(1, 4), Offset(2, -1));
|
||||
expect(segmentIntersection(s1, s2), const Offset(17 / 11, 14 / 11));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue