mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 08:52:11 +00:00
new add contact view for scanned qr via link
This commit is contained in:
parent
3c91f99008
commit
646b9c22d3
11 changed files with 174 additions and 11 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
@ -115,7 +116,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
bool _isUserCreated = false;
|
bool _isUserCreated = false;
|
||||||
bool _showOnboarding = true;
|
bool _showOnboarding = true;
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
bool _skipBackup = false;
|
bool _skipBackup = kDebugMode;
|
||||||
bool _isTwonlyLocked = true;
|
bool _isTwonlyLocked = true;
|
||||||
|
|
||||||
(Future<int>?, bool) _proofOfWork = (null, false);
|
(Future<int>?, bool) _proofOfWork = (null, false);
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_qr_scanner.view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_qr_scanner.view.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_send_to.view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_send_to.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user.view.dart';
|
|
||||||
import 'package:twonly/src/visual/views/chats/archived_chats.view.dart';
|
import 'package:twonly/src/visual/views/chats/archived_chats.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/media_viewer.view.dart';
|
import 'package:twonly/src/visual/views/chats/media_viewer.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/start_new_chat.view.dart';
|
import 'package:twonly/src/visual/views/chats/start_new_chat.view.dart';
|
||||||
|
import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact.view.dart';
|
import 'package:twonly/src/visual/views/contact/contact.view.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group_create_select_members.view.dart';
|
import 'package:twonly/src/visual/views/groups/group_create_select_members.view.dart';
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.utils.dart';
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user.view.dart';
|
import 'package:twonly/src/visual/views/contact/add_contact_via_qr_link.view.dart';
|
||||||
|
import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart';
|
||||||
|
|
||||||
Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
if (!uri.scheme.startsWith('http')) return false;
|
if (!uri.scheme.startsWith('http')) return false;
|
||||||
|
|
@ -50,9 +51,9 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await context.navPush(
|
await context.navPush(
|
||||||
AddNewUserView(
|
AddContactViaQrLinkView(
|
||||||
username: profile.username,
|
profile: profile,
|
||||||
publicKey: Uint8List.fromList(profile.publicIdentityKey),
|
qrCodeLink: uri.toString(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class QrCodeUtils {
|
||||||
profile.userId.toInt(),
|
profile.userId.toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (contact == null || !contact.accepted) {
|
if (contact == null) {
|
||||||
if (profile.username == userService.currentUser.username) {
|
if (profile.username == userService.currentUser.username) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
154
lib/src/visual/views/contact/add_contact_via_qr_link.view.dart
Normal file
154
lib/src/visual/views/contact/add_contact_via_qr_link.view.dart
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||||
|
as server;
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.api.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
|
|
||||||
|
class AddContactViaQrLinkView extends StatefulWidget {
|
||||||
|
const AddContactViaQrLinkView({
|
||||||
|
required this.profile,
|
||||||
|
this.qrCodeLink,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PublicProfile profile;
|
||||||
|
final String? qrCodeLink;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AddContactViaQrLinkView> createState() =>
|
||||||
|
_AddContactViaQrLinkViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddContactViaQrLinkViewState extends State<AddContactViaQrLinkView> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
Future<void> _sendFollowRequest() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final userData = server.Response_UserData(
|
||||||
|
userId: widget.profile.userId,
|
||||||
|
publicIdentityKey: widget.profile.publicIdentityKey,
|
||||||
|
signedPrekey: widget.profile.signedPrekey,
|
||||||
|
signedPrekeySignature: widget.profile.signedPrekeySignature,
|
||||||
|
signedPrekeyId: widget.profile.signedPrekeyId,
|
||||||
|
username: utf8.encode(widget.profile.username),
|
||||||
|
registrationId: widget.profile.registrationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
|
ContactsCompanion(
|
||||||
|
username: Value(widget.profile.username),
|
||||||
|
userId: Value(widget.profile.userId.toInt()),
|
||||||
|
requested: const Value(false),
|
||||||
|
blocked: const Value(false),
|
||||||
|
deletedByUser: const Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (added > 0) {
|
||||||
|
await importSignalContactAndCreateRequest(userData);
|
||||||
|
if (widget.qrCodeLink != null) {
|
||||||
|
// As the user does now exist he can now be marked as verified
|
||||||
|
await QrCodeUtils.handleQrCodeLink(widget.qrCodeLink!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(context.lang.addFriendTitle),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 50,
|
||||||
|
backgroundColor: context.color.primaryContainer,
|
||||||
|
child: FaIcon(
|
||||||
|
FontAwesomeIcons.user,
|
||||||
|
size: 40,
|
||||||
|
color: context.color.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
widget.profile.username,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: context.color.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
context.lang.userFoundBody,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Center(
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: _isLoading ? null : _sendFollowRequest,
|
||||||
|
child: _isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(context.lang.createContactRequest),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Center(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: _isLoading ? null : () => context.pop(),
|
||||||
|
child: Text(context.lang.cancel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,8 +14,8 @@ import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/utils.api.dart';
|
import 'package:twonly/src/services/api/utils.api.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user_components/friend_suggestions.comp.dart';
|
import 'package:twonly/src/visual/views/contact/add_new_contact_components/friend_suggestions.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user_components/open_requests_list.comp.dart';
|
import 'package:twonly/src/visual/views/contact/add_new_contact_components/open_requests_list.comp.dart';
|
||||||
|
|
||||||
class AddNewUserView extends StatefulWidget {
|
class AddNewUserView extends StatefulWidget {
|
||||||
const AddNewUserView({
|
const AddNewUserView({
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/headline.element.dart';
|
import 'package:twonly/src/visual/elements/headline.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user_components/friend_suggestions.comp.dart';
|
import 'package:twonly/src/visual/views/contact/add_new_contact_components/friend_suggestions.comp.dart';
|
||||||
|
|
||||||
class OpenRequestsListComp extends StatelessWidget {
|
class OpenRequestsListComp extends StatelessWidget {
|
||||||
const OpenRequestsListComp({
|
const OpenRequestsListComp({
|
||||||
|
|
@ -18,7 +18,7 @@ import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/components/restore_flame.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||||
|
|
||||||
class ContactView extends StatefulWidget {
|
class ContactView extends StatefulWidget {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,13 @@ class HomeViewState extends State<HomeView> {
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
unawaited(_initAsync());
|
unawaited(_initAsync());
|
||||||
|
|
||||||
|
handleIntentUrl(
|
||||||
|
context,
|
||||||
|
Uri.parse(
|
||||||
|
'https://me.twonly.eu/qr/#EAAauAEIgLDN0Nm7oKh0EghoYWhoaGhoaBohBRZQ8w_zpm1v7SRTdc8GEOMAxuf1caGDlBa-v0ZiTw9qIiEF05juEs1c3yw0STiSwQR7lowDX5hBaxN4YFR0HhkopGIoudTO5wIyQFQRtU1aO7P7O5s2ekB1ppAost3iQQizwhFObjOLgHQnpwcnwEONXZzSADYqCeEoNcvyE45w0v21z1Imhozk3Q44oI0GQhA9U_chIJwwZ7J9fpeXODZF',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Subscribe to all events (initial link and further)
|
// Subscribe to all events (initial link and further)
|
||||||
_deepLinkSub = AppLinks().uriLinkStream.listen((uri) async {
|
_deepLinkSub = AppLinks().uriLinkStream.listen((uri) async {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue