Merge pull request #424 from ente-io/search_improvements

Search improvements
This commit is contained in:
Ashil 2022-08-15 18:28:27 +05:30 committed by GitHub
commit 01a5b5dbdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 192 additions and 80 deletions

View file

@ -615,8 +615,9 @@ class FilesDB {
Future<List<File>> getFilesCreatedWithinDurations( Future<List<File>> getFilesCreatedWithinDurations(
List<List<int>> durations, List<List<int>> durations,
Set<int> ignoredCollectionIDs, Set<int> ignoredCollectionIDs, {
) async { String order = 'ASC',
}) async {
final db = await instance.database; final db = await instance.database;
String whereClause = "( "; String whereClause = "( ";
for (int index = 0; index < durations.length; index++) { for (int index = 0; index < durations.length; index++) {
@ -633,7 +634,7 @@ class FilesDB {
final results = await db.query( final results = await db.query(
table, table,
where: whereClause, where: whereClause,
orderBy: '$columnCreationTime ASC', orderBy: '$columnCreationTime ' + order,
); );
final files = _convertToFiles(results); final files = _convertToFiles(results);
return _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs); return _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);

View file

@ -149,6 +149,7 @@ class SearchService {
[yearInMicroseconds, nextYearInMicroseconds] [yearInMicroseconds, nextYearInMicroseconds]
], ],
null, null,
order: 'DESC',
); );
return yearSearchResults; return yearSearchResults;
} }

View file

@ -124,20 +124,22 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
} }
} }
Widget buildThumb() => Container( Widget buildThumb() => Padding(
alignment: Alignment.topRight,
margin: EdgeInsets.only(top: thumbOffset),
padding: widget.padding, padding: widget.padding,
child: ScrollBarThumb( child: Container(
widget.backgroundColor, alignment: Alignment.topRight,
widget.drawColor, margin: EdgeInsets.only(top: thumbOffset),
widget.heightScrollThumb, child: ScrollBarThumb(
widget.labelTextBuilder.call(currentFirstIndex), widget.backgroundColor,
_labelAnimation, widget.drawColor,
_thumbAnimation, widget.heightScrollThumb,
onDragStart, widget.labelTextBuilder.call(currentFirstIndex),
onDragUpdate, _labelAnimation,
onDragEnd, _thumbAnimation,
onDragStart,
onDragUpdate,
onDragEnd,
),
), ),
); );

View file

@ -54,6 +54,8 @@ class HugeListView<T> extends StatefulWidget {
final bool isDraggableScrollbarEnabled; final bool isDraggableScrollbarEnabled;
final EdgeInsetsGeometry thumbPadding;
const HugeListView({ const HugeListView({
Key key, Key key,
this.controller, this.controller,
@ -69,6 +71,7 @@ class HugeListView<T> extends StatefulWidget {
this.thumbDrawColor = Colors.yellow, //Colors.grey, this.thumbDrawColor = Colors.yellow, //Colors.grey,
this.thumbHeight = 48.0, this.thumbHeight = 48.0,
this.isDraggableScrollbarEnabled = true, this.isDraggableScrollbarEnabled = true,
this.thumbPadding,
}) : super(key: key); }) : super(key: key);
@override @override
@ -135,6 +138,7 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
heightScrollThumb: widget.thumbHeight, heightScrollThumb: widget.thumbHeight,
currentFirstIndex: _currentFirst(), currentFirstIndex: _currentFirst(),
isEnabled: widget.isDraggableScrollbarEnabled, isEnabled: widget.isDraggableScrollbarEnabled,
padding: widget.thumbPadding,
child: ScrollablePositionedList.builder( child: ScrollablePositionedList.builder(
itemScrollController: widget.controller, itemScrollController: widget.controller,
itemPositionsListener: listener, itemPositionsListener: listener,

View file

@ -72,16 +72,16 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
purchase.verificationData.serverVerificationData, purchase.verificationData.serverVerificationData,
); );
await InAppPurchaseConnection.instance.completePurchase(purchase); await InAppPurchaseConnection.instance.completePurchase(purchase);
String text = "thank you for subscribing!"; String text = "Thank you for subscribing!";
if (!widget.isOnboarding) { if (!widget.isOnboarding) {
final isUpgrade = _hasActiveSubscription && final isUpgrade = _hasActiveSubscription &&
newSubscription.storage > _currentSubscription.storage; newSubscription.storage > _currentSubscription.storage;
final isDowngrade = _hasActiveSubscription && final isDowngrade = _hasActiveSubscription &&
newSubscription.storage < _currentSubscription.storage; newSubscription.storage < _currentSubscription.storage;
if (isUpgrade) { if (isUpgrade) {
text = "your plan was successfully upgraded"; text = "Your plan was successfully upgraded";
} else if (isDowngrade) { } else if (isDowngrade) {
text = "your plan was successfully downgraded"; text = "Your plan was successfully downgraded";
} }
} }
showToast(context, text); showToast(context, text);
@ -98,8 +98,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
await _dialog.hide(); await _dialog.hide();
showErrorDialog( showErrorDialog(
context, context,
"payment failed", "Payment failed",
"please talk to " + "Please talk to " +
(Platform.isAndroid ? "PlayStore" : "AppStore") + (Platform.isAndroid ? "PlayStore" : "AppStore") +
" support if you were charged", " support if you were charged",
); );
@ -458,7 +458,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
if (_userDetails.subscription.productID == kFreeProductID) { if (_userDetails.subscription.productID == kFreeProductID) {
await showErrorDialog( await showErrorDialog(
context, context,
"Now you can share your storage plan with your family members!", "Share your storage plan with your family members!",
"Customers on paid plans can add up to 5 family members without paying extra. Each member gets their own private space.", "Customers on paid plans can add up to 5 family members without paying extra. Each member gets their own private space.",
); );
return; return;

View file

@ -234,6 +234,9 @@ class _GalleryState extends State<Gallery> {
thumbBackgroundColor: thumbBackgroundColor:
Theme.of(context).colorScheme.galleryThumbBackgroundColor, Theme.of(context).colorScheme.galleryThumbBackgroundColor,
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor, thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
thumbPadding: widget.header != null
? const EdgeInsets.only(top: 60)
: const EdgeInsets.all(0),
firstShown: (int firstIndex) { firstShown: (int firstIndex) {
Bus.instance Bus.instance
.fire(GalleryIndexUpdatedEvent(widget.tagPrefix, firstIndex)); .fire(GalleryIndexUpdatedEvent(widget.tagPrefix, firstIndex));

View file

@ -40,10 +40,13 @@ class AlbumSearchResultWidget extends StatelessWidget {
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( SizedBox(
albumSearchResult.collectionWithThumbnail.collection.name, width: 220,
style: const TextStyle(fontSize: 18), child: Text(
overflow: TextOverflow.ellipsis, albumSearchResult.collectionWithThumbnail.collection.name,
style: const TextStyle(fontSize: 18),
overflow: TextOverflow.ellipsis,
),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
FutureBuilder<int>( FutureBuilder<int>(
@ -92,7 +95,6 @@ class AlbumSearchResultWidget extends StatelessWidget {
albumSearchResult.collectionWithThumbnail, albumSearchResult.collectionWithThumbnail,
tagPrefix: "collection_search", tagPrefix: "collection_search",
), ),
forceCustomPageRoute: true,
); );
}, },
); );

View file

@ -72,6 +72,6 @@ class FileSearchResultWidget extends StatelessWidget {
"file_details", "file_details",
), ),
); );
routeToPage(context, page, forceCustomPageRoute: true); routeToPage(context, page);
} }
} }

View file

@ -43,10 +43,13 @@ class LocationSearchResultWidget extends StatelessWidget {
), ),
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( SizedBox(
locationSearchResult.location, width: 220,
style: const TextStyle(fontSize: 18), child: Text(
overflow: TextOverflow.ellipsis, locationSearchResult.location,
style: const TextStyle(fontSize: 18),
overflow: TextOverflow.ellipsis,
),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
RichText( RichText(
@ -79,7 +82,6 @@ class LocationSearchResultWidget extends StatelessWidget {
routeToPage( routeToPage(
context, context,
FilesInLocationPage(locationSearchResult, heroTagPrefix), FilesInLocationPage(locationSearchResult, heroTagPrefix),
forceCustomPageRoute: true,
); );
}, },
); );

View file

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:photos/ente_theme_data.dart';
class NoResultWidget extends StatelessWidget {
const NoResultWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: const EdgeInsets.only(top: 8),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.searchResultsColor,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: -3,
blurRadius: 6,
offset: const Offset(0, 8),
),
],
),
child: Column(
children: [
const Text("Sorry, no results found"),
const SizedBox(
height: 12,
),
Text(
"Try expanding your query",
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.subTextColor,
),
),
],
),
);
}
}

View file

@ -41,7 +41,7 @@ class YearSearchResultWidget extends StatelessWidget {
color: Theme.of(context).colorScheme.subTextColor, color: Theme.of(context).colorScheme.subTextColor,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 6),
Text( Text(
yearSearchResult.year.toString(), yearSearchResult.year.toString(),
style: const TextStyle(fontSize: 18), style: const TextStyle(fontSize: 18),
@ -78,7 +78,6 @@ class YearSearchResultWidget extends StatelessWidget {
routeToPage( routeToPage(
context, context,
FilesFromYearPage(yearSearchResult, heroTagPrefix), FilesFromYearPage(yearSearchResult, heroTagPrefix),
forceCustomPageRoute: true,
); );
}, },
); );

View file

@ -39,35 +39,37 @@ class SearchSuggestionsWidget extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container( child: Container(
margin: const EdgeInsets.only(top: 6), margin: const EdgeInsets.only(top: 6),
constraints: BoxConstraints( constraints: const BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.5, maxHeight: 324,
), ),
child: ListView.builder( child: Scrollbar(
physics: const ClampingScrollPhysics(), child: ListView.builder(
shrinkWrap: true, physics: const ClampingScrollPhysics(),
itemCount: results.length + 1, shrinkWrap: true,
itemBuilder: (context, index) { itemCount: results.length + 1,
if (results.length == index) { itemBuilder: (context, index) {
return Container( if (results.length == index) {
height: 6, return Container(
color: Theme.of(context).colorScheme.searchResultsColor, height: 6,
); color: Theme.of(context).colorScheme.searchResultsColor,
} );
final result = results[index]; }
if (result is AlbumSearchResult) { final result = results[index];
return AlbumSearchResultWidget(result); if (result is AlbumSearchResult) {
} else if (result is LocationSearchResult) { return AlbumSearchResultWidget(result);
return LocationSearchResultWidget(result); } else if (result is LocationSearchResult) {
} else if (result is FileSearchResult) { return LocationSearchResultWidget(result);
return FileSearchResultWidget(result); } else if (result is FileSearchResult) {
} else if (result is YearSearchResult) { return FileSearchResultWidget(result);
return YearSearchResultWidget(result); } else if (result is YearSearchResult) {
} else { return YearSearchResultWidget(result);
Logger('SearchSuggestionsWidget') } else {
.info("Invalid/Unsupported value"); Logger('SearchSuggestionsWidget')
return const SizedBox.shrink(); .info("Invalid/Unsupported value");
} return const SizedBox.shrink();
}, }
},
),
), ),
), ),
), ),

View file

@ -1,13 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:photos/ente_theme_data.dart'; import 'package:photos/ente_theme_data.dart';
import 'package:photos/models/collection_items.dart'; import 'package:photos/models/collection_items.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/search/album_search_result.dart'; import 'package:photos/models/search/album_search_result.dart';
import 'package:photos/models/search/file_search_result.dart';
import 'package:photos/models/search/location_search_result.dart'; import 'package:photos/models/search/location_search_result.dart';
import 'package:photos/models/search/search_results.dart'; import 'package:photos/models/search/search_results.dart';
import 'package:photos/models/search/year_search_result.dart'; import 'package:photos/models/search/year_search_result.dart';
import 'package:photos/services/search_service.dart'; import 'package:photos/services/search_service.dart';
import 'package:photos/ui/viewer/search/search_result_widgets/no_result_widget.dart';
import 'package:photos/ui/viewer/search/search_suggestions.dart'; import 'package:photos/ui/viewer/search/search_suggestions.dart';
import 'package:photos/utils/navigation_util.dart'; import 'package:photos/utils/navigation_util.dart';
@ -44,14 +43,15 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
} }
class SearchWidget extends StatefulWidget { class SearchWidget extends StatefulWidget {
final String searchQuery = '';
const SearchWidget({Key key}) : super(key: key); const SearchWidget({Key key}) : super(key: key);
@override @override
State<SearchWidget> createState() => _SearchWidgetState(); State<SearchWidget> createState() => _SearchWidgetState();
} }
class _SearchWidgetState extends State<SearchWidget> { class _SearchWidgetState extends State<SearchWidget> {
final List<SearchResult> results = []; String _query = "";
final List<SearchResult> _results = [];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
@ -64,6 +64,11 @@ class _SearchWidgetState extends State<SearchWidget> {
color: Theme.of(context).colorScheme.defaultBackgroundColor, color: Theme.of(context).colorScheme.defaultBackgroundColor,
child: TextFormField( child: TextFormField(
style: Theme.of(context).textTheme.subtitle1, style: Theme.of(context).textTheme.subtitle1,
// Below parameters are to disable auto-suggestion
enableSuggestions: false,
autocorrect: false,
keyboardType: TextInputType.visiblePassword,
// Above parameters are to disable auto-suggestion
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Search for albums, places & files', hintText: 'Search for albums, places & files',
filled: true, filled: true,
@ -106,17 +111,20 @@ class _SearchWidgetState extends State<SearchWidget> {
await getSearchResultsForQuery(value); await getSearchResultsForQuery(value);
if (mounted) { if (mounted) {
setState(() { setState(() {
results.clear(); _query = value;
results.addAll(allResults); _results.clear();
_results.addAll(allResults);
}); });
} }
}, },
autofocus: true, autofocus: true,
), ),
), ),
results.isNotEmpty _results.isNotEmpty
? SearchSuggestionsWidget(results) ? SearchSuggestionsWidget(_results)
: const SizedBox.shrink(), : _query.isNotEmpty
? const NoResultWidget()
: const SizedBox.shrink(),
], ],
), ),
), ),
@ -146,11 +154,6 @@ class _SearchWidgetState extends State<SearchWidget> {
for (LocationSearchResult result in locationResults) { for (LocationSearchResult result in locationResults) {
allResults.add(result); allResults.add(result);
} }
final fileResults =
await SearchService.instance.getFileSearchResults(query);
for (File file in fileResults) {
allResults.add(FileSearchResult(file));
}
return allResults; return allResults;
} }

View file

@ -13,8 +13,8 @@ Future<T> routeToPage<T extends Object>(
); );
} else { } else {
return Navigator.of(context).push( return Navigator.of(context).push(
MaterialPageRoute( SwipeableRouteBuilder(
builder: (BuildContext context) { pageBuilder: (context, animation, secondaryAnimation) {
return page; return page;
}, },
), ),
@ -55,6 +55,57 @@ PageRouteBuilder<T> _buildPageRoute<T extends Object>(Widget page) {
); );
} }
class SwipeableRouteBuilder<T> extends PageRoute<T> {
final RoutePageBuilder pageBuilder;
final PageTransitionsBuilder matchingBuilder =
const CupertinoPageTransitionsBuilder(); // Default iOS/macOS (to get the swipe right to go back gesture)
// final PageTransitionsBuilder matchingBuilder = const FadeUpwardsPageTransitionsBuilder(); // Default Android/Linux/Windows
SwipeableRouteBuilder({this.pageBuilder});
@override
Color get barrierColor => null;
@override
String get barrierLabel => null;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return pageBuilder(context, animation, secondaryAnimation);
}
@override
bool get maintainState => true;
@override
Duration get transitionDuration => const Duration(
milliseconds: 300,
); // Can give custom Duration, unlike in MaterialPageRoute
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return matchingBuilder.buildTransitions<T>(
this,
context,
animation,
secondaryAnimation,
child,
);
}
@override
bool get opaque => false;
}
class TransparentRoute extends PageRoute<void> { class TransparentRoute extends PageRoute<void> {
TransparentRoute({ TransparentRoute({
@required this.builder, @required this.builder,

View file

@ -11,7 +11,7 @@ description: ente photos application
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.6.23+353 version: 0.6.24+354
environment: environment:
sdk: ">=2.10.0 <3.0.0" sdk: ">=2.10.0 <3.0.0"