[mob] Render people section in GridView (#3958)
## Description ## Tests
This commit is contained in:
@@ -223,6 +223,8 @@ PODS:
|
||||
- sqlite3/fts5
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- system_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Toast (4.1.1)
|
||||
- ua_client_hints (1.4.0):
|
||||
- Flutter
|
||||
@@ -290,6 +292,7 @@ DEPENDENCIES:
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
- system_info_plus (from `.symlinks/plugins/system_info_plus/ios`)
|
||||
- ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`)
|
||||
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
@@ -418,6 +421,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
||||
sqlite3_flutter_libs:
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
||||
system_info_plus:
|
||||
:path: ".symlinks/plugins/system_info_plus/ios"
|
||||
ua_client_hints:
|
||||
:path: ".symlinks/plugins/ua_client_hints/ios"
|
||||
uni_links:
|
||||
@@ -501,6 +506,7 @@ SPEC CHECKSUMS:
|
||||
sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
ua_client_hints: 46bb5817a868f9e397c0ba7e3f2f5c5d90c35156
|
||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||
|
||||
@@ -334,6 +334,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/sqflite_darwin/sqflite_darwin.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/system_info_plus/system_info_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/ua_client_hints/ua_client_hints.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/uni_links/uni_links.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework",
|
||||
@@ -428,6 +429,7 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite_darwin.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/system_info_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ua_client_hints.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/uni_links.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework",
|
||||
|
||||
@@ -16,6 +16,7 @@ final lightThemeData = ThemeData(
|
||||
primary: Colors.black,
|
||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||
background: Colors.white,
|
||||
surfaceTint: Colors.transparent,
|
||||
),
|
||||
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||
bgDisabled: const Color.fromRGBO(158, 158, 158, 1),
|
||||
@@ -94,6 +95,7 @@ final darkThemeData = ThemeData(
|
||||
primary: Colors.white,
|
||||
background: Color.fromRGBO(0, 0, 0, 1),
|
||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||
surfaceTint: Colors.transparent,
|
||||
),
|
||||
buttonTheme: const ButtonThemeData().copyWith(
|
||||
buttonColor: const Color.fromRGBO(45, 194, 98, 1.0),
|
||||
|
||||
@@ -313,7 +313,7 @@ class _PersonActionSheetState extends State<PersonActionSheet> {
|
||||
await PersonService.instance.addPerson(text, clusterID);
|
||||
final bool extraPhotosFound = await ClusterFeedbackService.instance
|
||||
.checkAndDoAutomaticMerges(personEntity!,
|
||||
personClusterID: clusterID);
|
||||
personClusterID: clusterID,);
|
||||
if (extraPhotosFound) {
|
||||
showShortToast(context, S.of(context).extraPhotosFound);
|
||||
}
|
||||
|
||||
128
mobile/lib/ui/viewer/search/result/search_people_all_page.dart
Normal file
128
mobile/lib/ui/viewer/search/result/search_people_all_page.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:collection/collection.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_animate/flutter_animate.dart";
|
||||
import "package:photos/events/event.dart";
|
||||
import "package:photos/models/search/generic_search_result.dart";
|
||||
import "package:photos/models/search/search_result.dart";
|
||||
import "package:photos/models/search/search_types.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import "package:photos/ui/viewer/search/result/searchable_item.dart";
|
||||
import "package:photos/ui/viewer/search_tab/people_section.dart";
|
||||
|
||||
class PeopleAllPage extends StatefulWidget {
|
||||
final SectionType sectionType;
|
||||
const PeopleAllPage({required this.sectionType, super.key});
|
||||
|
||||
@override
|
||||
State<PeopleAllPage> createState() => _PeopleAllPageState();
|
||||
}
|
||||
|
||||
class _PeopleAllPageState extends State<PeopleAllPage> {
|
||||
late Future<List<SearchResult>> sectionData;
|
||||
final streamSubscriptions = <StreamSubscription>[];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
sectionData = widget.sectionType.getData(context);
|
||||
final streamsToListenTo = widget.sectionType.viewAllUpdateEvents();
|
||||
for (Stream<Event> stream in streamsToListenTo) {
|
||||
streamSubscriptions.add(
|
||||
stream.listen((event) async {
|
||||
setState(() {
|
||||
sectionData = widget.sectionType.getData(context);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var subscriptions in streamSubscriptions) {
|
||||
subscriptions.cancel();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
toolbarHeight: 48,
|
||||
leadingWidth: 48,
|
||||
leading: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.arrow_back_outlined,
|
||||
),
|
||||
),
|
||||
title: Text(widget.sectionType.sectionTitle(context)),
|
||||
centerTitle: false,
|
||||
),
|
||||
body: Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
child: FutureBuilder(
|
||||
future: sectionData,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final List<SearchResult> sectionResults = snapshot.data!;
|
||||
if (widget.sectionType.sortByName) {
|
||||
sectionResults.sort(
|
||||
(a, b) => compareAsciiLowerCaseNatural(b.name(), a.name()),
|
||||
);
|
||||
}
|
||||
return GridView.builder(
|
||||
padding: const EdgeInsets.all(0),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: MediaQuery.of(context).size.width > 600
|
||||
? 4
|
||||
: 3, // Dynamically adjust columns based on screen width
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 0,
|
||||
childAspectRatio:
|
||||
0.78, // Adjust this value to control item height ratio
|
||||
),
|
||||
itemCount: sectionResults.length,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
cacheExtent:
|
||||
widget.sectionType == SectionType.album ? 400 : null,
|
||||
itemBuilder: (context, index) {
|
||||
Widget resultWidget;
|
||||
if (sectionResults[index] is GenericSearchResult) {
|
||||
resultWidget = PeopleRowItem(
|
||||
searchResult: sectionResults[index],
|
||||
);
|
||||
} else {
|
||||
resultWidget = SearchableItemWidget(
|
||||
sectionResults[index],
|
||||
);
|
||||
}
|
||||
return resultWidget
|
||||
.animate()
|
||||
.fadeIn(
|
||||
duration: const Duration(milliseconds: 225),
|
||||
curve: Curves.easeIn,
|
||||
)
|
||||
.slide(
|
||||
begin: const Offset(0, -0.01),
|
||||
curve: Curves.easeIn,
|
||||
duration: const Duration(milliseconds: 225),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const EnteLoadingWidget();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,10 @@ class SearchableItemWidget extends StatelessWidget {
|
||||
final Function? onResultTap;
|
||||
const SearchableItemWidget(
|
||||
this.searchResult, {
|
||||
Key? key,
|
||||
super.key,
|
||||
this.resultCount,
|
||||
this.onResultTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -19,8 +19,8 @@ import "package:photos/ui/viewer/file/thumbnail_widget.dart";
|
||||
import "package:photos/ui/viewer/people/add_person_action_sheet.dart";
|
||||
import "package:photos/ui/viewer/people/people_page.dart";
|
||||
import 'package:photos/ui/viewer/search/result/person_face_widget.dart';
|
||||
import "package:photos/ui/viewer/search/result/search_people_all_page.dart";
|
||||
import "package:photos/ui/viewer/search/result/search_result_page.dart";
|
||||
import 'package:photos/ui/viewer/search/result/search_section_all_page.dart';
|
||||
import "package:photos/ui/viewer/search/search_section_cta.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
||||
@@ -88,7 +88,7 @@ class _PeopleSectionState extends State<PeopleSection> {
|
||||
if (shouldShowMore) {
|
||||
routeToPage(
|
||||
context,
|
||||
SearchSectionAllPage(
|
||||
PeopleAllPage(
|
||||
sectionType: widget.sectionType,
|
||||
),
|
||||
);
|
||||
@@ -119,7 +119,7 @@ class _PeopleSectionState extends State<PeopleSection> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
SearchExampleRow(_examples, widget.sectionType),
|
||||
PeopleRow(_examples, widget.sectionType),
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -163,11 +163,11 @@ class _PeopleSectionState extends State<PeopleSection> {
|
||||
}
|
||||
}
|
||||
|
||||
class SearchExampleRow extends StatelessWidget {
|
||||
class PeopleRow extends StatelessWidget {
|
||||
final SectionType sectionType;
|
||||
final List<SearchResult> examples;
|
||||
|
||||
const SearchExampleRow(this.examples, this.sectionType, {super.key});
|
||||
const PeopleRow(this.examples, this.sectionType, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -175,7 +175,7 @@ class SearchExampleRow extends StatelessWidget {
|
||||
final scrollableExamples = <Widget>[];
|
||||
examples.forEachIndexed((index, element) {
|
||||
scrollableExamples.add(
|
||||
SearchExample(
|
||||
PeopleRowItem(
|
||||
searchResult: examples.elementAt(index),
|
||||
),
|
||||
);
|
||||
@@ -193,9 +193,9 @@ class SearchExampleRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class SearchExample extends StatelessWidget {
|
||||
class PeopleRowItem extends StatelessWidget {
|
||||
final SearchResult searchResult;
|
||||
const SearchExample({required this.searchResult, super.key});
|
||||
const PeopleRowItem({required this.searchResult, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -204,9 +204,9 @@ class SearchExample extends StatelessWidget {
|
||||
int.tryParse(searchResult.name()) != null);
|
||||
late final double width;
|
||||
if (textScaleFactor <= 1.0) {
|
||||
width = 85.0;
|
||||
width = 120.0;
|
||||
} else {
|
||||
width = 85.0 + ((textScaleFactor - 1.0) * 64);
|
||||
width = 120.0 + ((textScaleFactor - 1.0) * 64);
|
||||
}
|
||||
final heroTag =
|
||||
searchResult.heroTag() + (searchResult.previewThumbnail()?.tag ?? "");
|
||||
@@ -238,19 +238,20 @@ class SearchExample extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 6, right: 6, top: 8),
|
||||
padding: const EdgeInsets.only(left: 4, right: 4, top: 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 64,
|
||||
height: 64,
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: searchResult.previewThumbnail() != null
|
||||
? Hero(
|
||||
tag: heroTag,
|
||||
child: ClipRRect(
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.elliptical(16, 12)),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.elliptical(16, 12),
|
||||
),
|
||||
child: searchResult.type() != ResultType.faces
|
||||
? ThumbnailWidget(
|
||||
searchResult.previewThumbnail()!,
|
||||
@@ -285,7 +286,7 @@ class SearchExample extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 16),
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Text(
|
||||
"Add name",
|
||||
maxLines: 1,
|
||||
@@ -296,7 +297,7 @@ class SearchExample extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 16),
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Text(
|
||||
searchResult.name(),
|
||||
maxLines: 2,
|
||||
|
||||
Reference in New Issue
Block a user