import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_dotenv/flutter_dotenv.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 '/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<UserSearchResult>{
|
|
bool showFailed = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 30),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: <Widget>[
|
|
CustomCircleAvatar(
|
|
initials: widget.user.username[0].toUpperCase(),
|
|
icon: const Icon(Icons.person, size: 80),
|
|
imagePath: null,
|
|
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<EdgeInsets>(
|
|
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<void> 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 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),
|
|
),
|
|
});
|
|
|
|
var resp = await http.post(
|
|
Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/friend_request'),
|
|
headers: {
|
|
'cookie': await getSessionCookie(),
|
|
},
|
|
body: payloadJson,
|
|
);
|
|
|
|
if (resp.statusCode != 200) {
|
|
showFailed = true;
|
|
setState(() {});
|
|
return;
|
|
}
|
|
|
|
Navigator.pop(context);
|
|
}
|
|
}
|