import 'dart:io' show Platform; import 'dart:convert'; import 'dart:typed_data'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:pointycastle/impl.dart'; import '/components/custom_circle_avatar.dart'; import '/data_models/user_search.dart'; import '/database/models/my_profile.dart'; import '/utils/encryption/aes_helper.dart'; import '/utils/storage/session_cookie.dart'; import '/utils/strings.dart'; import '/utils/encryption/crypto_utils.dart'; @immutable class UserSearchResult extends StatefulWidget { final UserSearch user; const UserSearchResult({ Key? key, required this.user, }) : super(key: key); @override _UserSearchResultState createState() => _UserSearchResultState(); } class _UserSearchResultState extends State{ bool showFailed = false; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.only(top: 30), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ CustomCircleAvatar( initials: widget.user.username[0].toUpperCase(), icon: const Icon(Icons.person, size: 80), radius: 50, ), const SizedBox(height: 10), Text( widget.user.username, style: const TextStyle( fontSize: 35, ), ), const SizedBox(height: 30), TextButton( onPressed: sendFriendRequest, child: Text( 'Send Friend Request', style: TextStyle( color: Theme.of(context).colorScheme.onPrimary, fontSize: 20, ), ), style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.primary), padding: MaterialStateProperty.all( const EdgeInsets.only(left: 20, right: 20, top: 8, bottom: 8)), ), ), showFailed ? const SizedBox(height: 20) : const SizedBox.shrink(), failedMessage(context), ], ), ), ); } Widget failedMessage(BuildContext context) { if (!showFailed) { return const SizedBox.shrink(); } return Text( 'Failed to send friend request', style: TextStyle( color: Theme.of(context).colorScheme.error, fontSize: 16, ), ); } Future sendFriendRequest() async { MyProfile profile = await MyProfile.getProfile(); String publicKeyString = CryptoUtils.encodeRSAPublicKeyToPem(profile.publicKey!); RSAPublicKey friendPublicKey = CryptoUtils.rsaPublicKeyFromPem(widget.user.publicKey); final symmetricKey = AesHelper.deriveKey(generateRandomString(32)); String? token = await FirebaseMessaging.instance.getToken(); String payloadJson = jsonEncode({ 'user_id': widget.user.id, 'friend_id': base64.encode(CryptoUtils.rsaEncrypt( Uint8List.fromList(profile.id.codeUnits), friendPublicKey, )), 'friend_username': base64.encode(CryptoUtils.rsaEncrypt( Uint8List.fromList(profile.username.codeUnits), friendPublicKey, )), 'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt( Uint8List.fromList(symmetricKey), friendPublicKey, )), 'asymmetric_public_key': AesHelper.aesEncrypt( symmetricKey, Uint8List.fromList(publicKeyString.codeUnits), ), 'tokens': token == null ? {} : [{ 'device_token': { 'device_type': base64.encode(CryptoUtils.rsaEncrypt( Uint8List.fromList((Platform.isAndroid ? 'android' : 'ios').codeUnits), friendPublicKey, )), 'token': base64.encode(CryptoUtils.rsaEncrypt( Uint8List.fromList(token.codeUnits), friendPublicKey, )), } }], }); var resp = await http.post( await MyProfile.getServerUrl('api/v1/auth/friend_request'), headers: { 'cookie': await getSessionCookie(), }, body: payloadJson, ); if (resp.statusCode != 200) { showFailed = true; setState(() {}); return; } Navigator.pop(context); } }