|
@ -1,22 +1,131 @@ |
|
|
import 'dart:convert'; |
|
|
import 'dart:convert'; |
|
|
import 'dart:typed_data'; |
|
|
import 'dart:typed_data'; |
|
|
import 'package:Envelope/models/conversation_users.dart'; |
|
|
|
|
|
import 'package:Envelope/models/friends.dart'; |
|
|
|
|
|
import 'package:Envelope/models/my_profile.dart'; |
|
|
|
|
|
|
|
|
|
|
|
import 'package:pointycastle/export.dart'; |
|
|
import 'package:pointycastle/export.dart'; |
|
|
import 'package:sqflite/sqflite.dart'; |
|
|
import 'package:sqflite/sqflite.dart'; |
|
|
import 'package:uuid/uuid.dart'; |
|
|
import 'package:uuid/uuid.dart'; |
|
|
import '/utils/encryption/crypto_utils.dart'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import '/models/conversation_users.dart'; |
|
|
|
|
|
import '/models/friends.dart'; |
|
|
|
|
|
import '/models/my_profile.dart'; |
|
|
import '/utils/encryption/aes_helper.dart'; |
|
|
import '/utils/encryption/aes_helper.dart'; |
|
|
|
|
|
import '/utils/encryption/crypto_utils.dart'; |
|
|
import '/utils/storage/database.dart'; |
|
|
import '/utils/storage/database.dart'; |
|
|
import '/utils/strings.dart'; |
|
|
import '/utils/strings.dart'; |
|
|
|
|
|
|
|
|
enum ConversationStatus { |
|
|
|
|
|
complete, |
|
|
|
|
|
pending, |
|
|
|
|
|
error, |
|
|
|
|
|
|
|
|
Future<Conversation> createConversation(String title, List<Friend> friends) 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)); |
|
|
|
|
|
|
|
|
|
|
|
String associationKey = generateRandomString(32); |
|
|
|
|
|
|
|
|
|
|
|
Conversation conversation = Conversation( |
|
|
|
|
|
id: conversationId, |
|
|
|
|
|
userId: profile.id, |
|
|
|
|
|
conversationDetailId: '', |
|
|
|
|
|
symmetricKey: base64.encode(symmetricKey), |
|
|
|
|
|
admin: true, |
|
|
|
|
|
name: title, |
|
|
|
|
|
status: ConversationStatus.pending, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
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: associationKey, |
|
|
|
|
|
admin: true, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
for (Friend friend in friends) { |
|
|
|
|
|
await db.insert( |
|
|
|
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
userId: friend.friendId, |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: friend.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: false, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Conversation findConversationByDetailId(List<Conversation> conversations, String id) { |
|
|
|
|
|
for (var conversation in conversations) { |
|
|
|
|
|
if (conversation.conversationDetailId == id) { |
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Or return `null`. |
|
|
|
|
|
throw ArgumentError.value(id, "id", "No element with that id"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<Conversation> getConversationById(String id) async { |
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query( |
|
|
|
|
|
'conversations', |
|
|
|
|
|
where: 'id = ?', |
|
|
|
|
|
whereArgs: [id], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (maps.length != 1) { |
|
|
|
|
|
throw ArgumentError('Invalid user id'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return Conversation( |
|
|
|
|
|
id: maps[0]['id'], |
|
|
|
|
|
userId: maps[0]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[0]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[0]['symmetric_key'], |
|
|
|
|
|
admin: maps[0]['admin'] == 1, |
|
|
|
|
|
name: maps[0]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[0]['status']], |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// A method that retrieves all the dogs from the dogs table. |
|
|
|
|
|
Future<List<Conversation>> getConversations() async { |
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query('conversations'); |
|
|
|
|
|
|
|
|
|
|
|
return List.generate(maps.length, (i) { |
|
|
|
|
|
return Conversation( |
|
|
|
|
|
id: maps[i]['id'], |
|
|
|
|
|
userId: maps[i]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[i]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[i]['symmetric_key'], |
|
|
|
|
|
admin: maps[i]['admin'] == 1, |
|
|
|
|
|
name: maps[i]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[i]['status']], |
|
|
|
|
|
); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Conversation { |
|
|
class Conversation { |
|
|
String id; |
|
|
String id; |
|
|
String userId; |
|
|
String userId; |
|
@ -113,17 +222,6 @@ class Conversation { |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
String toString() { |
|
|
|
|
|
return ''' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
id: $id |
|
|
|
|
|
userId: $userId |
|
|
|
|
|
name: $name |
|
|
|
|
|
admin: $admin'''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toMap() { |
|
|
Map<String, dynamic> toMap() { |
|
|
return { |
|
|
return { |
|
|
'id': id, |
|
|
'id': id, |
|
@ -135,118 +233,22 @@ class Conversation { |
|
|
'status': status.index, |
|
|
'status': status.index, |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<Conversation> createConversation(String title, List<Friend> friends) 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)); |
|
|
|
|
|
|
|
|
|
|
|
String associationKey = generateRandomString(32); |
|
|
|
|
|
|
|
|
|
|
|
Conversation conversation = Conversation( |
|
|
|
|
|
id: conversationId, |
|
|
|
|
|
userId: profile.id, |
|
|
|
|
|
conversationDetailId: '', |
|
|
|
|
|
symmetricKey: base64.encode(symmetricKey), |
|
|
|
|
|
admin: true, |
|
|
|
|
|
name: title, |
|
|
|
|
|
status: ConversationStatus.pending, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
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: associationKey, |
|
|
|
|
|
admin: true, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
for (Friend friend in friends) { |
|
|
|
|
|
await db.insert( |
|
|
|
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
userId: friend.friendId, |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: friend.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: false, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
String toString() { |
|
|
|
|
|
return ''' |
|
|
|
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Conversation findConversationByDetailId(List<Conversation> conversations, String id) { |
|
|
|
|
|
for (var conversation in conversations) { |
|
|
|
|
|
if (conversation.conversationDetailId == id) { |
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
id: $id |
|
|
|
|
|
userId: $userId |
|
|
|
|
|
name: $name |
|
|
|
|
|
admin: $admin'''; |
|
|
} |
|
|
} |
|
|
// Or return `null`. |
|
|
|
|
|
throw ArgumentError.value(id, "id", "No element with that id"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A method that retrieves all the dogs from the dogs table. |
|
|
|
|
|
Future<List<Conversation>> getConversations() async { |
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query('conversations'); |
|
|
|
|
|
|
|
|
|
|
|
return List.generate(maps.length, (i) { |
|
|
|
|
|
return Conversation( |
|
|
|
|
|
id: maps[i]['id'], |
|
|
|
|
|
userId: maps[i]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[i]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[i]['symmetric_key'], |
|
|
|
|
|
admin: maps[i]['admin'] == 1, |
|
|
|
|
|
name: maps[i]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[i]['status']], |
|
|
|
|
|
); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Future<Conversation> getConversationById(String id) async { |
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query( |
|
|
|
|
|
'conversations', |
|
|
|
|
|
where: 'id = ?', |
|
|
|
|
|
whereArgs: [id], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (maps.length != 1) { |
|
|
|
|
|
throw ArgumentError('Invalid user id'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return Conversation( |
|
|
|
|
|
id: maps[0]['id'], |
|
|
|
|
|
userId: maps[0]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[0]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[0]['symmetric_key'], |
|
|
|
|
|
admin: maps[0]['admin'] == 1, |
|
|
|
|
|
name: maps[0]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[0]['status']], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
enum ConversationStatus { |
|
|
|
|
|
complete, |
|
|
|
|
|
pending, |
|
|
|
|
|
error, |
|
|
} |
|
|
} |