Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
3.8 KiB

  1. import 'dart:convert';
  2. import 'dart:typed_data';
  3. import 'package:flutter/material.dart';
  4. import 'package:http/http.dart' as http;
  5. import 'package:pointycastle/impl.dart';
  6. import '/components/custom_circle_avatar.dart';
  7. import '/data_models/user_search.dart';
  8. import '/database/models/my_profile.dart';
  9. import '/utils/encryption/aes_helper.dart';
  10. import '/utils/storage/session_cookie.dart';
  11. import '/utils/strings.dart';
  12. import '/utils/encryption/crypto_utils.dart';
  13. @immutable
  14. class UserSearchResult extends StatefulWidget {
  15. final UserSearch user;
  16. const UserSearchResult({
  17. Key? key,
  18. required this.user,
  19. }) : super(key: key);
  20. @override
  21. _UserSearchResultState createState() => _UserSearchResultState();
  22. }
  23. class _UserSearchResultState extends State<UserSearchResult>{
  24. bool showFailed = false;
  25. @override
  26. Widget build(BuildContext context) {
  27. return Center(
  28. child: Padding(
  29. padding: const EdgeInsets.only(top: 30),
  30. child: Column(
  31. crossAxisAlignment: CrossAxisAlignment.center,
  32. children: <Widget>[
  33. CustomCircleAvatar(
  34. initials: widget.user.username[0].toUpperCase(),
  35. icon: const Icon(Icons.person, size: 80),
  36. radius: 50,
  37. ),
  38. const SizedBox(height: 10),
  39. Text(
  40. widget.user.username,
  41. style: const TextStyle(
  42. fontSize: 35,
  43. ),
  44. ),
  45. const SizedBox(height: 30),
  46. TextButton(
  47. onPressed: sendFriendRequest,
  48. child: Text(
  49. 'Send Friend Request',
  50. style: TextStyle(
  51. color: Theme.of(context).colorScheme.onPrimary,
  52. fontSize: 20,
  53. ),
  54. ),
  55. style: ButtonStyle(
  56. backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.primary),
  57. padding: MaterialStateProperty.all<EdgeInsets>(
  58. const EdgeInsets.only(left: 20, right: 20, top: 8, bottom: 8)),
  59. ),
  60. ),
  61. showFailed ? const SizedBox(height: 20) : const SizedBox.shrink(),
  62. failedMessage(context),
  63. ],
  64. ),
  65. ),
  66. );
  67. }
  68. Widget failedMessage(BuildContext context) {
  69. if (!showFailed) {
  70. return const SizedBox.shrink();
  71. }
  72. return Text(
  73. 'Failed to send friend request',
  74. style: TextStyle(
  75. color: Theme.of(context).colorScheme.error,
  76. fontSize: 16,
  77. ),
  78. );
  79. }
  80. Future<void> sendFriendRequest() async {
  81. MyProfile profile = await MyProfile.getProfile();
  82. String publicKeyString = CryptoUtils.encodeRSAPublicKeyToPem(profile.publicKey!);
  83. RSAPublicKey friendPublicKey = CryptoUtils.rsaPublicKeyFromPem(widget.user.publicKey);
  84. final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
  85. String payloadJson = jsonEncode({
  86. 'user_id': widget.user.id,
  87. 'friend_id': base64.encode(CryptoUtils.rsaEncrypt(
  88. Uint8List.fromList(profile.id.codeUnits),
  89. friendPublicKey,
  90. )),
  91. 'friend_username': base64.encode(CryptoUtils.rsaEncrypt(
  92. Uint8List.fromList(profile.username.codeUnits),
  93. friendPublicKey,
  94. )),
  95. 'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
  96. Uint8List.fromList(symmetricKey),
  97. friendPublicKey,
  98. )),
  99. 'asymmetric_public_key': AesHelper.aesEncrypt(
  100. symmetricKey,
  101. Uint8List.fromList(publicKeyString.codeUnits),
  102. ),
  103. });
  104. var resp = await http.post(
  105. await MyProfile.getServerUrl('api/v1/auth/friend_request'),
  106. headers: {
  107. 'cookie': await getSessionCookie(),
  108. },
  109. body: payloadJson,
  110. );
  111. if (resp.statusCode != 200) {
  112. showFailed = true;
  113. setState(() {});
  114. return;
  115. }
  116. Navigator.pop(context);
  117. }
  118. }