This commit is contained in:
otsmr 2025-12-15 20:20:30 +01:00
parent 25114daee2
commit 1a2aa5edb9
8 changed files with 177 additions and 15 deletions

View file

@ -4,6 +4,10 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:me.twonly.eu</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)eu.twonly.shared</string>

View file

@ -53,7 +53,7 @@
"searchUserNameBlockUserTooltip": "Benutzer ohne Benachrichtigung blockieren.",
"searchUserNameRejectUserTooltip": "Die Anfrage ablehnen und den Anfragenden informieren.",
"searchUserNameArchiveUserTooltip": "Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.",
"userFound": "Benutzer gefunden",
"userFound": "{username} gefunden",
"userFoundBody": "Möchtest du eine Folgeanfrage stellen?",
"chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!",
"chatListViewSendFirstTwonly": "Sende dein erstes twonly!",
@ -446,5 +446,10 @@
"voiceMessageCancel": "Abbrechen",
"shareYourProfile": "Teile dein Profil",
"scanOtherProfile": "Scanne ein anderes Profil",
"skipForNow": "Vorerst überspringen"
"skipForNow": "Vorerst überspringen",
"linkFromUsername": "Ist der Link von {username}?",
"linkFromUsernameLong": "Wenn du den Link von der Person direkt erhalten hast, kannst du den Kontakt als verifiziert markieren, da der öffentliche Schlüssel im Link mit dem bereits für diesen Benutzer gespeicherten öffentlichen Schlüssel übereinstimmt.",
"gotLinkFromFriend": "Ja, der Link kommt direkt von der Person.",
"couldNotVerifyUsername": "{username} konnte nicht verifiziert werden",
"linkPubkeyDoesNotMatch": "Der öffentliche Schlüssel im Link stimmt nicht mit dem für diesen Kontakt gespeicherten öffentlichen Schlüssel überein. Triff die Person persönlich und scanne den QR-Code direkt!"
}

View file

@ -205,7 +205,7 @@
"addEmoji": "Emoji",
"toggleFlashLight": "Toggle the flash light",
"toggleHighQuality": "Toggle better resolution",
"userFound": "User found",
"userFound": "{username} found",
"userFoundBody": "Do you want to create a follow request?",
"searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.",
"@searchUsernameNotFoundLong": {
@ -476,5 +476,10 @@
"voiceMessageCancel": "Cancel",
"shareYourProfile": "Share your profile",
"scanOtherProfile": "Scan other profile",
"skipForNow": "Skip for now"
"skipForNow": "Skip for now",
"linkFromUsername": "Is the link from {username}?",
"linkFromUsernameLong": "If you received the link from your friend, you can mark the user as verified, as the public key in the link matches the public key already stored for that user?",
"gotLinkFromFriend": "Yes, I got the link from my friend!",
"couldNotVerifyUsername": "Could not verify {username}",
"linkPubkeyDoesNotMatch": "The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!"
}

View file

@ -1181,8 +1181,8 @@ abstract class AppLocalizations {
/// No description provided for @userFound.
///
/// In en, this message translates to:
/// **'User found'**
String get userFound;
/// **'{username} found'**
String userFound(Object username);
/// No description provided for @userFoundBody.
///
@ -2779,6 +2779,36 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Skip for now'**
String get skipForNow;
/// No description provided for @linkFromUsername.
///
/// In en, this message translates to:
/// **'Is the link from {username}?'**
String linkFromUsername(Object username);
/// No description provided for @linkFromUsernameLong.
///
/// In en, this message translates to:
/// **'If you received the link from your friend, you can mark the user as verified, as the public key in the link matches the public key already stored for that user?'**
String get linkFromUsernameLong;
/// No description provided for @gotLinkFromFriend.
///
/// In en, this message translates to:
/// **'Yes, I got the link from my friend!'**
String get gotLinkFromFriend;
/// No description provided for @couldNotVerifyUsername.
///
/// In en, this message translates to:
/// **'Could not verify {username}'**
String couldNotVerifyUsername(Object username);
/// No description provided for @linkPubkeyDoesNotMatch.
///
/// In en, this message translates to:
/// **'The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!'**
String get linkPubkeyDoesNotMatch;
}
class _AppLocalizationsDelegate

View file

@ -598,7 +598,9 @@ class AppLocalizationsDe extends AppLocalizations {
String get toggleHighQuality => 'Bessere Auflösung umschalten';
@override
String get userFound => 'Benutzer gefunden';
String userFound(Object username) {
return '$username gefunden';
}
@override
String get userFoundBody => 'Möchtest du eine Folgeanfrage stellen?';
@ -1531,4 +1533,25 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get skipForNow => 'Vorerst überspringen';
@override
String linkFromUsername(Object username) {
return 'Ist der Link von $username?';
}
@override
String get linkFromUsernameLong =>
'Wenn du den Link von der Person direkt erhalten hast, kannst du den Kontakt als verifiziert markieren, da der öffentliche Schlüssel im Link mit dem bereits für diesen Benutzer gespeicherten öffentlichen Schlüssel übereinstimmt.';
@override
String get gotLinkFromFriend => 'Ja, der Link kommt direkt von der Person.';
@override
String couldNotVerifyUsername(Object username) {
return '$username konnte nicht verifiziert werden';
}
@override
String get linkPubkeyDoesNotMatch =>
'Der öffentliche Schlüssel im Link stimmt nicht mit dem für diesen Kontakt gespeicherten öffentlichen Schlüssel überein. Triff die Person persönlich und scanne den QR-Code direkt!';
}

View file

@ -593,7 +593,9 @@ class AppLocalizationsEn extends AppLocalizations {
String get toggleHighQuality => 'Toggle better resolution';
@override
String get userFound => 'User found';
String userFound(Object username) {
return '$username found';
}
@override
String get userFoundBody => 'Do you want to create a follow request?';
@ -1521,4 +1523,25 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get skipForNow => 'Skip for now';
@override
String linkFromUsername(Object username) {
return 'Is the link from $username?';
}
@override
String get linkFromUsernameLong =>
'If you received the link from your friend, you can mark the user as verified, as the public key in the link matches the public key already stored for that user?';
@override
String get gotLinkFromFriend => 'Yes, I got the link from my friend!';
@override
String couldNotVerifyUsername(Object username) {
return 'Could not verify $username';
}
@override
String get linkPubkeyDoesNotMatch =>
'The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!';
}

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -16,7 +17,14 @@ import 'package:twonly/src/views/components/avatar_icon.component.dart';
import 'package:twonly/src/views/components/headline.dart';
class AddNewUserView extends StatefulWidget {
const AddNewUserView({super.key});
const AddNewUserView({
this.username,
this.publicKey,
super.key,
});
final String? username;
final Uint8List? publicKey;
@override
State<AddNewUserView> createState() => _SearchUsernameView();
@ -38,6 +46,13 @@ class _SearchUsernameView extends State<AddNewUserView> {
contacts = update;
}),
);
if (widget.username != null) {
searchUserName.text = widget.username!;
WidgetsBinding.instance.addPostFrameCallback((_) {
_addNewUser(context);
});
}
}
@override
@ -73,7 +88,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
final addUser = await showAlertDialog(
context,
context.lang.userFound,
context.lang.userFound(searchUserName.text),
context.lang.userFoundBody,
);
@ -88,6 +103,10 @@ class _SearchUsernameView extends State<AddNewUserView> {
requested: const Value(false),
blocked: const Value(false),
deletedByUser: const Value(false),
verified: Value(
!(widget.publicKey == null) &&
userdata.publicIdentityKey.equals(widget.publicKey!),
),
),
);

View file

@ -1,11 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:app_links/app_links.dart';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
@ -15,7 +18,10 @@ import 'package:twonly/src/views/camera/camera_preview_components/camera_preview
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
import 'package:twonly/src/views/chats/add_new_user.view.dart';
import 'package:twonly/src/views/chats/chat_list.view.dart';
import 'package:twonly/src/views/components/alert_dialog.dart';
import 'package:twonly/src/views/contact/contact.view.dart';
import 'package:twonly/src/views/memories/memories.view.dart';
import 'package:twonly/src/views/public_profile.view.dart';
@ -144,18 +150,65 @@ class HomeViewState extends State<HomeView> {
final contacts =
await twonlyDB.contactsDao.getContactsByUsername(username);
if (contacts.isEmpty) {
// load user from server...
if (!mounted) return;
Uint8List? publicKeyBytes;
if (publicKey != null) {
publicKeyBytes = base64Url.decode(publicKey);
}
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return AddNewUserView(
username: username,
publicKey: publicKeyBytes,
);
},
),
);
} else if (publicKey != null) {
try {
final contact = contacts.first;
final storedPublicKey = await getPublicKeyFromContact(contact.userId);
final receivedPublicKey = base64Url.decode(publicKey);
if (storedPublicKey == null || receivedPublicKey.isEmpty) return;
if (storedPublicKey == null ||
receivedPublicKey.isEmpty ||
!mounted) {
return;
}
if (storedPublicKey.equals(receivedPublicKey)) {
Log.info('Could verify the user');
if (!contact.verified) {
final markAsVerified = await showAlertDialog(
context,
context.lang.linkFromUsername(contact.username),
context.lang.linkFromUsernameLong,
customOk: context.lang.gotLinkFromFriend,
);
if (markAsVerified) {
await twonlyDB.contactsDao.updateContact(
contact.userId,
const ContactsCompanion(
verified: Value(true),
),
);
}
} else {
Log.error('Show error message');
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ContactView(contact.userId);
},
),
);
}
} else {
await showAlertDialog(
context,
context.lang.couldNotVerifyUsername(contact.username),
context.lang.linkPubkeyDoesNotMatch,
customCancel: '',
);
}
} catch (e) {
Log.warn(e);