mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-12-08 04:09:07 +00:00
feature(mobile): sync assets, albums & users to local database on device (#1759)
* feature(mobile): sync assets, albums & users to local database on device * try to fix tests * move DB sync operations to new SyncService * clear db on user logout * fix reason for endless loading timeline * fix error when deleting album * fix thumbnail of device albums * add a few comments * fix Hive box not open in album service when loading local assets * adjust tests to int IDs * fix bug: show all albums when Recent is selected * update generated api * reworked Recents album isAll handling * guard against wrongly interleaved sync operations * fix: timeline asset ordering (sort asset state by created at) * fix: sort assets in albums by created at
This commit is contained in:
committed by
GitHub
parent
8f11529a75
commit
8708867c1c
@@ -13,14 +13,16 @@ void main() {
|
||||
|
||||
testAssets.add(
|
||||
Asset(
|
||||
deviceAssetId: '$i',
|
||||
deviceId: '',
|
||||
ownerId: '',
|
||||
localId: '$i',
|
||||
deviceId: 1,
|
||||
ownerId: 1,
|
||||
fileCreatedAt: date,
|
||||
fileModifiedAt: date,
|
||||
updatedAt: date,
|
||||
durationInSeconds: 0,
|
||||
fileName: '',
|
||||
isFavorite: false,
|
||||
isLocal: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
50
mobile/test/diff_test.dart
Normal file
50
mobile/test/diff_test.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
|
||||
void main() {
|
||||
final List<int> listA = [1, 2, 3, 4, 6];
|
||||
final List<int> listB = [1, 3, 5, 7];
|
||||
|
||||
group('Test grouped', () {
|
||||
test('test partial overlap', () async {
|
||||
final List<int> onlyInA = [];
|
||||
final List<int> onlyInB = [];
|
||||
final List<int> inBoth = [];
|
||||
final changes = await diffSortedLists(
|
||||
listA,
|
||||
listB,
|
||||
compare: (int a, int b) => a.compareTo(b),
|
||||
both: (int a, int b) {
|
||||
inBoth.add(b);
|
||||
return false;
|
||||
},
|
||||
onlyFirst: (int a) => onlyInA.add(a),
|
||||
onlySecond: (int b) => onlyInB.add(b),
|
||||
);
|
||||
expect(changes, true);
|
||||
expect(onlyInA, [2, 4, 6]);
|
||||
expect(onlyInB, [5, 7]);
|
||||
expect(inBoth, [1, 3]);
|
||||
});
|
||||
test('test partial overlap sync', () {
|
||||
final List<int> onlyInA = [];
|
||||
final List<int> onlyInB = [];
|
||||
final List<int> inBoth = [];
|
||||
final changes = diffSortedListsSync(
|
||||
listA,
|
||||
listB,
|
||||
compare: (int a, int b) => a.compareTo(b),
|
||||
both: (int a, int b) {
|
||||
inBoth.add(b);
|
||||
return false;
|
||||
},
|
||||
onlyFirst: (int a) => onlyInA.add(a),
|
||||
onlySecond: (int b) => onlyInB.add(b),
|
||||
);
|
||||
expect(changes, true);
|
||||
expect(onlyInA, [2, 4, 6]);
|
||||
expect(onlyInB, [5, 7]);
|
||||
expect(inBoth, [1, 3]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -12,75 +12,81 @@ import 'package:mockito/mockito.dart';
|
||||
])
|
||||
import 'favorite_provider_test.mocks.dart';
|
||||
|
||||
Asset _getTestAsset(String id, bool favorite) {
|
||||
return Asset(
|
||||
remoteId: id,
|
||||
deviceAssetId: '',
|
||||
deviceId: '',
|
||||
ownerId: '',
|
||||
Asset _getTestAsset(int id, bool favorite) {
|
||||
final Asset a = Asset(
|
||||
remoteId: id.toString(),
|
||||
localId: id.toString(),
|
||||
deviceId: 1,
|
||||
ownerId: 1,
|
||||
fileCreatedAt: DateTime.now(),
|
||||
fileModifiedAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
isLocal: false,
|
||||
durationInSeconds: 0,
|
||||
fileName: '',
|
||||
isFavorite: favorite,
|
||||
);
|
||||
a.id = id;
|
||||
return a;
|
||||
}
|
||||
|
||||
void main() {
|
||||
group("Test favoriteProvider", () {
|
||||
|
||||
late MockAssetsState assetsState;
|
||||
late MockAssetNotifier assetNotifier;
|
||||
late ProviderContainer container;
|
||||
late StateNotifierProvider<FavoriteSelectionNotifier, Set<String>> testFavoritesProvider;
|
||||
late StateNotifierProvider<FavoriteSelectionNotifier, Set<int>>
|
||||
testFavoritesProvider;
|
||||
|
||||
setUp(() {
|
||||
assetsState = MockAssetsState();
|
||||
assetNotifier = MockAssetNotifier();
|
||||
container = ProviderContainer();
|
||||
setUp(
|
||||
() {
|
||||
assetsState = MockAssetsState();
|
||||
assetNotifier = MockAssetNotifier();
|
||||
container = ProviderContainer();
|
||||
|
||||
testFavoritesProvider =
|
||||
StateNotifierProvider<FavoriteSelectionNotifier, Set<String>>((ref) {
|
||||
return FavoriteSelectionNotifier(
|
||||
assetsState,
|
||||
assetNotifier,
|
||||
);
|
||||
});
|
||||
},);
|
||||
testFavoritesProvider =
|
||||
StateNotifierProvider<FavoriteSelectionNotifier, Set<int>>((ref) {
|
||||
return FavoriteSelectionNotifier(
|
||||
assetsState,
|
||||
assetNotifier,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test("Empty favorites provider", () {
|
||||
when(assetsState.allAssets).thenReturn([]);
|
||||
expect(<String>{}, container.read(testFavoritesProvider));
|
||||
expect(<int>{}, container.read(testFavoritesProvider));
|
||||
});
|
||||
|
||||
test("Non-empty favorites provider", () {
|
||||
when(assetsState.allAssets).thenReturn([
|
||||
_getTestAsset("001", false),
|
||||
_getTestAsset("002", true),
|
||||
_getTestAsset("003", false),
|
||||
_getTestAsset("004", false),
|
||||
_getTestAsset("005", true),
|
||||
_getTestAsset(1, false),
|
||||
_getTestAsset(2, true),
|
||||
_getTestAsset(3, false),
|
||||
_getTestAsset(4, false),
|
||||
_getTestAsset(5, true),
|
||||
]);
|
||||
|
||||
expect(<String>{"002", "005"}, container.read(testFavoritesProvider));
|
||||
expect(<int>{2, 5}, container.read(testFavoritesProvider));
|
||||
});
|
||||
|
||||
test("Toggle favorite", () {
|
||||
when(assetNotifier.toggleFavorite(null, false))
|
||||
.thenAnswer((_) async => false);
|
||||
|
||||
final testAsset1 = _getTestAsset("001", false);
|
||||
final testAsset2 = _getTestAsset("002", true);
|
||||
final testAsset1 = _getTestAsset(1, false);
|
||||
final testAsset2 = _getTestAsset(2, true);
|
||||
|
||||
when(assetsState.allAssets).thenReturn([testAsset1, testAsset2]);
|
||||
|
||||
expect(<String>{"002"}, container.read(testFavoritesProvider));
|
||||
expect(<int>{2}, container.read(testFavoritesProvider));
|
||||
|
||||
container.read(testFavoritesProvider.notifier).toggleFavorite(testAsset2);
|
||||
expect(<String>{}, container.read(testFavoritesProvider));
|
||||
expect(<int>{}, container.read(testFavoritesProvider));
|
||||
|
||||
container.read(testFavoritesProvider.notifier).toggleFavorite(testAsset1);
|
||||
expect(<String>{"001"}, container.read(testFavoritesProvider));
|
||||
expect(<int>{1}, container.read(testFavoritesProvider));
|
||||
});
|
||||
|
||||
test("Add favorites", () {
|
||||
@@ -89,16 +95,16 @@ void main() {
|
||||
|
||||
when(assetsState.allAssets).thenReturn([]);
|
||||
|
||||
expect(<String>{}, container.read(testFavoritesProvider));
|
||||
expect(<int>{}, container.read(testFavoritesProvider));
|
||||
|
||||
container.read(testFavoritesProvider.notifier).addToFavorites(
|
||||
[
|
||||
_getTestAsset("001", false),
|
||||
_getTestAsset("002", false),
|
||||
_getTestAsset(1, false),
|
||||
_getTestAsset(2, false),
|
||||
],
|
||||
);
|
||||
|
||||
expect(<String>{"001", "002"}, container.read(testFavoritesProvider));
|
||||
expect(<int>{1, 2}, container.read(testFavoritesProvider));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ class MockAssetNotifier extends _i1.Mock implements _i2.AssetNotifier {
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
) as _i5.Future<void>);
|
||||
@override
|
||||
void onNewAssetUploaded(_i4.Asset? newAsset) => super.noSuchMethod(
|
||||
Future<void> onNewAssetUploaded(_i4.Asset? newAsset) => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#onNewAssetUploaded,
|
||||
[newAsset],
|
||||
@@ -195,7 +195,7 @@ class MockAssetNotifier extends _i1.Mock implements _i2.AssetNotifier {
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
@override
|
||||
dynamic deleteAssets(Set<_i4.Asset>? deleteAssets) => super.noSuchMethod(
|
||||
Future<void> deleteAssets(Set<_i4.Asset> deleteAssets) => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#deleteAssets,
|
||||
[deleteAssets],
|
||||
|
||||
Reference in New Issue
Block a user