Skip to content

Commit

Permalink
TW-1923: Improve search screen (#1961)
Browse files Browse the repository at this point in the history
  • Loading branch information
hieutbui authored Dec 13, 2024
1 parent 5f1e8ee commit 4dd46da
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 67 deletions.
56 changes: 3 additions & 53 deletions lib/pages/new_private_chat/widget/no_contacts_found.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,9 @@ class NoContactsFound extends StatelessWidget {
@override
Widget build(BuildContext context) {
return keyword != null
? Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
L10n.of(context)!.noResultForKeyword(keyword!),
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(
height: 8.0,
),
Text.rich(
TextSpan(
style: Theme.of(context).textTheme.bodyMedium,
children: [
TextSpan(
children: [
TextSpan(
text: L10n.of(context)!.searchResultNotFound1,
),
TextSpan(
text: L10n.of(context)!.searchResultNotFound2,
),
TextSpan(
text: L10n.of(context)!.searchResultNotFound3,
),
TextSpan(
text: L10n.of(context)!.searchResultNotFound4,
),
TextSpan(
text: L10n.of(context)!.searchResultNotFound5,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
],
),
],
),
),
const SizedBox(
height: 8.0,
),
const Align(
alignment: Alignment.center,
child: EmptySearchWidget(),
),
],
),
? const Align(
alignment: Alignment.center,
child: EmptySearchWidget(),
)
: SizedBox(
height: MediaQuery.sizeOf(context).height * 0.7,
Expand Down
3 changes: 3 additions & 0 deletions lib/pages/search/search.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class SearchController extends State<Search> {

String get searchWord => textEditingController.text;

bool get isSearchMatrixUserId =>
searchWord.isValidMatrixId && searchWord.startsWith('@');

String getBodyText(Event event, String searchWord) {
final senderName = event.senderFromMemoryOrFallback.calcDisplayname(
i18n: MatrixLocals(L10n.of(context)!),
Expand Down
13 changes: 2 additions & 11 deletions lib/pages/search/search_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import 'package:fluffychat/widgets/twake_components/twake_loading/center_loading
import 'package:flutter/material.dart' hide SearchController;
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:linagora_design_flutter/colors/linagora_sys_colors.dart';
import 'package:matrix/matrix.dart';

class SearchView extends StatelessWidget {
final SearchController searchController;
Expand Down Expand Up @@ -73,15 +72,7 @@ class SearchView extends StatelessWidget {
searchController.serverSearchController.searchResultsNotifier,
builder: ((context, searchResults, child) {
if (searchResults is PresentationServerSideEmptySearch) {
if (searchController.searchContactAndRecentChatController!
.recentAndContactsNotifier.value.isNotEmpty) {
return child!;
}
return _SearchHeader(
header: L10n.of(context)!.messages,
searchController: searchController,
needShowMore: false,
);
return child!;
}

if (searchResults is PresentationServerSideSearch) {
Expand Down Expand Up @@ -128,7 +119,7 @@ class SearchView extends StatelessWidget {
builder: (context, contacts, emptyChild) {
if (contacts.isEmpty) {
final keyword = searchController.textEditingController.text;
if (keyword.isValidMatrixId && keyword.startsWith("@")) {
if (searchController.isSearchMatrixUserId) {
return SearchExternalContactWidget(
keyword: keyword,
searchController: searchController,
Expand Down
7 changes: 4 additions & 3 deletions lib/pages/search/server_search_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ class ServerSearchMessagesList extends StatelessWidget {
builder: (context, serverSearchNotifier, child) {
if (serverSearchNotifier is PresentationServerSideEmptySearch) {
if (searchController.searchContactAndRecentChatController!
.recentAndContactsNotifier.value.isNotEmpty) {
return const SizedBox.shrink();
.recentAndContactsNotifier.value.isEmpty &&
!(searchController.isSearchMatrixUserId)) {
return child!;
}
return child!;
return const SizedBox.shrink();
}

if (serverSearchNotifier is PresentationServerSideSearch) {
Expand Down
154 changes: 154 additions & 0 deletions test/pages/search/server_search_view_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import 'package:fluffychat/config/localizations/localization_service.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/search/search.dart';
import 'package:fluffychat/pages/search/search_contacts_and_chats_controller.dart';
import 'package:fluffychat/pages/search/server_search_controller.dart';
import 'package:fluffychat/pages/search/server_search_view.dart';
import 'package:fluffychat/presentation/model/search/presentation_search.dart';
import 'package:fluffychat/presentation/model/search/presentation_server_side_empty_search.dart';
import 'package:fluffychat/presentation/model/search/presentation_server_side_state.dart';
import 'package:fluffychat/utils/custom_scroll_behaviour.dart';
import 'package:fluffychat/utils/responsive/responsive_utils.dart';
import 'package:fluffychat/widgets/search/empty_search_widget.dart';
import 'package:fluffychat/widgets/theme_builder.dart';
import 'package:flutter/material.dart' hide SearchController;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:linagora_design_flutter/linagora_design_flutter.dart';
import 'package:mockito/annotations.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:mockito/mockito.dart';
import 'server_search_view_test.mocks.dart';

@GenerateNiceMocks([
MockSpec<SearchController>(),
MockSpec<ServerSearchController>(),
MockSpec<TextEditingController>(),
MockSpec<SearchContactsAndChatsController>(),
])
void main() {
late final SearchController mockSearchController;
late final ServerSearchController mockServerSearchController;
late final TextEditingController mockTextEditingController;
late final SearchContactsAndChatsController
mockSearchContactAndRecentChatController;

setUpAll(() {
final getIt = GetIt.instance;
getIt.registerSingleton(ResponsiveUtils());
mockSearchController = MockSearchController();
mockServerSearchController = MockServerSearchController();
mockTextEditingController = MockTextEditingController();
mockSearchContactAndRecentChatController =
MockSearchContactsAndChatsController();
});

Future<void> makeTestable(WidgetTester tester) async {
await tester.pumpWidget(
ThemeBuilder(
builder: (context, themeMode, primaryColor) => MaterialApp(
locale: const Locale('en'),
scrollBehavior: CustomScrollBehavior(),
localizationsDelegates: const [
LocaleNamesLocalizationsDelegate(),
L10n.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: LocalizationService.supportedLocales,
theme: TwakeThemes.buildTheme(
context,
Brightness.light,
primaryColor,
),
home: Scaffold(
backgroundColor: LinagoraSysColors.material().onPrimary,
body: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: [
ServerSearchMessagesList(
searchController: mockSearchController,
),
],
),
),
),
),
);
}

group('[ServerSearchMessagesList] TEST', () {
group('GIVEN searchResultsNotifier is PresentationServerSideEmptySearch',
() {
testWidgets(
'GIVEN notifier value is empty\n'
'AND recentAndContactsNotifier value is empty\n'
'AND keyword is a Matrix ID\n'
'THEN should display SizedBox.shrink\n',
(WidgetTester tester) async {
when(mockSearchController.serverSearchController)
.thenReturn(mockServerSearchController);
when(mockSearchController.textEditingController)
.thenReturn(mockTextEditingController);
when(mockSearchController.searchContactAndRecentChatController)
.thenReturn(
mockSearchContactAndRecentChatController,
);
when(mockTextEditingController.text).thenReturn('@test:domain.com');
when(mockServerSearchController.searchResultsNotifier).thenReturn(
ValueNotifier<PresentationServerSideUIState>(
PresentationServerSideEmptySearch(),
),
);
when(
mockSearchContactAndRecentChatController.recentAndContactsNotifier,
).thenReturn(ValueNotifier<List<PresentationSearch>>([]));

await makeTestable(tester);

expect(find.byType(SizedBox), findsOneWidget);

final SizedBox foundSizedBox =
tester.firstWidget(find.byType(SizedBox));
expect(foundSizedBox.child, isNull);
expect(foundSizedBox.width, equals(0));
expect(foundSizedBox.height, equals(0));
},
);

testWidgets(
'GIVEN searchResultsNotifier value is empty\n'
'AND recentAndContactsNotifier value is empty\n'
'AND keyword is not a Matrix ID\n'
'THEN should display EmptySearchWidget\n',
(WidgetTester tester) async {
when(mockSearchController.serverSearchController)
.thenReturn(mockServerSearchController);
when(mockSearchController.textEditingController)
.thenReturn(mockTextEditingController);
when(mockSearchController.searchContactAndRecentChatController)
.thenReturn(
mockSearchContactAndRecentChatController,
);
when(mockTextEditingController.text).thenReturn('test');
when(mockServerSearchController.searchResultsNotifier).thenReturn(
ValueNotifier<PresentationServerSideUIState>(
PresentationServerSideEmptySearch(),
),
);
when(
mockSearchContactAndRecentChatController.recentAndContactsNotifier,
).thenReturn(ValueNotifier<List<PresentationSearch>>([]));

await makeTestable(tester);
await tester.pumpAndSettle();

expect(find.byType(EmptySearchWidget), findsOneWidget);
},
);
});
});
}

0 comments on commit 4dd46da

Please sign in to comment.