show toast and fix user deletion
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run

This commit is contained in:
otsmr 2026-03-13 23:56:42 +01:00
parent c1065772f8
commit 5383496bb6
14 changed files with 123 additions and 51 deletions

View file

@ -2,9 +2,11 @@
## 0.0.98
- Fix: Issue with contact requests
- Fix: Problem during contact requests
- Fix: Problem with deleting a contact
- Improve: Video compression with progress updates
- Improve: Show message "Flames restored"
- Improve: Show toast message if user was added via QR
## 0.0.96

View file

@ -685,7 +685,7 @@ abstract class AppLocalizations {
/// No description provided for @settingsPrivacy.
///
/// In en, this message translates to:
/// **'Privacy'**
/// **'Privacy & Security'**
String get settingsPrivacy;
/// No description provided for @settingsPrivacyBlockUsers.
@ -3039,6 +3039,18 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'{count} flames restored'**
String chatEntryFlameRestored(Object count);
/// No description provided for @requestedUserToastText.
///
/// In en, this message translates to:
/// **'{username} was successfully requested.'**
String requestedUserToastText(Object username);
/// No description provided for @profileYourQrCode.
///
/// In en, this message translates to:
/// **'Your QR code'**
String get profileYourQrCode;
}
class _AppLocalizationsDelegate

View file

@ -326,7 +326,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get settingsAppearance => 'Erscheinungsbild';
@override
String get settingsPrivacy => 'Datenschutz';
String get settingsPrivacy => 'Datenschutz & Sicherheit';
@override
String get settingsPrivacyBlockUsers => 'Benutzer blockieren';
@ -1700,4 +1700,12 @@ class AppLocalizationsDe extends AppLocalizations {
String chatEntryFlameRestored(Object count) {
return '$count Flammen wiederhergestellt';
}
@override
String requestedUserToastText(Object username) {
return '$username wurde erfolgreich angefragt.';
}
@override
String get profileYourQrCode => 'Dein QR-Code';
}

View file

@ -322,7 +322,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get settingsAppearance => 'Appearance';
@override
String get settingsPrivacy => 'Privacy';
String get settingsPrivacy => 'Privacy & Security';
@override
String get settingsPrivacyBlockUsers => 'Block users';
@ -1688,4 +1688,12 @@ class AppLocalizationsEn extends AppLocalizations {
String chatEntryFlameRestored(Object count) {
return '$count flames restored';
}
@override
String requestedUserToastText(Object username) {
return '$username was successfully requested.';
}
@override
String get profileYourQrCode => 'Your QR code';
}

View file

@ -322,7 +322,7 @@ class AppLocalizationsSv extends AppLocalizations {
String get settingsAppearance => 'Appearance';
@override
String get settingsPrivacy => 'Privacy';
String get settingsPrivacy => 'Privacy & Security';
@override
String get settingsPrivacyBlockUsers => 'Block users';
@ -1688,4 +1688,12 @@ class AppLocalizationsSv extends AppLocalizations {
String chatEntryFlameRestored(Object count) {
return '$count flames restored';
}
@override
String requestedUserToastText(Object username) {
return '$username was successfully requested.';
}
@override
String get profileYourQrCode => 'Your QR code';
}

@ -1 +1 @@
Subproject commit 6147155ce50caa97864d56e42e49a6f54702785d
Subproject commit 4c566ea0b455d7f5ee81dd93ee4f785dc634befa

View file

@ -81,22 +81,27 @@ Future<void> handleMediaError(MediaFile media) async {
);
}
Future<void> importSignalContactAndCreateRequest(
Future<bool> importSignalContactAndCreateRequest(
server.Response_UserData userdata,
) async {
if (await processSignalUserData(userdata)) {
// 1. Setup notifications keys with the other user
await setupNotificationWithUsers(
forceContact: userdata.userId.toInt(),
);
// 2. Then send user request
await sendCipherText(
userdata.userId.toInt(),
EncryptedContent(
contactRequest: EncryptedContent_ContactRequest(
type: EncryptedContent_ContactRequest_Type.REQUEST,
),
),
);
if (!await processSignalUserData(userdata)) {
return false;
}
// 1. Setup notifications keys with the other user
await setupNotificationWithUsers(
forceContact: userdata.userId.toInt(),
);
// 2. Then send user request
await sendCipherText(
userdata.userId.toInt(),
EncryptedContent(
contactRequest: EncryptedContent_ContactRequest(
type: EncryptedContent_ContactRequest_Type.REQUEST,
),
),
);
return true;
}

View file

@ -302,7 +302,7 @@ Color getMessageColorFromType(
) {
Color color;
if (message.type == MessageType.text.name) {
if (message.type == MessageType.restoreFlameCounter.name) {
color = Colors.orange;
} else if (message.type == MessageType.text.name) {
color = Colors.blueAccent;

View file

@ -55,7 +55,7 @@ PublicProfile? parseQrCodeData(Uint8List rawBytes) {
return null;
}
Future<void> addNewContactFromPublicProfile(PublicProfile profile) async {
Future<bool> addNewContactFromPublicProfile(PublicProfile profile) async {
final userdata = Response_UserData(
userId: profile.userId,
publicIdentityKey: profile.publicIdentityKey,
@ -77,5 +77,8 @@ Future<void> addNewContactFromPublicProfile(PublicProfile profile) async {
),
);
if (added > 0) await importSignalContactAndCreateRequest(userdata);
if (added > 0) {
return importSignalContactAndCreateRequest(userdata);
}
return false;
}

View file

@ -761,7 +761,21 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
onTap: () async {
c.isLoading = true;
widget.mainCameraController.setState();
await addNewContactFromPublicProfile(c.profile);
if (await addNewContactFromPublicProfile(
c.profile,
) &&
context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.lang.requestedUserToastText(
c.profile.username,
),
),
duration: const Duration(seconds: 8),
),
);
}
},
child: Container(
padding: const EdgeInsets.all(12),

View file

@ -10,6 +10,7 @@ import 'package:twonly/src/database/tables/messages.table.dart'
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/views/chats/chat_messages_components/chat_reaction_row.dart';
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_contacts.entry.dart';
@ -79,6 +80,10 @@ class _ChatListEntryState extends State<ChatListEntry> {
if (mediaFiles != null) {
mediaService = MediaFileService(mediaFiles);
if (mounted) setState(() {});
} else {
Log.error(
'Media file not found for ${widget.message.messageId} => ${widget.message.mediaId}',
);
}
});
}

View file

@ -29,7 +29,7 @@ class ContactView extends StatefulWidget {
class _ContactViewState extends State<ContactView> {
Contact? _contact;
bool _contactIsStillAGroupMember = true;
List<GroupMember> _memberOfGroups = [];
late StreamSubscription<Contact?> _contactSub;
late StreamSubscription<List<GroupMember>> _groupMemberSub;
@ -44,10 +44,8 @@ class _ContactViewState extends State<ContactView> {
});
_groupMemberSub = twonlyDB.groupsDao
.watchContactGroupMember(widget.userId)
.listen((update) {
setState(() {
_contactIsStillAGroupMember = update.isNotEmpty;
});
.listen((groups) async {
_memberOfGroups = groups;
});
super.initState();
}
@ -60,7 +58,18 @@ class _ContactViewState extends State<ContactView> {
}
Future<void> handleUserRemoveRequest(Contact contact) async {
if (_contactIsStillAGroupMember) {
var delete = true;
for (final groupM in _memberOfGroups) {
final group = await twonlyDB.groupsDao.getGroup(groupM.groupId);
if (group?.deletedContent ?? false) {
await twonlyDB.groupsDao.deleteGroup(group!.groupId);
} else {
delete = false;
}
}
if (!mounted) return;
if (!delete) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.lang.deleteUserErrorMessage),
@ -211,26 +220,6 @@ class _ContactViewState extends State<ContactView> {
setState(() {});
},
),
// BetterListTile(
// icon: FontAwesomeIcons.eraser,
// iconSize: 16,
// text: context.lang.deleteAllContactMessages,
// onTap: () async {
// final block = await showAlertDialog(
// context,
// context.lang.deleteAllContactMessages,
// context.lang.deleteAllContactMessagesBody(
// getContactDisplayName(contact),
// ),
// );
// if (block) {
// if (context.mounted) {
// await twonlyDB.messagesDao
// .deleteMessagesByContactId(contact.userId);
// }
// }
// },
// ),
BetterListTile(
icon: FontAwesomeIcons.flag,
text: context.lang.reportUser,

View file

@ -43,6 +43,13 @@ class _PrivacyViewState extends State<PrivacyView> {
),
onTap: () => context.push(Routes.settingsPrivacyBlockUsers),
),
ListTile(
title: Text(context.lang.contactVerifyNumberTitle),
onTap: () async {
await context.push(Routes.settingsPublicProfile);
setState(() {});
},
),
],
),
);

View file

@ -120,6 +120,17 @@ class _ProfileViewState extends State<ProfileView> {
),
const SizedBox(height: 20),
const Divider(),
BetterListTile(
leading: const Padding(
padding: EdgeInsets.only(right: 5, left: 1),
child: FaIcon(
FontAwesomeIcons.qrcode,
size: 20,
),
),
onTap: () => context.push(Routes.settingsPublicProfile),
text: context.lang.profileYourQrCode,
),
BetterListTile(
leading: const Padding(
padding: EdgeInsets.only(right: 5, left: 1),