mirror of
https://github.com/immich-app/immich.git
synced 2025-06-14 11:07:49 -04:00
add albums to dedicated item menu
This commit is contained in:
parent
bee7cd9683
commit
e69bcf3f70
@ -8,11 +8,14 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
import 'package:immich_mobile/main.dart';
|
||||||
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/albumv2.provider.dart';
|
import 'package:immich_mobile/providers/album/albumv2.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||||
|
|
||||||
enum QuickFilterMode {
|
enum QuickFilterMode {
|
||||||
@ -23,7 +26,10 @@ enum QuickFilterMode {
|
|||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AlbumsCollectionPage extends HookConsumerWidget {
|
class AlbumsCollectionPage extends HookConsumerWidget {
|
||||||
const AlbumsCollectionPage({super.key});
|
const AlbumsCollectionPage({super.key, this.showImmichAppbar = false});
|
||||||
|
|
||||||
|
final bool showImmichAppbar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final albums =
|
final albums =
|
||||||
@ -72,7 +78,13 @@ class AlbumsCollectionPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Albums ${albums.length}"),
|
title: showImmichAppbar ? null : Text('albums'.tr()),
|
||||||
|
bottom: showImmichAppbar
|
||||||
|
? const PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(0),
|
||||||
|
child: ImmichAppBar(),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -170,9 +182,11 @@ class AlbumsCollectionPage extends HookConsumerWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
child: ListTile(
|
child: LargeLeadingTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
sorted[index].name,
|
sorted[index].name,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
@ -180,6 +194,7 @@ class AlbumsCollectionPage extends HookConsumerWidget {
|
|||||||
subtitle: sorted[index].ownerId == userId
|
subtitle: sorted[index].ownerId == userId
|
||||||
? Text(
|
? Text(
|
||||||
'${sorted[index].assetCount} items',
|
'${sorted[index].assetCount} items',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color:
|
color:
|
||||||
context.colorScheme.onSurfaceSecondary,
|
context.colorScheme.onSurfaceSecondary,
|
||||||
@ -192,6 +207,7 @@ class AlbumsCollectionPage extends HookConsumerWidget {
|
|||||||
sorted[index].ownerName!,
|
sorted[index].ownerName!,
|
||||||
],
|
],
|
||||||
)}',
|
)}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodyMedium
|
style: context.textTheme.bodyMedium
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
color: context
|
color: context
|
||||||
@ -202,19 +218,19 @@ class AlbumsCollectionPage extends HookConsumerWidget {
|
|||||||
onTap: () => context.pushRoute(
|
onTap: () => context.pushRoute(
|
||||||
AlbumViewerRoute(albumId: sorted[index].id),
|
AlbumViewerRoute(albumId: sorted[index].id),
|
||||||
),
|
),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
leadingPadding: const EdgeInsets.only(
|
||||||
dense: false,
|
right: 16,
|
||||||
visualDensity: VisualDensity.comfortable,
|
),
|
||||||
leading: ClipRRect(
|
leading: ClipRRect(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
const BorderRadius.all(Radius.circular(15)),
|
const BorderRadius.all(Radius.circular(15)),
|
||||||
child: ImmichThumbnail(
|
child: ImmichThumbnail(
|
||||||
asset: sorted[index].thumbnail.value,
|
asset: sorted[index].thumbnail.value,
|
||||||
width: 60,
|
width: 80,
|
||||||
height: 90,
|
height: 80,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
minVerticalPadding: 1,
|
// minVerticalPadding: 1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -154,11 +154,11 @@ class AlbumsCollectionCard extends HookConsumerWidget {
|
|||||||
final size = MediaQuery.of(context).size.width * 0.5 - 20;
|
final size = MediaQuery.of(context).size.width * 0.5 - 20;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.pushRoute(
|
onTap: () => isLocal
|
||||||
isLocal
|
? context.pushRoute(
|
||||||
? const LocalAlbumsCollectionRoute()
|
const LocalAlbumsCollectionRoute(),
|
||||||
: const AlbumsCollectionRoute(),
|
)
|
||||||
),
|
: context.pushRoute(AlbumsCollectionRoute()),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -6,8 +6,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/search/search_curated_content.model.dart';
|
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
import 'package:immich_mobile/providers/search/search_page_state.provider.dart';
|
import 'package:immich_mobile/providers/search/search_page_state.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
@ -100,33 +100,25 @@ class PlaceTile extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return InkWell(
|
return LargeLeadingTile(
|
||||||
onTap: () => navigateToPlace(),
|
onTap: () => navigateToPlace(),
|
||||||
child: Row(
|
title: Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
name,
|
||||||
children: [
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
Padding(
|
fontWeight: FontWeight.w500,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16.0),
|
),
|
||||||
child: ClipRRect(
|
),
|
||||||
borderRadius: BorderRadius.circular(20),
|
leading: ClipRRect(
|
||||||
child: CachedNetworkImage(
|
borderRadius: BorderRadius.circular(20),
|
||||||
width: 80,
|
child: CachedNetworkImage(
|
||||||
height: 80,
|
width: 80,
|
||||||
fit: BoxFit.cover,
|
height: 80,
|
||||||
imageUrl: thumbnailUrl,
|
fit: BoxFit.cover,
|
||||||
httpHeaders: ApiService.getRequestHeaders(),
|
imageUrl: thumbnailUrl,
|
||||||
errorWidget: (context, url, error) =>
|
httpHeaders: ApiService.getRequestHeaders(),
|
||||||
const Icon(Icons.image_not_supported_outlined),
|
errorWidget: (context, url, error) =>
|
||||||
),
|
const Icon(Icons.image_not_supported_outlined),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
47
mobile/lib/pages/common/large_leading_tile.dart
Normal file
47
mobile/lib/pages/common/large_leading_tile.dart
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LargeLeadingTile extends StatelessWidget {
|
||||||
|
const LargeLeadingTile({
|
||||||
|
super.key,
|
||||||
|
required this.leading,
|
||||||
|
required this.onTap,
|
||||||
|
required this.title,
|
||||||
|
this.subtitle,
|
||||||
|
this.leadingPadding = const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget leading;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final Widget title;
|
||||||
|
final Widget? subtitle;
|
||||||
|
final EdgeInsetsGeometry leadingPadding;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: leadingPadding,
|
||||||
|
child: leading,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: title,
|
||||||
|
),
|
||||||
|
subtitle ?? const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,18 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
selectedIcon: const Icon(Icons.photo_library),
|
selectedIcon: const Icon(Icons.photo_library),
|
||||||
label: const Text('tab_controller_nav_photos').tr(),
|
label: const Text('tab_controller_nav_photos').tr(),
|
||||||
),
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
icon: const Icon(Icons.photo_album_outlined),
|
||||||
|
selectedIcon: const Icon(Icons.photo_album),
|
||||||
|
label: const Text('albums').tr(),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
icon: const Icon(Icons.space_dashboard_outlined),
|
||||||
|
selectedIcon: const Icon(Icons.space_dashboard_rounded),
|
||||||
|
label: const Text('collections').tr(),
|
||||||
|
),
|
||||||
NavigationRailDestination(
|
NavigationRailDestination(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
icon: const Icon(Icons.search_rounded),
|
icon: const Icon(Icons.search_rounded),
|
||||||
@ -94,18 +106,6 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
// selectedIcon: const Icon(Icons.photo_album),
|
// selectedIcon: const Icon(Icons.photo_album),
|
||||||
// label: const Text('tab_controller_nav_library').tr(),
|
// label: const Text('tab_controller_nav_library').tr(),
|
||||||
// ),
|
// ),
|
||||||
NavigationRailDestination(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
icon: const Icon(Icons.photo_album_outlined),
|
|
||||||
selectedIcon: const Icon(Icons.photo_album),
|
|
||||||
label: const Text('albums').tr(),
|
|
||||||
),
|
|
||||||
NavigationRailDestination(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
icon: const Icon(Icons.space_dashboard_outlined),
|
|
||||||
selectedIcon: const Icon(Icons.space_dashboard_rounded),
|
|
||||||
label: const Text('collections').tr(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -136,6 +136,30 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'albums'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.photo_album_outlined,
|
||||||
|
),
|
||||||
|
selectedIcon: buildIcon(
|
||||||
|
Icon(
|
||||||
|
Icons.photo_album_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'collections'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.space_dashboard_outlined,
|
||||||
|
),
|
||||||
|
selectedIcon: buildIcon(
|
||||||
|
Icon(
|
||||||
|
Icons.space_dashboard_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
label: 'tab_controller_nav_search'.tr(),
|
label: 'tab_controller_nav_search'.tr(),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
@ -168,43 +192,19 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
NavigationDestination(
|
|
||||||
label: 'albums'.tr(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.photo_album_outlined,
|
|
||||||
),
|
|
||||||
selectedIcon: buildIcon(
|
|
||||||
Icon(
|
|
||||||
Icons.photo_album_rounded,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
NavigationDestination(
|
|
||||||
label: 'collections'.tr(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.space_dashboard_outlined,
|
|
||||||
),
|
|
||||||
selectedIcon: buildIcon(
|
|
||||||
Icon(
|
|
||||||
Icons.space_dashboard_rounded,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final multiselectEnabled = ref.watch(multiselectProvider);
|
final multiselectEnabled = ref.watch(multiselectProvider);
|
||||||
return AutoTabsRouter(
|
return AutoTabsRouter(
|
||||||
routes: const [
|
routes: [
|
||||||
PhotosRoute(),
|
const PhotosRoute(),
|
||||||
SearchRoute(),
|
AlbumsCollectionRoute(showImmichAppbar: true),
|
||||||
|
const CollectionsRoute(),
|
||||||
// SharingRoute(),
|
// SharingRoute(),
|
||||||
// LibraryRoute(),
|
// LibraryRoute(),
|
||||||
AlbumsCollectionRoute(),
|
const SearchRoute(),
|
||||||
CollectionsRoute(),
|
|
||||||
],
|
],
|
||||||
duration: const Duration(milliseconds: 600),
|
duration: const Duration(milliseconds: 600),
|
||||||
transitionBuilder: (context, child, animation) => FadeTransition(
|
transitionBuilder: (context, child, animation) => FadeTransition(
|
||||||
|
@ -321,10 +321,17 @@ class AlbumViewerRouteArgs {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [AlbumsCollectionPage]
|
/// [AlbumsCollectionPage]
|
||||||
class AlbumsCollectionRoute extends PageRouteInfo<void> {
|
class AlbumsCollectionRoute extends PageRouteInfo<AlbumsCollectionRouteArgs> {
|
||||||
const AlbumsCollectionRoute({List<PageRouteInfo>? children})
|
AlbumsCollectionRoute({
|
||||||
: super(
|
Key? key,
|
||||||
|
bool showImmichAppbar = false,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
AlbumsCollectionRoute.name,
|
AlbumsCollectionRoute.name,
|
||||||
|
args: AlbumsCollectionRouteArgs(
|
||||||
|
key: key,
|
||||||
|
showImmichAppbar: showImmichAppbar,
|
||||||
|
),
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -333,11 +340,32 @@ class AlbumsCollectionRoute extends PageRouteInfo<void> {
|
|||||||
static PageInfo page = PageInfo(
|
static PageInfo page = PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const AlbumsCollectionPage();
|
final args = data.argsAs<AlbumsCollectionRouteArgs>(
|
||||||
|
orElse: () => const AlbumsCollectionRouteArgs());
|
||||||
|
return AlbumsCollectionPage(
|
||||||
|
key: args.key,
|
||||||
|
showImmichAppbar: args.showImmichAppbar,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AlbumsCollectionRouteArgs {
|
||||||
|
const AlbumsCollectionRouteArgs({
|
||||||
|
this.key,
|
||||||
|
this.showImmichAppbar = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final bool showImmichAppbar;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AlbumsCollectionRouteArgs{key: $key, showImmichAppbar: $showImmichAppbar}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [AllMotionPhotosPage]
|
/// [AllMotionPhotosPage]
|
||||||
class AllMotionPhotosRoute extends PageRouteInfo<void> {
|
class AllMotionPhotosRoute extends PageRouteInfo<void> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user