Merge pull request #424 from ente-io/search_improvements
Search improvements
This commit is contained in:
commit
01a5b5dbdd
|
@ -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);
|
||||
|
|
|
@ -149,6 +149,7 @@ class SearchService {
|
|||
[yearInMicroseconds, nextYearInMicroseconds]
|
||||
],
|
||||
null,
|
||||
order: 'DESC',
|
||||
);
|
||||
return yearSearchResults;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -72,6 +72,6 @@ class FileSearchResultWidget extends StatelessWidget {
|
|||
"file_details",
|
||||
),
|
||||
);
|
||||
routeToPage(context, page, forceCustomPageRoute: true);
|
||||
routeToPage(context, page);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue