Skip to content

Commit

Permalink
Search by title test with fix for proper updating
Browse files Browse the repository at this point in the history
  • Loading branch information
gnunicorn committed Aug 19, 2024
1 parent bdbf7a1 commit 195b684
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 123 deletions.
6 changes: 6 additions & 0 deletions app/lib/common/providers/room_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ final isDirectChatProvider =
return convo?.isDm() == true && members.length == 2;
});

final isConvoBookmarked =
FutureProvider.family<bool, String>((ref, roomIdOrAlias) async {
final convo = await ref.watch(chatProvider(roomIdOrAlias).future);
return convo?.isBookmarked() == true;
});

/// Caching the MemoryImage of each entry
final roomHierarchyAvatarProvider =
FutureProvider.family<MemoryImage?, SpaceHierarchyRoomInfo>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:acter/common/providers/room_providers.dart';
import 'package:acter_flutter_sdk/acter_flutter_sdk_ffi.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod/riverpod.dart';

Expand All @@ -8,16 +7,17 @@ part 'room_list_filter_state.freezed.dart';
Future<bool> roomListFilterStateAppliesToRoom(
RoomListFilterState state,
Ref ref,
Convo convo,
String convoId,
) async {
switch (state.selection) {
case FilterSelection.dmsOnly:
if (!convo.isDm()) {
final isDm = await ref.watch(isDirectChatProvider(convoId).future);
if (!isDm) {
return false;
}
break;
case FilterSelection.favorites:
if (!convo.isBookmarked()) {
if (!await ref.watch(isConvoBookmarked(convoId).future)) {
return false;
}
break;
Expand All @@ -26,16 +26,12 @@ Future<bool> roomListFilterStateAppliesToRoom(
}
if (state.searchTerm?.isNotEmpty == true) {
final searchTerm = state.searchTerm!.toLowerCase();
final convoId = convo.getRoomIdStr();
if (convoId.toLowerCase().contains(searchTerm)) {
return true;
}
final avatarInfo = ref.read(roomAvatarInfoProvider(convoId));
if (avatarInfo.displayName != null &&
avatarInfo.displayName!.toLowerCase().contains(searchTerm)) {
return true;
}
return false;
final displayName = await ref.read(roomDisplayNameProvider(convoId).future);
return (displayName != null &&
displayName.toLowerCase().contains(searchTerm));
}
return true;
}
Expand Down
35 changes: 9 additions & 26 deletions app/lib/features/chat/pages/room_profile_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -278,39 +278,22 @@ class _RoomProfilePageState extends ConsumerState<RoomProfilePage> {
}

Widget _actions(BuildContext context, Convo? convo, bool isDirectChat) {
final convoLoader = ref.watch(chatProvider(widget.roomId));
final myMembership = ref.watch(roomMembershipProvider(widget.roomId));
final isBookmarked =
ref.watch(isConvoBookmarked(widget.roomId)).valueOrNull ?? false;

return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Bookmark
convoLoader.when(
data: (conv) {
final isBookmarked = conv?.isBookmarked() == true;
return _actionItem(
context: context,
iconData: isBookmarked ? Icons.bookmark : Icons.bookmark_border,
actionName: L10n.of(context).bookmark,
onTap: () async => await conv?.setBookmarked(!isBookmarked),
);
},
error: (e, s) {
_log.severe('Failed to load convo', e, s);
return Skeletonizer(
child: IconButton.filled(
icon: const Icon(
Icons.bookmark_add_outlined,
size: 20,
),
onPressed: () {},
),
);
_actionItem(
context: context,
iconData: isBookmarked ? Icons.bookmark : Icons.bookmark_border,
actionName: L10n.of(context).bookmark,
onTap: () async {
(await ref.read(chatProvider(widget.roomId).future))
?.setBookmarked(!isBookmarked);
},
loading: () => ActionItemSkeleton(
iconData: Icons.bookmark_add_outlined,
actionName: L10n.of(context).bookmark,
),
),

// Invite
Expand Down
12 changes: 6 additions & 6 deletions app/lib/features/chat/providers/chat_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,18 @@ final timelineStreamProvider =
});

final filteredChatsProvider =
FutureProvider.autoDispose<List<Convo>>((ref) async {
final allRooms = ref.watch(chatsProvider);
FutureProvider.autoDispose<List<String>>((ref) async {
final allRooms = ref.watch(chatIdsProvider);
if (!ref.watch(hasRoomFilters)) {
throw 'No filters selected';
}

final foundRooms = List<Convo>.empty(growable: true);
final foundRooms = List<String>.empty(growable: true);

final search = ref.watch(roomListFilterProvider);
for (final convo in allRooms) {
if (await roomListFilterStateAppliesToRoom(search, ref, convo)) {
foundRooms.add(convo);
for (final convoId in allRooms) {
if (await roomListFilterStateAppliesToRoom(search, ref, convoId)) {
foundRooms.add(convoId);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:acter/common/providers/notifiers/client_pref_notifier.dart';
import 'package:acter/features/chat/models/room_list_filter_state/room_list_filter_state.dart';
import 'package:riverpod/riverpod.dart';
import 'package:logging/logging.dart';

final _log = Logger('a3::chat::room_list_filter_provider');

final persistentRoomListFilterSelector = createMapPrefProvider<FilterSelection>(
prefKey: 'chatRoomFilterSelection',
Expand All @@ -15,9 +18,10 @@ final roomListFilterProvider =
RoomListFilterNotifier(ref.read(persistentRoomListFilterSelector), ref),
);

final hasRoomFilters = StateProvider((ref) {
final hasRoomFilters = Provider((ref) {
final state = ref.watch(roomListFilterProvider);
if (state.searchTerm != null && state.searchTerm!.isNotEmpty) {
_log.info('has search term');
return true;
}
return state.selection != FilterSelection.all;
Expand Down
103 changes: 66 additions & 37 deletions app/lib/features/chat/widgets/chats_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ class ChatsList extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final hasSearchFilter = ref.watch(hasRoomFilters);
if (hasSearchFilter) {
if (ref.watch(hasRoomFilters)) {
return _renderFiltered(context, ref);
}
final chats = ref.watch(chatIdsProvider);
Expand All @@ -38,38 +37,40 @@ class ChatsList extends ConsumerWidget {
}

Widget _renderFiltered(BuildContext context, WidgetRef ref) {
return ref.watch(filteredChatsProvider).when(
data: (chats) {
if (chats.isEmpty) {
return SliverToBoxAdapter(
child: Center(
heightFactor: 10,
child: Text(L10n.of(context).noChatsFoundMatchingYourFilter),
),
);
}
return _renderList(
context,
chats.map((e) => e.getRoomIdStr()).toList(),
);
},
loading: () => const SliverToBoxAdapter(
final filteredChats = ref.watch(filteredChatsProvider);
return filteredChats.when(
data: (chatsIds) {
if (chatsIds.isEmpty) {
return SliverToBoxAdapter(
child: Center(
heightFactor: 10,
child: CircularProgressIndicator(),
child: Text(L10n.of(context).noChatsFoundMatchingYourFilter),
),
);
}
return _renderList(
context,
chatsIds,
);
},
loading: () => const SliverToBoxAdapter(
child: Center(
heightFactor: 10,
child: CircularProgressIndicator(),
),
),
error: (e, s) {
_log.severe('Failed to filter convos', e, s);
return SliverToBoxAdapter(
child: Center(
heightFactor: 10,
child: Text(L10n.of(context).searchingFailed(e)),
),
error: (e, s) {
_log.severe('Failed to filter convos', e, s);
return SliverToBoxAdapter(
child: Center(
heightFactor: 10,
child: Text(L10n.of(context).searchingFailed(e)),
),
);
},
skipLoadingOnReload: true,
);
},
skipLoadingOnReload: true,
skipLoadingOnRefresh: true,
);
}

Widget _renderSyncing(BuildContext context) {
Expand Down Expand Up @@ -126,23 +127,39 @@ class _AnimatedChatsList extends StatefulWidget {
}

class __AnimatedChatsListState extends State<_AnimatedChatsList> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
late GlobalKey<AnimatedListState> _listKey;
late List<String> _currentList;

@override
void initState() {
super.initState();
_currentList = widget.entries;
_reset();
}

void _reset() {
_listKey = GlobalKey<AnimatedListState>(debugLabel: 'chat rooms list');
_currentList = List.of(widget.entries);
}

@override
void didUpdateWidget(_AnimatedChatsList oldWidget) {
super.didUpdateWidget(oldWidget);
refreshList();
if (_listKey.currentState == null) {
_log.fine('no state, hard reset');
// we can ignore the diffing as we aren't live, just reset
setState(() {
_reset();
});
return;
} else {
refreshList();
}
}

void refreshList() {
_log.fine('refreshing');
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
_log.fine('diffing $_currentList, ${widget.entries}');
final diffResult = calculateListDiff<String>(
_currentList,
widget.entries,
Expand All @@ -162,20 +179,30 @@ class __AnimatedChatsListState extends State<_AnimatedChatsList> {
},
);
}
_log.fine('done diffing');
});
}

void _insert(int pos, String data) {
_log.fine('insert $pos: $data');
_currentList.insert(pos, data);
_listKey.currentState?.insertItem(pos);
if (_listKey.currentState != null) {
_listKey.currentState!.insertItem(pos);
} else {
_log.fine('we are not');
}
}

void _remove(int pos, String data) {
_currentList.removeAt(pos);
_listKey.currentState?.removeItem(
pos,
(context, animation) => _removedItemBuilder(data, context, animation),
);
if (_listKey.currentState != null) {
_listKey.currentState!.removeItem(
pos,
(context, animation) => _removedItemBuilder(data, context, animation),
);
} else {
_log.fine('we are not');
}
}

Widget _removedItemBuilder(
Expand Down Expand Up @@ -203,6 +230,7 @@ class __AnimatedChatsListState extends State<_AnimatedChatsList> {
return const SizedBox.shrink();
}
final roomId = _currentList[index];
_log.fine('render $roomId');
return ConvoCard(
animation: animation,
key: Key('convo-card-$roomId'),
Expand All @@ -214,6 +242,7 @@ class __AnimatedChatsListState extends State<_AnimatedChatsList> {

@override
Widget build(BuildContext context) {
_log.fine('render list $_currentList');
return SliverAnimatedList(
key: _listKey,
initialItemCount: _currentList.length,
Expand Down
Loading

0 comments on commit 195b684

Please sign in to comment.