mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Replace tuple with records
This commit is contained in:
parent
051c721d33
commit
1870830b39
34 changed files with 142 additions and 173 deletions
|
@ -19,7 +19,6 @@ import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:woozy_search/woozy_search.dart';
|
import 'package:woozy_search/woozy_search.dart';
|
||||||
|
|
||||||
part 'home_search_suggestion.g.dart';
|
part 'home_search_suggestion.g.dart';
|
||||||
|
@ -172,17 +171,17 @@ class HomeSearchSuggestionBloc
|
||||||
.map((e) {
|
.map((e) {
|
||||||
if (e.value!.toKeywords().any((k) => k.startsWith(ev.phrase))) {
|
if (e.value!.toKeywords().any((k) => k.startsWith(ev.phrase))) {
|
||||||
// prefer names that start exactly with the search phrase
|
// prefer names that start exactly with the search phrase
|
||||||
return Tuple2(e.score + 1, e.value);
|
return (score: e.score + 1, item: e.value);
|
||||||
} else {
|
} else {
|
||||||
return Tuple2(e.score, e.value);
|
return (score: e.score, item: e.value);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sorted((a, b) => b.item1.compareTo(a.item1))
|
.sorted((a, b) => b.score.compareTo(a.score))
|
||||||
.distinctIf(
|
.distinctIf(
|
||||||
(a, b) => identical(a.item2, b.item2),
|
(a, b) => identical(a.item, b.item),
|
||||||
(a) => a.item2.hashCode,
|
(a) => a.item.hashCode,
|
||||||
)
|
)
|
||||||
.map((e) => e.item2!.toResult())
|
.map((e) => e.item!.toResult())
|
||||||
.toList();
|
.toList();
|
||||||
emit(HomeSearchSuggestionBlocSuccess(matches));
|
emit(HomeSearchSuggestionBlocSuccess(matches));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:woozy_search/woozy_search.dart';
|
import 'package:woozy_search/woozy_search.dart';
|
||||||
|
|
||||||
part 'search_suggestion.g.dart';
|
part 'search_suggestion.g.dart';
|
||||||
|
@ -92,18 +91,18 @@ class SearchSuggestionBloc<T>
|
||||||
if (itemToKeywords(e.value as T)
|
if (itemToKeywords(e.value as T)
|
||||||
.any((k) => k.startsWith(ev.phrase))) {
|
.any((k) => k.startsWith(ev.phrase))) {
|
||||||
// prefer names that start exactly with the search phrase
|
// prefer names that start exactly with the search phrase
|
||||||
return Tuple2(e.score + 1, e.value as T);
|
return (score: e.score + 1, item: e.value as T);
|
||||||
} else {
|
} else {
|
||||||
return Tuple2(e.score, e.value as T);
|
return (score: e.score, item: e.value as T);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sorted((a, b) => a.item1.compareTo(b.item1))
|
.sorted((a, b) => a.score.compareTo(b.score))
|
||||||
.reversed
|
.reversed
|
||||||
.distinctIf(
|
.distinctIf(
|
||||||
(a, b) => identical(a.item2, b.item2),
|
(a, b) => identical(a.item, b.item),
|
||||||
(a) => a.item2.hashCode,
|
(a) => a.item.hashCode,
|
||||||
)
|
)
|
||||||
.map((e) => e.item2)
|
.map((e) => e.item)
|
||||||
.toList();
|
.toList();
|
||||||
emit(SearchSuggestionBlocSuccess(matches));
|
emit(SearchSuggestionBlocSuccess(matches));
|
||||||
_lastSearch = ev;
|
_lastSearch = ev;
|
||||||
|
|
|
@ -21,7 +21,6 @@ import 'package:nc_photos/use_case/inflate_file_descriptor.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'download_handler.g.dart';
|
part 'download_handler.g.dart';
|
||||||
|
|
||||||
|
@ -79,7 +78,7 @@ class _DownlaodHandlerAndroid extends _DownloadHandlerBase {
|
||||||
);
|
);
|
||||||
final id = await nm.notify(notif);
|
final id = await nm.notify(notif);
|
||||||
|
|
||||||
final successes = <Tuple2<File, dynamic>>[];
|
final successes = <({File file, dynamic result})>[];
|
||||||
StreamSubscription<DownloadCancelEvent>? subscription;
|
StreamSubscription<DownloadCancelEvent>? subscription;
|
||||||
try {
|
try {
|
||||||
bool isCancel = false;
|
bool isCancel = false;
|
||||||
|
@ -117,7 +116,7 @@ class _DownlaodHandlerAndroid extends _DownloadHandlerBase {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final result = await download();
|
final result = await download();
|
||||||
successes.add(Tuple2(f, result));
|
successes.add((file: f, result: result));
|
||||||
} on PermissionException catch (_) {
|
} on PermissionException catch (_) {
|
||||||
_log.warning("[downloadFiles] Permission not granted");
|
_log.warning("[downloadFiles] Permission not granted");
|
||||||
SnackBarManager().showSnackBar(SnackBar(
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
|
@ -143,8 +142,8 @@ class _DownlaodHandlerAndroid extends _DownloadHandlerBase {
|
||||||
} finally {
|
} finally {
|
||||||
unawaited(subscription?.cancel());
|
unawaited(subscription?.cancel());
|
||||||
if (successes.isNotEmpty) {
|
if (successes.isNotEmpty) {
|
||||||
await _onDownloadSuccessful(successes.map((e) => e.item1).toList(),
|
await _onDownloadSuccessful(successes.map((e) => e.file).toList(),
|
||||||
successes.map((e) => e.item2).toList(), id);
|
successes.map((e) => e.result).toList(), id);
|
||||||
} else {
|
} else {
|
||||||
await nm.dismiss(id);
|
await nm.dismiss(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_common/type.dart';
|
import 'package:np_common/type.dart';
|
||||||
import 'package:np_db/np_db.dart';
|
import 'package:np_db/np_db.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'upgrader.g.dart';
|
part 'upgrader.g.dart';
|
||||||
|
|
||||||
|
@ -143,22 +142,22 @@ class AlbumUpgraderV4 implements AlbumUpgrader {
|
||||||
// remove metadata
|
// remove metadata
|
||||||
e.remove("metadata");
|
e.remove("metadata");
|
||||||
if (latestItemTime != null) {
|
if (latestItemTime != null) {
|
||||||
return Tuple2(latestItemTime, e);
|
return (latestItemTime: latestItemTime, item: e);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.whereType<Tuple2<DateTime, JsonObj>>()
|
.whereType<({DateTime latestItemTime, JsonObj item})>()
|
||||||
.sorted((a, b) => a.item1.compareTo(b.item1))
|
.sorted((a, b) => a.latestItemTime.compareTo(b.latestItemTime))
|
||||||
.lastOrNull;
|
.lastOrNull;
|
||||||
if (latestItem != null) {
|
if (latestItem != null) {
|
||||||
// save the latest item time
|
// save the latest item time
|
||||||
result["provider"]["content"]["latestItemTime"] =
|
result["provider"]["content"]["latestItemTime"] =
|
||||||
latestItem.item1.toIso8601String();
|
latestItem.latestItemTime.toIso8601String();
|
||||||
if (result["coverProvider"]["type"] == "auto") {
|
if (result["coverProvider"]["type"] == "auto") {
|
||||||
// save the cover
|
// save the cover
|
||||||
result["coverProvider"]["content"]["coverFile"] =
|
result["coverProvider"]["content"]["coverFile"] =
|
||||||
Map.of(latestItem.item2);
|
Map.of(latestItem.item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
|
|
|
@ -35,7 +35,6 @@ import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:np_common/or_null.dart';
|
import 'package:np_common/or_null.dart';
|
||||||
import 'package:np_common/type.dart';
|
import 'package:np_common/type.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'album.g.dart';
|
part 'album.g.dart';
|
||||||
|
|
||||||
|
@ -135,20 +134,20 @@ class CollectionAlbumAdapter implements CollectionAdapter {
|
||||||
try {
|
try {
|
||||||
final group = items
|
final group = items
|
||||||
.withIndex()
|
.withIndex()
|
||||||
.groupListsBy((e) => e.item2 is AlbumAdaptedCollectionItem);
|
.groupListsBy((e) => e.e is AlbumAdaptedCollectionItem);
|
||||||
var failed = 0;
|
var failed = 0;
|
||||||
if (group[true]?.isNotEmpty ?? false) {
|
if (group[true]?.isNotEmpty ?? false) {
|
||||||
final newAlbum = await RemoveFromAlbum(_c)(
|
final newAlbum = await RemoveFromAlbum(_c)(
|
||||||
account,
|
account,
|
||||||
_provider.album,
|
_provider.album,
|
||||||
group[true]!
|
group[true]!
|
||||||
.map((e) => e.item2)
|
.map((e) => e.e)
|
||||||
.cast<AlbumAdaptedCollectionItem>()
|
.cast<AlbumAdaptedCollectionItem>()
|
||||||
.map((e) => e.albumItem)
|
.map((e) => e.albumItem)
|
||||||
.toList(),
|
.toList(),
|
||||||
onError: (i, item, e, stackTrace) {
|
onError: (i, item, e, stackTrace) {
|
||||||
++failed;
|
++failed;
|
||||||
final actualIndex = group[true]![i].item1;
|
final actualIndex = group[true]![i].i;
|
||||||
try {
|
try {
|
||||||
onError?.call(actualIndex, items[actualIndex], e, stackTrace);
|
onError?.call(actualIndex, items[actualIndex], e, stackTrace);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
|
@ -162,8 +161,9 @@ class CollectionAlbumAdapter implements CollectionAdapter {
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
for (final pair in (group[false] ?? const <Tuple2<int, int>>[])) {
|
for (final (:i, e: _)
|
||||||
final actualIndex = pair.item1;
|
in (group[false] ?? const <({int i, CollectionItem e})>[])) {
|
||||||
|
final actualIndex = i;
|
||||||
onError?.call(
|
onError?.call(
|
||||||
actualIndex,
|
actualIndex,
|
||||||
items[actualIndex],
|
items[actualIndex],
|
||||||
|
@ -174,8 +174,8 @@ class CollectionAlbumAdapter implements CollectionAdapter {
|
||||||
}
|
}
|
||||||
return (group[true] ?? []).length - failed;
|
return (group[true] ?? []).length - failed;
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
for (final pair in items.withIndex()) {
|
for (final (:i, e: item) in items.withIndex()) {
|
||||||
onError?.call(pair.item1, pair.item2, e, stackTrace);
|
onError?.call(i, item, e, stackTrace);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/entity/collection.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'util.g.dart';
|
part 'util.g.dart';
|
||||||
|
|
||||||
|
@ -43,28 +42,28 @@ enum CollectionShareResult {
|
||||||
|
|
||||||
extension CollectionListExtension on Iterable<Collection> {
|
extension CollectionListExtension on Iterable<Collection> {
|
||||||
List<Collection> sortedBy(CollectionSort by) {
|
List<Collection> sortedBy(CollectionSort by) {
|
||||||
return map<Tuple2<Comparable, Collection>>((e) {
|
return map<({Comparable comparable, Collection collection})>((e) {
|
||||||
switch (by) {
|
switch (by) {
|
||||||
case CollectionSort.nameAscending:
|
case CollectionSort.nameAscending:
|
||||||
case CollectionSort.nameDescending:
|
case CollectionSort.nameDescending:
|
||||||
return Tuple2(e.name.toLowerCase(), e);
|
return (comparable: e.name.toLowerCase(), collection: e);
|
||||||
|
|
||||||
case CollectionSort.dateAscending:
|
case CollectionSort.dateAscending:
|
||||||
case CollectionSort.dateDescending:
|
case CollectionSort.dateDescending:
|
||||||
return Tuple2(e.contentProvider.lastModified, e);
|
return (comparable: e.contentProvider.lastModified, collection: e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sorted((a, b) {
|
.sorted((a, b) {
|
||||||
final x = by.isAscending() ? a : b;
|
final x = by.isAscending() ? a : b;
|
||||||
final y = by.isAscending() ? b : a;
|
final y = by.isAscending() ? b : a;
|
||||||
final tmp = x.item1.compareTo(y.item1);
|
final tmp = x.comparable.compareTo(y.comparable);
|
||||||
if (tmp != 0) {
|
if (tmp != 0) {
|
||||||
return tmp;
|
return tmp;
|
||||||
} else {
|
} else {
|
||||||
return x.item2.name.compareTo(y.item2.name);
|
return x.collection.name.compareTo(y.collection.name);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map((e) => e.item2)
|
.map((e) => e.collection)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:nc_photos/entity/collection_item/util.dart';
|
||||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'sorter.g.dart';
|
part 'sorter.g.dart';
|
||||||
|
|
||||||
|
@ -58,24 +57,24 @@ class CollectionTimeSorter implements CollectionSorter {
|
||||||
prevFileTime = e.file.fdDateTime;
|
prevFileTime = e.file.fdDateTime;
|
||||||
}
|
}
|
||||||
// for non file items, use the sibling file's time
|
// for non file items, use the sibling file's time
|
||||||
return Tuple2(prevFileTime, e);
|
return (dateTime: prevFileTime, item: e);
|
||||||
})
|
})
|
||||||
.stableSorted((x, y) {
|
.stableSorted((x, y) {
|
||||||
if (x.item1 == null && y.item1 == null) {
|
if (x.dateTime == null && y.dateTime == null) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (x.item1 == null) {
|
} else if (x.dateTime == null) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (y.item1 == null) {
|
} else if (y.dateTime == null) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
if (isAscending) {
|
if (isAscending) {
|
||||||
return x.item1!.compareTo(y.item1!);
|
return x.dateTime!.compareTo(y.dateTime!);
|
||||||
} else {
|
} else {
|
||||||
return y.item1!.compareTo(x.item1!);
|
return y.dateTime!.compareTo(x.dateTime!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map((e) => e.item2)
|
.map((e) => e.item)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,24 +97,24 @@ class CollectionFilenameSorter implements CollectionSorter {
|
||||||
prevFilename = e.file.filename;
|
prevFilename = e.file.filename;
|
||||||
}
|
}
|
||||||
// for non file items, use the sibling file's name
|
// for non file items, use the sibling file's name
|
||||||
return Tuple2(prevFilename, e);
|
return (dateTime: prevFilename, item: e);
|
||||||
})
|
})
|
||||||
.stableSorted((x, y) {
|
.stableSorted((x, y) {
|
||||||
if (x.item1 == null && y.item1 == null) {
|
if (x.dateTime == null && y.dateTime == null) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (x.item1 == null) {
|
} else if (x.dateTime == null) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (y.item1 == null) {
|
} else if (y.dateTime == null) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
if (isAscending) {
|
if (isAscending) {
|
||||||
return compareNatural(x.item1!, y.item1!);
|
return compareNatural(x.dateTime!, y.dateTime!);
|
||||||
} else {
|
} else {
|
||||||
return compareNatural(y.item1!, x.item1!);
|
return compareNatural(y.dateTime!, x.dateTime!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map((e) => e.item2)
|
.map((e) => e.item)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,7 @@ class InternalDownloadHandler {
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final results = <MapEntry<File, dynamic>>[];
|
final results = <MapEntry<File, dynamic>>[];
|
||||||
for (final pair in files.withIndex()) {
|
for (final (:i, e: f) in files.withIndex()) {
|
||||||
final i = pair.item1, f = pair.item2;
|
|
||||||
controller.add(_DownloadProgress(current: i));
|
controller.add(_DownloadProgress(current: i));
|
||||||
try {
|
try {
|
||||||
final dynamic result;
|
final dynamic result;
|
||||||
|
@ -122,8 +121,7 @@ class InternalDownloadHandler {
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final results = <MapEntry<File, dynamic>>[];
|
final results = <MapEntry<File, dynamic>>[];
|
||||||
for (final pair in files.withIndex()) {
|
for (final (:i, e: f) in files.withIndex()) {
|
||||||
final i = pair.item1, f = pair.item2;
|
|
||||||
controller.add(_DownloadProgress(current: i));
|
controller.add(_DownloadProgress(current: i));
|
||||||
try {
|
try {
|
||||||
download = DownloadFile().build(
|
download = DownloadFile().build(
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:logging/logging.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:woozy_search/woozy_search.dart';
|
import 'package:woozy_search/woozy_search.dart';
|
||||||
|
|
||||||
part 'suggester.g.dart';
|
part 'suggester.g.dart';
|
||||||
|
@ -34,18 +33,18 @@ class Suggester<T> {
|
||||||
.map((e) {
|
.map((e) {
|
||||||
if (itemToKeywords(e.value as T).any((k) => k.startsWith(phrase))) {
|
if (itemToKeywords(e.value as T).any((k) => k.startsWith(phrase))) {
|
||||||
// prefer names that start exactly with the search phrase
|
// prefer names that start exactly with the search phrase
|
||||||
return Tuple2(e.score + 1, e.value as T);
|
return (score: e.score + 1, item: e.value as T);
|
||||||
} else {
|
} else {
|
||||||
return Tuple2(e.score, e.value as T);
|
return (score: e.score, item: e.value as T);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sorted((a, b) => a.item1.compareTo(b.item1))
|
.sorted((a, b) => a.score.compareTo(b.score))
|
||||||
.reversed
|
.reversed
|
||||||
.distinctIf(
|
.distinctIf(
|
||||||
(a, b) => identical(a.item2, b.item2),
|
(a, b) => identical(a.item, b.item),
|
||||||
(a) => a.item2.hashCode,
|
(a) => a.item.hashCode,
|
||||||
)
|
)
|
||||||
.map((e) => e.item2)
|
.map((e) => e.item)
|
||||||
.toList();
|
.toList();
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,7 @@ class Remove {
|
||||||
await _cleanUpAlbums(account, files);
|
await _cleanUpAlbums(account, files);
|
||||||
}
|
}
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for (final pair in files.withIndex()) {
|
for (final (:i, e: f) in files.withIndex()) {
|
||||||
final i = pair.item1;
|
|
||||||
final f = pair.item2;
|
|
||||||
try {
|
try {
|
||||||
await _c.fileRepo2.remove(account, f);
|
await _c.fileRepo2.remove(account, f);
|
||||||
++count;
|
++count;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'package:nc_photos/progress_util.dart';
|
||||||
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
|
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
|
||||||
import 'package:nc_photos/use_case/ls_single_file.dart';
|
import 'package:nc_photos/use_case/ls_single_file.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'sync_dir.g.dart';
|
part 'sync_dir.g.dart';
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ class SyncDir {
|
||||||
ValueChanged<Progress>? onProgressUpdate,
|
ValueChanged<Progress>? onProgressUpdate,
|
||||||
}) async {
|
}) async {
|
||||||
final status = await _checkContentUpdated(account, remoteDir, dirCache);
|
final status = await _checkContentUpdated(account, remoteDir, dirCache);
|
||||||
if (!status.item1) {
|
if (!status.isUpdated) {
|
||||||
_log.finer("[_syncDir] Dir unchanged: ${remoteDir.path}");
|
_log.finer("[_syncDir] Dir unchanged: ${remoteDir.path}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +59,7 @@ class SyncDir {
|
||||||
|
|
||||||
final dataSrc = FileCachedDataSource(_c, shouldCheckCache: true);
|
final dataSrc = FileCachedDataSource(_c, shouldCheckCache: true);
|
||||||
final syncState = await dataSrc.beginSync(account, remoteDir,
|
final syncState = await dataSrc.beginSync(account, remoteDir,
|
||||||
remoteTouchEtag: status.item2);
|
remoteTouchEtag: status.touchResult);
|
||||||
final children = syncState.files;
|
final children = syncState.files;
|
||||||
if (!isRecursive) {
|
if (!isRecursive) {
|
||||||
await dataSrc.concludeSync(syncState);
|
await dataSrc.concludeSync(syncState);
|
||||||
|
@ -97,19 +96,19 @@ class SyncDir {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Tuple2<bool, String?>> _checkContentUpdated(
|
Future<({bool isUpdated, String? touchResult})> _checkContentUpdated(
|
||||||
Account account, File remoteDir, Map<int, String> dirCache) async {
|
Account account, File remoteDir, Map<int, String> dirCache) async {
|
||||||
String? touchResult;
|
String? touchResult;
|
||||||
try {
|
try {
|
||||||
touchResult = await _c.touchManager.checkTouchEtag(account, remoteDir);
|
touchResult = await _c.touchManager.checkTouchEtag(account, remoteDir);
|
||||||
if (touchResult == null &&
|
if (touchResult == null &&
|
||||||
dirCache[remoteDir.fileId!] == remoteDir.etag!) {
|
dirCache[remoteDir.fileId!] == remoteDir.etag!) {
|
||||||
return const Tuple2(false, null);
|
return const (isUpdated: false, touchResult: null);
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
_log.severe("[_isContentUpdated] Uncaught exception", e, stackTrace);
|
_log.severe("[_isContentUpdated] Uncaught exception", e, stackTrace);
|
||||||
}
|
}
|
||||||
return Tuple2(true, touchResult);
|
return (isUpdated: true, touchResult: touchResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<int, String>> _queryAllDirEtags(
|
Future<Map<int, String>> _queryAllDirEtags(
|
||||||
|
|
|
@ -38,7 +38,6 @@ import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'file_sharer_dialog.g.dart';
|
part 'file_sharer_dialog.g.dart';
|
||||||
part 'file_sharer_dialog/bloc.dart';
|
part 'file_sharer_dialog/bloc.dart';
|
||||||
|
|
|
@ -97,9 +97,8 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
fileState: _FileState.init(count: files.length),
|
fileState: _FileState.init(count: files.length),
|
||||||
));
|
));
|
||||||
final results = <Tuple2<FileDescriptor, dynamic>>[];
|
final results = <({FileDescriptor file, dynamic result})>[];
|
||||||
for (final pair in files.withIndex()) {
|
for (final (:i, e: f) in files.withIndex()) {
|
||||||
final i = pair.item1, f = pair.item2;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
fileState: state.fileState?.copyWith(
|
fileState: state.fileState?.copyWith(
|
||||||
index: i,
|
index: i,
|
||||||
|
@ -124,7 +123,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
if (state.fileState?.shouldRun == false) {
|
if (state.fileState?.shouldRun == false) {
|
||||||
throw const JobCanceledException();
|
throw const JobCanceledException();
|
||||||
}
|
}
|
||||||
results.add(Tuple2(f, result));
|
results.add((file: f, result: result));
|
||||||
} on PermissionException catch (e, stackTrace) {
|
} on PermissionException catch (e, stackTrace) {
|
||||||
_log.warning("[_doShareFile] Permission not granted");
|
_log.warning("[_doShareFile] Permission not granted");
|
||||||
emit(state.copyWith(error: ExceptionEvent(e, stackTrace)));
|
emit(state.copyWith(error: ExceptionEvent(e, stackTrace)));
|
||||||
|
@ -141,7 +140,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
}
|
}
|
||||||
if (results.isNotEmpty) {
|
if (results.isNotEmpty) {
|
||||||
final share = AndroidFileShare(results
|
final share = AndroidFileShare(results
|
||||||
.map((e) => AndroidFileShareFile(e.item2 as String, e.item1.fdMime))
|
.map((e) => AndroidFileShareFile(e.result as String, e.file.fdMime))
|
||||||
.toList());
|
.toList());
|
||||||
unawaited(share.share());
|
unawaited(share.share());
|
||||||
}
|
}
|
||||||
|
@ -153,9 +152,8 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
previewState: _PreviewState(index: 0, count: files.length),
|
previewState: _PreviewState(index: 0, count: files.length),
|
||||||
));
|
));
|
||||||
final results = <Tuple2<FileDescriptor, dynamic>>[];
|
final results = <({FileDescriptor file, dynamic result})>[];
|
||||||
for (final pair in files.withIndex()) {
|
for (final (:i, e: f) in files.withIndex()) {
|
||||||
final i = pair.item1, f = pair.item2;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
previewState: state.previewState?.copyWith(index: i),
|
previewState: state.previewState?.copyWith(index: i),
|
||||||
));
|
));
|
||||||
|
@ -166,7 +164,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
} else {
|
} else {
|
||||||
uri = await DownloadFile()(account, f, shouldNotify: false);
|
uri = await DownloadFile()(account, f, shouldNotify: false);
|
||||||
}
|
}
|
||||||
results.add(Tuple2(f, uri));
|
results.add((file: f, result: uri));
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
_log.shout(
|
_log.shout(
|
||||||
"[_doSharePreview] Failed while DownloadPreview", e, stackTrace);
|
"[_doSharePreview] Failed while DownloadPreview", e, stackTrace);
|
||||||
|
@ -175,7 +173,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
}
|
}
|
||||||
if (results.isNotEmpty) {
|
if (results.isNotEmpty) {
|
||||||
final share = AndroidFileShare(results
|
final share = AndroidFileShare(results
|
||||||
.map((e) => AndroidFileShareFile(e.item2 as String, e.item1.fdMime))
|
.map((e) => AndroidFileShareFile(e.result as String, e.file.fdMime))
|
||||||
.toList());
|
.toList());
|
||||||
unawaited(share.share());
|
unawaited(share.share());
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,11 +477,12 @@ class _Bloc extends Bloc<_Event, _State>
|
||||||
month: localToday.month,
|
month: localToday.month,
|
||||||
day: localToday.day,
|
day: localToday.day,
|
||||||
cover: e.value
|
cover: e.value
|
||||||
.map((e) => Tuple2(e.bestDateTime.difference(center), e))
|
.map((e) =>
|
||||||
.sorted((a, b) => a.item1.compareTo(b.item1))
|
(comparable: e.bestDateTime.difference(center), item: e))
|
||||||
|
.sorted((a, b) => a.comparable.compareTo(b.comparable))
|
||||||
.firstOrNull
|
.firstOrNull
|
||||||
?.let((e) => DbFileDescriptorConverter.fromDb(
|
?.let((e) => DbFileDescriptorConverter.fromDb(
|
||||||
account.userId.toString(), e.item2)),
|
account.userId.toString(), e.item)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
|
|
@ -67,7 +67,6 @@ import 'package:np_db/np_db.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:visibility_detector/visibility_detector.dart';
|
import 'package:visibility_detector/visibility_detector.dart';
|
||||||
|
|
||||||
part 'home_photos/app_bar.dart';
|
part 'home_photos/app_bar.dart';
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'enhancement/bloc.dart';
|
part 'enhancement/bloc.dart';
|
||||||
part 'enhancement/state_event.dart';
|
part 'enhancement/state_event.dart';
|
||||||
|
@ -135,9 +134,9 @@ class _WrappedEnhancementSettingsState
|
||||||
_SizeSlider(
|
_SizeSlider(
|
||||||
initialWidth: initialSize.width,
|
initialWidth: initialSize.width,
|
||||||
initialHeight: initialSize.height,
|
initialHeight: initialSize.height,
|
||||||
onChanged: (value) {
|
onChanged: (size) {
|
||||||
width = value.item1;
|
width = size.w;
|
||||||
height = value.item2;
|
height = size.h;
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -175,7 +174,7 @@ class _SizeSlider extends StatefulWidget {
|
||||||
|
|
||||||
final int initialWidth;
|
final int initialWidth;
|
||||||
final int initialHeight;
|
final int initialHeight;
|
||||||
final ValueChanged<Tuple2<int, int>>? onChanged;
|
final ValueChanged<({int w, int h})>? onChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SizeSliderState extends State<_SizeSlider> {
|
class _SizeSliderState extends State<_SizeSlider> {
|
||||||
|
@ -202,8 +201,8 @@ class _SizeSliderState extends State<_SizeSlider> {
|
||||||
onChangeEnd: (value) async {
|
onChangeEnd: (value) async {
|
||||||
final resolution = sliderValueToResolution(value.toInt());
|
final resolution = sliderValueToResolution(value.toInt());
|
||||||
setState(() {
|
setState(() {
|
||||||
_width = resolution.item1;
|
_width = resolution.w;
|
||||||
_height = resolution.item2;
|
_height = resolution.h;
|
||||||
});
|
});
|
||||||
widget.onChanged?.call(resolution);
|
widget.onChanged?.call(resolution);
|
||||||
},
|
},
|
||||||
|
@ -212,22 +211,22 @@ class _SizeSliderState extends State<_SizeSlider> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Tuple2<int, int> sliderValueToResolution(int value) {
|
static ({int w, int h}) sliderValueToResolution(int value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case -3:
|
case -3:
|
||||||
return const Tuple2(1024, 768);
|
return const (w: 1024, h: 768);
|
||||||
case -2:
|
case -2:
|
||||||
return const Tuple2(1280, 960);
|
return const (w: 1280, h: 960);
|
||||||
case -1:
|
case -1:
|
||||||
return const Tuple2(1600, 1200);
|
return const (w: 1600, h: 1200);
|
||||||
case 1:
|
case 1:
|
||||||
return const Tuple2(2560, 1920);
|
return const (w: 2560, h: 1920);
|
||||||
case 2:
|
case 2:
|
||||||
return const Tuple2(3200, 2400);
|
return const (w: 3200, h: 2400);
|
||||||
case 3:
|
case 3:
|
||||||
return const Tuple2(4096, 3072);
|
return const (w: 4096, h: 3072);
|
||||||
default:
|
default:
|
||||||
return const Tuple2(2048, 1536);
|
return const (w: 2048, h: 1536);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
import 'package:path/path.dart' as path_lib;
|
import 'package:path/path.dart' as path_lib;
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'viewer_detail_pane.g.dart';
|
part 'viewer_detail_pane.g.dart';
|
||||||
|
|
||||||
|
@ -405,7 +404,7 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
||||||
final lng = exif.gpsLongitudeDeg;
|
final lng = exif.gpsLongitudeDeg;
|
||||||
if (lat != null && lng != null) {
|
if (lat != null && lng != null) {
|
||||||
_log.fine("GPS: ($lat, $lng)");
|
_log.fine("GPS: ($lat, $lng)");
|
||||||
_gps = Tuple2(lat, lng);
|
_gps = (lat: lat, lng: lng);
|
||||||
_location = _file!.location;
|
_location = _file!.location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,7 +502,7 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
||||||
if (getRawPlatform() == NpPlatform.android) {
|
if (getRawPlatform() == NpPlatform.android) {
|
||||||
final intent = AndroidIntent(
|
final intent = AndroidIntent(
|
||||||
action: "action_view",
|
action: "action_view",
|
||||||
data: Uri.encodeFull("geo:${_gps!.item1},${_gps!.item2}?z=16"),
|
data: Uri.encodeFull("geo:${_gps!.lat},${_gps!.lng}?z=16"),
|
||||||
);
|
);
|
||||||
intent.launch();
|
intent.launch();
|
||||||
}
|
}
|
||||||
|
@ -597,7 +596,7 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
||||||
String? _exposureTime;
|
String? _exposureTime;
|
||||||
double? _focalLength;
|
double? _focalLength;
|
||||||
int? _isoSpeedRatings;
|
int? _isoSpeedRatings;
|
||||||
Tuple2<double, double>? _gps;
|
({double lat, double lng})? _gps;
|
||||||
ImageLocation? _location;
|
ImageLocation? _location;
|
||||||
|
|
||||||
final _tags = <String>[];
|
final _tags = <String>[];
|
||||||
|
|
|
@ -1660,14 +1660,6 @@ packages:
|
||||||
url: "https://gitlab.com/nkming2/dart-to-string"
|
url: "https://gitlab.com/nkming2/dart-to-string"
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
tuple:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: tuple
|
|
||||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -150,7 +150,6 @@ dependencies:
|
||||||
url: https://gitlab.com/nkming2/dart-to-string
|
url: https://gitlab.com/nkming2/dart-to-string
|
||||||
ref: to_string-1.0.0
|
ref: to_string-1.0.0
|
||||||
path: to_string
|
path: to_string
|
||||||
tuple: ^2.0.2
|
|
||||||
url_launcher: ^6.2.6
|
url_launcher: ^6.2.6
|
||||||
uuid: ^3.0.7
|
uuid: ^3.0.7
|
||||||
video_player:
|
video_player:
|
||||||
|
|
|
@ -28,7 +28,6 @@ import 'package:np_db_sqlite/np_db_sqlite.dart';
|
||||||
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
||||||
import 'package:np_geocoder/np_geocoder.dart';
|
import 'package:np_geocoder/np_geocoder.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'test_compat_util.dart';
|
part 'test_compat_util.dart';
|
||||||
|
|
||||||
|
@ -589,10 +588,11 @@ Future<Map<File, Set<File>>> listSqliteDbDirs(compat.SqliteDb db) async {
|
||||||
}).get());
|
}).get());
|
||||||
|
|
||||||
final dirQuery = db.select(db.dirFiles);
|
final dirQuery = db.select(db.dirFiles);
|
||||||
final dirs = await dirQuery.map((r) => Tuple2(r.dir, r.child)).get();
|
final dirs =
|
||||||
|
await dirQuery.map((r) => (dirRowId: r.dir, childRowId: r.child)).get();
|
||||||
final result = <File, Set<File>>{};
|
final result = <File, Set<File>>{};
|
||||||
for (final d in dirs) {
|
for (final (:dirRowId, :childRowId) in dirs) {
|
||||||
(result[fileMap[d.item1]!] ??= <File>{}).add(fileMap[d.item2]!);
|
(result[fileMap[dirRowId]!] ??= <File>{}).add(fileMap[childRowId]!);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -616,18 +616,19 @@ Future<Set<Album>> listSqliteDbAlbums(compat.SqliteDb db) async {
|
||||||
null,
|
null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return Tuple2(
|
return (
|
||||||
r.read(db.albums.rowId)!,
|
rowId: r.read(db.albums.rowId)!,
|
||||||
|
album:
|
||||||
_SqliteAlbumConverter.fromSql(r.readTable(db.albums), albumFile, []),
|
_SqliteAlbumConverter.fromSql(r.readTable(db.albums), albumFile, []),
|
||||||
);
|
);
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
final results = <Album>{};
|
final results = <Album>{};
|
||||||
for (final a in albums) {
|
for (final (:rowId, :album) in albums) {
|
||||||
final shareQuery = db.select(db.albumShares)
|
final shareQuery = db.select(db.albumShares)
|
||||||
..where((t) => t.album.equals(a.item1));
|
..where((t) => t.album.equals(rowId));
|
||||||
final dbShares = await shareQuery.get();
|
final dbShares = await shareQuery.get();
|
||||||
results.add(a.item2.copyWith(
|
results.add(album.copyWith(
|
||||||
lastUpdated: const OrNull(null),
|
lastUpdated: const OrNull(null),
|
||||||
shares: dbShares.isEmpty
|
shares: dbShares.isEmpty
|
||||||
? null
|
? null
|
||||||
|
@ -635,7 +636,8 @@ Future<Set<Album>> listSqliteDbAlbums(compat.SqliteDb db) async {
|
||||||
.map((s) => AlbumShare(
|
.map((s) => AlbumShare(
|
||||||
userId: s.userId.toCi(),
|
userId: s.userId.toCi(),
|
||||||
displayName: s.displayName,
|
displayName: s.displayName,
|
||||||
sharedAt: s.sharedAt))
|
sharedAt: s.sharedAt,
|
||||||
|
))
|
||||||
.toList()),
|
.toList()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'package:nc_photos/use_case/face_recognition_person/sync_face_recognition
|
||||||
import 'package:np_db/np_db.dart';
|
import 'package:np_db/np_db.dart';
|
||||||
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
import '../../mock_type.dart';
|
import '../../mock_type.dart';
|
||||||
import '../../test_util.dart' as util;
|
import '../../test_util.dart' as util;
|
||||||
|
@ -157,13 +156,15 @@ Future<Map<String, Set<DbFaceRecognitionPerson>>> _listSqliteDbPersons(
|
||||||
db.accounts.rowId.equalsExp(db.faceRecognitionPersons.account)),
|
db.accounts.rowId.equalsExp(db.faceRecognitionPersons.account)),
|
||||||
]);
|
]);
|
||||||
final result = await query
|
final result = await query
|
||||||
.map((r) => Tuple2(
|
.map((r) => (
|
||||||
r.readTable(db.accounts), r.readTable(db.faceRecognitionPersons)))
|
account: r.readTable(db.accounts),
|
||||||
|
faceRecognitionPerson: r.readTable(db.faceRecognitionPersons),
|
||||||
|
))
|
||||||
.get();
|
.get();
|
||||||
final product = <String, Set<DbFaceRecognitionPerson>>{};
|
final product = <String, Set<DbFaceRecognitionPerson>>{};
|
||||||
for (final r in result) {
|
for (final r in result) {
|
||||||
(product[r.item1.userId] ??= {})
|
(product[r.account.userId] ??= {}).add(
|
||||||
.add(compat.FaceRecognitionPersonConverter.fromSql(r.item2));
|
compat.FaceRecognitionPersonConverter.fromSql(r.faceRecognitionPerson));
|
||||||
}
|
}
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'package:nc_photos/use_case/sync_tag.dart';
|
||||||
import 'package:np_db/np_db.dart';
|
import 'package:np_db/np_db.dart';
|
||||||
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
import 'package:np_db_sqlite/np_db_sqlite_compat.dart' as compat;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
import '../mock_type.dart';
|
import '../mock_type.dart';
|
||||||
import '../test_util.dart' as util;
|
import '../test_util.dart' as util;
|
||||||
|
@ -175,11 +174,11 @@ Future<Map<String, Set<DbTag>>> _listSqliteDbTags(compat.SqliteDb db) async {
|
||||||
sql.innerJoin(db.servers, db.servers.rowId.equalsExp(db.tags.server)),
|
sql.innerJoin(db.servers, db.servers.rowId.equalsExp(db.tags.server)),
|
||||||
]);
|
]);
|
||||||
final result = await query
|
final result = await query
|
||||||
.map((r) => Tuple2(r.readTable(db.servers), r.readTable(db.tags)))
|
.map((r) => (server: r.readTable(db.servers), tag: r.readTable(db.tags)))
|
||||||
.get();
|
.get();
|
||||||
final product = <String, Set<DbTag>>{};
|
final product = <String, Set<DbTag>>{};
|
||||||
for (final r in result) {
|
for (final r in result) {
|
||||||
(product[r.item1.address] ??= {}).add(compat.TagConverter.fromSql(r.item2));
|
(product[r.server.address] ??= {}).add(compat.TagConverter.fromSql(r.tag));
|
||||||
}
|
}
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,13 @@ Future<List<T>> waitOr<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final p in futures.withIndex()) {
|
for (final (:i, :e) in futures.withIndex()) {
|
||||||
unawaited(
|
unawaited(
|
||||||
p.item2.then((value) {
|
e.then((value) {
|
||||||
results[p.item1] = value;
|
results[i] = value;
|
||||||
onResult();
|
onResult();
|
||||||
}).onError((error, stackTrace) {
|
}).onError((error, stackTrace) {
|
||||||
results[p.item1] = onError(error!, stackTrace);
|
results[i] = onError(error!, stackTrace);
|
||||||
onResult();
|
onResult();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'dart:collection';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:np_collection/src/list_extension.dart';
|
import 'package:np_collection/src/list_extension.dart';
|
||||||
import 'package:quiver/iterables.dart';
|
import 'package:quiver/iterables.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
extension IterableExtension<T> on Iterable<T> {
|
extension IterableExtension<T> on Iterable<T> {
|
||||||
/// Return a new stable sorted list
|
/// Return a new stable sorted list
|
||||||
|
@ -15,7 +14,7 @@ extension IterableExtension<T> on Iterable<T> {
|
||||||
/// toString for each items
|
/// toString for each items
|
||||||
String toReadableString() => "[${join(', ')}]";
|
String toReadableString() => "[${join(', ')}]";
|
||||||
|
|
||||||
Iterable<Tuple2<int, T>> withIndex() => mapIndexed((i, e) => Tuple2(i, e));
|
Iterable<({int i, T e})> withIndex() => mapIndexed((i, e) => (i: i, e: e));
|
||||||
|
|
||||||
/// Whether the collection contains an element equal to [element] using the
|
/// Whether the collection contains an element equal to [element] using the
|
||||||
/// equality function [equalFn]
|
/// equality function [equalFn]
|
||||||
|
|
|
@ -12,7 +12,6 @@ dependencies:
|
||||||
np_math:
|
np_math:
|
||||||
path: ../np_math
|
path: ../np_math
|
||||||
quiver: ^3.2.1
|
quiver: ^3.2.1
|
||||||
tuple: ^2.0.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
np_lints:
|
np_lints:
|
||||||
|
|
|
@ -2,18 +2,17 @@ import 'package:np_collection/src/iterable_extension.dart';
|
||||||
import 'package:np_math/np_math.dart';
|
import 'package:np_math/np_math.dart';
|
||||||
import 'package:quiver/core.dart';
|
import 'package:quiver/core.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group("IterableExtension", () {
|
group("IterableExtension", () {
|
||||||
test("withIndex", () {
|
test("withIndex", () {
|
||||||
final src = [1, 4, 5, 2, 3];
|
final src = [1, 4, 5, 2, 3];
|
||||||
final result = src.withIndex().toList();
|
final result = src.withIndex().toList();
|
||||||
expect(result[0], const Tuple2(0, 1));
|
expect(result[0], const (i: 0, e: 1));
|
||||||
expect(result[1], const Tuple2(1, 4));
|
expect(result[1], const (i: 1, e: 4));
|
||||||
expect(result[2], const Tuple2(2, 5));
|
expect(result[2], const (i: 2, e: 5));
|
||||||
expect(result[3], const Tuple2(3, 2));
|
expect(result[3], const (i: 3, e: 2));
|
||||||
expect(result[4], const Tuple2(4, 3));
|
expect(result[4], const (i: 4, e: 3));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("containsIf", () {
|
test("containsIf", () {
|
||||||
|
|
|
@ -8,7 +8,7 @@ extension SqliteDbCompatExtension on SqliteDb {
|
||||||
final count = await countQ.map((r) => r.read(countExp)!).getSingle();
|
final count = await countQ.map((r) => r.read(countExp)!).getSingle();
|
||||||
onProgress?.call(0, count);
|
onProgress?.call(0, count);
|
||||||
|
|
||||||
final dateTimeUpdates = <Tuple2<int, DateTime>>[];
|
final dateTimeUpdates = <({int rowId, DateTime dateTime})>[];
|
||||||
final imageRemoves = <int>[];
|
final imageRemoves = <int>[];
|
||||||
for (var i = 0; i < count; i += 1000) {
|
for (var i = 0; i < count; i += 1000) {
|
||||||
final q = select(files).join([
|
final q = select(files).join([
|
||||||
|
@ -40,7 +40,10 @@ extension SqliteDbCompatExtension on SqliteDb {
|
||||||
);
|
);
|
||||||
if (f.accountFile.bestDateTime != bestDateTime) {
|
if (f.accountFile.bestDateTime != bestDateTime) {
|
||||||
// need update
|
// need update
|
||||||
dateTimeUpdates.add(Tuple2(f.accountFile.rowId, bestDateTime));
|
dateTimeUpdates.add((
|
||||||
|
rowId: f.accountFile.rowId,
|
||||||
|
dateTime: bestDateTime,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.file.contentType == "image/heic" &&
|
if (f.file.contentType == "image/heic" &&
|
||||||
|
@ -56,7 +59,7 @@ extension SqliteDbCompatExtension on SqliteDb {
|
||||||
"[migrateV55] ${dateTimeUpdates.length} rows require updating, ${imageRemoves.length} rows require removing");
|
"[migrateV55] ${dateTimeUpdates.length} rows require updating, ${imageRemoves.length} rows require removing");
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
_log.fine(
|
_log.fine(
|
||||||
"[migrateV55] dateTimeUpdates: ${dateTimeUpdates.map((e) => e.item1).toReadableString()}");
|
"[migrateV55] dateTimeUpdates: ${dateTimeUpdates.map((e) => e.rowId).toReadableString()}");
|
||||||
_log.fine(
|
_log.fine(
|
||||||
"[migrateV55] imageRemoves: ${imageRemoves.map((e) => e).toReadableString()}");
|
"[migrateV55] imageRemoves: ${imageRemoves.map((e) => e).toReadableString()}");
|
||||||
}
|
}
|
||||||
|
@ -65,9 +68,9 @@ extension SqliteDbCompatExtension on SqliteDb {
|
||||||
batch.update(
|
batch.update(
|
||||||
accountFiles,
|
accountFiles,
|
||||||
AccountFilesCompanion(
|
AccountFilesCompanion(
|
||||||
bestDateTime: Value(pair.item2),
|
bestDateTime: Value(pair.dateTime),
|
||||||
),
|
),
|
||||||
where: ($AccountFilesTable table) => table.rowId.equals(pair.item1),
|
where: ($AccountFilesTable table) => table.rowId.equals(pair.rowId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (final r in imageRemoves) {
|
for (final r in imageRemoves) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import 'package:np_db_sqlite/src/util.dart';
|
||||||
import 'package:np_geocoder/np_geocoder.dart';
|
import 'package:np_geocoder/np_geocoder.dart';
|
||||||
import 'package:np_platform_lock/np_platform_lock.dart';
|
import 'package:np_platform_lock/np_platform_lock.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
part 'database/account_extension.dart';
|
part 'database/account_extension.dart';
|
||||||
part 'database/album_extension.dart';
|
part 'database/album_extension.dart';
|
||||||
|
|
|
@ -50,7 +50,6 @@ dependencies:
|
||||||
url: https://gitlab.com/nkming2/dart-to-string
|
url: https://gitlab.com/nkming2/dart-to-string
|
||||||
ref: to_string-1.0.0
|
ref: to_string-1.0.0
|
||||||
path: to_string
|
path: to_string
|
||||||
tuple: ^2.0.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.9
|
build_runner: ^2.4.9
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:np_gps_map/src/native/google_gps_map.dart'
|
||||||
if (dart.library.html) 'package:np_gps_map/src/web/google_gps_map.dart';
|
if (dart.library.html) 'package:np_gps_map/src/web/google_gps_map.dart';
|
||||||
import 'package:np_gps_map/src/osm_gps_map.dart';
|
import 'package:np_gps_map/src/osm_gps_map.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
enum GpsMapProvider {
|
enum GpsMapProvider {
|
||||||
google,
|
google,
|
||||||
|
@ -50,7 +49,7 @@ class GpsMap extends StatelessWidget {
|
||||||
final GpsMapProvider providerHint;
|
final GpsMapProvider providerHint;
|
||||||
|
|
||||||
/// A pair of latitude and longitude coordinates, stored as degrees
|
/// A pair of latitude and longitude coordinates, stored as degrees
|
||||||
final Tuple2<double, double> center;
|
final ({double lat, double lng}) center;
|
||||||
final double zoom;
|
final double zoom;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class GoogleGpsMap extends StatelessWidget {
|
class GoogleGpsMap extends StatelessWidget {
|
||||||
const GoogleGpsMap({
|
const GoogleGpsMap({
|
||||||
|
@ -12,7 +11,7 @@ class GoogleGpsMap extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final centerLl = LatLng(center.item1, center.item2);
|
final centerLl = LatLng(center.lat, center.lng);
|
||||||
return GoogleMap(
|
return GoogleMap(
|
||||||
compassEnabled: false,
|
compassEnabled: false,
|
||||||
mapToolbarEnabled: false,
|
mapToolbarEnabled: false,
|
||||||
|
@ -41,7 +40,7 @@ class GoogleGpsMap extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Tuple2<double, double> center;
|
final ({double lat, double lng}) center;
|
||||||
final double zoom;
|
final double zoom;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class OsmGpsMap extends StatelessWidget {
|
class OsmGpsMap extends StatelessWidget {
|
||||||
|
@ -15,11 +14,11 @@ class OsmGpsMap extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const double pinSize = 48;
|
const double pinSize = 48;
|
||||||
final centerLl = LatLng(center.item1, center.item2);
|
final centerLl = LatLng(center.lat, center.lng);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrlString(
|
launchUrlString(
|
||||||
"https://www.openstreetmap.org/?mlat=${center.item1}&mlon=${center.item2}#map=${zoom.toInt()}/${center.item1}/${center.item2}",
|
"https://www.openstreetmap.org/?mlat=${center.lat}&mlon=${center.lng}#map=${zoom.toInt()}/${center.lat}/${center.lng}",
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -62,7 +61,7 @@ class OsmGpsMap extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Tuple2<double, double> center;
|
final ({double lat, double lng}) center;
|
||||||
final double zoom;
|
final double zoom;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'dart:html';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:np_gps_map/src/ui_hack.dart' if (dart.library.html) 'dart:ui'
|
import 'package:np_gps_map/src/ui_hack.dart' if (dart.library.html) 'dart:ui'
|
||||||
as ui;
|
as ui;
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class GoogleGpsMap extends StatefulWidget {
|
class GoogleGpsMap extends StatefulWidget {
|
||||||
const GoogleGpsMap({
|
const GoogleGpsMap({
|
||||||
|
@ -17,7 +16,7 @@ class GoogleGpsMap extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _GoogleGpsMapState();
|
State<StatefulWidget> createState() => _GoogleGpsMapState();
|
||||||
|
|
||||||
final Tuple2<double, double> center;
|
final ({double lat, double lng}) center;
|
||||||
final double zoom;
|
final double zoom;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +27,7 @@ class _GoogleGpsMapState extends State<GoogleGpsMap> {
|
||||||
super.initState();
|
super.initState();
|
||||||
final iframe = IFrameElement()
|
final iframe = IFrameElement()
|
||||||
..src = "https://www.google.com/maps/embed/v1/place?key=$_apiKey"
|
..src = "https://www.google.com/maps/embed/v1/place?key=$_apiKey"
|
||||||
"&q=${widget.center.item1},${widget.center.item2}"
|
"&q=${widget.center.lat},${widget.center.lng}"
|
||||||
"&zoom=${widget.zoom}"
|
"&zoom=${widget.zoom}"
|
||||||
..style.border = "none";
|
..style.border = "none";
|
||||||
ui.platformViewRegistry.registerViewFactory(viewType, (_) => iframe);
|
ui.platformViewRegistry.registerViewFactory(viewType, (_) => iframe);
|
||||||
|
@ -44,5 +43,5 @@ class _GoogleGpsMapState extends State<GoogleGpsMap> {
|
||||||
static const _apiKey = "";
|
static const _apiKey = "";
|
||||||
|
|
||||||
String get viewType =>
|
String get viewType =>
|
||||||
"googleMapIframe(${widget.center.item1},${widget.center.item2})";
|
"googleMapIframe(${widget.center.lat},${widget.center.lng})";
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ dependencies:
|
||||||
path: ../np_async
|
path: ../np_async
|
||||||
np_platform_util:
|
np_platform_util:
|
||||||
path: ../np_platform_util
|
path: ../np_platform_util
|
||||||
tuple: ^2.0.2
|
|
||||||
url_launcher: ^6.1.11
|
url_launcher: ^6.1.11
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
Loading…
Reference in a new issue