diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index f710bec06d63a..04eb7dd6efa57 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -376,5 +376,8 @@ "app_bar_signout_dialog_ok": "Yes", "shared_album_activities_input_hint": "Say something", "shared_album_activity_remove_title": "Delete Activity", - "shared_album_activity_remove_content": "Do you want to delete this activity?" + "shared_album_activity_remove_content": "Do you want to delete this activity?", + "shared_album_activity_setting_title": "Comments & likes", + "shared_album_activity_setting_subtitle": "Let others respond", + "shared_album_activities_input_disable": "Comment is disabled" } diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index c6c23d942af29..75168ce1c910e 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -169,4 +169,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382 -COCOAPODS: 1.12.1 +COCOAPODS: 1.11.3 diff --git a/mobile/lib/modules/activities/views/activities_page.dart b/mobile/lib/modules/activities/views/activities_page.dart index 69afe2e5da691..106500cc9721a 100644 --- a/mobile/lib/modules/activities/views/activities_page.dart +++ b/mobile/lib/modules/activities/views/activities_page.dart @@ -19,12 +19,14 @@ class ActivitiesPage extends HookConsumerWidget { final bool withAssetThumbs; final String appBarTitle; final bool isOwner; + final bool isReadOnly; const ActivitiesPage( this.albumId, { this.appBarTitle = "", this.assetId, this.withAssetThumbs = true, this.isOwner = false, + this.isReadOnly = false, super.key, }); @@ -45,6 +47,7 @@ class ActivitiesPage extends HookConsumerWidget { }, [], ); + buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) { final textColor = Theme.of(context).brightness == Brightness.dark ? Colors.white @@ -116,6 +119,7 @@ class ActivitiesPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 10), child: TextField( controller: inputController, + enabled: !isReadOnly, focusNode: inputFocusNode, textInputAction: TextInputAction.send, autofocus: false, @@ -150,7 +154,9 @@ class ActivitiesPage extends HookConsumerWidget { ), ), suffixIconColor: liked ? Colors.red[700] : null, - hintText: 'shared_album_activities_input_hint'.tr(), + hintText: isReadOnly + ? 'shared_album_activities_input_disable'.tr() + : 'shared_album_activities_input_hint'.tr(), hintStyle: TextStyle( fontWeight: FontWeight.normal, fontSize: 14, @@ -240,70 +246,72 @@ class ActivitiesPage extends HookConsumerWidget { a.assetId == assetId, ); - return Stack( - children: [ - ListView.builder( - controller: listViewScrollController, - itemCount: data.length + 1, - itemBuilder: (context, index) { - // Vertical gap after the last element - if (index == data.length) { - return const SizedBox( - height: 80, - ); - } + return SafeArea( + child: Stack( + children: [ + ListView.builder( + controller: listViewScrollController, + itemCount: data.length + 1, + itemBuilder: (context, index) { + // Vertical gap after the last element + if (index == data.length) { + return const SizedBox( + height: 80, + ); + } - final activity = data[index]; - final canDelete = - activity.user.id == currentUser?.id || isOwner; + final activity = data[index]; + final canDelete = + activity.user.id == currentUser?.id || isOwner; - return Padding( - padding: const EdgeInsets.all(5), - child: activity.type == ActivityType.comment - ? getDismissibleWidget( - ListTile( - minVerticalPadding: 15, - leading: UserCircleAvatar(user: activity.user), - title: buildTitleWithTimestamp( - activity, - leftAlign: - withAssetThumbs && activity.assetId != null, + return Padding( + padding: const EdgeInsets.all(5), + child: activity.type == ActivityType.comment + ? getDismissibleWidget( + ListTile( + minVerticalPadding: 15, + leading: UserCircleAvatar(user: activity.user), + title: buildTitleWithTimestamp( + activity, + leftAlign: withAssetThumbs && + activity.assetId != null, + ), + titleAlignment: ListTileTitleAlignment.top, + trailing: buildAssetThumbnail(activity), + subtitle: Text(activity.comment!), ), - titleAlignment: ListTileTitleAlignment.top, - trailing: buildAssetThumbnail(activity), - subtitle: Text(activity.comment!), - ), - activity, - canDelete, - ) - : getDismissibleWidget( - ListTile( - minVerticalPadding: 15, - leading: Container( - width: 44, - alignment: Alignment.center, - child: Icon( - Icons.favorite_rounded, - color: Colors.red[700], + activity, + canDelete, + ) + : getDismissibleWidget( + ListTile( + minVerticalPadding: 15, + leading: Container( + width: 44, + alignment: Alignment.center, + child: Icon( + Icons.favorite_rounded, + color: Colors.red[700], + ), ), + title: buildTitleWithTimestamp(activity), + trailing: buildAssetThumbnail(activity), ), - title: buildTitleWithTimestamp(activity), - trailing: buildAssetThumbnail(activity), + activity, + canDelete, ), - activity, - canDelete, - ), - ); - }, - ), - Align( - alignment: Alignment.bottomCenter, - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: buildTextField(liked?.id), + ); + }, ), - ), - ], + Align( + alignment: Alignment.bottomCenter, + child: Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: buildTextField(liked?.id), + ), + ), + ], + ), ); }, ), diff --git a/mobile/lib/modules/album/providers/shared_album.provider.dart b/mobile/lib/modules/album/providers/shared_album.provider.dart index 4f36c4633d9c1..f8084da00dfa6 100644 --- a/mobile/lib/modules/album/providers/shared_album.provider.dart +++ b/mobile/lib/modules/album/providers/shared_album.provider.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/services/album.service.dart'; import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/asset.dart'; @@ -10,7 +11,7 @@ import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:isar/isar.dart'; class SharedAlbumNotifier extends StateNotifier> { - SharedAlbumNotifier(this._albumService, Isar db) : super([]) { + SharedAlbumNotifier(this._albumService, Isar db, this._ref) : super([]) { final query = db.albums.filter().sharedEqualTo(true).sortByCreatedAtDesc(); query.findAll().then((value) => state = value); _streamSub = query.watch().listen((data) => state = data); @@ -18,6 +19,7 @@ class SharedAlbumNotifier extends StateNotifier> { final AlbumService _albumService; late final StreamSubscription> _streamSub; + final Ref _ref; Future createSharedAlbum( String albumName, @@ -66,6 +68,17 @@ class SharedAlbumNotifier extends StateNotifier> { return result; } + Future setActivityEnabled(Album album, bool activityEnabled) async { + final result = + await _albumService.setActivityEnabled(album, activityEnabled); + + if (result) { + _ref.invalidate(albumDetailProvider(album.id)); + } + + return result; + } + @override void dispose() { _streamSub.cancel(); @@ -78,5 +91,6 @@ final sharedAlbumProvider = return SharedAlbumNotifier( ref.watch(albumServiceProvider), ref.watch(dbProvider), + ref, ); }); diff --git a/mobile/lib/modules/album/services/album.service.dart b/mobile/lib/modules/album/services/album.service.dart index 4488eca23e72a..fdb07f563d11d 100644 --- a/mobile/lib/modules/album/services/album.service.dart +++ b/mobile/lib/modules/album/services/album.service.dart @@ -284,6 +284,23 @@ class AlbumService { return false; } + Future setActivityEnabled(Album album, bool enabled) async { + try { + final result = await _apiService.albumApi.updateAlbumInfo( + album.remoteId!, + UpdateAlbumDto(isActivityEnabled: enabled), + ); + if (result != null) { + album.activityEnabled = enabled; + await _db.writeTxn(() => _db.albums.put(album)); + return true; + } + } catch (e) { + debugPrint("Error setActivityEnabled ${e.toString()}"); + } + return false; + } + Future deleteAlbum(Album album) async { try { final userId = Store.get(StoreKey.currentUser).isarId; diff --git a/mobile/lib/modules/album/ui/album_viewer_appbar.dart b/mobile/lib/modules/album/ui/album_viewer_appbar.dart index 05db82e10897d..3843282ad7c8c 100644 --- a/mobile/lib/modules/album/ui/album_viewer_appbar.dart +++ b/mobile/lib/modules/album/ui/album_viewer_appbar.dart @@ -216,32 +216,36 @@ class AlbumViewerAppbar extends HookConsumerWidget ).tr(), onTap: () => onShareAssetsTo(), ), - album.ownerId == userId ? ListTile( - leading: const Icon(Icons.delete_sweep_rounded), - title: const Text( - 'album_viewer_appbar_share_remove', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - onTap: () => onRemoveFromAlbumPressed(), - ) : const SizedBox(), + album.ownerId == userId + ? ListTile( + leading: const Icon(Icons.delete_sweep_rounded), + title: const Text( + 'album_viewer_appbar_share_remove', + style: TextStyle(fontWeight: FontWeight.bold), + ).tr(), + onTap: () => onRemoveFromAlbumPressed(), + ) + : const SizedBox(), ]; } else { return [ - album.ownerId == userId ? ListTile( - leading: const Icon(Icons.delete_forever_rounded), - title: const Text( - 'album_viewer_appbar_share_delete', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - onTap: () => onDeleteAlbumPressed(), - ) : ListTile( - leading: const Icon(Icons.person_remove_rounded), - title: const Text( - 'album_viewer_appbar_share_leave', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - onTap: () => onLeaveAlbumPressed(), - ), + album.ownerId == userId + ? ListTile( + leading: const Icon(Icons.delete_forever_rounded), + title: const Text( + 'album_viewer_appbar_share_delete', + style: TextStyle(fontWeight: FontWeight.bold), + ).tr(), + onTap: () => onDeleteAlbumPressed(), + ) + : ListTile( + leading: const Icon(Icons.person_remove_rounded), + title: const Text( + 'album_viewer_appbar_share_leave', + style: TextStyle(fontWeight: FontWeight.bold), + ).tr(), + onTap: () => onLeaveAlbumPressed(), + ), ]; } } @@ -390,7 +394,8 @@ class AlbumViewerAppbar extends HookConsumerWidget title: selected.isNotEmpty ? Text('${selected.length}') : null, centerTitle: false, actions: [ - if (album.shared) buildActivitiesButton(), + if (album.shared && (album.activityEnabled || comments != 0)) + buildActivitiesButton(), if (album.isRemote) IconButton( splashRadius: 25, diff --git a/mobile/lib/modules/album/views/album_options_part.dart b/mobile/lib/modules/album/views/album_options_part.dart index eb08b6bda2c98..615fe5c7f2cd3 100644 --- a/mobile/lib/modules/album/views/album_options_part.dart +++ b/mobile/lib/modules/album/views/album_options_part.dart @@ -23,6 +23,7 @@ class AlbumOptionsPage extends HookConsumerWidget { final sharedUsers = useState(album.sharedUsers.toList()); final owner = album.owner.value; final userId = ref.watch(authenticationProvider).userId; + final activityEnabled = useState(album.activityEnabled); final isOwner = owner?.id == userId; void showErrorMessage() { @@ -195,6 +196,31 @@ class AlbumOptionsPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (isOwner && album.shared) + SwitchListTile.adaptive( + value: activityEnabled.value, + onChanged: (bool value) async { + activityEnabled.value = value; + if (await ref + .read(sharedAlbumProvider.notifier) + .setActivityEnabled(album, value)) { + album.activityEnabled = value; + } + }, + activeColor: activityEnabled.value + ? Theme.of(context).primaryColor + : Theme.of(context).disabledColor, + dense: true, + title: Text( + "shared_album_activity_setting_title", + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(fontWeight: FontWeight.bold), + ).tr(), + subtitle: + const Text("shared_album_activity_setting_subtitle").tr(), + ), buildSectionTitle("PEOPLE"), buildOwnerInfo(), buildSharedUsersList(), diff --git a/mobile/lib/modules/album/views/album_viewer_page.dart b/mobile/lib/modules/album/views/album_viewer_page.dart index dc30b3718e0b4..830698b3eccea 100644 --- a/mobile/lib/modules/album/views/album_viewer_page.dart +++ b/mobile/lib/modules/album/views/album_viewer_page.dart @@ -239,6 +239,7 @@ class AlbumViewerPage extends HookConsumerWidget { albumId: album.remoteId!, appBarTitle: album.name, isOwner: userId == album.ownerId, + isReadOnly: !album.activityEnabled, ), ); } @@ -279,7 +280,8 @@ class AlbumViewerPage extends HookConsumerWidget { ], ), isOwner: userId == data.ownerId, - sharedAlbumId: data.remoteId, + sharedAlbumId: + data.shared && data.activityEnabled ? data.remoteId : null, ), ), ), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 885b556436431..c6a54ffcd84ca 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -348,6 +348,7 @@ class _$AppRouter extends RootStackRouter { assetId: args.assetId, withAssetThumbs: args.withAssetThumbs, isOwner: args.isOwner, + isReadOnly: args.isReadOnly, key: args.key, ), transitionsBuilder: TransitionsBuilders.slideLeft, @@ -1568,6 +1569,7 @@ class ActivitiesRoute extends PageRouteInfo { String? assetId, bool withAssetThumbs = true, bool isOwner = false, + bool isReadOnly = false, Key? key, }) : super( ActivitiesRoute.name, @@ -1578,6 +1580,7 @@ class ActivitiesRoute extends PageRouteInfo { assetId: assetId, withAssetThumbs: withAssetThumbs, isOwner: isOwner, + isReadOnly: isReadOnly, key: key, ), ); @@ -1592,6 +1595,7 @@ class ActivitiesRouteArgs { this.assetId, this.withAssetThumbs = true, this.isOwner = false, + this.isReadOnly = false, this.key, }); @@ -1605,11 +1609,13 @@ class ActivitiesRouteArgs { final bool isOwner; + final bool isReadOnly; + final Key? key; @override String toString() { - return 'ActivitiesRouteArgs{albumId: $albumId, appBarTitle: $appBarTitle, assetId: $assetId, withAssetThumbs: $withAssetThumbs, isOwner: $isOwner, key: $key}'; + return 'ActivitiesRouteArgs{albumId: $albumId, appBarTitle: $appBarTitle, assetId: $assetId, withAssetThumbs: $withAssetThumbs, isOwner: $isOwner, isReadOnly: $isReadOnly, key: $key}'; } } diff --git a/mobile/lib/shared/models/album.dart b/mobile/lib/shared/models/album.dart index 1438e6f30c6bb..ceba6074237c3 100644 --- a/mobile/lib/shared/models/album.dart +++ b/mobile/lib/shared/models/album.dart @@ -22,6 +22,7 @@ class Album { this.endDate, this.lastModifiedAssetTimestamp, required this.shared, + required this.activityEnabled, }); Id id = Isar.autoIncrement; @@ -36,6 +37,7 @@ class Album { DateTime? endDate; DateTime? lastModifiedAssetTimestamp; bool shared; + bool activityEnabled; final IsarLink owner = IsarLink(); final IsarLink thumbnail = IsarLink(); final IsarLinks sharedUsers = IsarLinks(); @@ -106,6 +108,7 @@ class Album { modifiedAt.isAtSameMomentAs(other.modifiedAt) && lastModifiedAssetTimestampIsSetAndEqual && shared == other.shared && + activityEnabled == other.activityEnabled && owner.value == other.owner.value && thumbnail.value == other.thumbnail.value && sharedUsers.length == other.sharedUsers.length && @@ -123,6 +126,7 @@ class Album { modifiedAt.hashCode ^ lastModifiedAssetTimestamp.hashCode ^ shared.hashCode ^ + activityEnabled.hashCode ^ owner.value.hashCode ^ thumbnail.value.hashCode ^ sharedUsers.length.hashCode ^ @@ -134,6 +138,7 @@ class Album { createdAt: ape.lastModified?.toUtc() ?? DateTime.now().toUtc(), modifiedAt: ape.lastModified?.toUtc() ?? DateTime.now().toUtc(), shared: false, + activityEnabled: false, ); a.owner.value = Store.get(StoreKey.currentUser); a.localId = ape.id; @@ -151,6 +156,7 @@ class Album { shared: dto.shared, startDate: dto.startDate, endDate: dto.endDate, + activityEnabled: dto.isActivityEnabled, ); a.owner.value = await db.users.getById(dto.ownerId); if (dto.albumThumbnailAssetId != null) { diff --git a/mobile/lib/shared/models/album.g.dart b/mobile/lib/shared/models/album.g.dart index 9cdb59a5e8e2b..e9fcc49aacd6a 100644 --- a/mobile/lib/shared/models/album.g.dart +++ b/mobile/lib/shared/models/album.g.dart @@ -17,48 +17,53 @@ const AlbumSchema = CollectionSchema( name: r'Album', id: -1355968412107120937, properties: { - r'createdAt': PropertySchema( + r'activityEnabled': PropertySchema( id: 0, + name: r'activityEnabled', + type: IsarType.bool, + ), + r'createdAt': PropertySchema( + id: 1, name: r'createdAt', type: IsarType.dateTime, ), r'endDate': PropertySchema( - id: 1, + id: 2, name: r'endDate', type: IsarType.dateTime, ), r'lastModifiedAssetTimestamp': PropertySchema( - id: 2, + id: 3, name: r'lastModifiedAssetTimestamp', type: IsarType.dateTime, ), r'localId': PropertySchema( - id: 3, + id: 4, name: r'localId', type: IsarType.string, ), r'modifiedAt': PropertySchema( - id: 4, + id: 5, name: r'modifiedAt', type: IsarType.dateTime, ), r'name': PropertySchema( - id: 5, + id: 6, name: r'name', type: IsarType.string, ), r'remoteId': PropertySchema( - id: 6, + id: 7, name: r'remoteId', type: IsarType.string, ), r'shared': PropertySchema( - id: 7, + id: 8, name: r'shared', type: IsarType.bool, ), r'startDate': PropertySchema( - id: 8, + id: 9, name: r'startDate', type: IsarType.dateTime, ) @@ -157,15 +162,16 @@ void _albumSerialize( List offsets, Map> allOffsets, ) { - writer.writeDateTime(offsets[0], object.createdAt); - writer.writeDateTime(offsets[1], object.endDate); - writer.writeDateTime(offsets[2], object.lastModifiedAssetTimestamp); - writer.writeString(offsets[3], object.localId); - writer.writeDateTime(offsets[4], object.modifiedAt); - writer.writeString(offsets[5], object.name); - writer.writeString(offsets[6], object.remoteId); - writer.writeBool(offsets[7], object.shared); - writer.writeDateTime(offsets[8], object.startDate); + writer.writeBool(offsets[0], object.activityEnabled); + writer.writeDateTime(offsets[1], object.createdAt); + writer.writeDateTime(offsets[2], object.endDate); + writer.writeDateTime(offsets[3], object.lastModifiedAssetTimestamp); + writer.writeString(offsets[4], object.localId); + writer.writeDateTime(offsets[5], object.modifiedAt); + writer.writeString(offsets[6], object.name); + writer.writeString(offsets[7], object.remoteId); + writer.writeBool(offsets[8], object.shared); + writer.writeDateTime(offsets[9], object.startDate); } Album _albumDeserialize( @@ -175,15 +181,16 @@ Album _albumDeserialize( Map> allOffsets, ) { final object = Album( - createdAt: reader.readDateTime(offsets[0]), - endDate: reader.readDateTimeOrNull(offsets[1]), - lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[2]), - localId: reader.readStringOrNull(offsets[3]), - modifiedAt: reader.readDateTime(offsets[4]), - name: reader.readString(offsets[5]), - remoteId: reader.readStringOrNull(offsets[6]), - shared: reader.readBool(offsets[7]), - startDate: reader.readDateTimeOrNull(offsets[8]), + activityEnabled: reader.readBool(offsets[0]), + createdAt: reader.readDateTime(offsets[1]), + endDate: reader.readDateTimeOrNull(offsets[2]), + lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[3]), + localId: reader.readStringOrNull(offsets[4]), + modifiedAt: reader.readDateTime(offsets[5]), + name: reader.readString(offsets[6]), + remoteId: reader.readStringOrNull(offsets[7]), + shared: reader.readBool(offsets[8]), + startDate: reader.readDateTimeOrNull(offsets[9]), ); object.id = id; return object; @@ -197,22 +204,24 @@ P _albumDeserializeProp

( ) { switch (propertyId) { case 0: - return (reader.readDateTime(offset)) as P; + return (reader.readBool(offset)) as P; case 1: - return (reader.readDateTimeOrNull(offset)) as P; + return (reader.readDateTime(offset)) as P; case 2: return (reader.readDateTimeOrNull(offset)) as P; case 3: - return (reader.readStringOrNull(offset)) as P; + return (reader.readDateTimeOrNull(offset)) as P; case 4: - return (reader.readDateTime(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 5: - return (reader.readString(offset)) as P; + return (reader.readDateTime(offset)) as P; case 6: - return (reader.readStringOrNull(offset)) as P; + return (reader.readString(offset)) as P; case 7: - return (reader.readBool(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 8: + return (reader.readBool(offset)) as P; + case 9: return (reader.readDateTimeOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -442,6 +451,16 @@ extension AlbumQueryWhere on QueryBuilder { } extension AlbumQueryFilter on QueryBuilder { + QueryBuilder activityEnabledEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'activityEnabled', + value: value, + )); + }); + } + QueryBuilder createdAtEqualTo( DateTime value) { return QueryBuilder.apply(this, (query) { @@ -1385,6 +1404,18 @@ extension AlbumQueryLinks on QueryBuilder { } extension AlbumQuerySortBy on QueryBuilder { + QueryBuilder sortByActivityEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'activityEnabled', Sort.asc); + }); + } + + QueryBuilder sortByActivityEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'activityEnabled', Sort.desc); + }); + } + QueryBuilder sortByCreatedAt() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.asc); @@ -1496,6 +1527,18 @@ extension AlbumQuerySortBy on QueryBuilder { } extension AlbumQuerySortThenBy on QueryBuilder { + QueryBuilder thenByActivityEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'activityEnabled', Sort.asc); + }); + } + + QueryBuilder thenByActivityEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'activityEnabled', Sort.desc); + }); + } + QueryBuilder thenByCreatedAt() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.asc); @@ -1619,6 +1662,12 @@ extension AlbumQuerySortThenBy on QueryBuilder { } extension AlbumQueryWhereDistinct on QueryBuilder { + QueryBuilder distinctByActivityEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'activityEnabled'); + }); + } + QueryBuilder distinctByCreatedAt() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'createdAt'); @@ -1684,6 +1733,12 @@ extension AlbumQueryProperty on QueryBuilder { }); } + QueryBuilder activityEnabledProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'activityEnabled'); + }); + } + QueryBuilder createdAtProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'createdAt');