Skip to content

Commit

Permalink
TW-1098 [Search] Load more in Server side Search
Browse files Browse the repository at this point in the history
  • Loading branch information
drminh2807 authored and hoangdat committed Dec 19, 2023
1 parent c833418 commit a7fe5f9
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 52 deletions.
3 changes: 3 additions & 0 deletions lib/domain/app_state/search/server_search_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ class ServerSearchInitial extends Initial {

class ServerSearchChatSuccess extends Success {
final List<Result>? results;
final String? nextBatch;

const ServerSearchChatSuccess({
this.results,
this.nextBatch,
});

@override
List<Object?> get props => [
results,
nextBatch,
];
}

Expand Down
29 changes: 29 additions & 0 deletions lib/domain/model/search/server_side_search_categories.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:equatable/equatable.dart';
import 'package:matrix/matrix.dart';

class ServerSideSearchCategories extends Equatable {
final String searchTerm;
final SearchFilter? searchFilter;

const ServerSideSearchCategories({
required this.searchTerm,
this.searchFilter,
});

@override
List<Object?> get props => [searchTerm, searchFilter];

Categories get searchCategories {
return Categories(
roomEvents: RoomEventsCriteria(
searchTerm: searchTerm,
groupings: Groupings(
groupBy: [Group(key: GroupKey.roomId)],
),
keys: [KeyKind.contentBody],
orderBy: SearchOrder.recent,
filter: searchFilter,
),
);
}
}
29 changes: 13 additions & 16 deletions lib/domain/usecase/search/server_search_interactor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:fluffychat/app_state/failure.dart';
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/app_state/search/server_search_state.dart';
import 'package:fluffychat/domain/model/search/server_side_search_categories.dart';
import 'package:fluffychat/domain/repository/server_search_repository.dart';
import 'package:matrix/matrix.dart';

Expand All @@ -11,36 +12,32 @@ class ServerSearchInteractor {
getIt.get<ServerSearchRepository>();

Stream<Either<Failure, Success>> execute({
required ServerSideSearchCategories searchCategories,
String? nextBatch,
required String searchTerm,
List<KeyKind>? keys = const [KeyKind.contentBody],
SearchOrder orderBy = SearchOrder.recent,
List<GroupKey> groupKeys = const [GroupKey.roomId],
}) async* {
try {
yield Right(ServerSearchInitial());
if (nextBatch == null) {
yield Right(ServerSearchInitial());
}
final response = await _repository.search(
nextBatch: nextBatch,
searchCategories: Categories(
roomEvents: RoomEventsCriteria(
searchTerm: searchTerm,
groupings: Groupings(
groupBy: groupKeys.map((e) => Group(key: e)).toList(),
),
keys: keys,
orderBy: orderBy,
),
),
searchCategories: searchCategories.searchCategories,
);

final roomEventsResult = response.searchCategories.roomEvents;

Logs().d(
'ServerSearchInteractor::execute(): Search success - ${response.searchCategories.roomEvents?.results?.length}.',
);

yield Right(
ServerSearchChatSuccess(
results: roomEventsResult?.results,
nextBatch: roomEventsResult?.nextBatch,
),
);
} catch (e) {
Logs().d(
Logs().e(
'ServerSearchInteractor::execute(): Exception - $e}.',
);
yield Left(ServerSearchChatFailed(exception: e));
Expand Down
22 changes: 15 additions & 7 deletions lib/pages/search/search_view.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/domain/app_state/search/pre_search_state.dart';
import 'package:fluffychat/domain/app_state/search/server_search_state.dart';
import 'package:fluffychat/pages/dialer/pip/dismiss_keyboard.dart';
import 'package:fluffychat/pages/search/recent_contacts_banner_widget.dart';
import 'package:fluffychat/pages/search/recent_item_widget.dart';
import 'package:fluffychat/pages/search/search.dart';
import 'package:fluffychat/pages/search/search_view_style.dart';
import 'package:fluffychat/pages/search/server_search_view.dart';
import 'package:fluffychat/widgets/twake_components/twake_icon_button.dart';
import 'package:fluffychat/widgets/twake_components/twake_loading/center_loading_indicator.dart';
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';
Expand All @@ -32,6 +31,7 @@ class SearchView extends StatelessWidget {
},
child: CustomScrollView(
physics: const ClampingScrollPhysics(),
controller: searchController.serverSearchController.scrollController,
slivers: [
ValueListenableBuilder(
valueListenable: searchController.preSearchRecentContactsNotifier,
Expand Down Expand Up @@ -71,12 +71,9 @@ class SearchView extends StatelessWidget {
_recentChatsWidget(),
ValueListenableBuilder(
valueListenable:
searchController.serverSearchController.serverSearchNotifier,
searchController.serverSearchController.searchResultsNotifier,
builder: ((context, searchResults, _) {
final messagesFound =
searchResults.getSuccessOrNull<ServerSearchChatSuccess>();
if (messagesFound?.results == null ||
messagesFound!.results!.isEmpty) {
if (searchResults.searchResults.isEmpty) {
return _EmptySliverBox();
}
return _SearchHeader(
Expand All @@ -87,6 +84,17 @@ class SearchView extends StatelessWidget {
}),
),
ServerSearchMessagesList(searchController: searchController),
ValueListenableBuilder(
valueListenable:
searchController.serverSearchController.isLoadingMoreNotifier,
builder: (context, isLoadingMore, _) {
return SliverToBoxAdapter(
child: isLoadingMore
? const CenterLoadingIndicator()
: const SizedBox(),
);
},
),
],
),
),
Expand Down
116 changes: 103 additions & 13 deletions lib/pages/search/server_search_controller.dart
Original file line number Diff line number Diff line change
@@ -1,47 +1,137 @@
import 'dart:async';

import 'package:dartz/dartz.dart';
import 'package:fluffychat/app_state/failure.dart';
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/model/search/server_side_search_categories.dart';
import 'package:fluffychat/pages/search/search_debouncer_mixin.dart';
import 'package:fluffychat/domain/usecase/search/server_search_interactor.dart';
import 'package:fluffychat/presentation/model/search/presentation_server_side_search.dart';
import 'package:fluffychat/utils/scroll_controller_extension.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/domain/app_state/search/server_search_state.dart';
import 'package:matrix/matrix.dart';

class ServerSearchController with SearchDebouncerMixin {
final _serverSearchInteractor = getIt.get<ServerSearchInteractor>();

final serverSearchNotifier = ValueNotifier<Either<Failure, Success>>(
Right(ServerSearchInitial()),
final searchResultsNotifier = ValueNotifier<PresentationServerSideSearch>(
const PresentationServerSideSearch(
searchResults: [],
),
);

String keyword = '';
static const int _limitServerSideSearchFilter = 20;

final isLoadingMoreNotifier = ValueNotifier<bool>(false);

final scrollController = ScrollController();

StreamSubscription? _searchSubscription;

String? _nextBatch;

ServerSideSearchCategories? _searchCategories;

void init() {
initializeDebouncer((searchTerm) {
updateSearchCategories(searchTerm);
resetSearchResults();
if (searchTerm.isNotEmpty) {
keyword = searchTerm;
searchUnencryptedMessages(searchTerm);
searchUnencryptedMessages();
} else {
serverSearchNotifier.value = Right(ServerSearchInitial());
resetSearchResults();
}
});
scrollController.addLoadMoreListener(loadMore);
}

void dispose() {
super.disposeDebouncer();
scrollController.dispose();
_searchSubscription?.cancel();
isLoadingMoreNotifier.dispose();
resetNextBatch();
resetSearchResults();
_searchCategories = null;
}

void searchUnencryptedMessages(String searchTerm) {
_serverSearchInteractor
.execute(
void _handleListenServerSearch(Either<Failure, Success> searchResult) {
searchResult.fold(
(failure) => resetNextBatch(),
(success) {
if (success is ServerSearchChatSuccess) {
updateNextBatch(success.nextBatch);
searchResultsNotifier.value = PresentationServerSideSearch(
searchResults: [
...searchResultsNotifier.value.searchResults,
...success.results ?? [],
],
);
}
},
);
}

void resetSearchResults() {
searchResultsNotifier.value = const PresentationServerSideSearch(
searchResults: [],
);
}

void resetNextBatch() {
_nextBatch = null;
Logs().d('ServerSearchController::resetNextBatch(): $_nextBatch');
}

void updateNextBatch(String? newBatch) {
_nextBatch = newBatch;
Logs().d('ServerSearchController::updateNextBatch(): $_nextBatch');
}

void updateSearchCategories(String searchTerm) {
resetNextBatch();
_searchCategories = ServerSideSearchCategories(
searchTerm: searchTerm,
)
.listen((searchResult) {
serverSearchNotifier.value = searchResult;
});
searchFilter: SearchFilter(
limit: _limitServerSideSearchFilter,
),
);
}

void searchUnencryptedMessages() {
_searchSubscription = _serverSearchInteractor
.execute(
searchCategories: _searchCategories!,
)
.listen(
(searchResult) => _handleListenServerSearch(searchResult),
);
}

void onSearchBarChanged(String keyword) {
setDebouncerValue(keyword);
}

void loadMore() {
if (_searchCategories == null ||
isLoadingMoreNotifier.value ||
searchResultsNotifier.value.searchResults.isEmpty ||
_nextBatch == null) {
return;
}
isLoadingMoreNotifier.value = true;
_searchSubscription = _serverSearchInteractor
.execute(
searchCategories: _searchCategories!,
nextBatch: _nextBatch,
)
.listen(
(searchResult) => _handleListenServerSearch(searchResult),
onDone: () {
isLoadingMoreNotifier.value = false;
},
);
}
}
23 changes: 9 additions & 14 deletions lib/pages/search/server_search_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/domain/app_state/search/server_search_state.dart';
import 'package:fluffychat/pages/search/search.dart';
import 'package:fluffychat/pages/search/server_search_view_style.dart';
import 'package:fluffychat/presentation/decorators/chat_list/subtitle_text_style_decorator/subtitle_text_style_view.dart';
Expand All @@ -23,25 +21,21 @@ class ServerSearchMessagesList extends StatelessWidget {
return SliverToBoxAdapter(
child: ValueListenableBuilder(
valueListenable:
searchController.serverSearchController.serverSearchNotifier,
builder: ((context, searchResults, child) {
final messagesFound =
searchResults.getSuccessOrNull<ServerSearchChatSuccess>();
if (messagesFound == null ||
messagesFound.results == null ||
messagesFound.results!.isEmpty) {
return const SizedBox();
searchController.serverSearchController.searchResultsNotifier,
builder: (context, severSearchNotifier, child) {
if (severSearchNotifier.searchResults.isEmpty) {
return child!;
}

return Padding(
padding: ServerSearchViewStyle.paddingList,
child: ListView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: messagesFound.results!.length,
itemCount: severSearchNotifier.searchResults.length,
padding: ServerSearchViewStyle.paddingListItem,
itemBuilder: ((context, index) {
final searchResult = messagesFound.results?[index].result;
final searchResult =
severSearchNotifier.searchResults[index].result;
final room = searchResult?.getRoom(context);
if (room == null || searchResult == null) {
return const SizedBox.shrink();
Expand Down Expand Up @@ -89,7 +83,8 @@ class ServerSearchMessagesList extends StatelessWidget {
}),
),
);
}),
},
child: const SizedBox(),
),
);
}
Expand Down
13 changes: 13 additions & 0 deletions lib/presentation/model/search/presentation_server_side_search.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:equatable/equatable.dart';
import 'package:matrix/matrix.dart';

class PresentationServerSideSearch extends Equatable {
final List<Result> searchResults;

const PresentationServerSideSearch({
required this.searchResults,
});

@override
List<Object> get props => [searchResults];
}
16 changes: 14 additions & 2 deletions lib/utils/string_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,21 @@ extension StringCasingExtension on String {
if (prefixLength < 0) return this;
final index = toLowerCase().indexOf(highlightText.toLowerCase());
if (index > prefixLength) {
return '...${substring(index - prefixLength)}';
final enterIndex =
substring(index - prefixLength, index).lastIndexOf(RegExp(r'\n'));
if (enterIndex <= -1) {
return '...${substring(index - prefixLength)}';
}
return '...${substring(index - prefixLength + enterIndex + 1)}';
}
if (index <= -1) {
return this;
}
final enterIndex = substring(0, index).lastIndexOf(RegExp(r'\n'));
if (enterIndex <= -1) {
return this;
}
return this;
return '...${substring(enterIndex + 1)}';
}

String msisdnSanitizer() {
Expand Down
Loading

0 comments on commit a7fe5f9

Please sign in to comment.