Skip to content

Commit 9a44d99

Browse files
authored
fix: push notification action not handled + enhance: contacts search list (#202)
1 parent a6a2582 commit 9a44d99

21 files changed

Lines changed: 210 additions & 150 deletions

lam7a/lib/features/messaging/repository/conversations_repositories.dart

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:lam7a/features/messaging/dtos/conversation_dto.dart';
88
import 'package:lam7a/features/messaging/model/contact.dart';
99
import 'package:lam7a/features/messaging/model/conversation.dart';
1010
import 'package:lam7a/features/messaging/services/dms_api_service.dart';
11+
import 'package:lam7a/features/profile/repository/profile_repository.dart';
1112
import 'package:riverpod_annotation/riverpod_annotation.dart';
1213

1314
part 'conversations_repositories.g.dart';
@@ -17,16 +18,18 @@ ConversationsRepository conversationsRepository(Ref ref) {
1718
return ConversationsRepository(
1819
ref.read(dmsApiServiceProvider),
1920
ref.watch(authenticationProvider),
20-
ref.read(authenticationImplRepositoryProvider)
21+
ref.read(authenticationImplRepositoryProvider),
22+
ref.read(profileRepositoryProvider),
2123
);
2224
}
2325

2426
class ConversationsRepository {
2527
final DMsApiService _apiService;
2628
final AuthState _authState;
2729
final AuthenticationRepositoryImpl authRepo;
30+
final ProfileRepository _profileRepository;
2831

29-
ConversationsRepository(this._apiService, this._authState, this.authRepo);
32+
ConversationsRepository(this._apiService, this._authState, this.authRepo, this._profileRepository);
3033

3134
Future<(List<Conversation> data, bool hasMore)> fetchConversations() async {
3235
if (!_authState.isAuthenticated) return ([] as List<Conversation>, false);
@@ -72,18 +75,28 @@ class ConversationsRepository {
7275
String query,
7376
int page, [
7477
int limit = 20,
75-
]) async {
76-
return await _apiService.searchForContacts(query, page, limit);
77-
}
78-
79-
Future<List<Contact>> searchForContactsExtended(
80-
String query,
81-
int page, [
82-
int limit = 20,
8378
]) async {
8479
if(query.length <= 1){
85-
var res = await authRepo.getUsersToFollow(50);
86-
return res.map((x)=> Contact(id: x.id??-1, name: x.profile?.name?? "Unkown", handle: x.username?? "@unkown")).toList();
80+
81+
if (!(_authState.isAuthenticated)) {
82+
return [];
83+
}
84+
85+
var followersRes = await _profileRepository.getFollowers(_authState.user!.id!);
86+
var followers = followersRes.map((x)=> Contact(id: x.id ?? -1, name: x.name?? "Unkown", handle: x.username?? "@unkown", avatarUrl: x.profileImageUrl)).toList();
87+
88+
var followingRes = await _profileRepository.getFollowing(_authState.user!.id!);
89+
var following = followingRes.map((x)=> Contact(id: x.id ?? -1, name: x.name?? "Unkown", handle: x.username?? "@unkown", avatarUrl: x.profileImageUrl)).toList();
90+
91+
var suggestedRes = await authRepo.getUsersToFollow(50);
92+
var suggested = suggestedRes.map((x)=> Contact(id: x.id??-1, name: x.profile?.name?? "Unkown", handle: x.username?? "@unkown", avatarUrl: x.profile?.profileImageUrl)).toList();
93+
94+
// Remove duplicates ids and combine lists
95+
var allContactsMap = <int, Contact>{};
96+
for (var contact in [...followers, ...following, ...suggested]) {
97+
allContactsMap[contact.id] = contact;
98+
}
99+
return allContactsMap.values.toList();
87100
}else{
88101
return await _apiService.searchForContacts(query, page, limit);
89102
}

lam7a/lib/features/messaging/repository/messages_repository.dart

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,6 @@ class MessagesRepository {
111111
_logger.i("Socket reconnected, rejoining conversations");
112112
for (var conversationId in _joinedConversations) {
113113
_socket.joinConversation(conversationId);
114-
}
115-
116-
for (var conversationId in _joinedConversations) {
117114
_reSyncMessageHistory(conversationId);
118115
}
119116
}
@@ -174,66 +171,7 @@ class MessagesRepository {
174171
}
175172
}
176173

177-
Future<void> sendMessage(int senderId, int conversationId, String message) async {
178-
_logger.i("Sending message to conversation $conversationId: $message");
179-
180-
final request = CreateMessageRequest(
181-
conversationId: conversationId,
182-
senderId: senderId,
183-
text: message,
184-
);
185-
186-
// 1. Create a temporary local message (with negative ID to avoid conflicts)
187-
final tempId = -DateTime.now().millisecondsSinceEpoch;
188-
189-
final optimisticMessage = ChatMessage(
190-
id: tempId,
191-
text: message,
192-
time: DateTime.now(),
193-
isMine: true,
194-
isDelivered: false,
195-
isSeen: false,
196-
senderId: senderId,
197-
conversationId: conversationId,
198-
);
199-
200-
// Add optimistic message
201-
_cache.addMessage(conversationId, optimisticMessage);
202-
_getNotifier(conversationId).add(null);
203-
204-
MessageDto? messageDto;
205-
206-
try {
207-
messageDto = await _socket.sendMessage(request);
208-
} on BlockedUserError {
209-
// Remove the optimistic message
210-
_cache.removeMessage(conversationId, tempId);
211-
_getNotifier(conversationId).add(null);
212-
rethrow;
213-
}
214-
215-
// If server sent nothing, just remove local optimistic message
216-
if (messageDto == null) {
217-
_logger.e("Failed to send message, server returned null");
218-
_cache.removeMessage(conversationId, tempId);
219-
_getNotifier(conversationId).add(null);
220-
return;
221-
}
222-
223-
// Remove the optimistic message
224-
_cache.removeMessage(conversationId, tempId);
225-
226-
// Add the real server message
227-
_cache.addMessage(
228-
conversationId,
229-
ChatMessage.fromDto(
230-
messageDto,
231-
currentUserId: _authState.user!.id!,
232-
),
233-
);
234-
235-
_getNotifier(conversationId).add(null);
236-
}
174+
237175

238176

239177
Future<bool> _reSyncMessageHistory(int conversationId) async {
@@ -315,6 +253,67 @@ class MessagesRepository {
315253
return messagesDto.metadata.hasMore ?? false;
316254
}
317255

256+
Future<void> sendMessage(int senderId, int conversationId, String message) async {
257+
_logger.i("Sending message to conversation $conversationId: $message");
258+
259+
final request = CreateMessageRequest(
260+
conversationId: conversationId,
261+
senderId: senderId,
262+
text: message,
263+
);
264+
265+
// 1. Create a temporary local message (with negative ID to avoid conflicts)
266+
final tempId = -DateTime.now().millisecondsSinceEpoch;
267+
268+
final optimisticMessage = ChatMessage(
269+
id: tempId,
270+
text: message,
271+
time: DateTime.now(),
272+
isMine: true,
273+
isDelivered: false,
274+
isSeen: false,
275+
senderId: senderId,
276+
conversationId: conversationId,
277+
);
278+
279+
// Add optimistic message
280+
_cache.addMessage(conversationId, optimisticMessage);
281+
_getNotifier(conversationId).add(null);
282+
283+
MessageDto? messageDto;
284+
285+
try {
286+
messageDto = await _socket.sendMessage(request);
287+
} on BlockedUserError {
288+
// Remove the optimistic message
289+
_cache.removeMessage(conversationId, tempId);
290+
_getNotifier(conversationId).add(null);
291+
rethrow;
292+
}
293+
294+
// If server sent nothing, just remove local optimistic message
295+
if (messageDto == null) {
296+
_logger.e("Failed to send message, server returned null");
297+
_cache.removeMessage(conversationId, tempId);
298+
_getNotifier(conversationId).add(null);
299+
return;
300+
}
301+
302+
// Remove the optimistic message
303+
_cache.removeMessage(conversationId, tempId);
304+
305+
// Add the real server message
306+
_cache.addMessage(
307+
conversationId,
308+
ChatMessage.fromDto(
309+
messageDto,
310+
currentUserId: _authState.user!.id!,
311+
),
312+
);
313+
314+
_getNotifier(conversationId).add(null);
315+
}
316+
318317
void sendMarkAsSeen(int conversationId) async {
319318

320319
final req = MarkSeenRequest(

lam7a/lib/features/messaging/ui/view/chat_screen.dart

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,17 +206,10 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
206206
),
207207

208208
actions: [
209-
if (kDebugMode)
209+
if ((!connectionState.hasValue || !connectionState.value!))
210210
Padding(
211211
padding: const EdgeInsets.all(8.0),
212-
child: CircleAvatar(
213-
key: Key(MessagingUIKeys.chatScreenConnectionStatus),
214-
radius: 8,
215-
backgroundColor:
216-
!connectionState.hasValue || !connectionState.value!
217-
? Colors.red
218-
: Colors.green,
219-
),
212+
child: Icon(Icons.signal_wifi_statusbar_connected_no_internet_4, size: 24, color: Colors.redAccent,),
220213
),
221214
],
222215
);

lam7a/lib/features/messaging/ui/view/find_contacts_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class FindContactsScreen extends ConsumerWidget {
5757
style: const TextStyle(fontWeight: FontWeight.w600),
5858
),
5959
subtitle: Text(
60-
c.handle,
60+
'@'+c.handle,
6161
style: TextStyle(color: Colors.grey[600], fontSize: 14),
6262
),
6363
onTap: () async {

lam7a/lib/features/messaging/ui/viewmodel/chat_viewmodel.dart

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,7 @@ class ChatViewModel extends _$ChatViewModel {
5151
_messagesRepository.sendMarkAsSeen(_conversationId);
5252

5353
Future.microtask(() async {
54-
55-
ref.read(conversationViewmodelProvider(_conversationId).notifier).markConversationAsSeen();
56-
_loadContact();
57-
_prepareConversation();
58-
_loadMessages();
59-
60-
await requestInitMessages();
61-
54+
init();
6255
});
6356

6457

@@ -83,6 +76,15 @@ class ChatViewModel extends _$ChatViewModel {
8376

8477
}
8578

79+
Future<void> init() async {
80+
ref.read(conversationViewmodelProvider(_conversationId).notifier).markConversationAsSeen();
81+
_loadContact();
82+
_prepareConversation();
83+
_loadMessages();
84+
85+
await requestInitMessages();
86+
}
87+
8688
Future<void> _prepareConversation() async {
8789
var conversationViewmodel = ref.read(conversationViewmodelProvider(_conversationId).notifier);
8890
if (conversationViewmodel.state.conversation == null) {
@@ -169,10 +171,11 @@ class ChatViewModel extends _$ChatViewModel {
169171
}
170172

171173
Future<void> sendMessage() async {
174+
String message = state.draftMessage.trim();
175+
state = state.copyWith(draftMessage: "");
172176
try {
173177
_messagesRepository.updateTypingStatus(_conversationId, false);
174-
await _messagesRepository.sendMessage(_authState.user!.id!, _conversationId, state.draftMessage.trim());
175-
state = state.copyWith(draftMessage: "");
178+
await _messagesRepository.sendMessage(_authState.user!.id!, _conversationId, message);
176179

177180
} on BlockedUserError catch (e) {
178181
_logger.w("Cannot send message, user is blocked: $e");

lam7a/lib/features/messaging/ui/viewmodel/contact_search_viewmodel.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ContactSearchViewModel extends _$ContactSearchViewModel {
2626
Future<void> loadSearchSuggestion() async {
2727
state = state.copyWith(contacts: AsyncLoading());
2828

29-
var contacts = await _conversationsRepository.searchForContactsExtended("", 1);
29+
var contacts = await _conversationsRepository.searchForContacts("", 1);
3030
state = state.copyWith(contacts: AsyncData(contacts));
3131
}
3232

@@ -47,7 +47,7 @@ class ContactSearchViewModel extends _$ContactSearchViewModel {
4747
// }
4848

4949
try {
50-
var data = await _conversationsRepository.searchForContactsExtended(query, 1);
50+
var data = await _conversationsRepository.searchForContacts(query, 1);
5151
state = state.copyWith(contacts: AsyncData(data));
5252
} catch (e, st) {
5353
state = state.copyWith(contacts: AsyncError(e, st));

lam7a/lib/features/messaging/ui/widgets/message_tile.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ class MessageTile extends StatelessWidget {
9393
style: const TextStyle(fontSize: 11, color: Colors.grey),
9494
),
9595
),
96+
if (!showFooter && !isDelivered)
97+
Padding(
98+
padding: const EdgeInsets.only(bottom: 6),
99+
child: Text("Sending",
100+
style: const TextStyle(fontSize: 11, color: Colors.grey),
101+
),
102+
),
96103
],
97104
),
98105
),);

lam7a/lib/features/notifications/notifiactions_calls.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,6 @@ void handlePostViewNotificationAction(String postId) {
9292
getLogger(
9393
NotificationsReceiver,
9494
).i("Handling Post View notification action for postId: $postId");
95-
navigatorKey.currentState?.pushNamed("/post", arguments: {'postId': postId});
95+
navigatorKey.currentState?.pushNamed("/tweet", arguments: {'tweetId': postId});
96+
9697
}

lam7a/lib/features/notifications/notifications_receiver.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,16 @@ void handleFCMTokenUpdate(
5050

5151
@riverpod
5252
NotificationsReceiver notificationsReceiver(Ref ref){
53-
return NotificationsReceiver(ref.read(unReadNotificationCountProvider.notifier));
53+
ref.keepAlive();
54+
return NotificationsReceiver(ref, ref.read(unReadNotificationCountProvider.notifier));
5455
}
5556

5657
class NotificationsReceiver {
5758
Logger logger = getLogger(NotificationsReceiver);
5859
NewNotificationCount _newNotificationCount;
59-
60-
NotificationsReceiver(this._newNotificationCount);
60+
61+
Ref ref;
62+
NotificationsReceiver(this.ref, this._newNotificationCount);
6163

6264
RemoteMessage? _initialMessage;
6365

@@ -79,7 +81,9 @@ class NotificationsReceiver {
7981
}
8082

8183
void handleInitialMessageIfAny() {
84+
logger.i("Handling initial message if any.");
8285
if (_initialMessage != null) {
86+
logger.i("Handling initial message: $_initialMessage");
8387
_onNotificationTapped(_initialMessage!);
8488
_initialMessage = null;
8589
}
@@ -134,19 +138,20 @@ class NotificationsReceiver {
134138
Future<void> _onNotificationTapped(
135139
RemoteMessage message,
136140
) async {
141+
logger.i("Initializing Firebase in background message handler");
137142
await Firebase.initializeApp();
138143

139144
logger.i("Handling a background message: ${message.messageId}");
140145
logger.i('Message data: ${message.data.toString()} ${message.data.runtimeType.toString()}');
141146
NotificationModel notifiacation = NotificationModel.fromJson(message.data);
142147

143-
144148
handleNotificationAction(notifiacation);
149+
150+
ref.read(notificationsRepositoryProvider).markAsRead(notifiacation.notificationId);
145151
_newNotificationCount.updateNotificationsCount();
146152
}
147153

148154
void handleNotificationAction(NotificationModel notifiacation) {
149-
150155
switch (notifiacation.type) {
151156
case NotificationType.dm:
152157
if (notifiacation.conversationId != null) {

lam7a/lib/features/notifications/repositories/notifications_repository.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,9 @@ class NotificationsRepository {
7575
void markAllAsRead(){
7676
_apiService.markAllAsRead();
7777
}
78+
79+
void markAsRead(String notificationId){
80+
logger.i("Marking notification $notificationId as read");
81+
_apiService.markAsRead(notificationId);
82+
}
7883
}

0 commit comments

Comments
 (0)