Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions mobile/lib/domain/services/remote_album.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ class RemoteAlbumService {
return _repository.getCount();
}

Future<List<RemoteAlbum>> getAlbumsContainingAsset(String assetId) {
return _repository.getAlbumsContainingAsset(assetId);
}

Future<List<RemoteAlbum>> _sortByNewestAsset(List<RemoteAlbum> albums) async {
// map album IDs to their newest asset dates
final Map<String, Future<DateTime?>> assetTimestampFutures = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,61 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {

return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get();
}

Future<List<RemoteAlbum>> getAlbumsContainingAsset(String assetId) async {
// Note: this needs to be 2 queries as the where clause filtering causes the assetCount to always be 1
final albumIdsQuery = _db.remoteAlbumAssetEntity.selectOnly()
..addColumns([_db.remoteAlbumAssetEntity.albumId])
..where(_db.remoteAlbumAssetEntity.assetId.equals(assetId));

final albumIds = await albumIdsQuery.map((row) => row.read(_db.remoteAlbumAssetEntity.albumId)!).get();

if (albumIds.isEmpty) {
return [];
}

final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false,
),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin(
_db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
])
..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..addColumns([_db.userEntity.name])
..groupBy([_db.remoteAlbumEntity.id]);

return query
.map(
(row) => row
.readTable(_db.remoteAlbumEntity)
.toDto(
ownerName: row.read(_db.userEntity.name) ?? '',
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
assetCount: row.read(assetCount) ?? 0,
),
)
.get();
}
}

extension on RemoteAlbumEntityData {
Expand Down
113 changes: 59 additions & 54 deletions mobile/lib/presentation/pages/drift_activities.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
Expand All @@ -18,12 +19,13 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart';

@RoutePage()
class DriftActivitiesPage extends HookConsumerWidget {
const DriftActivitiesPage({super.key});
final RemoteAlbum album;

const DriftActivitiesPage({super.key, required this.album});

@override
Widget build(BuildContext context, WidgetRef ref) {
final album = ref.watch(currentRemoteAlbumProvider)!;
final asset = ref.read(currentAssetNotifier) as RemoteAsset?;
final asset = ref.watch(currentAssetNotifier) as RemoteAsset?;
final user = ref.watch(currentUserProvider);

final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier);
Expand All @@ -43,62 +45,65 @@ class DriftActivitiesPage extends HookConsumerWidget {
scrollToBottom();
}

return Scaffold(
appBar: AppBar(
title: asset == null ? Text(album.name) : null,
actions: [const LikeActivityActionButton(menuItem: true)],
actionsPadding: const EdgeInsets.only(right: 8),
),
body: activities.widgetWhen(
onData: (data) {
final liked = data.firstWhereOrNull(
(a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.id,
);
return ProviderScope(
overrides: [currentRemoteAlbumScopedProvider.overrideWithValue(album)],
child: Scaffold(
appBar: AppBar(
title: asset == null ? Text(album.name) : null,
actions: [const LikeActivityActionButton(menuItem: true)],
actionsPadding: const EdgeInsets.only(right: 8),
),
body: activities.widgetWhen(
onData: (data) {
final liked = data.firstWhereOrNull(
(a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.id,
);

return SafeArea(
child: Stack(
children: [
ListView.builder(
controller: listViewScrollController,
itemCount: data.length + 1,
itemBuilder: (context, index) {
if (index == data.length) {
return const SizedBox(height: 80);
}
final activity = data[index];
final canDelete = activity.user.id == user?.id || album.ownerId == user?.id;
return Padding(
padding: const EdgeInsets.all(5),
child: DismissibleActivity(
activity.id,
ActivityTile(activity),
onDismiss: canDelete
? (activityId) async => await activityNotifier.removeActivity(activity.id)
: null,
return SafeArea(
child: Stack(
children: [
ListView.builder(
controller: listViewScrollController,
itemCount: data.length + 1,
itemBuilder: (context, index) {
if (index == data.length) {
return const SizedBox(height: 80);
}
final activity = data[index];
final canDelete = activity.user.id == user?.id || album.ownerId == user?.id;
return Padding(
padding: const EdgeInsets.all(5),
child: DismissibleActivity(
activity.id,
ActivityTile(activity),
onDismiss: canDelete
? (activityId) async => await activityNotifier.removeActivity(activity.id)
: null,
),
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
border: Border(top: BorderSide(color: context.colorScheme.secondaryContainer, width: 1)),
),
child: DriftActivityTextField(
isEnabled: album.isActivityEnabled,
likeId: liked?.id,
onSubmit: onAddComment,
),
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
border: Border(top: BorderSide(color: context.colorScheme.secondaryContainer, width: 1)),
),
child: DriftActivityTextField(
isEnabled: album.isActivityEnabled,
likeId: liked?.id,
onSubmit: onAddComment,
),
),
),
],
),
);
},
],
),
);
},
),
resizeToAvoidBottomInset: true,
),
resizeToAvoidBottomInset: true,
);
}
}
2 changes: 0 additions & 2 deletions mobile/lib/presentation/pages/drift_album.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';

Expand Down Expand Up @@ -43,7 +42,6 @@ class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
),
AlbumSelector(
onAlbumSelected: (album) {
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
context.router.push(RemoteAlbumRoute(album: album));
},
),
Expand Down
92 changes: 46 additions & 46 deletions mobile/lib/presentation/pages/drift_album_options.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
Expand All @@ -22,15 +23,11 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';

@RoutePage()
class DriftAlbumOptionsPage extends HookConsumerWidget {
const DriftAlbumOptionsPage({super.key});
final RemoteAlbum album;
const DriftAlbumOptionsPage({super.key, required this.album});

@override
Widget build(BuildContext context, WidgetRef ref) {
final album = ref.watch(currentRemoteAlbumProvider);
if (album == null) {
return const SizedBox();
}

final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(album.id));
final userId = ref.watch(authProvider).userId;
final activityEnabled = useState(album.isActivityEnabled);
Expand Down Expand Up @@ -191,48 +188,51 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
);
}

return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded),
onPressed: () => context.maybePop(null),
return ProviderScope(
overrides: [currentRemoteAlbumScopedProvider.overrideWithValue(album)],
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded),
onPressed: () => context.maybePop(null),
),
centerTitle: true,
title: Text("options".t(context: context)),
),
centerTitle: true,
title: Text("options".t(context: context)),
),
body: ListView(
children: [
const SizedBox(height: 8),
if (isOwner)
SwitchListTile.adaptive(
value: activityEnabled.value,
onChanged: (bool value) async {
activityEnabled.value = value;
await ref.read(remoteAlbumProvider.notifier).setActivityStatus(album.id, value);
},
activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
dense: true,
title: Text(
"comments_and_likes",
style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
).t(context: context),
subtitle: Text(
"let_others_respond",
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary),
).t(context: context),
),
buildSectionTitle("shared_album_section_people_title".t(context: context)),
if (isOwner) ...[
ListTile(
leading: const Icon(Icons.person_add_rounded),
title: Text("invite_people".t(context: context)),
onTap: () async => addUsers(),
),
const Divider(indent: 16),
body: ListView(
children: [
const SizedBox(height: 8),
if (isOwner)
SwitchListTile.adaptive(
value: activityEnabled.value,
onChanged: (bool value) async {
activityEnabled.value = value;
await ref.read(remoteAlbumProvider.notifier).setActivityStatus(album.id, value);
},
activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
dense: true,
title: Text(
"comments_and_likes",
style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
).t(context: context),
subtitle: Text(
"let_others_respond",
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary),
).t(context: context),
),
buildSectionTitle("shared_album_section_people_title".t(context: context)),
if (isOwner) ...[
ListTile(
leading: const Icon(Icons.person_add_rounded),
title: Text("invite_people".t(context: context)),
onTap: () async => addUsers(),
),
const Divider(indent: 16),
],
buildOwnerInfo(),
buildSharedUsersList(),
],
buildOwnerInfo(),
buildSharedUsersList(),
],
),
),
);
}
Expand Down
2 changes: 0 additions & 2 deletions mobile/lib/presentation/pages/drift_create_album.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';

Expand Down Expand Up @@ -180,7 +179,6 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
);

if (album != null) {
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
unawaited(context.replaceRoute(RemoteAlbumRoute(album: album)));
}
}
Expand Down
Loading
Loading