import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; import 'package:sqflite/sqflite.dart'; import 'package:uuid/uuid.dart'; import '/database/repositories/conversation_users_repository.dart'; import '/database/repositories/conversations_repository.dart'; import '/database/repositories/device_tokens_repository.dart'; import '/database/models/messages.dart'; import '/database/models/image_message.dart'; import '/database/models/text_messages.dart'; import '/database/models/conversation_users.dart'; import '/database/models/conversations.dart'; import '/database/models/my_profile.dart'; import '/utils/storage/database.dart'; import '/utils/storage/session_cookie.dart'; import '/utils/storage/write_file.dart'; class MessagesService { static Future sendMessage(Conversation conversation, { String? data, List files = const [] }) async { MyProfile profile = await MyProfile.getProfile(); var uuid = const Uuid(); ConversationUser currentUser = await ConversationUsersRepository.getConversationUser(conversation, profile.id); List messages = []; Map payload = { 'tokens': await DeviceTokensRepository.getDeviceTokensForConversation(conversation), 'sender': profile.username, 'conversation_id': conversation.id, 'messages': [], }; final db = await getDatabaseConnection(); if (data != null) { TextMessage message = TextMessage( id: uuid.v4(), symmetricKey: '', userSymmetricKey: '', senderId: currentUser.userId, senderUsername: profile.username, associationKey: currentUser.associationKey, createdAt: DateTime.now().toIso8601String(), failedToSend: false, text: data, ); messages.add(message); payload['messages'].add(await message.payloadJson( conversation, )); await db.insert( 'messages', message.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } for (File file in files) { String messageId = uuid.v4(); File writtenFile = await writeImage( messageId, file.readAsBytesSync(), ); ImageMessage message = ImageMessage( id: messageId, symmetricKey: '', userSymmetricKey: '', senderId: currentUser.userId, senderUsername: profile.username, associationKey: currentUser.associationKey, createdAt: DateTime.now().toIso8601String(), failedToSend: false, file: writtenFile, ); messages.add(message); payload['messages'].add(await message.payloadJson( conversation, )); await db.insert( 'messages', message.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } String sessionCookie = await getSessionCookie(); return http.post( await MyProfile.getServerUrl('api/v1/auth/message'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'cookie': sessionCookie, }, body: jsonEncode(payload), ) .then((resp) { if (resp.statusCode != 204) { throw Exception('Unable to send message'); } }) .catchError((exception) { for (Message message in messages) { message.failedToSend = true; db.update( 'messages', message.toMap(), where: 'id = ?', whereArgs: [message.id], ); } throw exception; }); } static Future updateMessageThread(Conversation conversation, {MyProfile? profile}) async { profile ??= await MyProfile.getProfile(); ConversationUser currentUser = await ConversationUsersRepository.getConversationUser(conversation, profile.id); var resp = await http.get( await MyProfile.getServerUrl('api/v1/auth/messages/${currentUser.associationKey}'), headers: { 'cookie': await getSessionCookie(), } ); if (resp.statusCode != 200) { throw Exception(resp.body); } List messageThreadJson = jsonDecode(resp.body); final db = await getDatabaseConnection(); for (var i = 0; i < messageThreadJson.length; i++) { var messageJson = messageThreadJson[i] as Map; var message = messageJson['message_data']['attachment_id'] != null ? await ImageMessage.fromJson( messageJson, profile.privateKey!, ) : TextMessage.fromJson( messageJson, profile.privateKey!, ); ConversationUser messageUser = await ConversationUsersRepository.getConversationUser(conversation, message.senderId); message.senderUsername = messageUser.username; await db.insert( 'messages', message.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } } static Future updateMessageThreads({List? conversations}) async { MyProfile profile = await MyProfile.getProfile(); conversations ??= await ConversationsRepository.getConversations(); for (var i = 0; i < conversations.length; i++) { await updateMessageThread(conversations[i], profile: profile); } } }