-
Notifications
You must be signed in to change notification settings - Fork 216
Added Room Search Functionality #561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 4 commits
3da6af6
a2bbd77
9b3addf
17a02a2
89ef8cb
9badc8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,8 +6,10 @@ import 'package:flutter/material.dart'; | |
| import 'package:resonate/l10n/app_localizations.dart'; | ||
| import 'package:get/get.dart'; | ||
| import 'package:loading_animation_widget/loading_animation_widget.dart'; | ||
| import 'package:meilisearch/meilisearch.dart'; | ||
| import 'package:resonate/controllers/tabview_controller.dart'; | ||
| import 'package:resonate/models/appwrite_room.dart'; | ||
| import 'package:resonate/models/appwrite_upcomming_room.dart'; | ||
| import 'package:resonate/services/appwrite_service.dart'; | ||
| import 'package:resonate/services/room_service.dart'; | ||
| import 'package:resonate/themes/theme_controller.dart'; | ||
|
|
@@ -23,11 +25,25 @@ class RoomsController extends GetxController { | |
| final Databases databases = AppwriteService.getDatabases(); | ||
| RxList<AppwriteRoom> rooms = <AppwriteRoom>[].obs; | ||
| final ThemeController themeController = Get.find<ThemeController>(); | ||
| RxBool isSearching = false.obs; | ||
| RxBool searchBarIsEmpty = true.obs; | ||
| RxList<AppwriteRoom> filteredRooms = <AppwriteRoom>[].obs; | ||
| RxList<AppwriteUpcommingRoom> filteredUpcomingRooms = | ||
| <AppwriteUpcommingRoom>[].obs; | ||
| final MeiliSearchClient meilisearchClient = MeiliSearchClient( | ||
| meilisearchEndpoint, | ||
| meilisearchApiKey, | ||
| ); | ||
| late final MeiliSearchIndex roomsIndex; | ||
| late final MeiliSearchIndex upcomingRoomsIndex; | ||
|
|
||
|
Comment on lines
26
to
29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify MeiliSearch API key scope and exposure. Ensure meilisearchApiKey is a search‑only key (not master/admin) and isn’t hard‑coded in source control. 🏁 Script executed: #!/bin/bash
# Inspect constants and usages to ensure a scoped search key is used and not inlined.
rg -n --no-ignore -C2 -e 'meilisearch(Api)?Key|Meili(Search)?(Key|API)' -e 'meilisearchEndpoint' \
-g '!*build*' -g '!*ios*' -g '!*android*' -g '!*web/*'
# Show constant definition
fd -a constants.dart | xargs -I{} sh -c 'echo "---- {} ----"; sed -n "1,200p" "{}"'Length of output: 6174 Use a search-only API key for MeiliSearch, not a master key; remove hardcoded default. The 🤖 Prompt for AI Agents |
||
| @override | ||
| void onInit() async { | ||
| super.onInit(); | ||
| roomsIndex = meilisearchClient.index('live_rooms'); | ||
| upcomingRoomsIndex = meilisearchClient.index('upcoming_rooms'); | ||
| await getRooms(); | ||
| filteredRooms.value = rooms; | ||
| } | ||
|
|
||
| Future<AppwriteRoom> createRoomObject(Document room, String userUid) async { | ||
|
|
@@ -82,6 +98,7 @@ class RoomsController extends GetxController { | |
| } | ||
| } | ||
| update(); | ||
| updateFilteredRooms(); | ||
| } catch (e) { | ||
| log(e.toString()); | ||
| } finally { | ||
|
|
@@ -142,4 +159,149 @@ class RoomsController extends GetxController { | |
| Get.back(); | ||
| } | ||
| } | ||
|
|
||
| Future<void> searchRooms( | ||
|
||
| String query, { | ||
| bool isLiveRooms = true, | ||
| List<AppwriteUpcommingRoom>? upcomingRooms, | ||
| }) async { | ||
| if (query.isEmpty) { | ||
| if (isLiveRooms) { | ||
| filteredRooms.value = rooms; | ||
| searchBarIsEmpty.value = true; | ||
| } else { | ||
| filteredUpcomingRooms.value = upcomingRooms ?? []; | ||
| } | ||
| return; | ||
| } | ||
| if (isLiveRooms) { | ||
| isSearching.value = true; | ||
| searchBarIsEmpty.value = false; | ||
| } | ||
| try { | ||
| if (isUsingMeilisearch) { | ||
| try { | ||
| final indexToUse = isLiveRooms ? roomsIndex : upcomingRoomsIndex; | ||
| final meilisearchResult = await indexToUse.search(query); | ||
|
|
||
| if (isLiveRooms) { | ||
| filteredRooms.value = await convertMeilisearchResults( | ||
| meilisearchResult.hits, | ||
| isLiveRooms: true, | ||
| ); | ||
| } else { | ||
| filteredUpcomingRooms.value = await convertMeilisearchResults( | ||
| meilisearchResult.hits, | ||
| isLiveRooms: false, | ||
| originalUpcomingRooms: upcomingRooms ?? [], | ||
| ); | ||
| } | ||
| return; | ||
| } catch (meilisearchError) { | ||
| log( | ||
| 'Meilisearch failed, falling back to local search: $meilisearchError', | ||
| ); | ||
| } | ||
| } | ||
| // Local search | ||
| final lowerQuery = query.toLowerCase(); | ||
| if (isLiveRooms) { | ||
| filteredRooms.value = rooms.where((room) { | ||
| return room.name.toLowerCase().contains(lowerQuery) || | ||
| room.description.toLowerCase().contains(lowerQuery) || | ||
| room.tags.any((tag) => tag.toLowerCase().contains(lowerQuery)); | ||
| }).toList(); | ||
| } else { | ||
| filteredUpcomingRooms.value = (upcomingRooms ?? []).where((room) { | ||
| return room.name.toLowerCase().contains(lowerQuery) || | ||
| room.description.toLowerCase().contains(lowerQuery) || | ||
| room.tags.any((tag) => tag.toLowerCase().contains(lowerQuery)); | ||
| }).toList(); | ||
| } | ||
| } catch (e) { | ||
| log('Error searching ${isLiveRooms ? 'rooms' : 'upcoming rooms'}: $e'); | ||
| if (isLiveRooms) { | ||
| filteredRooms.value = []; | ||
| } else { | ||
| filteredUpcomingRooms.value = []; | ||
| } | ||
| } finally { | ||
| if (isLiveRooms) { | ||
| isSearching.value = false; | ||
| } | ||
| } | ||
| } | ||
|
||
|
|
||
| Future<dynamic> convertMeilisearchResults( | ||
| List<Map<String, dynamic>> meilisearchHits, { | ||
| required bool isLiveRooms, | ||
| List<AppwriteUpcommingRoom>? originalUpcomingRooms, | ||
| }) async { | ||
| if (isLiveRooms) { | ||
| //live rooms | ||
| return await Future.wait( | ||
| meilisearchHits.map((hit) async { | ||
| String userUid = Get.find<AuthStateController>().uid!; | ||
| var participantCollectionRef = await databases.listDocuments( | ||
| databaseId: masterDatabaseId, | ||
| collectionId: participantsCollectionId, | ||
| queries: [Query.equal('roomId', hit['\$id']), Query.limit(3)], | ||
| ); | ||
| List<String> memberAvatarUrls = []; | ||
| for (var p in participantCollectionRef.documents) { | ||
| Document participantDoc = await databases.getDocument( | ||
| databaseId: userDatabaseID, | ||
| collectionId: usersCollectionID, | ||
| documentId: p.data['uid'], | ||
| ); | ||
| memberAvatarUrls.add(participantDoc.data['profileImageUrl']); | ||
| } | ||
| return AppwriteRoom( | ||
| id: hit['\$id'], | ||
| name: hit['name'], | ||
| description: hit['description'], | ||
| totalParticipants: hit['totalParticipants'], | ||
| tags: List<String>.from(hit['tags'] ?? []), | ||
| memberAvatarUrls: memberAvatarUrls, | ||
| state: RoomState.live, | ||
| isUserAdmin: hit['adminUid'] == userUid, | ||
| reportedUsers: List<String>.from(hit['reportedUsers'] ?? []), | ||
| ); | ||
| }).toList(), | ||
| ); | ||
| } else { | ||
| //upcoming rooms | ||
| return meilisearchHits.map((hit) { | ||
| final originalRoom = (originalUpcomingRooms ?? []).firstWhere( | ||
| (room) => room.id == hit['\$id'], | ||
| orElse: () => AppwriteUpcommingRoom( | ||
| id: hit['\$id'] ?? '', | ||
| name: hit['name'] ?? 'Unknown', | ||
| isTime: hit['isTime'] ?? false, | ||
| scheduledDateTime: | ||
| DateTime.tryParse(hit['scheduledDateTime'] ?? '') ?? | ||
| DateTime.now(), | ||
| totalSubscriberCount: 0, | ||
| tags: List<String>.from(hit['tags'] ?? []), | ||
| subscribersAvatarUrls: [], | ||
| userIsCreator: false, | ||
| description: hit['description'] ?? '', | ||
| hasUserSubscribed: false, | ||
| ), | ||
| ); | ||
| return originalRoom; | ||
| }).toList(); | ||
| } | ||
| } | ||
|
|
||
| void clearSearch() { | ||
| filteredRooms.value = rooms; | ||
| searchBarIsEmpty.value = true; | ||
| } | ||
|
|
||
| void updateFilteredRooms() { | ||
| if (searchBarIsEmpty.value) { | ||
| filteredRooms.value = rooms; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -663,6 +663,31 @@ class AppLocalizationsGu extends AppLocalizations { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get noSearchResults => 'કોઈ શોધ પરિણામો નથી'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get searchRooms => 'Search rooms...'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get searchingRooms => 'Searching rooms...'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get clearSearch => 'Clear search'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get searchError => 'Search Error'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get searchRoomsError => 'Failed to search rooms. Please try again.'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get searchUpcomingRoomsError => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Failed to search upcoming rooms. Please try again.'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get search => 'Search'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get clear => 'Clear'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+666
to
+690
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Localize Gujarati strings (currently English). Translate to match locale for a consistent experience. - String get searchRooms => 'Search rooms...';
+ String get searchRooms => 'રૂમ્સ શોધો...';
- String get searchingRooms => 'Searching rooms...';
+ String get searchingRooms => 'રૂમ્સ શોધી રહ્યા છીએ...';
- String get clearSearch => 'Clear search';
+ String get clearSearch => 'શોધ સાફ કરો';
- String get searchError => 'Search Error';
+ String get searchError => 'શોધ ભૂલ';
- String get searchRoomsError => 'Failed to search rooms. Please try again.';
+ String get searchRoomsError => 'રૂમ્સ શોધવામાં નિષ્ફળ. કૃપા કરીને ફરી પ્રયાસ કરો.';
- String get searchUpcomingRoomsError =>
- 'Failed to search upcoming rooms. Please try again.';
+ String get searchUpcomingRoomsError =>
+ 'આગામી રૂમ્સ શોધવામાં નિષ્ફળ. કૃપા કરીને ફરી પ્રયાસ કરો.';
- String get search => 'Search';
+ String get search => 'શોધો';
- String get clear => 'Clear';
+ String get clear => 'સાફ કરો';📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String shareRoomMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String roomName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Search Logic for Upcoming rooms should be in the Upcoming Rooms Controller