import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:Envelope/database/models/conversation_users.dart'; import 'package:sqflite/sql.dart'; import 'package:uuid/uuid.dart'; import '/database/models/conversations.dart'; import '/database/models/friends.dart'; import '/database/models/my_profile.dart'; import '/utils/encryption/aes_helper.dart'; import '/utils/strings.dart'; import '/utils/storage/database.dart'; class ConversationsRepository { static Future createConversation(String title, List friends, bool twoUser) async { final db = await getDatabaseConnection(); MyProfile profile = await MyProfile.getProfile(); var uuid = const Uuid(); final String conversationId = uuid.v4(); Uint8List symmetricKey = AesHelper.deriveKey(generateRandomString(32)); Conversation conversation = Conversation( id: conversationId, userId: profile.id, symmetricKey: base64.encode(symmetricKey), admin: true, name: title, twoUser: twoUser, status: ConversationStatus.pending, isRead: true, messageExpiryDefault: 'no_expiry', adminAddMembers: true, adminEditInfo: true, adminSendMessages: false, createdAt: DateTime.now(), updatedAt: DateTime.now(), ); await db.insert( 'conversations', conversation.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); await db.insert( 'conversation_users', ConversationUser( id: uuid.v4(), userId: profile.id, conversationId: conversationId, username: profile.username, associationKey: uuid.v4(), publicKey: profile.publicKey!, admin: true, ).toMap(), conflictAlgorithm: ConflictAlgorithm.fail, ); for (Friend friend in friends) { await db.insert( 'conversation_users', ConversationUser( id: uuid.v4(), userId: friend.friendId, conversationId: conversationId, username: friend.username, associationKey: uuid.v4(), publicKey: friend.publicKey, admin: twoUser ? true : false, ).toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } if (twoUser) { List> maps = await db.query( 'conversation_users', where: 'conversation_id = ? AND user_id != ?', whereArgs: [ conversation.id, profile.id ], ); if (maps.length != 1) { throw ArgumentError('Invalid user id'); } conversation.name = maps[0]['username']; await db.insert( 'conversations', conversation.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } return conversation; } static Future getConversationById(String id) async { final db = await getDatabaseConnection(); final List> maps = await db.query( 'conversations', where: 'id = ?', whereArgs: [id], ); if (maps.length != 1) { throw ArgumentError('Invalid user id'); } File? file; if (maps[0]['file'] != null && maps[0]['file'] != '') { file = File(maps[0]['file']); } return Conversation( id: maps[0]['id'], userId: maps[0]['user_id'], symmetricKey: maps[0]['symmetric_key'], admin: maps[0]['admin'] == 1, name: maps[0]['name'], twoUser: maps[0]['two_user'] == 1, status: ConversationStatus.values[maps[0]['status']], isRead: maps[0]['is_read'] == 1, icon: file, messageExpiryDefault: maps[0]['message_expiry'], adminAddMembers: maps[0]['admin_add_members'] == 1, adminEditInfo: maps[0]['admin_edit_info'] == 1, adminSendMessages: maps[0]['admin_send_messages'] == 1, createdAt: DateTime.parse(maps[0]['created_at']), updatedAt: DateTime.parse(maps[0]['updated_at']), ); } static Future> getConversations() async { final db = await getDatabaseConnection(); final List> maps = await db.query( 'conversations', orderBy: 'name', ); return List.generate(maps.length, (i) { File? file; if (maps[i]['file'] != null && maps[i]['file'] != '') { file = File(maps[i]['file']); } return Conversation( id: maps[i]['id'], userId: maps[i]['user_id'], symmetricKey: maps[i]['symmetric_key'], admin: maps[i]['admin'] == 1, name: maps[i]['name'], twoUser: maps[i]['two_user'] == 1, status: ConversationStatus.values[maps[i]['status']], isRead: maps[i]['is_read'] == 1, icon: file, messageExpiryDefault: maps[i]['message_expiry'] ?? 'no_expiry', adminAddMembers: maps[i]['admin_add_members'] == 1, adminEditInfo: maps[i]['admin_edit_info'] == 1, adminSendMessages: maps[i]['admin_send_messages'] == 1, createdAt: DateTime.parse(maps[i]['created_at']), updatedAt: DateTime.parse(maps[i]['updated_at']), ); }); } static Future getTwoUserConversation(String userId) async { final db = await getDatabaseConnection(); MyProfile profile = await MyProfile.getProfile(); final List> maps = await db.rawQuery( ''' SELECT conversations.* FROM conversations LEFT JOIN conversation_users ON conversation_users.conversation_id = conversations.id WHERE conversation_users.user_id = ? AND conversation_users.user_id != ? AND conversations.two_user = 1 ''', [ userId, profile.id ], ); if (maps.length != 1) { return null; } return Conversation( id: maps[0]['id'], userId: maps[0]['user_id'], symmetricKey: maps[0]['symmetric_key'], admin: maps[0]['admin'] == 1, name: maps[0]['name'], twoUser: maps[0]['two_user'] == 1, status: ConversationStatus.values[maps[0]['status']], isRead: maps[0]['is_read'] == 1, messageExpiryDefault: maps[0]['message_expiry'], adminAddMembers: maps[0]['admin_add_members'] == 1, adminEditInfo: maps[0]['admin_edit_info'] == 1, adminSendMessages: maps[0]['admin_send_messages'] == 1, createdAt: DateTime.parse(maps[0]['created_at']), updatedAt: DateTime.parse(maps[0]['updated_at']), ); } static Future getLatestUpdatedAt() async { final db = await getDatabaseConnection(); final List> maps = await db.rawQuery( ''' SELECT conversations.updated_at FROM conversations ORDER BY updated_at DESC LIMIT 1; ''' ); return maps.isNotEmpty ? DateTime.parse(maps[0]['updated_at']) : null; } }