Skip to content

Commit

Permalink
FIX: Send and Receive messages (#82)
Browse files Browse the repository at this point in the history
* feat: sockets auto reconnect v1

* fix: sockets auto reconnect v2

* undate

* fix: missing function call

* fix: messageing step 1

* fix: remove the chats map from the chatViewModel to insure consistency

* feature: add missing attributes

* feature: get missing attributes from the request

* Untrack the generated files

* refactor: add some organization to the chat screen

* feature: clear all chats and users data on log out

* fix: remove dublicate user name from msg

* feat: receive messages correctly
  • Loading branch information
Ahmed-Aladdiin authored Dec 11, 2024
1 parent 4f6357b commit 83aa956
Show file tree
Hide file tree
Showing 17 changed files with 794 additions and 659 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ flutter_*.png
*.DS_Store
*.iml
.flutter-plugins*
coverage/
coverage/

*.g.dart
36 changes: 23 additions & 13 deletions lib/core/models/chat_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,44 @@ class ChatModel {
@HiveField(1)
final List<String> userIds;
@HiveField(2)
final String? photo;
final List<String>? admins;
@HiveField(3)
final ChatType type;
final List<String>? creators;
@HiveField(4)
Uint8List? photoBytes;
final String? photo;
@HiveField(5)
String? id;
final ChatType type;
@HiveField(6)
final List<String>? admins;
Uint8List? photoBytes;
@HiveField(7)
final String? description;
String? id;
@HiveField(8)
final DateTime? lastMessageTimestamp;
final String? description;
@HiveField(9)
final bool isArchived;
final DateTime? lastMessageTimestamp;
@HiveField(10)
final bool isMuted;
final bool isArchived;
@HiveField(11)
final String? draft;
final bool isMuted;
@HiveField(12)
final bool isMentioned;
final String? draft;
@HiveField(13)
List<MessageModel> messages;
final bool isMentioned;
@HiveField(14)
List<MessageModel> messages;
@HiveField(15)
final DateTime? muteUntil; // Add this field


ChatModel({
required this.title,
required this.userIds,
this.admins,
this.creators,
this.photo,
required this.type,
this.id,
this.photoBytes,
this.admins,
this.description,
this.lastMessageTimestamp,
this.isArchived = false,
Expand Down Expand Up @@ -92,6 +96,7 @@ class ChatModel {
other.photo == photo &&
other.id == id &&
other.admins == admins &&
other.creators == creators &&
other.description == description &&
other.lastMessageTimestamp == lastMessageTimestamp &&
other.isArchived == isArchived &&
Expand All @@ -115,6 +120,7 @@ class ChatModel {
isArchived.hashCode ^
isMuted.hashCode ^
draft.hashCode ^
creators.hashCode ^
isMentioned.hashCode ^
messages.hashCode; // Include messages in hashCode
}
Expand All @@ -128,6 +134,7 @@ class ChatModel {
'photo: $photo,\n'
'id: $id,\n'
'admins: $admins,\n'
'creators: $creators,\n'
'description: $description,\n'
'lastMessageTimestamp: $lastMessageTimestamp,\n'
'isArchived: $isArchived,\n'
Expand All @@ -146,6 +153,7 @@ class ChatModel {
String? id,
Uint8List? photoBytes,
List<String>? admins,
List<String>? creators,
String? description,
DateTime? lastMessageTimestamp,
bool? isArchived,
Expand All @@ -163,6 +171,7 @@ class ChatModel {
id: id ?? this.id,
photoBytes: photoBytes ?? this.photoBytes,
admins: admins ?? this.admins,
creators: creators ?? this.creators,
description: description ?? this.description,
lastMessageTimestamp: lastMessageTimestamp ?? this.lastMessageTimestamp,
isArchived: isArchived ?? this.isArchived,
Expand All @@ -182,6 +191,7 @@ class ChatModel {
'photo': photo,
'id': id,
'admins': admins,
'creators': creators,
'description': description,
'lastMessageTimestamp': lastMessageTimestamp?.toIso8601String(),
'isArchived': isArchived,
Expand Down
75 changes: 27 additions & 48 deletions lib/core/models/message_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import 'package:telware_cross_platform/features/chat/enum/message_enums.dart';

import 'package:telware_cross_platform/features/chat/classes/message_content.dart';

import 'package:telware_cross_platform/features/stories/utils/utils_functions.dart';

import '../constants/server_constants.dart';

part 'message_model.g.dart';

@HiveType(typeId: 6)
Expand All @@ -29,19 +25,19 @@ class MessageModel {
@HiveField(6)
String? id;
@HiveField(7)
final String? photo;
final Map<String, MessageState> userStates; // userID -> state of the message
@HiveField(8)
Uint8List? photoBytes;
final bool isPinned;
@HiveField(9)
final Map<String, MessageState> userStates; // userID -> state of the message
final String? parentMessage;
@HiveField(10)
final bool isPinned;
final String localId;
@HiveField(11)
final String? parentMessage;
final bool isForward;
@HiveField(12)
final String localId;
final bool isAnnouncement;
@HiveField(13)
final bool isForward;
List<String> threadMessages;

//<editor-fold desc="Data Methods">
MessageModel({
Expand All @@ -52,33 +48,14 @@ class MessageModel {
this.content,
required this.timestamp,
this.id,
this.photo,
this.photoBytes,
required this.userStates,
this.isPinned=false,
this.parentMessage,
this.localId = '',
this.isForward = false
});

Future<void> _setPhotoBytes() async {
if (photo == null || photo!.isEmpty) return;

String url =
photo!.startsWith('http') ? photo! : '$API_URL_PICTURES/$photo';

if (url.isEmpty) return;

Uint8List? tempImage;
try {
tempImage = await downloadImage(url);
if (tempImage != null) {
photoBytes = tempImage;
}
} catch (e) {
photoBytes = null;
}
}
this.isForward = false,
this.isAnnouncement = false,
List<String>? threadMessages,
}) : threadMessages = threadMessages ?? [];

void updateUserState(String userId, MessageState state) {
userStates[userId] = state;
Expand All @@ -99,8 +76,8 @@ class MessageModel {
other.timestamp == timestamp &&
other.autoDeleteTimestamp == autoDeleteTimestamp &&
other.id == id &&
other.photo == photo &&
other.photoBytes == photoBytes &&
other.threadMessages == threadMessages &&
other.isAnnouncement == isAnnouncement &&
other.localId == localId &&
other.isForward == isForward &&
other.userStates == userStates;
Expand All @@ -115,8 +92,8 @@ class MessageModel {
content.hashCode ^
timestamp.hashCode ^
id.hashCode ^
photo.hashCode ^
photoBytes.hashCode ^
isAnnouncement.hashCode ^
threadMessages.hashCode ^
isForward.hashCode ^
localId.hashCode ^
userStates.hashCode;
Expand All @@ -130,13 +107,14 @@ class MessageModel {
'timestamp: $timestamp,\n'
'autoDeleteTimestamp: $autoDeleteTimestamp,\n'
'id: $id,\n'
'photo: $photo,\n'
'userStates: $userStates,\n'
'messageType: ${messageType.name},\n'
'messageContentType: ${messageContentType.name},\n'
'isPhotoBytesSet: ${photoBytes != null}\n'
'localId: $localId\n'
'isAnnouncement: $isAnnouncement\n'
'isForward: $isForward\n'
'isPinned: $isPinned\n'
'threadMessages: $threadMessages'
')');
}

Expand All @@ -154,21 +132,23 @@ class MessageModel {
String? localId,
bool? isForward,
bool? isPinned,
bool? isAnnouncement,
List<String>? threadMessages,
}) {
return MessageModel(
senderId: senderId ?? this.senderId,
content: content ?? this.content,
timestamp: timestamp ?? this.timestamp,
autoDeleteTimestamp: autoDeleteTimestamp ?? this.autoDeleteTimestamp,
id: id ?? this.id,
photo: photo ?? this.photo,
photoBytes: photoBytes ?? this.photoBytes,
userStates: userStates ?? Map.from(this.userStates),
messageType: messageType ?? this.messageType,
messageContentType: messageContentType ?? this.messageContentType,
localId: localId ?? this.localId,
isForward: isForward ?? this.isForward,
isPinned: isPinned ?? this.isPinned
isPinned: isPinned ?? this.isPinned,
isAnnouncement: isAnnouncement ?? this.isAnnouncement,
threadMessages: threadMessages ?? this.threadMessages,
);
}

Expand All @@ -179,7 +159,6 @@ class MessageModel {
'timestamp': timestamp.toIso8601String(),
'autoDeleteTimestamp': autoDeleteTimestamp?.microsecondsSinceEpoch,
'id': id,
'photo': photo,
'userStates': forSender
? userStates.map(
(key, value) => MapEntry(key, value.toString().split('.').last))
Expand All @@ -189,18 +168,19 @@ class MessageModel {
'localId': localId,
'isForward': isForward,
'isPinned': isPinned,
'isAnnouncement': isAnnouncement,
'threadMessages': threadMessages,
};

return map;
}

static Future<MessageModel> fromMap(Map<String, dynamic> map) async {
final message = MessageModel(
return MessageModel(
senderId: map['senderId'] as String,
content: map['content'] as MessageContent?,
timestamp: DateTime.parse(map['timestamp'] as String),
id: map['messageId'] as String?,
photo: map['photo'] as String?,
messageType: MessageType.getType(map['messageType']),
messageContentType: MessageContentType.getType(map['messageContentType']),
autoDeleteTimestamp: map['autoDeleteTimeStamp'] != null
Expand All @@ -217,9 +197,8 @@ class MessageModel {
),
isForward: map['isForward'] ?? false,
isPinned: map['isPinned'] ?? false,
isAnnouncement: map['isAnnouncement'] ?? false,
);
await message._setPhotoBytes();
return message;
}

String toJson({bool forSender = false}) =>
Expand Down
13 changes: 9 additions & 4 deletions lib/core/services/socket_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'package:socket_io_client/socket_io_client.dart';
import 'package:flutter/foundation.dart';
import 'package:telware_cross_platform/core/constants/server_constants.dart';
import 'package:telware_cross_platform/core/mock/constants_mock.dart';
import 'package:telware_cross_platform/features/chat/view_model/event_handler.dart';

class SocketService {
Expand Down Expand Up @@ -32,7 +33,7 @@ class SocketService {
}

void _connect() {
if (isConnected) return;
if (isConnected || USE_MOCK_DATA) return;
debugPrint('*** Entered the connect method');
debugPrint(_serverUrl);
debugPrint(_userId);
Expand All @@ -45,6 +46,8 @@ class SocketService {
'auth': {'sessionId': _sessionId}
});

_socket.io.options?['debug'] = true; // Enable debug logs

_socket.connect();

_socket.onConnect((_) {
Expand All @@ -56,17 +59,18 @@ class SocketService {
});

_socket.onConnectError((error) {
debugPrint('Connection error: $error');
debugPrint('### Connection error: $error');
onError();
});

_socket.onError((error) {
debugPrint('Socket error: $error');
debugPrint('### Socket error: $error');
onError();
});

_socket.onDisconnect((_) {
debugPrint('Disconnected from server');
_isReconnecting = false;
});
}

Expand All @@ -83,7 +87,8 @@ class SocketService {
});
}

void _disconnect() {
void disconnect() {
debugPrint('*** called the socket disconnect');
_socket.disconnect();
isConnected = false;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/features/chat/repository/chat_local_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class ChatLocalRepository {
return chats;
}

Future<void> clearChats(String userId) async {
await _chatsBox.delete(_chatsBoxKey+userId);
}

/////////////////////////////////////
// get other users
Future<bool> setOtherUsers(Map<String, UserModel> otherUsers, String userId) async {
Expand All @@ -76,6 +80,10 @@ class ChatLocalRepository {
return otherUsersMap;
}

void clearOtherUsers(String userId) async {
await _otherUsersBox.delete(_otherUsersBoxKey+userId);
}

/////////////////////////////////////
// sets event queue
Future<bool> setEventQueue(Queue<MessageEvent> queue, String userId) async {
Expand All @@ -102,4 +110,8 @@ class ChatLocalRepository {
final queue = Queue<MessageEvent>.from(eventsList);
return queue;
}

Future<void> clearEventQueue(String userId) async {
await _eventsBox.delete(_eventsBoxKey+userId);
}
}
Loading

0 comments on commit 83aa956

Please sign in to comment.