mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat(mobile): Explore favorites, recently added, videos, and motion photos (#2076)
* Added placeholder for search explore * refactor immich asset grid to use ref and provider * all videos page * got favorites, recently added, videos, and motion videos all using the immich grid * Fixed issue with hero animations * theming * localization * delete empty file * style text * Styling icons * more styling --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
d2600e0ddd
commit
501b96baf7
23 changed files with 1013 additions and 524 deletions
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
|
||||
final allMotionPhotosProvider = FutureProvider<List<Asset>>( (ref) async {
|
||||
final search = await ref.watch(apiServiceProvider).searchApi.search(
|
||||
motion: true,
|
||||
);
|
||||
|
||||
if (search == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ref.watch(dbProvider)
|
||||
.assets
|
||||
.getAllByRemoteId(
|
||||
search.assets.items.map((e) => e.id),
|
||||
);
|
||||
|
||||
|
||||
/// This works offline, but we use the above
|
||||
/*
|
||||
return ref.watch(dbProvider).assets
|
||||
.filter()
|
||||
.livePhotoVideoIdIsNotNull()
|
||||
.findAll();
|
||||
*/
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
|
||||
final allVideoAssetsProvider = FutureProvider<List<Asset>>( (ref) async {
|
||||
final search = await ref.watch(apiServiceProvider).searchApi.search(
|
||||
type: 'VIDEO',
|
||||
);
|
||||
|
||||
if (search == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ref.watch(dbProvider)
|
||||
.assets
|
||||
.getAllByRemoteId(
|
||||
search.assets.items.map((e) => e.id),
|
||||
);
|
||||
|
||||
/// This works offline, but we use the above
|
||||
/*
|
||||
return ref.watch(dbProvider).assets
|
||||
.filter()
|
||||
.durationInSecondsGreaterThan(0)
|
||||
.findAll();
|
||||
*/
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
|
||||
final recentlyAddedProvider = FutureProvider<List<Asset>>( (ref) async {
|
||||
final search = await ref.watch(apiServiceProvider).searchApi.search(
|
||||
recent: true,
|
||||
);
|
||||
|
||||
if (search == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ref.watch(dbProvider)
|
||||
.assets
|
||||
.getAllByRemoteId(
|
||||
search.assets.items.map((e) => e.id),
|
||||
);
|
||||
});
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart';
|
||||
|
||||
import 'package:immich_mobile/modules/search/services/search.service.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
|
||||
class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
|
||||
|
|
@ -55,15 +53,6 @@ final searchResultPageProvider =
|
|||
});
|
||||
|
||||
final searchRenderListProvider = FutureProvider((ref) {
|
||||
var settings = ref.watch(appSettingsServiceProvider);
|
||||
|
||||
final assets = ref.watch(searchResultPageProvider).searchResult;
|
||||
|
||||
final layout = AssetGridLayoutParameters(
|
||||
settings.getSetting(AppSettingsEnum.tilesPerRow),
|
||||
settings.getSetting(AppSettingsEnum.dynamicLayout),
|
||||
GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)],
|
||||
);
|
||||
|
||||
return RenderList.fromAssets(assets, layout);
|
||||
return ref.watch(renderListProvider(assets));
|
||||
});
|
||||
|
|
|
|||
67
mobile/lib/modules/search/ui/curated_row.dart
Normal file
67
mobile/lib/modules/search/ui/curated_row.dart
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/modules/search/models/curated_content.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
|
||||
class CuratedRow extends StatelessWidget {
|
||||
final List<CuratedContent> content;
|
||||
final double imageSize;
|
||||
|
||||
/// Callback with the content and the index when tapped
|
||||
final Function(CuratedContent, int)? onTap;
|
||||
|
||||
const CuratedRow({
|
||||
super.key,
|
||||
required this.content,
|
||||
this.imageSize = 200,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Guard empty [content]
|
||||
if (content.isEmpty) {
|
||||
// Return empty thumbnail
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
child: ThumbnailWithInfo(
|
||||
textInfo: '',
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final object = content[index];
|
||||
final thumbnailRequestUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${object.id}';
|
||||
return SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: ThumbnailWithInfo(
|
||||
imageUrl: thumbnailRequestUrl,
|
||||
textInfo: object.label,
|
||||
onTap: () => onTap?.call(object, index),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: content.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
35
mobile/lib/modules/search/views/all_motion_videos_page.dart
Normal file
35
mobile/lib/modules/search/views/all_motion_videos_page.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/all_motion_photos.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class AllMotionPhotosPage extends HookConsumerWidget {
|
||||
const AllMotionPhotosPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final motionPhotos = ref.watch(allMotionPhotosProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('motion_photos_page_title').tr(),
|
||||
leading: IconButton(
|
||||
onPressed: () => AutoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: motionPhotos.when(
|
||||
data: (assets) => ImmichAssetGrid(
|
||||
assets: assets,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
35
mobile/lib/modules/search/views/all_videos_page.dart
Normal file
35
mobile/lib/modules/search/views/all_videos_page.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/all_video_assets.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class AllVideosPage extends HookConsumerWidget {
|
||||
const AllVideosPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final videos = ref.watch(allVideoAssetsProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('all_videos_page_title').tr(),
|
||||
leading: IconButton(
|
||||
onPressed: () => AutoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: videos.when(
|
||||
data: (assets) => ImmichAssetGrid(
|
||||
assets: assets,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
|
@ -25,6 +26,10 @@ class CuratedLocationPage extends HookConsumerWidget {
|
|||
fontSize: 16.0,
|
||||
),
|
||||
).tr(),
|
||||
leading: IconButton(
|
||||
onPressed: () => AutoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: curatedLocation.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
|
@ -28,6 +29,10 @@ class CuratedObjectPage extends HookConsumerWidget {
|
|||
fontSize: 16.0,
|
||||
),
|
||||
).tr(),
|
||||
leading: IconButton(
|
||||
onPressed: () => AutoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: curatedObjects.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
|
|
|
|||
35
mobile/lib/modules/search/views/recently_added_page.dart
Normal file
35
mobile/lib/modules/search/views/recently_added_page.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/recently_added.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class RecentlyAddedPage extends HookConsumerWidget {
|
||||
const RecentlyAddedPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final recents = ref.watch(recentlyAddedProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('recently_added_page_title').tr(),
|
||||
leading: IconButton(
|
||||
onPressed: () => AutoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: recents.when(
|
||||
data: (searchResponse) => ImmichAssetGrid(
|
||||
assets: searchResponse,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,13 @@ import 'package:easy_localization/easy_localization.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/modules/search/models/curated_content.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/curated_row.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/search_bar.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/utils/capitalize_first_letter.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
|
|
@ -26,8 +25,14 @@ class SearchPage extends HookConsumerWidget {
|
|||
ref.watch(getCuratedLocationProvider);
|
||||
AsyncValue<List<CuratedObjectsResponseDto>> curatedObjects =
|
||||
ref.watch(getCuratedObjectProvider);
|
||||
|
||||
var isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
double imageSize = MediaQuery.of(context).size.width / 3;
|
||||
TextStyle categoryTitleStyle = const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
);
|
||||
|
||||
Color categoryIconColor = isDarkTheme ? Colors.white : Colors.black;
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
|
@ -50,100 +55,55 @@ class SearchPage extends HookConsumerWidget {
|
|||
child: curatedLocation.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
error: (err, stack) => Center(child: Text('Error: $err')),
|
||||
data: (curatedLocations) => ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
final locationInfo = curatedLocations[index];
|
||||
final thumbnailRequestUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${locationInfo.id}';
|
||||
return SizedBox(
|
||||
width: imageSize,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: ThumbnailWithInfo(
|
||||
imageUrl: thumbnailRequestUrl,
|
||||
textInfo: locationInfo.city,
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(searchTerm: locationInfo.city),
|
||||
);
|
||||
},
|
||||
data: (locations) => CuratedRow(
|
||||
content: locations
|
||||
.map(
|
||||
(o) => CuratedContent(
|
||||
id: o.id,
|
||||
label: o.city,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
imageSize: imageSize,
|
||||
onTap: (content, index) {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(searchTerm: content.label),
|
||||
);
|
||||
},
|
||||
itemCount: curatedLocations.length.clamp(0, 10),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildEmptyThumbnail() {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
child: ThumbnailWithInfo(
|
||||
textInfo: '',
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildThings() {
|
||||
return curatedObjects.when(
|
||||
loading: () => SizedBox(
|
||||
height: imageSize,
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
error: (err, stack) => SizedBox(
|
||||
height: imageSize,
|
||||
child: Center(child: Text('Error: $err')),
|
||||
),
|
||||
data: (objects) => objects.isEmpty
|
||||
? buildEmptyThumbnail()
|
||||
: SizedBox(
|
||||
height: imageSize,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
return SizedBox(
|
||||
height: imageSize,
|
||||
child: curatedObjects.when(
|
||||
loading: () => SizedBox(
|
||||
height: imageSize,
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
error: (err, stack) => SizedBox(
|
||||
height: imageSize,
|
||||
child: Center(child: Text('Error: $err')),
|
||||
),
|
||||
data: (objects) => CuratedRow(
|
||||
content: objects
|
||||
.map(
|
||||
(o) => CuratedContent(
|
||||
id: o.id,
|
||||
label: o.object,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final curatedObjectInfo = objects[index];
|
||||
final thumbnailRequestUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${curatedObjectInfo.id}';
|
||||
return SizedBox(
|
||||
width: imageSize,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: ThumbnailWithInfo(
|
||||
imageUrl: thumbnailRequestUrl,
|
||||
textInfo: curatedObjectInfo.object,
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(
|
||||
searchTerm: curatedObjectInfo.object
|
||||
.capitalizeFirstLetter(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: objects.length.clamp(0, 10),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
imageSize: imageSize,
|
||||
onTap: (content, index) {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(searchTerm: content.label),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -169,12 +129,9 @@ class SearchPage extends HookConsumerWidget {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
"search_page_places",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
).tr(),
|
||||
TextButton(
|
||||
child: Text(
|
||||
|
|
@ -194,19 +151,18 @@ class SearchPage extends HookConsumerWidget {
|
|||
),
|
||||
buildPlaces(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 4.0,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 24.0,
|
||||
bottom: 4.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
"search_page_things",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
).tr(),
|
||||
TextButton(
|
||||
child: Text(
|
||||
|
|
@ -225,6 +181,85 @@ class SearchPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
buildThings(),
|
||||
const SizedBox(height: 24.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'search_page_your_activity',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
).tr(),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.star_outline,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
title:
|
||||
Text('search_page_favorites', style: categoryTitleStyle)
|
||||
.tr(),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
const FavoritesRoute(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 72,
|
||||
right: 16,
|
||||
),
|
||||
child: Divider(),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.schedule_outlined,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
title: Text(
|
||||
'search_page_recently_added',
|
||||
style: categoryTitleStyle,
|
||||
).tr(),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
const RecentlyAddedRoute(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Text(
|
||||
'search_page_categories',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
).tr(),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('search_page_videos', style: categoryTitleStyle)
|
||||
.tr(),
|
||||
leading: Icon(
|
||||
Icons.play_circle_outline,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
const AllVideosRoute(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 72,
|
||||
right: 16,
|
||||
),
|
||||
child: Divider(),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'search_page_motion_photos',
|
||||
style: categoryTitleStyle,
|
||||
).tr(),
|
||||
leading: Icon(
|
||||
Icons.motion_photos_on_outlined,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
const AllMotionPhotosRoute(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isSearchEnabled)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'
|
|||
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/search_result_page.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class SearchResultPage extends HookConsumerWidget {
|
||||
|
|
@ -110,14 +108,8 @@ class SearchResultPage extends HookConsumerWidget {
|
|||
|
||||
buildSearchResult() {
|
||||
var searchResultPageState = ref.watch(searchResultPageProvider);
|
||||
var searchResultRenderList = ref.watch(searchRenderListProvider);
|
||||
var allSearchAssets = ref.watch(searchResultPageProvider).searchResult;
|
||||
|
||||
var settings = ref.watch(appSettingsServiceProvider);
|
||||
final assetsPerRow = settings.getSetting(AppSettingsEnum.tilesPerRow);
|
||||
final showStorageIndicator =
|
||||
settings.getSetting(AppSettingsEnum.storageIndicator);
|
||||
|
||||
if (searchResultPageState.isError) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
|
|
@ -129,22 +121,10 @@ class SearchResultPage extends HookConsumerWidget {
|
|||
return const Center(child: ImmichLoadingIndicator());
|
||||
}
|
||||
|
||||
|
||||
if (searchResultPageState.isSuccess) {
|
||||
return searchResultRenderList.when(
|
||||
data: (result) {
|
||||
return ImmichAssetGrid(
|
||||
allAssets: allSearchAssets,
|
||||
renderList: result,
|
||||
assetsPerRow: assetsPerRow,
|
||||
showStorageIndicator: showStorageIndicator,
|
||||
);
|
||||
},
|
||||
error: (err, stack) {
|
||||
return Text("$err");
|
||||
},
|
||||
loading: () {
|
||||
return const CircularProgressIndicator();
|
||||
},
|
||||
return ImmichAssetGrid(
|
||||
assets: allSearchAssets,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue