mirror of
https://github.com/KevinMidboe/immich.git
synced 2026-01-05 16:55:48 +00:00
feat(mobile): improve explore page and allow metadata search (#2097)
This commit is contained in:
@@ -27,6 +27,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
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,
|
||||
@@ -46,7 +47,11 @@ class SearchPage extends HookConsumerWidget {
|
||||
searchFocusNode.unfocus();
|
||||
ref.watch(searchPageStateProvider.notifier).disableSearch();
|
||||
|
||||
AutoRouter.of(context).push(SearchResultRoute(searchTerm: searchTerm));
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(
|
||||
searchTerm: searchTerm,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildPlaces() {
|
||||
@@ -67,7 +72,9 @@ class SearchPage extends HookConsumerWidget {
|
||||
imageSize: imageSize,
|
||||
onTap: (content, index) {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(searchTerm: content.label),
|
||||
SearchResultRoute(
|
||||
searchTerm: 'm:${content.label}',
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -99,7 +106,9 @@ class SearchPage extends HookConsumerWidget {
|
||||
imageSize: imageSize,
|
||||
onTap: (content, index) {
|
||||
AutoRouter.of(context).push(
|
||||
SearchResultRoute(searchTerm: content.label),
|
||||
SearchResultRoute(
|
||||
searchTerm: 'm:${content.label}',
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -131,7 +140,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
"search_page_places",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
).tr(),
|
||||
TextButton(
|
||||
child: Text(
|
||||
@@ -162,7 +171,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
"search_page_things",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
).tr(),
|
||||
TextButton(
|
||||
child: Text(
|
||||
@@ -186,7 +195,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'search_page_your_activity',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
).tr(),
|
||||
),
|
||||
ListTile(
|
||||
@@ -201,13 +210,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
const FavoritesRoute(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 72,
|
||||
right: 16,
|
||||
),
|
||||
child: Divider(),
|
||||
),
|
||||
const CategoryDivider(),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.schedule_outlined,
|
||||
@@ -226,9 +229,36 @@ class SearchPage extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Text(
|
||||
'search_page_categories',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
).tr(),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Screenshots', style: categoryTitleStyle).tr(),
|
||||
leading: Icon(
|
||||
Icons.screenshot,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
SearchResultRoute(
|
||||
searchTerm: 'screenshots',
|
||||
),
|
||||
),
|
||||
),
|
||||
const CategoryDivider(),
|
||||
ListTile(
|
||||
title: Text('search_page_selfies', style: categoryTitleStyle)
|
||||
.tr(),
|
||||
leading: Icon(
|
||||
Icons.photo_camera_front_outlined,
|
||||
color: categoryIconColor,
|
||||
),
|
||||
onTap: () => AutoRouter.of(context).push(
|
||||
SearchResultRoute(
|
||||
searchTerm: 'selfies',
|
||||
),
|
||||
),
|
||||
),
|
||||
const CategoryDivider(),
|
||||
ListTile(
|
||||
title: Text('search_page_videos', style: categoryTitleStyle)
|
||||
.tr(),
|
||||
@@ -240,13 +270,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
const AllVideosRoute(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 72,
|
||||
right: 16,
|
||||
),
|
||||
child: Divider(),
|
||||
),
|
||||
const CategoryDivider(),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'search_page_motion_photos',
|
||||
@@ -270,3 +294,20 @@ class SearchPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryDivider extends StatelessWidget {
|
||||
const CategoryDivider({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 72,
|
||||
right: 16,
|
||||
),
|
||||
child: Divider(
|
||||
height: 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,30 @@ 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/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_result_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class SearchType {
|
||||
SearchType({required this.isClip, required this.searchTerm});
|
||||
|
||||
final bool isClip;
|
||||
final String searchTerm;
|
||||
}
|
||||
|
||||
SearchType _getSearchType(String searchTerm) {
|
||||
if (searchTerm.startsWith('m:')) {
|
||||
return SearchType(isClip: false, searchTerm: searchTerm.substring(2));
|
||||
} else {
|
||||
return SearchType(isClip: true, searchTerm: searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchResultPage extends HookConsumerWidget {
|
||||
const SearchResultPage({Key? key, required this.searchTerm})
|
||||
: super(key: key);
|
||||
const SearchResultPage({
|
||||
Key? key,
|
||||
required this.searchTerm,
|
||||
}) : super(key: key);
|
||||
|
||||
final String searchTerm;
|
||||
|
||||
@@ -20,6 +38,8 @@ class SearchResultPage extends HookConsumerWidget {
|
||||
final searchTermController = useTextEditingController(text: "");
|
||||
final isNewSearch = useState(false);
|
||||
final currentSearchTerm = useState(searchTerm);
|
||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
final isDisplayDateGroup = useState(true);
|
||||
|
||||
FocusNode? searchFocusNode;
|
||||
|
||||
@@ -27,9 +47,16 @@ class SearchResultPage extends HookConsumerWidget {
|
||||
() {
|
||||
searchFocusNode = FocusNode();
|
||||
|
||||
var searchType = _getSearchType(searchTerm);
|
||||
searchType.isClip
|
||||
? isDisplayDateGroup.value = false
|
||||
: isDisplayDateGroup.value = true;
|
||||
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => ref.read(searchResultPageProvider.notifier).search(searchTerm),
|
||||
() => ref
|
||||
.read(searchResultPageProvider.notifier)
|
||||
.search(searchType.searchTerm, clipEnable: searchType.isClip),
|
||||
);
|
||||
return () => searchFocusNode?.dispose();
|
||||
},
|
||||
@@ -41,7 +68,15 @@ class SearchResultPage extends HookConsumerWidget {
|
||||
searchFocusNode?.unfocus();
|
||||
isNewSearch.value = false;
|
||||
currentSearchTerm.value = newSearchTerm;
|
||||
ref.watch(searchResultPageProvider.notifier).search(newSearchTerm);
|
||||
|
||||
var searchType = _getSearchType(newSearchTerm);
|
||||
searchType.isClip
|
||||
? isDisplayDateGroup.value = false
|
||||
: isDisplayDateGroup.value = true;
|
||||
|
||||
ref
|
||||
.watch(searchResultPageProvider.notifier)
|
||||
.search(searchType.searchTerm, clipEnable: searchType.isClip);
|
||||
}
|
||||
|
||||
buildTextField() {
|
||||
@@ -74,6 +109,12 @@ class SearchResultPage extends HookConsumerWidget {
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.transparent),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16.0,
|
||||
color:
|
||||
isDarkTheme ? Colors.grey[500] : Colors.black.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -121,11 +162,16 @@ class SearchResultPage extends HookConsumerWidget {
|
||||
return const Center(child: ImmichLoadingIndicator());
|
||||
}
|
||||
|
||||
|
||||
if (searchResultPageState.isSuccess) {
|
||||
return ImmichAssetGrid(
|
||||
if (isDisplayDateGroup.value) {
|
||||
return ImmichAssetGrid(
|
||||
assets: allSearchAssets,
|
||||
);
|
||||
);
|
||||
} else {
|
||||
return SearchResultGrid(
|
||||
assets: allSearchAssets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return const SizedBox();
|
||||
|
||||
Reference in New Issue
Block a user