From 52bc6287527635ff06b0098d1b12217d8397edbd Mon Sep 17 00:00:00 2001 From: otsmr Date: Tue, 5 May 2026 00:41:13 +0200 Subject: [PATCH] add message that a user has changed there name --- lib/src/database/daos/groups.dao.dart | 14 +++++++ lib/src/database/tables/groups.table.dart | 2 + .../generated/app_localizations.dart | 12 ++++++ .../generated/app_localizations_de.dart | 10 +++++ .../generated/app_localizations_en.dart | 10 +++++ lib/src/localization/translations | 2 +- .../api/client2client/contact.c2c.dart | 39 +++++++++++++++++++ .../api/client2client/groups.c2c.dart | 2 + .../views/chats/chat_messages.view.dart | 3 +- .../chat_group_action.dart | 18 +++++++++ 10 files changed, 109 insertions(+), 3 deletions(-) diff --git a/lib/src/database/daos/groups.dao.dart b/lib/src/database/daos/groups.dao.dart index 67812253..28fe0883 100644 --- a/lib/src/database/daos/groups.dao.dart +++ b/lib/src/database/daos/groups.dao.dart @@ -332,4 +332,18 @@ class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { return query.map((row) => row.readTable(groups)).watch(); } + + Future> getGroupsForMember(int contactId) { + final query = + select(groups).join([ + innerJoin( + groupMembers, + groupMembers.groupId.equalsExp(groups.groupId), + ), + ])..where( + groupMembers.contactId.equals(contactId), + ); + + return query.map((row) => row.readTable(groups)).get(); + } } diff --git a/lib/src/database/tables/groups.table.dart b/lib/src/database/tables/groups.table.dart index ffe01b40..ab42b249 100644 --- a/lib/src/database/tables/groups.table.dart +++ b/lib/src/database/tables/groups.table.dart @@ -83,6 +83,8 @@ enum GroupActionType { demoteToMember, updatedGroupName, changeDisplayMaxTime, + updatedContactUsername, + updatedContactDisplayName, } @DataClassName('GroupHistory') diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index 0e3312d5..a18d6f96 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -3049,6 +3049,18 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Example'** String get onboardingExampleLabel; + + /// No description provided for @makerChangedUsername. + /// + /// In en, this message translates to: + /// **'{maker} changed their username from {oldName} to {newName}.'** + String makerChangedUsername(Object maker, Object oldName, Object newName); + + /// No description provided for @makerChangedDisplayName. + /// + /// In en, this message translates to: + /// **'{maker} changed their display name from {oldName} to {newName}.'** + String makerChangedDisplayName(Object maker, Object oldName, Object newName); } class _AppLocalizationsDelegate diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index 91796dd8..d1cfd1c8 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -1711,4 +1711,14 @@ class AppLocalizationsDe extends AppLocalizations { @override String get onboardingExampleLabel => 'Beispiel'; + + @override + String makerChangedUsername(Object maker, Object oldName, Object newName) { + return '$maker hat seinen Benutzernamen von $oldName zu $newName geändert.'; + } + + @override + String makerChangedDisplayName(Object maker, Object oldName, Object newName) { + return '$maker hat seinen Anzeigenamen von $oldName zu $newName geändert.'; + } } diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index 6b9adbd7..555d59c5 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -1696,4 +1696,14 @@ class AppLocalizationsEn extends AppLocalizations { @override String get onboardingExampleLabel => 'Example'; + + @override + String makerChangedUsername(Object maker, Object oldName, Object newName) { + return '$maker changed their username from $oldName to $newName.'; + } + + @override + String makerChangedDisplayName(Object maker, Object oldName, Object newName) { + return '$maker changed their display name from $oldName to $newName.'; + } } diff --git a/lib/src/localization/translations b/lib/src/localization/translations index 73d1af8c..781626f6 160000 --- a/lib/src/localization/translations +++ b/lib/src/localization/translations @@ -1 +1 @@ -Subproject commit 73d1af8ca42b10abf6204acf3720d40f9ab6d4a8 +Subproject commit 781626f66c5f992ffad861abb7b4937f82319392 diff --git a/lib/src/services/api/client2client/contact.c2c.dart b/lib/src/services/api/client2client/contact.c2c.dart index d074f2eb..2d300bcd 100644 --- a/lib/src/services/api/client2client/contact.c2c.dart +++ b/lib/src/services/api/client2client/contact.c2c.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:drift/drift.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/tables/groups.table.dart'; import 'package:twonly/src/database/twonly.db.dart' hide Message; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/services/api/messages.api.dart'; @@ -126,6 +127,44 @@ Future handleContactUpdate( if (contactUpdate.hasDisplayName() && contactUpdate.hasUsername() && senderProfileCounter != null) { + final contact = await twonlyDB.contactsDao + .getContactByUserId(fromUserId) + .getSingleOrNull(); + + if (contact != null) { + final sharedGroups = await twonlyDB.groupsDao.getGroupsForMember( + fromUserId, + ); + + if (contact.username != contactUpdate.username) { + for (final group in sharedGroups) { + await twonlyDB.groupsDao.insertGroupAction( + GroupHistoriesCompanion( + groupId: Value(group.groupId), + type: const Value(GroupActionType.updatedContactUsername), + contactId: Value(fromUserId), + oldGroupName: Value('@${contact.username}'), + newGroupName: Value('@${contactUpdate.username}'), + ), + ); + } + } + + if (contact.displayName != contactUpdate.displayName) { + for (final group in sharedGroups) { + await twonlyDB.groupsDao.insertGroupAction( + GroupHistoriesCompanion( + groupId: Value(group.groupId), + type: const Value(GroupActionType.updatedContactDisplayName), + contactId: Value(fromUserId), + oldGroupName: Value(contact.displayName ?? ''), + newGroupName: Value(contactUpdate.displayName), + ), + ); + } + } + } + await twonlyDB.contactsDao.updateContact( fromUserId, ContactsCompanion( diff --git a/lib/src/services/api/client2client/groups.c2c.dart b/lib/src/services/api/client2client/groups.c2c.dart index a6a1c463..cf3be4a0 100644 --- a/lib/src/services/api/client2client/groups.c2c.dart +++ b/lib/src/services/api/client2client/groups.c2c.dart @@ -179,6 +179,8 @@ Future handleGroupUpdate( ), ); case GroupActionType.createdGroup: + case GroupActionType.updatedContactUsername: + case GroupActionType.updatedContactDisplayName: break; } } diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart index 0a522354..0e4c856e 100644 --- a/lib/src/visual/views/chats/chat_messages.view.dart +++ b/lib/src/visual/views/chats/chat_messages.view.dart @@ -77,7 +77,6 @@ class _ChatMessagesViewState extends State contactSub?.cancel(); groupActionsSub?.cancel(); _nextTypingIndicator?.cancel(); - textFieldFocus?.dispose(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @@ -108,7 +107,7 @@ class _ChatMessagesViewState extends State }); protectMessageUpdating.protect(() async { - if (groupActionsSub == null && !newGroup.isDirectChat) { + if (groupActionsSub == null) { final actionsStream = twonlyDB.groupsDao.watchGroupActions( newGroup.groupId, ); diff --git a/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart b/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart index 167a6ea5..9ed5d44a 100644 --- a/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart +++ b/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart @@ -105,6 +105,24 @@ class _ChatGroupActionState extends State { text = (contact == null) ? context.lang.youLeftGroup : context.lang.makerLeftGroup(maker); + case GroupActionType.updatedContactUsername: + if (contact != null) { + icon = FontAwesomeIcons.userPen; + text = context.lang.makerChangedUsername( + maker, + widget.action.oldGroupName!, + widget.action.newGroupName!, + ); + } + case GroupActionType.updatedContactDisplayName: + if (contact != null) { + icon = FontAwesomeIcons.userPen; + text = context.lang.makerChangedDisplayName( + maker, + widget.action.oldGroupName!, + widget.action.newGroupName!, + ); + } } // switch (widget.action.type) {