From d2edc0bffe862b7adaa5bca37c86b5bb81cde934 Mon Sep 17 00:00:00 2001
From: Alex Tran <alex.tran1502@gmail.com>
Date: Sun, 27 Feb 2022 12:45:12 -0600
Subject: [PATCH] ok

---
 .../search/models/store_model_here.txt        |   0
 .../providers/search_page_state.provider.dart | 107 ++++++++++++++++++
 .../search/services/store_services_here.txt   |   0
 mobile/lib/modules/search/ui/search_bar.dart  |  55 +++++++++
 .../search/ui/search_suggestion_list.dart     |  35 ++++++
 .../lib/modules/search/views/search_page.dart |  68 +++++++++++
 6 files changed, 265 insertions(+)
 create mode 100644 mobile/lib/modules/search/models/store_model_here.txt
 create mode 100644 mobile/lib/modules/search/providers/search_page_state.provider.dart
 create mode 100644 mobile/lib/modules/search/services/store_services_here.txt
 create mode 100644 mobile/lib/modules/search/ui/search_bar.dart
 create mode 100644 mobile/lib/modules/search/ui/search_suggestion_list.dart
 create mode 100644 mobile/lib/modules/search/views/search_page.dart

diff --git a/mobile/lib/modules/search/models/store_model_here.txt b/mobile/lib/modules/search/models/store_model_here.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/mobile/lib/modules/search/providers/search_page_state.provider.dart b/mobile/lib/modules/search/providers/search_page_state.provider.dart
new file mode 100644
index 0000000000..2782da2af3
--- /dev/null
+++ b/mobile/lib/modules/search/providers/search_page_state.provider.dart
@@ -0,0 +1,107 @@
+import 'dart:convert';
+
+import 'package:collection/collection.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class SearchPageState {
+  final String searchTerm;
+  final bool isSearchEnabled;
+  final List<String> searchSuggestion;
+
+  SearchPageState({
+    required this.searchTerm,
+    required this.isSearchEnabled,
+    required this.searchSuggestion,
+  });
+
+  SearchPageState copyWith({
+    String? searchTerm,
+    bool? isSearchEnabled,
+    List<String>? searchSuggestion,
+  }) {
+    return SearchPageState(
+      searchTerm: searchTerm ?? this.searchTerm,
+      isSearchEnabled: isSearchEnabled ?? this.isSearchEnabled,
+      searchSuggestion: searchSuggestion ?? this.searchSuggestion,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return {
+      'searchTerm': searchTerm,
+      'isSearchEnabled': isSearchEnabled,
+      'searchSuggestion': searchSuggestion,
+    };
+  }
+
+  factory SearchPageState.fromMap(Map<String, dynamic> map) {
+    return SearchPageState(
+      searchTerm: map['searchTerm'] ?? '',
+      isSearchEnabled: map['isSearchEnabled'] ?? false,
+      searchSuggestion: List<String>.from(map['searchSuggestion']),
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory SearchPageState.fromJson(String source) => SearchPageState.fromMap(json.decode(source));
+
+  @override
+  String toString() =>
+      'SearchPageState(searchTerm: $searchTerm, isSearchEnabled: $isSearchEnabled, searchSuggestion: $searchSuggestion)';
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    final listEquals = const DeepCollectionEquality().equals;
+
+    return other is SearchPageState &&
+        other.searchTerm == searchTerm &&
+        other.isSearchEnabled == isSearchEnabled &&
+        listEquals(other.searchSuggestion, searchSuggestion);
+  }
+
+  @override
+  int get hashCode => searchTerm.hashCode ^ isSearchEnabled.hashCode ^ searchSuggestion.hashCode;
+}
+
+class SearchPageStateNotifier extends StateNotifier<SearchPageState> {
+  SearchPageStateNotifier()
+      : super(
+          SearchPageState(
+            searchTerm: "",
+            isSearchEnabled: false,
+            searchSuggestion: [],
+          ),
+        );
+
+  void enableSearch() {
+    state = state.copyWith(isSearchEnabled: true);
+  }
+
+  void disableSearch() {
+    state = state.copyWith(isSearchEnabled: false);
+  }
+
+  void setSearchTerm(String value) {
+    state = state.copyWith(searchTerm: value);
+
+    _getSearchSuggestion(state.searchTerm);
+  }
+
+  void _getSearchSuggestion(String searchTerm) {
+    var searchList = ['January', '01 2022', 'feburary', "February", 'home', '3413'];
+
+    var newList = searchList.where((e) => e.toLowerCase().contains(searchTerm));
+
+    state = state.copyWith(searchSuggestion: [...newList]);
+
+    if (searchTerm.isEmpty) {
+      state = state.copyWith(searchSuggestion: []);
+    }
+  }
+}
+
+final searchPageStateProvider = StateNotifierProvider<SearchPageStateNotifier, SearchPageState>((ref) {
+  return SearchPageStateNotifier();
+});
diff --git a/mobile/lib/modules/search/services/store_services_here.txt b/mobile/lib/modules/search/services/store_services_here.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/mobile/lib/modules/search/ui/search_bar.dart b/mobile/lib/modules/search/ui/search_bar.dart
new file mode 100644
index 0000000000..af3b2fd6ce
--- /dev/null
+++ b/mobile/lib/modules/search/ui/search_bar.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
+
+class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
+  SearchBar({Key? key, required this.searchFocusNode}) : super(key: key);
+  FocusNode searchFocusNode;
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final searchTermController = useTextEditingController(text: "");
+    final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
+
+    return AppBar(
+      automaticallyImplyLeading: false,
+      leading: isSearchEnabled
+          ? IconButton(
+              onPressed: () {
+                searchFocusNode.unfocus();
+                ref.watch(searchPageStateProvider.notifier).disableSearch();
+              },
+              icon: const Icon(Icons.arrow_back_ios_rounded))
+          : const Icon(Icons.search_rounded),
+      title: TextField(
+        controller: searchTermController,
+        focusNode: searchFocusNode,
+        autofocus: false,
+        onTap: () {
+          ref.watch(searchPageStateProvider.notifier).enableSearch();
+          searchFocusNode.requestFocus();
+        },
+        onSubmitted: (searchTerm) {
+          ref.watch(searchPageStateProvider.notifier).disableSearch();
+          searchFocusNode.unfocus();
+        },
+        onChanged: (value) {
+          ref.watch(searchPageStateProvider.notifier).setSearchTerm(value);
+        },
+        decoration: const InputDecoration(
+          hintText: 'Search your photos',
+          enabledBorder: UnderlineInputBorder(
+            borderSide: BorderSide(color: Colors.transparent),
+          ),
+          focusedBorder: UnderlineInputBorder(
+            borderSide: BorderSide(color: Colors.transparent),
+          ),
+        ),
+      ),
+    );
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(kToolbarHeight);
+}
diff --git a/mobile/lib/modules/search/ui/search_suggestion_list.dart b/mobile/lib/modules/search/ui/search_suggestion_list.dart
new file mode 100644
index 0000000000..b3a73b5fc9
--- /dev/null
+++ b/mobile/lib/modules/search/ui/search_suggestion_list.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
+
+class SearchSuggestionList extends ConsumerWidget {
+  const SearchSuggestionList({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final searchTerm = ref.watch(searchPageStateProvider).searchTerm;
+    final searchSuggestion = ref.watch(searchPageStateProvider).searchSuggestion;
+
+    return Container(
+      color: searchTerm.isEmpty ? Colors.black.withOpacity(0.5) : Theme.of(context).scaffoldBackgroundColor,
+      child: CustomScrollView(
+        slivers: [
+          SliverFillRemaining(
+            hasScrollBody: true,
+            child: ListView.builder(
+              itemBuilder: ((context, index) {
+                return ListTile(
+                  onTap: () {
+                    print("navigate to this search result: ${searchSuggestion[index]} ");
+                  },
+                  title: Text(searchSuggestion[index]),
+                );
+              }),
+              itemCount: searchSuggestion.length,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart
new file mode 100644
index 0000000000..b6b36db1b4
--- /dev/null
+++ b/mobile/lib/modules/search/views/search_page.dart
@@ -0,0 +1,68 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
+import 'package:immich_mobile/modules/search/ui/search_bar.dart';
+import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
+
+// ignore: must_be_immutable
+class SearchPage extends HookConsumerWidget {
+  SearchPage({Key? key}) : super(key: key);
+
+  late FocusNode searchFocusNode;
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
+
+    useEffect(() {
+      searchFocusNode = FocusNode();
+      return () {
+        searchFocusNode.dispose();
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: SearchBar(searchFocusNode: searchFocusNode),
+      body: GestureDetector(
+        onTap: () {
+          searchFocusNode.unfocus();
+          ref.watch(searchPageStateProvider.notifier).disableSearch();
+        },
+        child: Stack(
+          children: [
+            ListView(
+              children: [
+                Container(
+                  height: 300,
+                  color: Colors.blue,
+                ),
+                Container(
+                  height: 300,
+                  color: Colors.red,
+                ),
+                Container(
+                  height: 300,
+                  color: Colors.green,
+                ),
+                Container(
+                  height: 300,
+                  color: Colors.blue,
+                ),
+                Container(
+                  height: 300,
+                  color: Colors.red,
+                ),
+                Container(
+                  height: 300,
+                  color: Colors.green,
+                ),
+              ],
+            ),
+            isSearchEnabled ? const SearchSuggestionList() : Container(),
+          ],
+        ),
+      ),
+    );
+  }
+}