feat(mobile): unify partner assets on timeline (#4974)

* feat(mobile): unify partner assets on timeline

* skip non-owned assets in bulk actions

* add message when trying to delete partner assets
This commit is contained in:
Fynn Petersen-Frey
2023-11-13 16:54:41 +01:00
committed by GitHub
parent 0c482960ce
commit 9fa9ad05b1
17 changed files with 274 additions and 138 deletions
+6 -5
View File
@@ -31,7 +31,8 @@ class User {
isPartnerSharedWith = false,
profileImagePath = dto.profileImagePath,
isAdmin = dto.isAdmin,
memoryEnabled = dto.memoriesEnabled;
memoryEnabled = dto.memoriesEnabled ?? false,
inTimeline = false;
User.fromPartnerDto(PartnerResponseDto dto)
: id = dto.id,
@@ -42,8 +43,8 @@ class User {
isPartnerSharedWith = false,
profileImagePath = dto.profileImagePath,
isAdmin = dto.isAdmin,
memoryEnabled = dto.memoriesEnabled,
inTimeline = dto.inTimeline;
memoryEnabled = dto.memoriesEnabled ?? false,
inTimeline = dto.inTimeline ?? false;
@Index(unique: true, replace: false, type: IndexType.hash)
String id;
@@ -54,8 +55,8 @@ class User {
bool isPartnerSharedWith;
bool isAdmin;
String profileImagePath;
bool? memoryEnabled;
bool? inTimeline;
bool memoryEnabled;
bool inTimeline;
@Backlink(to: 'owner')
final IsarLinks<Album> albums = IsarLinks<Album>();
+8 -40
View File
@@ -151,11 +151,11 @@ User _userDeserialize(
final object = User(
email: reader.readString(offsets[0]),
id: reader.readString(offsets[1]),
inTimeline: reader.readBoolOrNull(offsets[2]),
inTimeline: reader.readBoolOrNull(offsets[2]) ?? false,
isAdmin: reader.readBool(offsets[3]),
isPartnerSharedBy: reader.readBoolOrNull(offsets[4]) ?? false,
isPartnerSharedWith: reader.readBoolOrNull(offsets[5]) ?? false,
memoryEnabled: reader.readBoolOrNull(offsets[6]),
memoryEnabled: reader.readBoolOrNull(offsets[6]) ?? true,
name: reader.readString(offsets[7]),
profileImagePath: reader.readStringOrNull(offsets[8]) ?? '',
updatedAt: reader.readDateTime(offsets[9]),
@@ -175,7 +175,7 @@ P _userDeserializeProp<P>(
case 1:
return (reader.readString(offset)) as P;
case 2:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readBoolOrNull(offset) ?? false) as P;
case 3:
return (reader.readBool(offset)) as P;
case 4:
@@ -183,7 +183,7 @@ P _userDeserializeProp<P>(
case 5:
return (reader.readBoolOrNull(offset) ?? false) as P;
case 6:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readBoolOrNull(offset) ?? true) as P;
case 7:
return (reader.readString(offset)) as P;
case 8:
@@ -638,24 +638,8 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
});
}
QueryBuilder<User, User, QAfterFilterCondition> inTimelineIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'inTimeline',
));
});
}
QueryBuilder<User, User, QAfterFilterCondition> inTimelineIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'inTimeline',
));
});
}
QueryBuilder<User, User, QAfterFilterCondition> inTimelineEqualTo(
bool? value) {
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'inTimeline',
@@ -745,24 +729,8 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
});
}
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'memoryEnabled',
));
});
}
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'memoryEnabled',
));
});
}
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledEqualTo(
bool? value) {
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'memoryEnabled',
@@ -1540,7 +1508,7 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
});
}
QueryBuilder<User, bool?, QQueryOperations> inTimelineProperty() {
QueryBuilder<User, bool, QQueryOperations> inTimelineProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'inTimeline');
});
@@ -1564,7 +1532,7 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
});
}
QueryBuilder<User, bool?, QQueryOperations> memoryEnabledProperty() {
QueryBuilder<User, bool, QQueryOperations> memoryEnabledProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'memoryEnabled');
});
@@ -54,6 +54,7 @@ class AppStateNotiifer extends StateNotifier<AppStateEnum> {
switch (ref.read(tabProvider)) {
case TabEnum.home:
ref.read(assetProvider.notifier).getAllAsset();
ref.read(assetProvider.notifier).getPartnerAssets();
case TabEnum.search:
// nothing to do
case TabEnum.sharing:
+32 -22
View File
@@ -8,12 +8,11 @@ import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:immich_mobile/shared/providers/user.provider.dart';
import 'package:immich_mobile/shared/services/asset.service.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/services/sync.service.dart';
import 'package:immich_mobile/shared/services/user.service.dart';
import 'package:immich_mobile/utils/db.dart';
import 'package:immich_mobile/utils/renderlist_generator.dart';
import 'package:isar/isar.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
@@ -251,26 +250,23 @@ final assetWatcher =
return db.assets.watchObject(asset.id, fireImmediately: true);
});
final assetsProvider =
StreamProvider.family<RenderList, int?>((ref, userId) async* {
if (userId == null) return;
final query = ref
.watch(dbProvider)
.assets
.where()
.ownerIdEqualToAnyChecksum(userId)
.filter()
.isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.stackParentIdIsNull()
.sortByFileCreatedAtDesc();
final settings = ref.watch(appSettingsServiceProvider);
final groupBy =
GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)];
yield await RenderList.fromQuery(query, groupBy);
await for (final _ in query.watchLazy()) {
yield await RenderList.fromQuery(query, groupBy);
}
final assetsProvider = StreamProvider.family<RenderList, int?>((ref, userId) {
if (userId == null) return const Stream.empty();
final query = _commonFilterAndSort(
_assets(ref).where().ownerIdEqualToAnyChecksum(userId),
);
return renderListGenerator(query, ref);
});
final multiUserAssetsProvider =
StreamProvider.family<RenderList, List<int>>((ref, userIds) {
if (userIds.isEmpty) return const Stream.empty();
final query = _commonFilterAndSort(
_assets(ref)
.where()
.anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u)),
);
return renderListGenerator(query, ref);
});
QueryBuilder<Asset, Asset, QAfterSortBy>? getRemoteAssetQuery(WidgetRef ref) {
@@ -289,3 +285,17 @@ QueryBuilder<Asset, Asset, QAfterSortBy>? getRemoteAssetQuery(WidgetRef ref) {
.stackParentIdIsNull()
.sortByFileCreatedAtDesc();
}
IsarCollection<Asset> _assets(StreamProviderRef<RenderList> ref) =>
ref.watch(dbProvider).assets;
QueryBuilder<Asset, Asset, QAfterSortBy> _commonFilterAndSort(
QueryBuilder<Asset, Asset, QAfterWhereClause> query,
) {
return query
.filter()
.isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.stackParentIdIsNull()
.sortByFileCreatedAtDesc();
}
@@ -3,6 +3,8 @@ import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/models/user.dart';
import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:isar/isar.dart';
class CurrentUserProvider extends StateNotifier<User?> {
CurrentUserProvider() : super(null) {
@@ -24,3 +26,32 @@ final currentUserProvider =
StateNotifierProvider<CurrentUserProvider, User?>((ref) {
return CurrentUserProvider();
});
class TimelineUserIdsProvider extends StateNotifier<List<int>> {
TimelineUserIdsProvider(Isar db, User? currentUser) : super([]) {
final query = db.users
.filter()
.inTimelineEqualTo(true)
.or()
.isarIdEqualTo(currentUser?.isarId ?? Isar.autoIncrement)
.isarIdProperty();
query.findAll().then((users) => state = users);
streamSub = query.watch().listen((users) => state = users);
}
late final StreamSubscription<List<int>> streamSub;
@override
void dispose() {
streamSub.cancel();
super.dispose();
}
}
final timelineUsersIdsProvider =
StateNotifierProvider<TimelineUserIdsProvider, List<int>>((ref) {
return TimelineUserIdsProvider(
ref.watch(dbProvider),
ref.watch(currentUserProvider),
);
});
+5 -1
View File
@@ -99,7 +99,11 @@ class UserService {
users,
sharedWith,
compare: (User a, User b) => a.id.compareTo(b.id),
both: (User a, User b) => a.isPartnerSharedWith = true,
both: (User a, User b) {
a.isPartnerSharedWith = true;
a.inTimeline = b.inTimeline;
return true;
},
onlyFirst: (_) {},
onlySecond: (_) {},
);