chore: bump dart sdk to 3.8 (#20355)
* chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -14,10 +14,7 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
||||
class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
final Album album;
|
||||
|
||||
const AlbumAdditionalSharedUserSelectionPage({
|
||||
super.key,
|
||||
required this.album,
|
||||
});
|
||||
const AlbumAdditionalSharedUserSelectionPage({super.key, required this.album});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -30,17 +27,9 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
buildTileIcon(UserDto user) {
|
||||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
),
|
||||
);
|
||||
return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25));
|
||||
} else {
|
||||
return UserCircleAvatar(
|
||||
user: user,
|
||||
);
|
||||
return UserCircleAvatar(user: user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,31 +42,19 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withValues(alpha: 0.15),
|
||||
label: Text(
|
||||
user.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return ListView(
|
||||
children: [
|
||||
Wrap(
|
||||
children: [...usersChip],
|
||||
),
|
||||
Wrap(children: [...usersChip]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'suggestions'.tr(),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
ListView.builder(
|
||||
@@ -87,31 +64,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
return ListTile(
|
||||
leading: buildTileIcon(users[index]),
|
||||
dense: true,
|
||||
title: Text(
|
||||
users[index].name,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
users[index].email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
if (sharedUsersList.value.contains(users[index])) {
|
||||
sharedUsersList.value = sharedUsersList.value
|
||||
.where(
|
||||
(selectedUser) => selectedUser.id != users[index].id,
|
||||
)
|
||||
.where((selectedUser) => selectedUser.id != users[index].id)
|
||||
.toSet();
|
||||
} else {
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index],
|
||||
};
|
||||
sharedUsersList.value = {...sharedUsersList.value, users[index]};
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -124,9 +85,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'invite_to_album',
|
||||
).tr(),
|
||||
title: const Text('invite_to_album').tr(),
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
leading: IconButton(
|
||||
@@ -138,19 +97,14 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler,
|
||||
child: const Text(
|
||||
"add",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: suggestedShareUsers.widgetWhen(
|
||||
onData: (users) {
|
||||
for (var sharedUsers in album.sharedUsers) {
|
||||
users.removeWhere(
|
||||
(u) => u.id == sharedUsers.id || u.id == album.ownerId,
|
||||
);
|
||||
users.removeWhere((u) => u.id == sharedUsers.id || u.id == album.ownerId);
|
||||
}
|
||||
|
||||
return buildUserList(users);
|
||||
|
||||
@@ -13,11 +13,7 @@ import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
||||
|
||||
@RoutePage()
|
||||
class AlbumAssetSelectionPage extends HookConsumerWidget {
|
||||
const AlbumAssetSelectionPage({
|
||||
super.key,
|
||||
required this.existingAssets,
|
||||
this.canDeselect = false,
|
||||
});
|
||||
const AlbumAssetSelectionPage({super.key, required this.existingAssets, this.canDeselect = false});
|
||||
|
||||
final Set<Asset> existingAssets;
|
||||
final bool canDeselect;
|
||||
@@ -52,10 +48,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
title: selected.value.isEmpty
|
||||
? const Text(
|
||||
'add_photos',
|
||||
style: TextStyle(fontSize: 18),
|
||||
).tr()
|
||||
? const Text('add_photos', style: TextStyle(fontSize: 18)).tr()
|
||||
: const Text(
|
||||
'share_assets_selected',
|
||||
style: TextStyle(fontSize: 18),
|
||||
@@ -70,17 +63,12 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
|
||||
},
|
||||
child: Text(
|
||||
canDeselect ? "done" : "add",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: assetSelectionRenderList.widgetWhen(
|
||||
onData: (data) => buildBody(data),
|
||||
),
|
||||
body: assetSelectionRenderList.widgetWhen(onData: (data) => buildBody(data)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,7 @@ class AlbumControlButton extends ConsumerWidget {
|
||||
final void Function()? onAddPhotosPressed;
|
||||
final void Function()? onAddUsersPressed;
|
||||
|
||||
const AlbumControlButton({
|
||||
super.key,
|
||||
this.onAddPhotosPressed,
|
||||
this.onAddUsersPressed,
|
||||
});
|
||||
const AlbumControlButton({super.key, this.onAddPhotosPressed, this.onAddUsersPressed});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
@@ -33,9 +33,7 @@ class AlbumDateRange extends ConsumerWidget {
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
_getDateRangeText(startDate, endDate),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -46,8 +44,9 @@ class AlbumDateRange extends ConsumerWidget {
|
||||
return DateFormat.yMMMd().format(startDate);
|
||||
}
|
||||
|
||||
final String startDateText =
|
||||
(startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format(startDate);
|
||||
final String startDateText = (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format(
|
||||
startDate,
|
||||
);
|
||||
final String endDateText = DateFormat.yMMMd().format(endDate);
|
||||
return "$startDateText - $endDateText";
|
||||
}
|
||||
|
||||
@@ -36,10 +36,7 @@ class AlbumDescription extends ConsumerWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||
child: Text(
|
||||
albumDescription ?? 'add_a_description'.tr(),
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
child: Text(albumDescription ?? 'add_a_description'.tr(), style: context.textTheme.bodyLarge),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album);
|
||||
|
||||
if (isSuccess) {
|
||||
context.navigateTo(
|
||||
const TabControllerRoute(children: [AlbumsRoute()]),
|
||||
);
|
||||
context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]));
|
||||
} else {
|
||||
showErrorMessage();
|
||||
}
|
||||
@@ -110,10 +108,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [...actions],
|
||||
),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -123,20 +118,9 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
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(),
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -148,22 +132,9 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final user = sharedUsers.value[index];
|
||||
return ListTile(
|
||||
leading: UserCircleAvatar(
|
||||
user: user,
|
||||
radius: 22,
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
user.email,
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
),
|
||||
leading: UserCircleAvatar(user: user, radius: 22),
|
||||
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,
|
||||
);
|
||||
@@ -206,9 +177,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
).tr(),
|
||||
subtitle: Text(
|
||||
"let_others_respond",
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||
).tr(),
|
||||
),
|
||||
buildSectionTitle("shared_album_section_people_title".tr()),
|
||||
|
||||
@@ -41,11 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: UserCircleAvatar(
|
||||
user: sharedUsers.value[index],
|
||||
radius: 18,
|
||||
size: 36,
|
||||
),
|
||||
child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36),
|
||||
);
|
||||
}),
|
||||
itemCount: sharedUsers.value.length,
|
||||
|
||||
@@ -25,10 +25,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
final suggestedShareUsers = ref.watch(otherUsersProvider);
|
||||
|
||||
createSharedAlbum() async {
|
||||
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(
|
||||
ref.watch(albumTitleProvider),
|
||||
assets,
|
||||
);
|
||||
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(ref.watch(albumTitleProvider), assets);
|
||||
|
||||
if (newAlbum != null) {
|
||||
ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
|
||||
@@ -40,9 +37,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
child: SnackBar(
|
||||
content: Text(
|
||||
'select_user_for_sharing_page_err_album',
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
);
|
||||
@@ -50,17 +45,9 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
buildTileIcon(UserDto user) {
|
||||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
),
|
||||
);
|
||||
return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25));
|
||||
} else {
|
||||
return UserCircleAvatar(
|
||||
user: user,
|
||||
);
|
||||
return UserCircleAvatar(user: user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +62,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
backgroundColor: context.primaryColor.withValues(alpha: 0.15),
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -87,18 +70,12 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
}
|
||||
return ListView(
|
||||
children: [
|
||||
Wrap(
|
||||
children: [...usersChip],
|
||||
),
|
||||
Wrap(children: [...usersChip]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const Text(
|
||||
'suggestions',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
ListView.builder(
|
||||
@@ -107,25 +84,14 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
itemBuilder: ((context, index) {
|
||||
return ListTile(
|
||||
leading: buildTileIcon(users[index]),
|
||||
title: Text(
|
||||
users[index].email,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
title: Text(users[index].email, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
onTap: () {
|
||||
if (sharedUsersList.value.contains(users[index])) {
|
||||
sharedUsersList.value = sharedUsersList.value
|
||||
.where(
|
||||
(selectedUser) => selectedUser.id != users[index].id,
|
||||
)
|
||||
.where((selectedUser) => selectedUser.id != users[index].id)
|
||||
.toSet();
|
||||
} else {
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index],
|
||||
};
|
||||
sharedUsersList.value = {...sharedUsersList.value, users[index]};
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -138,10 +104,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'invite_to_album',
|
||||
style: TextStyle(color: context.primaryColor),
|
||||
).tr(),
|
||||
title: Text('invite_to_album', style: TextStyle(color: context.primaryColor)).tr(),
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
leading: IconButton(
|
||||
@@ -152,9 +115,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: context.primaryColor,
|
||||
),
|
||||
style: TextButton.styleFrom(foregroundColor: context.primaryColor),
|
||||
onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum,
|
||||
child: const Text(
|
||||
"create_album",
|
||||
|
||||
@@ -19,32 +19,20 @@ class AlbumTitle extends ConsumerWidget {
|
||||
return const (false, false, '');
|
||||
}
|
||||
|
||||
return (
|
||||
album.ownerId == userId,
|
||||
album.isRemote,
|
||||
album.name,
|
||||
);
|
||||
return (album.ownerId == userId, album.isRemote, album.name);
|
||||
}),
|
||||
);
|
||||
|
||||
if (isOwner && isRemote) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: AlbumViewerEditableTitle(
|
||||
albumName: albumName,
|
||||
titleFocusNode: titleFocusNode,
|
||||
),
|
||||
child: AlbumViewerEditableTitle(albumName: albumName, titleFocusNode: titleFocusNode),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||
child: Text(
|
||||
albumName,
|
||||
style: context.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
child: Text(albumName, style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,10 +65,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
/// If they exist, add to selected asset state to show they are already selected.
|
||||
void onAddPhotosPressed() async {
|
||||
AssetSelectionPageResult? returnPayload = await context.pushRoute<AssetSelectionPageResult?>(
|
||||
AlbumAssetSelectionRoute(
|
||||
existingAssets: album.assets,
|
||||
canDeselect: false,
|
||||
),
|
||||
AlbumAssetSelectionRoute(existingAssets: album.assets, canDeselect: false),
|
||||
);
|
||||
|
||||
if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) {
|
||||
@@ -98,9 +95,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
onActivitiesPressed() {
|
||||
if (album.remoteId != null) {
|
||||
ref.read(currentAssetProvider.notifier).set(null);
|
||||
context.pushRoute(
|
||||
const ActivitiesRoute(),
|
||||
);
|
||||
context.pushRoute(const ActivitiesRoute());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +124,8 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
children: [
|
||||
const SizedBox(height: 32),
|
||||
const AlbumDateRange(),
|
||||
AlbumTitle(
|
||||
key: const ValueKey("albumTitle"),
|
||||
titleFocusNode: titleFocusNode,
|
||||
),
|
||||
AlbumDescription(
|
||||
key: const ValueKey("albumDescription"),
|
||||
descriptionFocusNode: descriptionFocusNode,
|
||||
),
|
||||
AlbumTitle(key: const ValueKey("albumTitle"), titleFocusNode: titleFocusNode),
|
||||
AlbumDescription(key: const ValueKey("albumDescription"), descriptionFocusNode: descriptionFocusNode),
|
||||
const AlbumSharedUserIcons(),
|
||||
if (album.isRemote)
|
||||
Padding(
|
||||
|
||||
@@ -21,9 +21,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
ref.listen(assetSelectionTimelineProvider, (_, __) {});
|
||||
|
||||
ref.listen(albumWatcher(albumId), (_, albumFuture) {
|
||||
albumFuture.whenData(
|
||||
(value) => ref.read(currentAlbumProvider.notifier).set(value),
|
||||
);
|
||||
albumFuture.whenData((value) => ref.read(currentAlbumProvider.notifier).set(value));
|
||||
});
|
||||
|
||||
return const Scaffold(body: AlbumViewer());
|
||||
|
||||
Reference in New Issue
Block a user