TF-524 Create RecentSearchCache and TypeAHeadQuickSearch widget

This commit is contained in:
dab246
2022-05-23 18:48:29 +07:00
committed by Dat H. Pham
parent 5ca238153a
commit 3513c363ff
23 changed files with 2349 additions and 127 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5706 9.46587C11.8051 9.23183 12.185 9.23228 12.4191 9.46687C12.6531 9.70146 12.6527 10.0814 12.4181 10.3154L9.3762 13.3501C7.7024 15.0199 4.99184 15.0167 3.32199 13.3429C3.30381 13.3246 3.30382 13.3246 3.28579 13.3062C1.56579 11.5401 1.57222 8.72328 3.30028 6.96504L7.85747 2.32825C9.05863 1.1061 11.0231 1.08909 12.2453 2.29025C12.2714 2.3159 12.297 2.34201 12.3222 2.36856L12.3432 2.39077C13.5495 3.66331 13.536 5.66121 12.3126 6.91732L7.71787 11.635C7.01104 12.3607 5.86386 12.4178 5.08852 11.7657L5.03807 11.7233C4.26524 11.0734 4.16561 9.91997 4.81554 9.14713C4.83936 9.11881 4.86403 9.09122 4.88952 9.0644L7.98177 5.81056C8.21004 5.57036 8.58982 5.56069 8.83002 5.78896C9.07022 6.01724 9.07989 6.39701 8.85162 6.63722L5.75937 9.89105C5.75061 9.90027 5.74213 9.90975 5.73395 9.91949C5.51057 10.1851 5.54481 10.5815 5.81043 10.8049L5.86088 10.8473C6.15497 11.0946 6.59011 11.073 6.85821 10.7977L11.453 6.08007C12.227 5.28539 12.2355 4.0214 11.4723 3.21633L11.4513 3.19412C11.4358 3.17784 11.4201 3.16183 11.4041 3.1461C10.6546 2.40949 9.44992 2.41992 8.71331 3.1694L4.15612 7.80619C2.88545 9.09906 2.88071 11.1703 4.14547 12.469C4.15844 12.4823 4.15844 12.4823 4.17152 12.4954C5.37329 13.7 7.32406 13.7023 8.52868 12.5006L11.5706 9.46587Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.8571 0.572266C11.1727 0.572266 11.4285 0.828103 11.4285 1.14369L11.4289 1.80645C11.9088 1.8828 12.2196 2.00614 12.5317 2.17306C13.0904 2.47188 13.5289 2.91037 13.8277 3.4691C14.0992 3.97671 14.2554 4.48059 14.2817 5.71459L14.2857 5.71512V9.89132L14.2826 10.2399C14.2593 11.5083 14.1026 12.0185 13.8277 12.5326C13.5289 13.0913 13.0904 13.5298 12.5317 13.8286C11.973 14.1274 11.4187 14.2866 9.89043 14.2866H6.10946L5.76087 14.2835C4.49245 14.2602 3.98224 14.1035 3.46821 13.8286C2.90948 13.5298 2.47099 13.0913 2.17218 12.5326C1.88532 11.9962 1.72719 11.464 1.715 10.0701L1.71423 6.11035V5.71512C1.74452 4.48059 1.9007 3.97671 2.17218 3.4691C2.47099 2.91037 2.90948 2.47188 3.46821 2.17306C3.78047 2.00607 4.09131 1.8827 4.57155 1.80635L4.57138 1.14369C4.57138 0.828103 4.82721 0.572266 5.1428 0.572266C5.4584 0.572266 5.71423 0.828103 5.71423 1.14369L5.7137 1.7191C5.83797 1.71645 5.96964 1.71512 6.10946 1.71512H9.89043C10.0305 1.71512 10.1623 1.71646 10.2868 1.71912L10.2857 1.14369C10.2857 0.828103 10.5415 0.572266 10.8571 0.572266ZM13.1422 6.85798H2.85709L2.85762 10.053L2.86267 10.3781C2.88489 11.2393 2.97589 11.612 3.17996 11.9936C3.37226 12.3532 3.64762 12.6285 4.00718 12.8208C4.43365 13.0489 4.84906 13.1357 5.94778 13.1432H10.0521L10.3772 13.1381C11.2384 13.1159 11.6111 13.0249 11.9927 12.8208C12.3523 12.6285 12.6276 12.3532 12.8199 11.9936C13.048 11.5671 13.1349 11.1517 13.1423 10.053L13.1422 6.85798ZM10.8834 9.71512C11.1307 9.71512 11.3254 9.76154 11.4884 9.84869C11.6513 9.93584 11.7792 10.0637 11.8664 10.2267C11.9535 10.3897 11.9999 10.5844 11.9999 10.8317V10.8843C11.9999 11.1316 11.9535 11.3263 11.8664 11.4893C11.7792 11.6522 11.6513 11.7801 11.4884 11.8673C11.3254 11.9544 11.1307 12.0008 10.8834 12.0008H10.8308C10.5835 12.0008 10.3888 11.9544 10.2258 11.8673C10.0628 11.7801 9.93495 11.6522 9.8478 11.4893C9.76065 11.3263 9.71423 11.1316 9.71423 10.8843V10.8317C9.71423 10.5844 9.76065 10.3897 9.8478 10.2267C9.93495 10.0637 10.0628 9.93584 10.2258 9.84869C10.3888 9.76154 10.5835 9.71512 10.8308 9.71512H10.8834ZM10.0521 2.85851H5.94778L5.71423 2.86141V3.42941C5.71423 3.745 5.4584 4.00084 5.1428 4.00084C4.82721 4.00084 4.57138 3.745 4.57138 3.42941L4.57095 2.96799C4.35341 3.01822 4.18122 3.08778 4.00718 3.18085C3.64762 3.37315 3.37226 3.64851 3.17996 4.00807C2.96886 4.40279 2.87875 4.78803 2.86062 5.71435L13.1388 5.71512L13.1372 5.62361C13.115 4.76237 13.024 4.38965 12.8199 4.00807C12.6276 3.64851 12.3523 3.37315 11.9927 3.18085C11.8187 3.0878 11.6466 3.01825 11.4291 2.96802L11.4285 3.42941C11.4285 3.745 11.1727 4.00084 10.8571 4.00084C10.5415 4.00084 10.2857 3.745 10.2857 3.42941L10.287 2.86152C10.2123 2.86006 10.1341 2.85906 10.0521 2.85851Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.99996 1.66602C14.6012 1.66602 18.3333 5.39679 18.3333 9.99935C18.3333 14.6019 14.6012 18.3327 9.99996 18.3327C5.3974 18.3327 1.66663 14.6019 1.66663 9.99935C1.66663 5.39679 5.3974 1.66602 9.99996 1.66602ZM9.99996 3.33268C6.31791 3.33268 3.33329 6.3173 3.33329 9.99935C3.33329 13.6814 6.31791 16.666 9.99996 16.666C13.681 16.666 16.6666 13.6814 16.6666 9.99935C16.6666 6.3173 13.681 3.33268 9.99996 3.33268ZM9.449 5.87114C9.90924 5.87114 10.2823 6.24424 10.2823 6.70448V9.89942L13.4162 10.9459C13.8521 11.0935 14.0859 11.5665 13.9383 12.0025C13.7908 12.4384 13.3177 12.6722 12.8818 12.5246L9.18179 11.272C8.84339 11.1575 8.61566 10.8399 8.61566 10.4827V6.70448C8.61566 6.24424 8.98876 5.87114 9.449 5.87114Z" fill="#99A2AD"/>
</svg>

After

Width:  |  Height:  |  Size: 840 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.00001 10.1997L3.50001 7.69972C3.11341 7.31312 2.48661 7.31312 2.10001 7.69972C1.71341 8.08631 1.71341 8.71312 2.10001 9.09972L5.2929 12.2926C5.68343 12.6831 6.31659 12.6831 6.70712 12.2926L14.3 4.69972C14.6866 4.31312 14.6866 3.68631 14.3 3.29972C13.9134 2.91312 13.2866 2.91312 12.9 3.29972L6.00001 10.1997Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 442 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3409 5.34091C11.3409 7.18605 9.84508 8.68183 7.99994 8.68183C6.1548 8.68183 4.65903 7.18605 4.65903 5.34091C4.65903 3.49578 6.1548 2 7.99994 2C9.84508 2 11.3409 3.49578 11.3409 5.34091ZM10.0045 5.34091C10.0045 6.448 9.10702 7.34546 7.99994 7.34546C6.89286 7.34546 5.99539 6.448 5.99539 5.34091C5.99539 4.23383 6.89286 3.33637 7.99994 3.33637C9.10702 3.33637 10.0045 4.23383 10.0045 5.34091Z" fill="#007AFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99995 9.35001C6.25096 9.35001 4.89549 9.57366 3.95484 10.0667C3.47439 10.3186 3.08558 10.6495 2.81918 11.0702C2.55122 11.4935 2.43176 11.9689 2.43176 12.4682V12.6909C2.43176 13.552 3.12979 14.25 3.99086 14.25H12.009C12.8704 14.25 13.5681 13.5514 13.5681 12.6906V12.4682C13.5681 11.9689 13.4487 11.4935 13.1807 11.0702C12.9143 10.6495 12.5255 10.3186 12.0451 10.0667C11.1044 9.57366 9.74895 9.35001 7.99995 9.35001ZM3.76813 12.4682C3.76813 12.1879 3.83287 11.9674 3.94828 11.7851C4.06527 11.6003 4.259 11.4161 4.57526 11.2503C5.22801 10.9082 6.32255 10.6864 7.99995 10.6864C9.67736 10.6864 10.7719 10.9082 11.4246 11.2503C11.7409 11.4161 11.9346 11.6003 12.0516 11.7851C12.167 11.9674 12.2318 12.1879 12.2318 12.4682V12.6906C12.2318 12.8139 12.1317 12.9137 12.009 12.9137H3.99086C3.86785 12.9137 3.76813 12.8139 3.76813 12.6909L3.76813 12.4682Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -62,6 +62,7 @@ export 'presentation/views/modal_sheets/edit_text_modal_sheet_builder.dart';
export 'presentation/views/search/search_bar_view.dart';
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
export 'presentation/views/tab_bar/custom_tab_indicator.dart';
export 'presentation/views/typeahead/typeahead_for_quick_search.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
@@ -112,6 +112,11 @@ class ImagePaths {
String get icTeam => _getImagePath('ic_team.svg');
String get loginGraphic => _getImagePath('login_graphic.png');
String get icPowerByLinagora => _getImagePath('power_by_linagora.svg');
String get icAttachmentSB => _getImagePath('ic_attachment_sb.svg');
String get icCalendarSB => _getImagePath('ic_calendar_sb.svg');
String get icClockSB => _getImagePath('ic_clock_sb.svg');
String get icSelectedSB => _getImagePath('ic_selected_sb.svg');
String get icUserSB => _getImagePath('ic_user_sb.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
typedef FutureOr<Iterable<T>> SuggestionsCallback<T>(String pattern);
typedef FutureOr<Iterable<R>> FetchRecentActionCallback<R>(String pattern);
typedef Widget ItemBuilder<T>(BuildContext context, T itemData);
typedef void SuggestionSelectionCallback<T>(T suggestion);
typedef void RecentSelectionCallback<R>(R recent);
typedef Widget ErrorBuilder(BuildContext context, Object? error);
typedef Widget ButtonActionBuilder(BuildContext context, dynamic action);
typedef void ButtonActionCallback(dynamic action);
typedef Widget AnimationTransitionBuilder(
BuildContext context, Widget child, AnimationController? controller);
@@ -0,0 +1,5 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
final supportedPlatform = (kIsWeb || Platform.isAndroid || Platform.isIOS);
@@ -0,0 +1,111 @@
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/recent_search_cache.dart';
class RecentSearchCacheClient extends HiveCacheClient<RecentSearchCache> {
@override
String get tableName => 'RecentSearchCache';
@override
Future<void> clearAllData() {
return Future.sync(() async {
final boxState = await openBox();
boxState.clear();
}).catchError((error) {
throw error;
});
}
@override
Future<void> deleteItem(String key) {
return Future.sync(() async {
final boxState = await openBox();
return boxState.delete(key);
}).catchError((error) {
throw error;
});
}
@override
Future<void> deleteMultipleItem(List<String> listKey) {
throw UnimplementedError();
}
@override
Future<List<RecentSearchCache>> getAll() {
return Future.sync(() async {
final boxState = await openBox();
return boxState.values.toList();
}).catchError((error) {
throw error;
});
}
@override
Future<RecentSearchCache?> getItem(String key) {
throw UnimplementedError();
}
@override
Future<void> insertItem(String key, RecentSearchCache newObject) {
return Future.sync(() async {
final boxState = await openBox();
boxState.put(key, newObject);
}).catchError((error) {
throw error;
});
}
@override
Future<void> insertMultipleItem(Map<String, RecentSearchCache> mapObject) {
throw UnimplementedError();
}
@override
Future<bool> isExistItem(String key) {
return Future.sync(() async {
final boxState = await openBox();
return boxState.containsKey(key);
}).catchError((error) {
throw error;
});
}
@override
Future<bool> isExistTable() {
return Future.sync(() async {
return await Hive.boxExists(tableName);
}).catchError((error) {
throw error;
});
}
@override
Future<Box<RecentSearchCache>> openBox() {
return Future.sync(() async {
if (Hive.isBoxOpen(tableName)) {
return Hive.box<RecentSearchCache>(tableName);
}
return await Hive.openBox<RecentSearchCache>(tableName);
}).catchError((error) {
throw error;
});
}
@override
Future<void> updateItem(String key, RecentSearchCache newObject) {
return Future.sync(() async {
final boxState = await openBox();
boxState.put(key, newObject);
}).catchError((error) {
throw error;
});
}
@override
Future<void> updateMultipleItem(Map<String, RecentSearchCache> mapObject) {
throw UnimplementedError();
}
}
@@ -6,4 +6,5 @@ class CachingConstants {
static const int STATE_TYPE_IDENTIFY = 4;
static const int EMAIL_CACHE_IDENTIFY = 5;
static const int EMAIL_ADDRESS_HIVE_CACHE_IDENTIFY = 6;
static const int RECENT_SEARCH_HIVE_CACHE_IDENTIFY = 7;
}
@@ -1,64 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnNewSearchQuery = Function(String);
class SearchFormWidgetBuilder {
final _typeAheadController = TextEditingController();
final BuildContext _context;
final ImagePaths _imagePaths;
OnNewSearchQuery? _onNewSearchQuery;
SearchFormWidgetBuilder(this._context, this._imagePaths);
SearchFormWidgetBuilder onNewSearchQuery(OnNewSearchQuery onNewSearchQuery) {
_onNewSearchQuery = onNewSearchQuery;
return this;
}
Widget build() {
return Container(
key: const Key('search_folder_form'),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColor.searchBorderColor),
color: AppColor.primaryLightColor),
child: TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
controller: _typeAheadController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 0, top: 15, bottom: 15, right: 15),
hintText: AppLocalizations.of(_context).search_folder,
hintStyle: const TextStyle(color: AppColor.searchHintTextColor, fontSize: 15.0, fontWeight: FontWeight.w500),
icon: Padding(
padding: const EdgeInsets.only(left: 20),
child: SvgPicture.asset(_imagePaths.icSearch, width: 24, height: 24, fit: BoxFit.fill),
))),
debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: (pattern) async {
if (_onNewSearchQuery != null) {
_onNewSearchQuery!(pattern);
}
return [];
},
itemBuilder: (BuildContext context, itemData) => const SizedBox.shrink(),
onSuggestionSelected: (suggestion) {},
noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true,
hideOnError: true,
hideOnLoading: true,
hideSuggestionsOnKeyboardHide: true,
));
}
}
@@ -0,0 +1,21 @@
import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
part 'recent_search_cache.g.dart';
@HiveType(typeId: CachingConstants.RECENT_SEARCH_HIVE_CACHE_IDENTIFY)
class RecentSearchCache extends HiveObject with EquatableMixin {
@HiveField(0)
final String value;
@HiveField(1)
final DateTime creationTime;
RecentSearchCache(this.value, this.creationTime);
@override
List<Object?> get props => [value, creationTime];
}
@@ -0,0 +1,12 @@
import 'package:equatable/equatable.dart';
class RecentSearch with EquatableMixin {
final String value;
final DateTime creationDate;
RecentSearch(this.value, this.creationDate);
@override
List<Object?> get props => [value, creationDate];
}
@@ -1,21 +1,24 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/mixin/network_connection_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view_web.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recent_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
@@ -157,7 +160,9 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
}),
const SizedBox(width: 16),
Obx(() => !controller.isSearchActive() ? const Spacer() : const SizedBox.shrink()),
Obx(() => controller.isSearchActive() ? Expanded(child: _buildSearchForm(context)) : const SizedBox.shrink()),
Obx(() => controller.isSearchActive()
? Expanded(child: _buildSearchForm(context))
: const SizedBox.shrink()),
Obx(() => !controller.isSearchActive()
? (SearchBarView(_imagePaths)
..hintTextSearch(AppLocalizations.of(context).search_emails)
@@ -396,24 +401,119 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
}
Widget _buildSearchForm(BuildContext context) {
return (SearchAppBarWidget(context, _imagePaths, _responsiveUtils,
controller.searchQuery,
controller.searchFocus,
controller.searchInputController,
suggestionSearch: controller.suggestionSearch)
..addDecoration(const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar))
..setMargin(const EdgeInsets.only(right: 10))
..setHeightSearchBar(45)
..setHintText(AppLocalizations.of(context).search_mail)
..addOnCancelSearchPressed(() {
controller.disableSearch();
controller.dispatchAction(CancelSelectionAllEmailAction());
})
..addOnClearTextSearchAction(() => controller.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.searchEmail(context, query)))
.build();
return Row(
children: [
buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icBack, color: AppColor.colorTextButton, fit: BoxFit.fill),
onTap: () {
controller.disableSearch();
controller.dispatchAction(CancelSelectionAllEmailAction());
}),
Expanded(child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar),
height: 45,
child: TypeAheadFormFieldQuickSearch<PresentationEmail, RecentSearch>(
textFieldConfiguration: QuickSearchTextFieldConfiguration(
controller: controller.searchInputController,
autofocus: true,
focusNode: controller.searchFocus,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
hintText: AppLocalizations.of(context).search_mail,
hintStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0),
labelStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0)
),
leftButton: buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icSearchBar, width: 16, height: 16, fit: BoxFit.fill),
onTap: () => {}),
rightButton: buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icComposerClose, width: 18, height: 18, fit: BoxFit.fill),
onTap: () {
controller.searchInputController.clear();
controller.clearSearchText();
})
),
suggestionsBoxDecoration: QuickSearchSuggestionsBoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
constraints: BoxConstraints(
maxWidth: _responsiveUtils.isDesktop(context) ? 556 : double.infinity,
maxHeight: 322),
),
actionButton: Padding(
padding: const EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 20),
child: Row(
children: [
(ButtonBuilder(_imagePaths.icAttachmentSB)
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 5))
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..size(16)
..textStyle(const TextStyle(fontSize: 13, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => {})
..text(AppLocalizations.of(context).hasAttachment, isVertical: false))
.build(),
const SizedBox(width: 8),
(ButtonBuilder(_imagePaths.icCalendarSB)
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 5))
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..size(16)
..textStyle(const TextStyle(fontSize: 13, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => {})
..text(AppLocalizations.of(context).last7Days, isVertical: false))
.build(),
const SizedBox(width: 8),
(ButtonBuilder(_imagePaths.icUserSB)
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 5))
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..size(16)
..textStyle(const TextStyle(fontSize: 13, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => {})
..text(AppLocalizations.of(context).fromMe, isVertical: false))
.build(),
],
),
),
titleHeaderRecent: Padding(
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 8),
child: Text(AppLocalizations.of(context).recent,
style: const TextStyle(fontSize: 13.0,
color: AppColor.colorTextButtonHeaderThread,
fontWeight: FontWeight.w500))),
getRecentCallback: (pattern) async {
return [];
},
itemRecentBuilder: (context, recent) {
return RecentSearchItemTileWidget(recent);
},
suggestionsCallback: (pattern) async {
return [];
},
itemBuilder: (context, email) {
return EmailQuickSearchItemTileWidget(email, controller.selectedMailbox.value);
},
onSuggestionSelected: (autoCompleteResult) async {
}),
)),
const SizedBox(width: 16),
]
);
}
}
@@ -0,0 +1,92 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
class EmailQuickSearchItemTileWidget extends StatelessWidget {
final imagePath = Get.find<ImagePaths>();
final PresentationEmail _presentationEmail;
final PresentationMailbox? _presentationMailbox;
EmailQuickSearchItemTileWidget(
this._presentationEmail,
this._presentationMailbox,
{Key? key}
) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset(
_presentationEmail.hasStarred ? imagePath.icStar : imagePath.icUnStar,
width: 20, height: 20, fit: BoxFit.fill),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(_getInformationSender(),
maxLines: 1,
overflow: BuildUtils.isWeb ? null : TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.black)),
const SizedBox(width: 16),
Expanded(
child: Text(_presentationEmail.getEmailTitle(),
maxLines: 1,
overflow: BuildUtils.isWeb ? null : TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: Colors.black)),
),
if (_presentationEmail.hasAttachment == true)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: SvgPicture.asset(imagePath.icAttachment, width: 14, height: 14, fit: BoxFit.fill),
),
Text(_presentationEmail.getReceivedAt(Localizations.localeOf(context).toLanguageTag()),
textAlign: TextAlign.right,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: Colors.black))
]),
const SizedBox(height: 3),
Text(_presentationEmail.getPartialContent(),
maxLines: 1,
overflow: BuildUtils.isWeb ? null : TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail))
]
),
),
],
),
);
}
String _getInformationSender() {
if (_presentationMailbox?.role == PresentationMailbox.roleSent
|| _presentationMailbox?.role == PresentationMailbox.roleDrafts
|| _presentationMailbox?.role == PresentationMailbox.roleOutbox) {
return _presentationEmail.recipientsName();
}
return _presentationEmail.getSenderName();
}
}
@@ -0,0 +1,35 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart';
class RecentSearchItemTileWidget extends StatelessWidget {
final imagePath = Get.find<ImagePaths>();
final RecentSearch recentSearch;
RecentSearchItemTileWidget(this.recentSearch, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: Row(
children: [
SvgPicture.asset(imagePath.icClockSB),
const SizedBox(width: 8),
Expanded(
child: Text(recentSearch.value,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.normal,
color: Colors.black)),
),
],
),
);
}
}
+43 -1
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2022-05-06T16:58:28.388035",
"@@last_modified": "2022-05-23T18:43:32.474820",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -60,6 +60,24 @@
"placeholders_order": [],
"placeholders": {}
},
"requiredEmail": "Email is required",
"@requiredEmail": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"requiredPassword": "Password is required",
"@requiredPassword": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"requiredUrl": "Server address is required",
"@requiredUrl": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"jmapBasedMailSolution": "JMAP-based\ncollaborative team mail solution",
"@jmapBasedMailSolution": {
"type": "text",
@@ -1275,5 +1293,29 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"hasAttachment": "Has attachment",
"@hasAttachment": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"last7Days": "Last 7 days",
"@last7Days": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"fromMe": "From me",
"@fromMe": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"recent": "Recent",
"@recent": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
@@ -1326,4 +1326,28 @@ class AppLocalizations {
'Save',
name: 'save');
}
String get hasAttachment {
return Intl.message(
'Has attachment',
name: 'hasAttachment');
}
String get last7Days {
return Intl.message(
'Last 7 days',
name: 'last7Days');
}
String get fromMe {
return Intl.message(
'From me',
name: 'fromMe');
}
String get recent {
return Intl.message(
'Recent',
name: 'recent');
}
}
-3
View File
@@ -64,9 +64,6 @@ dependencies:
# flutter_dotenv
flutter_dotenv: 5.0.2
# flutter_typeahead
flutter_typeahead: 3.1.1
built_collection: 5.1.1
# jmap_dart_client
@@ -58,25 +58,6 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> refreshAll(_i5.AccountId? accountId,
{_i6.UnsignedInt? limit,
Set<_i7.Comparator>? sort,
_i8.EmailFilter? emailFilter,
_i9.Properties? propertiesCreated,
_i9.Properties? propertiesUpdated}) =>
(super.noSuchMethod(
Invocation.method(#refreshAll, [
accountId
], {
#limit: limit,
#sort: sort,
#emailFilter: emailFilter,
#propertiesCreated: propertiesCreated,
#propertiesUpdated: propertiesUpdated
}),
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> refreshChanges(
_i5.AccountId? accountId, _i10.State? currentState,
{Set<_i7.Comparator>? sort,
@@ -58,25 +58,6 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> refreshAll(_i5.AccountId? accountId,
{_i6.UnsignedInt? limit,
Set<_i7.Comparator>? sort,
_i8.EmailFilter? emailFilter,
_i9.Properties? propertiesCreated,
_i9.Properties? propertiesUpdated}) =>
(super.noSuchMethod(
Invocation.method(#refreshAll, [
accountId
], {
#limit: limit,
#sort: sort,
#emailFilter: emailFilter,
#propertiesCreated: propertiesCreated,
#propertiesUpdated: propertiesUpdated
}),
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> refreshChanges(
_i5.AccountId? accountId, _i10.State? currentState,
{Set<_i7.Comparator>? sort,