Browse Source

Seperate views into subdirectories

pull/1/head
Tovi Jaeschke-Rogers 2 years ago
parent
commit
9502692958
14 changed files with 601 additions and 588 deletions
  1. +128
    -126
      mobile/lib/models/conversations.dart
  2. +59
    -57
      mobile/lib/utils/storage/messages.dart
  3. +66
    -65
      mobile/lib/views/main/conversation/create_add_users.dart
  4. +0
    -0
      mobile/lib/views/main/conversation/create_add_users_list.dart
  5. +47
    -46
      mobile/lib/views/main/conversation/detail.dart
  6. +3
    -2
      mobile/lib/views/main/conversation/edit_details.dart
  7. +55
    -54
      mobile/lib/views/main/conversation/list.dart
  8. +12
    -11
      mobile/lib/views/main/conversation/list_item.dart
  9. +103
    -102
      mobile/lib/views/main/conversation/settings.dart
  10. +4
    -3
      mobile/lib/views/main/conversation/settings_user_list_item.dart
  11. +53
    -52
      mobile/lib/views/main/friend/list.dart
  12. +0
    -0
      mobile/lib/views/main/friend/list_item.dart
  13. +71
    -70
      mobile/lib/views/main/home.dart
  14. +0
    -0
      mobile/lib/views/main/profile/profile.dart

+ 128
- 126
mobile/lib/models/conversations.dart View File

@ -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,
} }

+ 59
- 57
mobile/lib/utils/storage/messages.dart View File

@ -1,65 +1,17 @@
import 'dart:convert'; import 'dart:convert';
import 'package:Envelope/models/my_profile.dart';
import 'package:uuid/uuid.dart';
import 'package:Envelope/models/conversation_users.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:sqflite/sqflite.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '/utils/storage/session_cookie.dart';
import '/utils/storage/database.dart';
import 'package:sqflite/sqflite.dart';
import 'package:uuid/uuid.dart';
import '/models/conversation_users.dart';
import '/models/conversations.dart'; import '/models/conversations.dart';
import '/models/messages.dart'; import '/models/messages.dart';
Future<void> updateMessageThread(Conversation conversation, {MyProfile? profile}) async {
profile ??= await MyProfile.getProfile();
ConversationUser currentUser = await getConversationUser(conversation, profile.id);
var resp = await http.get(
Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/messages/${currentUser.associationKey}'),
headers: {
'cookie': await getSessionCookie(),
}
);
if (resp.statusCode != 200) {
throw Exception(resp.body);
}
List<dynamic> messageThreadJson = jsonDecode(resp.body);
final db = await getDatabaseConnection();
for (var i = 0; i < messageThreadJson.length; i++) {
Message message = Message.fromJson(
messageThreadJson[i] as Map<String, dynamic>,
profile.privateKey!,
);
ConversationUser messageUser = await getConversationUser(conversation, message.senderId);
message.senderUsername = messageUser.username;
await db.insert(
'messages',
message.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
Future<void> updateMessageThreads({List<Conversation>? conversations}) async {
// try {
MyProfile profile = await MyProfile.getProfile();
conversations ??= await getConversations();
for (var i = 0; i < conversations.length; i++) {
await updateMessageThread(conversations[i], profile: profile);
}
// } catch(SocketException) {
// return;
// }
}
import '/models/my_profile.dart';
import '/utils/storage/database.dart';
import '/utils/storage/session_cookie.dart';
Future<void> sendMessage(Conversation conversation, String data) async { Future<void> sendMessage(Conversation conversation, String data) async {
final preferences = await SharedPreferences.getInstance(); final preferences = await SharedPreferences.getInstance();
@ -122,3 +74,53 @@ Future<void> sendMessage(Conversation conversation, String data) async {
); );
}); });
} }
Future<void> updateMessageThread(Conversation conversation, {MyProfile? profile}) async {
profile ??= await MyProfile.getProfile();
ConversationUser currentUser = await getConversationUser(conversation, profile.id);
var resp = await http.get(
Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/messages/${currentUser.associationKey}'),
headers: {
'cookie': await getSessionCookie(),
}
);
if (resp.statusCode != 200) {
throw Exception(resp.body);
}
List<dynamic> messageThreadJson = jsonDecode(resp.body);
final db = await getDatabaseConnection();
for (var i = 0; i < messageThreadJson.length; i++) {
Message message = Message.fromJson(
messageThreadJson[i] as Map<String, dynamic>,
profile.privateKey!,
);
ConversationUser messageUser = await getConversationUser(conversation, message.senderId);
message.senderUsername = messageUser.username;
await db.insert(
'messages',
message.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
Future<void> updateMessageThreads({List<Conversation>? conversations}) async {
// try {
MyProfile profile = await MyProfile.getProfile();
conversations ??= await getConversations();
for (var i = 0; i < conversations.length; i++) {
await updateMessageThread(conversations[i], profile: profile);
}
// } catch(SocketException) {
// return;
// }
}

mobile/lib/views/main/conversation_create_add_users.dart → mobile/lib/views/main/conversation/create_add_users.dart View File


mobile/lib/views/main/conversation_create_add_users_list.dart → mobile/lib/views/main/conversation/create_add_users_list.dart View File


mobile/lib/views/main/conversation_detail.dart → mobile/lib/views/main/conversation/detail.dart View File


mobile/lib/views/main/conversation_edit_details.dart → mobile/lib/views/main/conversation/edit_details.dart View File


mobile/lib/views/main/conversation_list.dart → mobile/lib/views/main/conversation/list.dart View File


mobile/lib/views/main/conversation_list_item.dart → mobile/lib/views/main/conversation/list_item.dart View File


mobile/lib/views/main/conversation_settings.dart → mobile/lib/views/main/conversation/settings.dart View File


mobile/lib/views/main/conversation_settings_user_list_item.dart → mobile/lib/views/main/conversation/settings_user_list_item.dart View File


mobile/lib/views/main/friend_list.dart → mobile/lib/views/main/friend/list.dart View File


mobile/lib/views/main/friend_list_item.dart → mobile/lib/views/main/friend/list_item.dart View File


+ 71
- 70
mobile/lib/views/main/home.dart View File

@ -1,16 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import '/views/main/conversation_list.dart';
import '/views/main/friend_list.dart';
import '/views/main/profile.dart';
import '/utils/storage/friends.dart';
import '/utils/storage/conversations.dart';
import '/utils/storage/messages.dart';
import '/utils/storage/session_cookie.dart';
import 'package:http/http.dart' as http;
import '/models/conversations.dart'; import '/models/conversations.dart';
import '/models/friends.dart'; import '/models/friends.dart';
import '/models/my_profile.dart'; import '/models/my_profile.dart';
import '/utils/storage/conversations.dart';
import '/utils/storage/friends.dart';
import '/utils/storage/messages.dart';
import '/utils/storage/session_cookie.dart';
import '/views/main/conversation/list.dart';
import '/views/main/friend/list.dart';
import '/views/main/profile/profile.dart';
class Home extends StatefulWidget { class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key); const Home({Key? key}) : super(key: key);
@ -41,35 +42,36 @@ class _HomeState extends State<Home> {
]; ];
@override @override
void initState() {
updateData();
super.initState();
}
void updateData() async {
if (!await checkLogin()) {
return;
}
await updateFriends();
await updateConversations();
await updateMessageThreads();
conversations = await getConversations();
friends = await getFriends();
profile = await MyProfile.getProfile();
setState(() {
_widgetOptions = <Widget>[
ConversationList(
conversations: conversations,
friends: friends,
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: isLoading ? loading() : Scaffold(
body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: isLoading ? const SizedBox.shrink() : BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Theme.of(context).hintColor,
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600),
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600),
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: "Chats",
),
BottomNavigationBarItem(
icon: Icon(Icons.group_work),
label: "Friends",
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
label: "Profile",
),
],
),
), ),
FriendList(friends: friends),
Profile(profile: profile),
];
isLoading = false;
});
);
} }
Future<bool> checkLogin() async { Future<bool> checkLogin() async {
@ -112,10 +114,10 @@ class _HomeState extends State<Home> {
return false; return false;
} }
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
@override
void initState() {
updateData();
super.initState();
} }
Widget loading() { Widget loading() {
@ -140,36 +142,35 @@ class _HomeState extends State<Home> {
); );
} }
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: isLoading ? loading() : Scaffold(
body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: isLoading ? const SizedBox.shrink() : BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Theme.of(context).hintColor,
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600),
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600),
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: "Chats",
),
BottomNavigationBarItem(
icon: Icon(Icons.group_work),
label: "Friends",
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
label: "Profile",
),
],
),
void updateData() async {
if (!await checkLogin()) {
return;
}
await updateFriends();
await updateConversations();
await updateMessageThreads();
conversations = await getConversations();
friends = await getFriends();
profile = await MyProfile.getProfile();
setState(() {
_widgetOptions = <Widget>[
ConversationList(
conversations: conversations,
friends: friends,
), ),
);
FriendList(friends: friends),
Profile(profile: profile),
];
isLoading = false;
});
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
} }
} }

mobile/lib/views/main/profile.dart → mobile/lib/views/main/profile/profile.dart View File


Loading…
Cancel
Save