Browse Source

Merge pull request #416 from ente-io/file-name-search

File name search
Ashil 3 years ago
parent
commit
8b32152bba

+ 11 - 0
lib/db/files_db.dart

@@ -1142,6 +1142,17 @@ class FilesDB {
     return result;
   }
 
+  Future<List<File>> getFilesOnFileNameSearch(String query) async {
+    final db = await instance.database;
+    final results = await db.query(
+      table,
+      where: '$columnTitle LIKE ?',
+      whereArgs: ["%$query%"],
+    );
+    final files = _convertToFiles(results);
+    return files;
+  }
+
   List<File> _convertToFiles(List<Map<String, dynamic>> results) {
     final List<File> files = [];
     for (final result in results) {

+ 6 - 4
lib/services/collections_service.dart

@@ -203,10 +203,12 @@ class CollectionsService {
         .toList();
     List<CollectionWithThumbnail> result = [];
     for (Collection collection in matchedCollection) {
-      result.add(CollectionWithThumbnail(
-        collection,
-        collectionIDToLatestFileMap[collection.id],
-      ));
+      result.add(
+        CollectionWithThumbnail(
+          collection,
+          collectionIDToLatestFileMap[collection.id],
+        ),
+      );
     }
     return result;
   }

+ 1 - 1
lib/ui/huge_listview/lazy_loading_gallery.dart

@@ -145,7 +145,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
   @override
   Widget build(BuildContext context) {
     if (_files.isEmpty) {
-      return Container();
+      return const SizedBox.shrink();
     }
     return Padding(
       padding: const EdgeInsets.only(bottom: 12),

+ 57 - 42
lib/ui/viewer/search/collection_result_widget.dart

@@ -19,56 +19,71 @@ class CollectionResultWidget extends StatelessWidget {
         child: Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           children: [
-            Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              children: [
-                const Text(
-                  'Album',
-                  style: TextStyle(fontSize: 12),
-                ),
-                const SizedBox(height: 8),
-                Text(
-                  c.collection.name,
-                  style: const TextStyle(fontSize: 18),
-                ),
-                FutureBuilder<int>(
-                  future: FilesDB.instance.collectionFileCount(
-                    c.collection.id,
+            Flexible(
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  const Text(
+                    'Album',
+                    style: TextStyle(fontSize: 12),
                   ),
-                  builder: (context, snapshot) {
-                    if (snapshot.hasData && snapshot.data > 0) {
-                      int noOfMemories = snapshot.data;
-                      return RichText(
-                        text: TextSpan(
-                          style: TextStyle(
-                            color:
-                                Theme.of(context).colorScheme.defaultTextColor,
-                          ),
-                          children: [
-                            TextSpan(text: noOfMemories.toString()),
-                            TextSpan(
-                              text: noOfMemories != 1 ? ' memories' : ' memory',
+                  const SizedBox(height: 8),
+                  Text(
+                    c.collection.name,
+                    style: const TextStyle(fontSize: 18),
+                    overflow: TextOverflow.ellipsis,
+                  ),
+                  FutureBuilder<int>(
+                    future: FilesDB.instance.collectionFileCount(
+                      c.collection.id,
+                    ),
+                    builder: (context, snapshot) {
+                      if (snapshot.hasData && snapshot.data > 0) {
+                        int noOfMemories = snapshot.data;
+                        return RichText(
+                          text: TextSpan(
+                            style: TextStyle(
+                              color: Theme.of(context)
+                                  .colorScheme
+                                  .defaultTextColor,
                             ),
-                          ],
-                        ),
-                      );
-                    } else {
-                      return const SizedBox.shrink();
-                    }
-                  },
-                ),
-              ],
+                            children: [
+                              TextSpan(text: noOfMemories.toString()),
+                              TextSpan(
+                                text:
+                                    noOfMemories != 1 ? ' memories' : ' memory',
+                              ),
+                            ],
+                          ),
+                        );
+                      } else {
+                        return const SizedBox.shrink();
+                      }
+                    },
+                  ),
+                ],
+              ),
             ),
-            SizedBox(
-              height: 50,
-              width: 50,
-              child: ThumbnailWidget(c.thumbnail),
+            Hero(
+              tag: "collectionSearch" + c.thumbnail.tag(),
+              child: SizedBox(
+                height: 50,
+                width: 50,
+                child: ThumbnailWidget(c.thumbnail),
+              ),
             )
           ],
         ),
       ),
       onTap: () {
-        routeToPage(context, CollectionPage(c), forceCustomPageRoute: true);
+        routeToPage(
+          context,
+          CollectionPage(
+            c,
+            tagPrefix: "collectionSearch",
+          ),
+          forceCustomPageRoute: true,
+        );
       },
     );
   }

+ 65 - 0
lib/ui/viewer/search/filename_result_widget.dart

@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+import 'package:photos/models/file.dart';
+import 'package:photos/ui/viewer/file/detail_page.dart';
+import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
+import 'package:photos/utils/navigation_util.dart';
+
+class FilenameResultWidget extends StatelessWidget {
+  final File matchedFile;
+  const FilenameResultWidget(this.matchedFile, {Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      child: Padding(
+        padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: [
+            Flexible(
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  const Text(
+                    'File',
+                    style: TextStyle(fontSize: 12),
+                  ),
+                  const SizedBox(height: 8),
+                  Text(
+                    matchedFile.title,
+                    style: const TextStyle(fontSize: 18),
+                    overflow: TextOverflow.ellipsis,
+                  ),
+                  const Text('1 memory')
+                ],
+              ),
+            ),
+            Hero(
+              tag: "fileDetails" + matchedFile.tag(),
+              child: SizedBox(
+                height: 50,
+                width: 50,
+                child: ThumbnailWidget(matchedFile),
+              ),
+            ),
+          ],
+        ),
+      ),
+      onTap: () {
+        _routeToDetailPage(matchedFile, context);
+      },
+    );
+  }
+
+  void _routeToDetailPage(File file, BuildContext context) {
+    final page = DetailPage(
+      DetailPageConfiguration(
+        List.unmodifiable([file]),
+        null,
+        0,
+        "fileDetails",
+      ),
+    );
+    routeToPage(context, page, forceCustomPageRoute: true);
+  }
+}

+ 23 - 8
lib/ui/viewer/search/search_results_suggestions.dart

@@ -1,27 +1,42 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 import 'package:photos/models/collection_items.dart';
+import 'package:photos/models/file.dart';
 import 'package:photos/ui/viewer/search/collection_result_widget.dart';
+import 'package:photos/ui/viewer/search/filename_result_widget.dart';
 
 class SearchResultsSuggestions extends StatelessWidget {
-  final List<CollectionWithThumbnail> collectionsWithThumbnail;
-  const SearchResultsSuggestions({Key key, this.collectionsWithThumbnail})
-      : super(key: key);
+  final List<CollectionWithThumbnail> matchedCollectionsWithThumbnail;
+  final List<File> matchedFiles;
+  const SearchResultsSuggestions(
+    this.matchedCollectionsWithThumbnail,
+    this.matchedFiles, {
+    Key key,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    List<Widget> suggestions = [];
-    for (CollectionWithThumbnail c in collectionsWithThumbnail) {
-      suggestions.add(CollectionResultWidget(c));
+    List<dynamic> suggestions = [];
+    for (CollectionWithThumbnail c in matchedCollectionsWithThumbnail) {
+      suggestions.add(c);
+    }
+    for (File file in matchedFiles) {
+      suggestions.add(file);
     }
-
     return Container(
       constraints:
           BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.6),
       child: ListView.builder(
         itemCount: suggestions.length,
         itemBuilder: (context, index) {
-          return suggestions[index];
+          dynamic value = suggestions[index];
+          if (value is File) {
+            return FilenameResultWidget(value);
+          } else if (value is CollectionWithThumbnail) {
+            return CollectionResultWidget(value);
+          } else {
+            throw StateError("Invalid/Unsupported value");
+          }
         },
       ),
     );

+ 70 - 74
lib/ui/viewer/search/search_widget.dart

@@ -1,10 +1,13 @@
 import 'package:flutter/material.dart';
+import 'package:photos/db/files_db.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/models/collection_items.dart';
+import 'package:photos/models/file.dart';
 import 'package:photos/services/collections_service.dart';
 import 'package:photos/ui/viewer/search/search_results_suggestions.dart';
 
 class SearchIconWidget extends StatefulWidget {
+  final String searchQuery = '';
   const SearchIconWidget({Key key}) : super(key: key);
 
   @override
@@ -12,6 +15,7 @@ class SearchIconWidget extends StatefulWidget {
 }
 
 class _SearchIconWidgetState extends State<SearchIconWidget> {
+  final ValueNotifier<String> _searchQuery = ValueNotifier('');
   bool showSearchWidget;
   @override
   void initState() {
@@ -21,8 +25,11 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
 
   @override
   Widget build(BuildContext context) {
+    List<CollectionWithThumbnail> matchedCollections = [];
+    List<File> matchedFiles = [];
+    //when false - show the search icon, when true - show the textfield for search
     return showSearchWidget
-        ? Searchwidget(showSearchWidget)
+        ? searchWidget(matchedCollections, matchedFiles)
         : IconButton(
             onPressed: () {
               setState(
@@ -34,82 +41,71 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
             icon: const Icon(Icons.search),
           );
   }
-}
 
-// ignore: must_be_immutable
-class Searchwidget extends StatefulWidget {
-  bool openSearch;
-  final String searchQuery = '';
-  Searchwidget(this.openSearch, {Key key}) : super(key: key);
-  @override
-  State<Searchwidget> createState() => _SearchwidgetState();
-}
-
-class _SearchwidgetState extends State<Searchwidget> {
-  final ValueNotifier<String> _searchQ = ValueNotifier('');
-  @override
-  Widget build(BuildContext context) {
-    List<CollectionWithThumbnail> matchedCollections;
-    return widget.openSearch
-        ? Column(
-            children: [
-              Row(
-                children: [
-                  const SizedBox(width: 12),
-                  Flexible(
-                    child: Container(
-                      color:
-                          Theme.of(context).colorScheme.defaultBackgroundColor,
-                      child: TextFormField(
-                        style: Theme.of(context).textTheme.subtitle1,
-                        decoration: InputDecoration(
-                          filled: true,
-                          contentPadding: const EdgeInsets.symmetric(
-                            horizontal: 16,
-                            vertical: 14,
-                          ),
-                          border: UnderlineInputBorder(
-                            borderSide: BorderSide.none,
-                            borderRadius: BorderRadius.circular(8),
-                          ),
-                          prefixIcon: const Icon(Icons.search),
-                        ),
-                        onChanged: (value) async {
-                          matchedCollections = await CollectionsService.instance
-                              .getFilteredCollectionsWithThumbnail(value);
-                          _searchQ.value = value;
-                        },
-                        autofocus: true,
-                      ),
+  Widget searchWidget(
+    List<CollectionWithThumbnail> matchedCollections,
+    List<File> matchedFiles,
+  ) {
+    return Column(
+      children: [
+        Row(
+          children: [
+            const SizedBox(width: 12),
+            Flexible(
+              child: Container(
+                color: Theme.of(context).colorScheme.defaultBackgroundColor,
+                child: TextFormField(
+                  style: Theme.of(context).textTheme.subtitle1,
+                  decoration: InputDecoration(
+                    filled: true,
+                    contentPadding: const EdgeInsets.symmetric(
+                      horizontal: 16,
+                      vertical: 14,
                     ),
+                    border: UnderlineInputBorder(
+                      borderSide: BorderSide.none,
+                      borderRadius: BorderRadius.circular(8),
+                    ),
+                    prefixIcon: const Icon(Icons.search),
                   ),
-                  IconButton(
-                    onPressed: () {
-                      setState(() {
-                        widget.openSearch = !widget.openSearch;
-                      });
-                    },
-                    icon: const Icon(Icons.close),
-                  ),
-                ],
+                  onChanged: (value) async {
+                    matchedCollections = await CollectionsService.instance
+                        .getFilteredCollectionsWithThumbnail(value);
+                    matchedFiles =
+                        await FilesDB.instance.getFilesOnFileNameSearch(value);
+                    _searchQuery.value = value;
+                  },
+                  autofocus: true,
+                ),
               ),
-              const SizedBox(height: 20),
-              ValueListenableBuilder(
-                valueListenable: _searchQ,
-                builder: (
-                  BuildContext context,
-                  String newQuery,
-                  Widget child,
-                ) {
-                  return newQuery != ''
-                      ? SearchResultsSuggestions(
-                          collectionsWithThumbnail: matchedCollections,
-                        )
-                      : const SizedBox.shrink();
-                },
-              ),
-            ],
-          )
-        : SearchIconWidget();
+            ),
+            IconButton(
+              onPressed: () {
+                setState(() {
+                  showSearchWidget = !showSearchWidget;
+                });
+              },
+              icon: const Icon(Icons.close),
+            ),
+          ],
+        ),
+        const SizedBox(height: 20),
+        ValueListenableBuilder(
+          valueListenable: _searchQuery,
+          builder: (
+            BuildContext context,
+            String newQuery,
+            Widget child,
+          ) {
+            return newQuery != ''
+                ? SearchResultsSuggestions(
+                    matchedCollections,
+                    matchedFiles,
+                  )
+                : const SizedBox.shrink();
+          },
+        ),
+      ],
+    );
   }
 }