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

View file

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

View file

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

View file

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

View file

@ -72,16 +72,16 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
purchase.verificationData.serverVerificationData,
);
await InAppPurchaseConnection.instance.completePurchase(purchase);
String text = "thank you for subscribing!";
String text = "Thank you for subscribing!";
if (!widget.isOnboarding) {
final isUpgrade = _hasActiveSubscription &&
newSubscription.storage > _currentSubscription.storage;
final isDowngrade = _hasActiveSubscription &&
newSubscription.storage < _currentSubscription.storage;
if (isUpgrade) {
text = "your plan was successfully upgraded";
text = "Your plan was successfully upgraded";
} else if (isDowngrade) {
text = "your plan was successfully downgraded";
text = "Your plan was successfully downgraded";
}
}
showToast(context, text);
@ -98,8 +98,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
await _dialog.hide();
showErrorDialog(
context,
"payment failed",
"please talk to " +
"Payment failed",
"Please talk to " +
(Platform.isAndroid ? "PlayStore" : "AppStore") +
" support if you were charged",
);
@ -458,7 +458,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
if (_userDetails.subscription.productID == kFreeProductID) {
await showErrorDialog(
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.",
);
return;

View file

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

View file

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

View file

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

View file

@ -43,10 +43,13 @@ class LocationSearchResultWidget extends StatelessWidget {
),
),
const SizedBox(height: 6),
Text(
locationSearchResult.location,
style: const TextStyle(fontSize: 18),
overflow: TextOverflow.ellipsis,
SizedBox(
width: 220,
child: Text(
locationSearchResult.location,
style: const TextStyle(fontSize: 18),
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 2),
RichText(
@ -79,7 +82,6 @@ class LocationSearchResultWidget extends StatelessWidget {
routeToPage(
context,
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,
),
),
const SizedBox(height: 8),
const SizedBox(height: 6),
Text(
yearSearchResult.year.toString(),
style: const TextStyle(fontSize: 18),
@ -78,7 +78,6 @@ class YearSearchResultWidget extends StatelessWidget {
routeToPage(
context,
FilesFromYearPage(yearSearchResult, heroTagPrefix),
forceCustomPageRoute: true,
);
},
);

View file

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

View file

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:photos/ente_theme_data.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/file_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/year_search_result.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/utils/navigation_util.dart';
@ -44,14 +43,15 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
}
class SearchWidget extends StatefulWidget {
final String searchQuery = '';
const SearchWidget({Key key}) : super(key: key);
@override
State<SearchWidget> createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final List<SearchResult> results = [];
String _query = "";
final List<SearchResult> _results = [];
@override
Widget build(BuildContext context) {
return SafeArea(
@ -64,6 +64,11 @@ class _SearchWidgetState extends State<SearchWidget> {
color: Theme.of(context).colorScheme.defaultBackgroundColor,
child: TextFormField(
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(
hintText: 'Search for albums, places & files',
filled: true,
@ -106,17 +111,20 @@ class _SearchWidgetState extends State<SearchWidget> {
await getSearchResultsForQuery(value);
if (mounted) {
setState(() {
results.clear();
results.addAll(allResults);
_query = value;
_results.clear();
_results.addAll(allResults);
});
}
},
autofocus: true,
),
),
results.isNotEmpty
? SearchSuggestionsWidget(results)
: const SizedBox.shrink(),
_results.isNotEmpty
? SearchSuggestionsWidget(_results)
: _query.isNotEmpty
? const NoResultWidget()
: const SizedBox.shrink(),
],
),
),
@ -146,11 +154,6 @@ class _SearchWidgetState extends State<SearchWidget> {
for (LocationSearchResult result in locationResults) {
allResults.add(result);
}
final fileResults =
await SearchService.instance.getFileSearchResults(query);
for (File file in fileResults) {
allResults.add(FileSearchResult(file));
}
return allResults;
}

View file

@ -13,8 +13,8 @@ Future<T> routeToPage<T extends Object>(
);
} else {
return Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
SwipeableRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
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> {
TransparentRoute({
@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.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.6.23+353
version: 0.6.24+354
environment:
sdk: ">=2.10.0 <3.0.0"