mirror of
https://github.com/immich-app/immich.git
synced 2026-02-28 17:49:05 +03:00
Some widgets, like Icon widgets, automatically inherit opacity from the icon theme in the context. Many other widgets however, do not. The Immich logo, profile picture, and backup badge are examples of widgets of this. All unsupported toolbar widgets have been updated to support inheriting the opacity from the icon theme. IconButtons internally animate properties like opacity, which is kind of nice, but means we have to do more work to replicate that behaviour for other widgets. In most cases, we can simply use an IconButton widget and forward the correct opacity. The Immich logo however is not a button, and therefore we need to use a custom TweenAnimationBuilder. All widgets are using efficient, native opacity rather than the heavy Opacity widget.
193 lines
6.5 KiB
Dart
193 lines
6.5 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:auto_route/auto_route.dart';
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
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/user.model.dart';
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
|
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
|
import 'package:immich_mobile/routing/router.dart';
|
|
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
|
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
|
|
|
@RoutePage()
|
|
class AlbumOptionsPage extends HookConsumerWidget {
|
|
const AlbumOptionsPage({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final album = ref.watch(currentAlbumProvider);
|
|
if (album == null) {
|
|
return const SizedBox();
|
|
}
|
|
|
|
final sharedUsers = useState(album.sharedUsers.map((u) => u.toDto()).toList());
|
|
final owner = album.owner.value;
|
|
final userId = ref.watch(authProvider).userId;
|
|
final activityEnabled = useState(album.activityEnabled);
|
|
final isProcessing = useProcessingOverlay();
|
|
final isOwner = owner?.id == userId;
|
|
|
|
void showErrorMessage() {
|
|
context.pop();
|
|
ImmichToast.show(
|
|
context: context,
|
|
msg: "shared_album_section_people_action_error".tr(),
|
|
toastType: ToastType.error,
|
|
gravity: ToastGravity.BOTTOM,
|
|
);
|
|
}
|
|
|
|
void leaveAlbum() async {
|
|
isProcessing.value = true;
|
|
|
|
try {
|
|
final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album);
|
|
|
|
if (isSuccess) {
|
|
unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])));
|
|
} else {
|
|
showErrorMessage();
|
|
}
|
|
} catch (_) {
|
|
showErrorMessage();
|
|
}
|
|
|
|
isProcessing.value = false;
|
|
}
|
|
|
|
void removeUserFromAlbum(UserDto user) async {
|
|
isProcessing.value = true;
|
|
|
|
try {
|
|
await ref.read(albumProvider.notifier).removeUser(album, user);
|
|
album.sharedUsers.remove(entity.User.fromDto(user));
|
|
sharedUsers.value = album.sharedUsers.map((u) => u.toDto()).toList();
|
|
} catch (error) {
|
|
showErrorMessage();
|
|
}
|
|
|
|
context.pop();
|
|
isProcessing.value = false;
|
|
}
|
|
|
|
void handleUserClick(UserDto user) {
|
|
var actions = [];
|
|
|
|
if (user.id == userId) {
|
|
actions = [
|
|
ListTile(
|
|
leading: const Icon(Icons.exit_to_app_rounded),
|
|
title: const Text("shared_album_section_people_action_leave").tr(),
|
|
onTap: leaveAlbum,
|
|
),
|
|
];
|
|
}
|
|
|
|
if (isOwner) {
|
|
actions = [
|
|
ListTile(
|
|
leading: const Icon(Icons.person_remove_rounded),
|
|
title: const Text("shared_album_section_people_action_remove_user").tr(),
|
|
onTap: () => removeUserFromAlbum(user),
|
|
),
|
|
];
|
|
}
|
|
|
|
showModalBottomSheet(
|
|
backgroundColor: context.colorScheme.surfaceContainer,
|
|
isScrollControlled: false,
|
|
context: context,
|
|
builder: (context) {
|
|
return SafeArea(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 24.0),
|
|
child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
buildOwnerInfo() {
|
|
return ListTile(
|
|
leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(),
|
|
title: Text(album.owner.value?.name ?? "", style: const TextStyle(fontWeight: FontWeight.w500)),
|
|
subtitle: Text(album.owner.value?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)),
|
|
trailing: Text("owner", style: context.textTheme.labelLarge).tr(),
|
|
);
|
|
}
|
|
|
|
buildSharedUsersList() {
|
|
return ListView.builder(
|
|
primary: false,
|
|
shrinkWrap: true,
|
|
itemCount: sharedUsers.value.length,
|
|
itemBuilder: (context, index) {
|
|
final user = sharedUsers.value[index];
|
|
return ListTile(
|
|
leading: UserCircleAvatar(user: user),
|
|
title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)),
|
|
subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)),
|
|
trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(),
|
|
onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
buildSectionTitle(String text) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Text(text, style: context.textTheme.bodySmall),
|
|
);
|
|
}
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
|
onPressed: () => context.maybePop(null),
|
|
),
|
|
centerTitle: true,
|
|
title: Text("options".tr()),
|
|
),
|
|
body: ListView(
|
|
children: [
|
|
if (isOwner && album.shared)
|
|
SwitchListTile.adaptive(
|
|
value: activityEnabled.value,
|
|
onChanged: (bool value) async {
|
|
activityEnabled.value = value;
|
|
if (await ref.read(albumProvider.notifier).setActivitystatus(album, value)) {
|
|
album.activityEnabled = value;
|
|
}
|
|
},
|
|
activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
|
|
dense: true,
|
|
title: Text(
|
|
"comments_and_likes",
|
|
style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
|
).tr(),
|
|
subtitle: Text(
|
|
"let_others_respond",
|
|
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
|
).tr(),
|
|
),
|
|
buildSectionTitle("shared_album_section_people_title".tr()),
|
|
buildOwnerInfo(),
|
|
buildSharedUsersList(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|