mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 16:28:40 +00:00
fix #79
This commit is contained in:
parent
ebc4570b9b
commit
27fa177a2e
18 changed files with 266 additions and 106 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +1,6 @@
|
||||||
[submodule "dependencies/flutter_secure_storage"]
|
[submodule "dependencies/flutter_secure_storage"]
|
||||||
path = dependencies/flutter_secure_storage
|
path = dependencies/flutter_secure_storage
|
||||||
url = https://github.com/juliansteenbakker/flutter_secure_storage
|
url = https://github.com/juliansteenbakker/flutter_secure_storage
|
||||||
|
[submodule "dependencies/flutter_zxing"]
|
||||||
|
path = dependencies/flutter_zxing
|
||||||
|
url = https://github.com/khoren93/flutter_zxing.git
|
||||||
|
|
|
||||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"files.exclude": {
|
"files.watcherExclude": {
|
||||||
"dependencies": false
|
"dependencies": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,5 +13,9 @@ pub.dev or because they require some special installation.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
|
||||||
|
cd dependencies/flutter_zxing
|
||||||
|
git submodule update --init --recursive
|
||||||
|
./scripts/update_ios_macos_src.s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
1
assets/animations/failed.json
Normal file
1
assets/animations/failed.json
Normal file
File diff suppressed because one or more lines are too long
1
assets/animations/success.json
Normal file
1
assets/animations/success.json
Normal file
File diff suppressed because one or more lines are too long
1
dependencies/flutter_zxing
vendored
Submodule
1
dependencies/flutter_zxing
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit ba65f2fb4a09f4e68f6de64aa1de41ba3dc4977e
|
||||||
|
|
@ -71,6 +71,8 @@ PODS:
|
||||||
- flutter_secure_storage_darwin (10.0.0):
|
- flutter_secure_storage_darwin (10.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- flutter_zxing (0.0.1):
|
||||||
|
- Flutter
|
||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
@ -234,6 +236,7 @@ DEPENDENCIES:
|
||||||
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
||||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||||
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
|
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
|
||||||
|
- flutter_zxing (from `.symlinks/plugins/flutter_zxing/ios`)
|
||||||
- gal (from `.symlinks/plugins/gal/darwin`)
|
- gal (from `.symlinks/plugins/gal/darwin`)
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
|
|
@ -290,6 +293,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||||
flutter_secure_storage_darwin:
|
flutter_secure_storage_darwin:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
|
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
|
||||||
|
flutter_zxing:
|
||||||
|
:path: ".symlinks/plugins/flutter_zxing/ios"
|
||||||
gal:
|
gal:
|
||||||
:path: ".symlinks/plugins/gal/darwin"
|
:path: ".symlinks/plugins/gal/darwin"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
|
|
@ -337,6 +342,7 @@ SPEC CHECKSUMS:
|
||||||
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
|
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
|
||||||
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
||||||
flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468
|
flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468
|
||||||
|
flutter_zxing: e8bcc43bd3056c70c271b732ed94e7a16fd62f93
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
GoogleAppMeasurement: 36684bfb3ee034e2b42b4321eb19da3a1b81e65d
|
GoogleAppMeasurement: 36684bfb3ee034e2b42b4321eb19da3a1b81e65d
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@
|
||||||
"settingsAccountDeleteModalTitle": "Bist du sicher?",
|
"settingsAccountDeleteModalTitle": "Bist du sicher?",
|
||||||
"settingsAccountDeleteModalBody": "Dein Konto wird gelöscht. Es gibt keine Möglichkeit, es wiederherzustellen.",
|
"settingsAccountDeleteModalBody": "Dein Konto wird gelöscht. Es gibt keine Möglichkeit, es wiederherzustellen.",
|
||||||
"contactVerifyNumberTitle": "Sicherheitsnummer verifizieren",
|
"contactVerifyNumberTitle": "Sicherheitsnummer verifizieren",
|
||||||
|
"contactVerifyNumberTapToScan": "Zum Scannen tippen",
|
||||||
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
|
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
|
||||||
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
|
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
|
||||||
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
|
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,8 @@
|
||||||
"@settingsAccountDeleteModalBody": {},
|
"@settingsAccountDeleteModalBody": {},
|
||||||
"contactVerifyNumberTitle": "Verify safety number",
|
"contactVerifyNumberTitle": "Verify safety number",
|
||||||
"@contactVerifyNumberTitle": {},
|
"@contactVerifyNumberTitle": {},
|
||||||
|
"contactVerifyNumberTapToScan": "Tap to scan",
|
||||||
|
"@contactVerifyNumberTapToScan": {},
|
||||||
"contactVerifyNumberMarkAsVerified": "Mark as verified",
|
"contactVerifyNumberMarkAsVerified": "Mark as verified",
|
||||||
"@contactVerifyNumberMarkAsVerified": {},
|
"@contactVerifyNumberMarkAsVerified": {},
|
||||||
"contactVerifyNumberClearVerification": "Clear verification",
|
"contactVerifyNumberClearVerification": "Clear verification",
|
||||||
|
|
|
||||||
|
|
@ -788,6 +788,12 @@ abstract class AppLocalizations {
|
||||||
/// **'Verify safety number'**
|
/// **'Verify safety number'**
|
||||||
String get contactVerifyNumberTitle;
|
String get contactVerifyNumberTitle;
|
||||||
|
|
||||||
|
/// No description provided for @contactVerifyNumberTapToScan.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Tap to scan'**
|
||||||
|
String get contactVerifyNumberTapToScan;
|
||||||
|
|
||||||
/// No description provided for @contactVerifyNumberMarkAsVerified.
|
/// No description provided for @contactVerifyNumberMarkAsVerified.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get contactVerifyNumberTitle => 'Sicherheitsnummer verifizieren';
|
String get contactVerifyNumberTitle => 'Sicherheitsnummer verifizieren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactVerifyNumberTapToScan => 'Zum Scannen tippen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contactVerifyNumberMarkAsVerified => 'Als verifiziert markieren';
|
String get contactVerifyNumberMarkAsVerified => 'Als verifiziert markieren';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -380,6 +380,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get contactVerifyNumberTitle => 'Verify safety number';
|
String get contactVerifyNumberTitle => 'Verify safety number';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactVerifyNumberTapToScan => 'Tap to scan';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contactVerifyNumberMarkAsVerified => 'Mark as verified';
|
String get contactVerifyNumberMarkAsVerified => 'Mark as verified';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class FormattedStringWidget extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SelectableText(
|
return SelectableText(
|
||||||
formatString(longString),
|
formatString(longString),
|
||||||
style: TextStyle(fontSize: 18, color: Colors.black),
|
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:drift/drift.dart' hide Column;
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:flutter_zxing/flutter_zxing.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/views/components/format_long_string.dart';
|
import 'package:twonly/src/views/components/format_long_string.dart';
|
||||||
|
|
@ -10,7 +12,9 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/contact/contact_verify_qr_scan.view.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:image/image.dart' as imglib;
|
||||||
|
|
||||||
class ContactVerifyView extends StatefulWidget {
|
class ContactVerifyView extends StatefulWidget {
|
||||||
const ContactVerifyView(this.contact, {super.key});
|
const ContactVerifyView(this.contact, {super.key});
|
||||||
|
|
@ -20,31 +24,127 @@ class ContactVerifyView extends StatefulWidget {
|
||||||
State<ContactVerifyView> createState() => _ContactVerifyViewState();
|
State<ContactVerifyView> createState() => _ContactVerifyViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScanResult { None, Success, Failed }
|
||||||
|
|
||||||
class _ContactVerifyViewState extends State<ContactVerifyView> {
|
class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
Fingerprint? fingerprint;
|
Fingerprint? _fingerprint;
|
||||||
|
late Contact _contact;
|
||||||
|
late StreamSubscription<Contact?> _contactSub;
|
||||||
|
ScanResult _scanResult = ScanResult.None;
|
||||||
|
Uint8List? _qrCodeImageBytes;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_contact = widget.contact;
|
||||||
loadAsync();
|
loadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future loadAsync() async {
|
@override
|
||||||
fingerprint = await generateSessionFingerPrint(widget.contact.userId);
|
void dispose() {
|
||||||
setState(() {});
|
_contactSub.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future loadAsync() async {
|
||||||
|
_fingerprint = await generateSessionFingerPrint(widget.contact.userId);
|
||||||
|
|
||||||
|
if (_fingerprint != null) {
|
||||||
|
final Encode result = zx.encodeBarcode(
|
||||||
|
contents: base64Encode(
|
||||||
|
_fingerprint!.scannableFingerprint.fingerprints,
|
||||||
|
),
|
||||||
|
params: EncodeParams(
|
||||||
|
format: Format.qrCode,
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
margin: 0,
|
||||||
|
eccLevel: EccLevel.low,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Stream<Contact?> contact = twonlyDB.contactsDao
|
Stream<Contact?> contact = twonlyDB.contactsDao
|
||||||
.getContactByUserId(widget.contact.userId)
|
.getContactByUserId(widget.contact.userId)
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
|
_contactSub = contact.listen((contact) {
|
||||||
|
if (contact == null) return;
|
||||||
|
setState(() {
|
||||||
|
_contact = contact;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future openQrScanner() async {
|
||||||
|
if (_fingerprint == null) return;
|
||||||
|
bool? isValid = await Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return ContactVerifyQrScanView(
|
||||||
|
widget.contact,
|
||||||
|
fingerprint: _fingerprint!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
if (isValid == null) {
|
||||||
|
return; // user just returned...
|
||||||
|
}
|
||||||
|
if (isValid) {
|
||||||
|
_scanResult = ScanResult.Success;
|
||||||
|
updateUserVerifyState(true);
|
||||||
|
} else {
|
||||||
|
_scanResult = ScanResult.Failed;
|
||||||
|
updateUserVerifyState(false);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future updateUserVerifyState(bool verified) async {
|
||||||
|
final update = ContactsCompanion(verified: Value(verified));
|
||||||
|
await twonlyDB.contactsDao.updateContact(_contact.userId, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget get qrWidget => (_qrCodeImageBytes == null)
|
||||||
|
? 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(Duration(seconds: 3), () {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_scanResult = ScanResult.None;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.lang.contactVerifyNumberTitle),
|
title: Text(context.lang.contactVerifyNumberTitle),
|
||||||
),
|
),
|
||||||
body: (fingerprint == null)
|
body: (_fingerprint == null)
|
||||||
? Center(child: CircularProgressIndicator())
|
? Center(child: CircularProgressIndicator())
|
||||||
: ListView(
|
: ListView(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -57,6 +157,8 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: openQrScanner,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -64,26 +166,33 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
child: QrImageView(
|
padding: EdgeInsets.symmetric(vertical: 20),
|
||||||
data: base64Encode(fingerprint!
|
child: Column(
|
||||||
.scannableFingerprint.fingerprints),
|
children: [
|
||||||
version: QrVersions.auto,
|
(_scanResult == ScanResult.None)
|
||||||
size: 150.0,
|
? qrWidget
|
||||||
),
|
: resultAnimation,
|
||||||
),
|
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Text(
|
child: Text(
|
||||||
"QR Code scanning is coming soon. Please compare the numbers manual.",
|
(_scanResult == ScanResult.None)
|
||||||
style:
|
? context
|
||||||
TextStyle(color: Colors.black, fontSize: 10),
|
.lang.contactVerifyNumberTapToScan
|
||||||
|
: "",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
FormattedStringWidget(
|
FormattedStringWidget(
|
||||||
fingerprint!.displayableFingerprint
|
_fingerprint!.displayableFingerprint
|
||||||
.getDisplayText(),
|
.getDisplayText(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -91,21 +200,14 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
StreamBuilder(
|
),
|
||||||
stream: contact,
|
Padding(
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.contactVerifyNumberLongDesc(
|
context.lang.contactVerifyNumberLongDesc(
|
||||||
getContactDisplayName(snapshot.data!)),
|
getContactDisplayName(_contact)),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
|
|
@ -133,36 +235,19 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
StreamBuilder(
|
(_contact.verified)
|
||||||
stream: contact,
|
? OutlinedButton.icon(
|
||||||
builder: (context, snapshot) {
|
onPressed: () => updateUserVerifyState(false),
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
final contact = snapshot.data!;
|
|
||||||
if (contact.verified) {
|
|
||||||
return OutlinedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
final update =
|
|
||||||
ContactsCompanion(verified: Value(false));
|
|
||||||
twonlyDB.contactsDao
|
|
||||||
.updateContact(contact.userId, update);
|
|
||||||
},
|
|
||||||
label: Text(
|
label: Text(
|
||||||
context.lang.contactVerifyNumberClearVerification),
|
context.lang.contactVerifyNumberClearVerification),
|
||||||
);
|
)
|
||||||
}
|
: FilledButton.icon(
|
||||||
return FilledButton.icon(
|
|
||||||
icon: FaIcon(FontAwesomeIcons.shieldHeart),
|
icon: FaIcon(FontAwesomeIcons.shieldHeart),
|
||||||
onPressed: () {
|
onPressed: () => updateUserVerifyState(true),
|
||||||
final update = ContactsCompanion(verified: Value(true));
|
label: Text(
|
||||||
twonlyDB.contactsDao
|
context.lang.contactVerifyNumberMarkAsVerified,
|
||||||
.updateContact(contact.userId, update);
|
|
||||||
},
|
|
||||||
label: Text(context.lang.contactVerifyNumberMarkAsVerified),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
43
lib/src/views/contact/contact_verify_qr_scan.view.dart
Normal file
43
lib/src/views/contact/contact_verify_qr_scan.view.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_zxing/flutter_zxing.dart';
|
||||||
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
|
class ContactVerifyQrScanView extends StatefulWidget {
|
||||||
|
const ContactVerifyQrScanView(this.contact,
|
||||||
|
{super.key, required this.fingerprint});
|
||||||
|
final Fingerprint fingerprint;
|
||||||
|
final Contact contact;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ContactVerifyQrScanView> createState() =>
|
||||||
|
_ContactVerifyQrScanViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactVerifyQrScanViewState extends State<ContactVerifyQrScanView> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ReaderWidget(
|
||||||
|
onScan: (result) async {
|
||||||
|
bool isValid = false;
|
||||||
|
try {
|
||||||
|
if (result.text != null) {
|
||||||
|
Uint8List otherFingerPrint = base64Decode(result.text!);
|
||||||
|
isValid = widget.fingerprint.scannableFingerprint.compareTo(
|
||||||
|
otherFingerPrint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Log.error("$e");
|
||||||
|
}
|
||||||
|
return Navigator.pop(context, isValid);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,40 +63,51 @@ class CreditsView extends StatelessWidget {
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Free selfie fast Animation",
|
title: "selfie fast Animation",
|
||||||
subtitle: "Brandon Ambuila",
|
subtitle: "Brandon Ambuila",
|
||||||
url:
|
url:
|
||||||
"https://lottiefiles.com/free-animation/selfie-fast-JZx4Ftrg1E",
|
"https://lottiefiles.com/free-animation/selfie-fast-JZx4Ftrg1E",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Free Security status - Safe Animation",
|
title: "Security status - Safe Animation",
|
||||||
subtitle: "Yogesh Pal",
|
subtitle: "Yogesh Pal",
|
||||||
url:
|
url:
|
||||||
"https://lottiefiles.com/free-animation/security-status-safe-CePJPAwLVx",
|
"https://lottiefiles.com/free-animation/security-status-safe-CePJPAwLVx",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Free send mail Animation",
|
title: "send mail Animation",
|
||||||
subtitle: "jignesh gajjar",
|
subtitle: "jignesh gajjar",
|
||||||
url: "https://lottiefiles.com/free-animation/send-mail-3pvzm2kmNq",
|
url: "https://lottiefiles.com/free-animation/send-mail-3pvzm2kmNq",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Free Present for you Animation",
|
title: "Present for you Animation",
|
||||||
subtitle: "Tatsiana Melnikova",
|
subtitle: "Tatsiana Melnikova",
|
||||||
url:
|
url:
|
||||||
"https://lottiefiles.com/free-animation/present-for-you-QalWyuNptY",
|
"https://lottiefiles.com/free-animation/present-for-you-QalWyuNptY",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Free Take a photo Animation",
|
title: "Take a photo Animation",
|
||||||
subtitle: "Nguyễn Như Lân",
|
subtitle: "Nguyễn Như Lân",
|
||||||
url:
|
url:
|
||||||
"https://lottiefiles.com/free-animation/take-a-photo-CzOUerxwPP?color-palette=true",
|
"https://lottiefiles.com/free-animation/take-a-photo-CzOUerxwPP?color-palette=true",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Kostenlose Valentine's Day-Animation",
|
title: "Valentine's Day-Animation",
|
||||||
subtitle: "Strezha",
|
subtitle: "Strezha",
|
||||||
url:
|
url:
|
||||||
"https://lottiefiles.com/de/free-animation/valentines-day-1UiMkPHnPK?color-palette=true",
|
"https://lottiefiles.com/de/free-animation/valentines-day-1UiMkPHnPK?color-palette=true",
|
||||||
),
|
),
|
||||||
|
UrlListTitle(
|
||||||
|
title: "success-Animation",
|
||||||
|
subtitle: "Aman Awasthy",
|
||||||
|
url:
|
||||||
|
"https://lottiefiles.com/de/free-animation/success-tick-cuwjLHAR7g",
|
||||||
|
),
|
||||||
|
UrlListTitle(
|
||||||
|
title: "Failed-Animation",
|
||||||
|
subtitle: "Ahmed Shami أحمد شامي",
|
||||||
|
url: "https://lottiefiles.com/de/free-animation/failed-e5cQFDEtLv",
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Center(
|
title: Center(
|
||||||
|
|
@ -105,18 +116,6 @@ class CreditsView extends StatelessWidget {
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
|
||||||
title: "Germany",
|
|
||||||
subtitle: "by GDJ",
|
|
||||||
url:
|
|
||||||
"https://pixabay.com/vectors/republic-germany-deutschland-map-1220652/",
|
|
||||||
),
|
|
||||||
UrlListTitle(
|
|
||||||
title: "Frankfurt am Main",
|
|
||||||
subtitle: "by GDJ",
|
|
||||||
url:
|
|
||||||
"https://pixabay.com/vectors/frankfurt-germany-skyline-cityscape-3166262/",
|
|
||||||
),
|
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Avo Cardio",
|
title: "Avo Cardio",
|
||||||
subtitle: "by RalfDesign",
|
subtitle: "by RalfDesign",
|
||||||
|
|
@ -129,12 +128,6 @@ class CreditsView extends StatelessWidget {
|
||||||
url:
|
url:
|
||||||
"https://pixabay.com/illustrations/sloth-swimming-summer-pool-cartoon-4575121/",
|
"https://pixabay.com/illustrations/sloth-swimming-summer-pool-cartoon-4575121/",
|
||||||
),
|
),
|
||||||
UrlListTitle(
|
|
||||||
title: "Sloth",
|
|
||||||
subtitle: "by RalfDesign",
|
|
||||||
url:
|
|
||||||
"https://pixabay.com/illustrations/sloth-swimming-summer-pool-cartoon-4575121/",
|
|
||||||
),
|
|
||||||
UrlListTitle(
|
UrlListTitle(
|
||||||
title: "Duck",
|
title: "Duck",
|
||||||
subtitle: "by lachkegeetanjali",
|
subtitle: "by lachkegeetanjali",
|
||||||
|
|
|
||||||
|
|
@ -716,6 +716,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_zxing:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "dependencies/flutter_zxing"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "2.1.0"
|
||||||
font_awesome_flutter:
|
font_awesome_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ dependencies:
|
||||||
# flutter_secure_storage: ^10.0.0-beta.4
|
# flutter_secure_storage: ^10.0.0-beta.4
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
path: ./dependencies/flutter_secure_storage/flutter_secure_storage
|
path: ./dependencies/flutter_secure_storage/flutter_secure_storage
|
||||||
|
flutter_zxing:
|
||||||
|
path: ./dependencies/flutter_zxing
|
||||||
font_awesome_flutter: ^10.8.0
|
font_awesome_flutter: ^10.8.0
|
||||||
gal: ^2.3.1
|
gal: ^2.3.1
|
||||||
hand_signature: ^3.0.3
|
hand_signature: ^3.0.3
|
||||||
|
|
@ -46,7 +48,6 @@ dependencies:
|
||||||
protobuf: ^4.0.0
|
protobuf: ^4.0.0
|
||||||
cryptography_plus: ^2.7.0
|
cryptography_plus: ^2.7.0
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
qr_flutter: ^4.1.0
|
|
||||||
restart_app: ^1.3.2
|
restart_app: ^1.3.2
|
||||||
screenshot: ^3.0.0
|
screenshot: ^3.0.0
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue