mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
262 lines
8.8 KiB
Dart
262 lines
8.8 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:drift/drift.dart' hide Column;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|
import 'package:lottie/lottie.dart';
|
|
import 'package:twonly/globals.dart';
|
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
|
import 'package:twonly/src/database/twonly.db.dart';
|
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
|
import 'package:twonly/src/utils/misc.dart';
|
|
import 'package:twonly/src/views/components/fingerprint_text.dart';
|
|
import 'package:twonly/src/views/contact/contact_verify_qr_scan.view.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
class ContactVerifyView extends StatefulWidget {
|
|
const ContactVerifyView(this.contact, {super.key});
|
|
final Contact contact;
|
|
|
|
@override
|
|
State<ContactVerifyView> createState() => _ContactVerifyViewState();
|
|
}
|
|
|
|
// ignore: constant_identifier_names
|
|
enum ScanResult { None, Success, Failed }
|
|
|
|
class _ContactVerifyViewState extends State<ContactVerifyView> {
|
|
Fingerprint? _fingerprint;
|
|
late Contact _contact;
|
|
late StreamSubscription<Contact?> _contactSub;
|
|
ScanResult _scanResult = ScanResult.None;
|
|
Uint8List? _qrCodeImageBytes;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_contact = widget.contact;
|
|
unawaited(loadAsync());
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
unawaited(_contactSub.cancel());
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> loadAsync() async {
|
|
_fingerprint = await generateSessionFingerPrint(widget.contact.userId);
|
|
|
|
if (_fingerprint != null) {
|
|
// final result = zx.encodeBarcode(
|
|
// contents: base64Encode(
|
|
// _fingerprint!.scannableFingerprint.fingerprints,
|
|
// ),
|
|
// params: EncodeParams(
|
|
// width: 150,
|
|
// height: 150,
|
|
// ),
|
|
// );
|
|
// if (result.isValid && result.data != null) {
|
|
// final img = imglib.Image.fromBytes(
|
|
// width: 150,
|
|
// height: 150,
|
|
// bytes: result.data!.buffer,
|
|
// numChannels: 1,
|
|
// );
|
|
// _qrCodeImageBytes = imglib.encodePng(img);
|
|
// }
|
|
}
|
|
|
|
// final contact = twonlyDB.contactsDao
|
|
// .getContactByUserId(widget.contact.userId)
|
|
// .watchSingleOrNull();
|
|
// _contactSub = contact.listen((contact) {
|
|
// if (contact == null) return;
|
|
// setState(() {
|
|
// _contact = contact;
|
|
// });
|
|
// });
|
|
// setState(() {});
|
|
}
|
|
|
|
Future<void> openQrScanner() async {
|
|
if (_fingerprint == null) return;
|
|
final isValid = await Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) {
|
|
return ContactVerifyQrScanView(
|
|
widget.contact,
|
|
fingerprint: _fingerprint!,
|
|
);
|
|
},
|
|
),
|
|
) as bool?;
|
|
if (isValid == null) {
|
|
return; // user just returned...
|
|
}
|
|
if (isValid) {
|
|
_scanResult = ScanResult.Success;
|
|
await updateUserVerifyState(true);
|
|
} else {
|
|
_scanResult = ScanResult.Failed;
|
|
await updateUserVerifyState(false);
|
|
}
|
|
setState(() {});
|
|
}
|
|
|
|
Future<void> updateUserVerifyState(bool verified) async {
|
|
final update = ContactsCompanion(verified: Value(verified));
|
|
await twonlyDB.contactsDao.updateContact(_contact.userId, update);
|
|
}
|
|
|
|
Widget get qrWidget => (_qrCodeImageBytes == null)
|
|
? const SizedBox(
|
|
width: 150,
|
|
height: 150,
|
|
)
|
|
: Image.memory(_qrCodeImageBytes!);
|
|
Widget get resultAnimation => SizedBox(
|
|
width: 150,
|
|
child: Lottie.asset(
|
|
(_scanResult == ScanResult.Success)
|
|
? 'assets/animations/success.json'
|
|
: 'assets/animations/failed.json',
|
|
repeat: false,
|
|
onLoaded: (p0) {
|
|
Future.delayed(const Duration(seconds: 3), () {
|
|
if (mounted) {
|
|
setState(() {
|
|
_scanResult = ScanResult.None;
|
|
});
|
|
}
|
|
});
|
|
},
|
|
),
|
|
);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(context.lang.contactVerifyNumberTitle),
|
|
),
|
|
body: (_fingerprint == null)
|
|
? const Center(child: CircularProgressIndicator())
|
|
: ListView(
|
|
children: [
|
|
Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(25),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(10),
|
|
color: Theme.of(context).colorScheme.primary,
|
|
),
|
|
child: GestureDetector(
|
|
onTap: openQrScanner,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(10),
|
|
color: Colors.white,
|
|
),
|
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
|
child: Column(
|
|
children: [
|
|
if (_scanResult == ScanResult.None)
|
|
qrWidget
|
|
else
|
|
resultAnimation,
|
|
const SizedBox(height: 10),
|
|
SizedBox(
|
|
width: 200,
|
|
child: Text(
|
|
(_scanResult == ScanResult.None)
|
|
? context
|
|
.lang.contactVerifyNumberTapToScan
|
|
: '',
|
|
style: const TextStyle(
|
|
color: Colors.black,
|
|
fontSize: 15,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
FingerprintText(
|
|
_fingerprint!.displayableFingerprint
|
|
.getDisplayText(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 30),
|
|
child: Text(
|
|
context.lang.contactVerifyNumberLongDesc(
|
|
getContactDisplayName(_contact),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
await launchUrl(
|
|
Uri.parse(
|
|
'https://twonly.eu/en/faq/security/verify-security-number.html',
|
|
),
|
|
);
|
|
},
|
|
child: Text(
|
|
'Read more.',
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
bottomNavigationBar: SafeArea(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(bottom: 60),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
if (_contact.verified)
|
|
OutlinedButton.icon(
|
|
onPressed: () => updateUserVerifyState(false),
|
|
label:
|
|
Text(context.lang.contactVerifyNumberClearVerification),
|
|
)
|
|
else
|
|
FilledButton.icon(
|
|
icon: const FaIcon(FontAwesomeIcons.shieldHeart),
|
|
onPressed: () => updateUserVerifyState(true),
|
|
label: Text(
|
|
context.lang.contactVerifyNumberMarkAsVerified,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|