diff --git a/lib/backend/modules/contacts.dart b/lib/backend/modules/contacts.dart index 1ef25ea..0803533 100644 --- a/lib/backend/modules/contacts.dart +++ b/lib/backend/modules/contacts.dart @@ -417,6 +417,11 @@ class ContactsModule { return rows.map(CachedContact.fromDbRow).toList(); } + static Future getContact(int accountId, int id) async { + final row = await AppDatabase.loadContact(accountId, id); + return row == null ? null : CachedContact.fromDbRow(row); + } + static const List _debugFirstNames = [ 'Алиса', 'Борис', 'Вера', 'Глеб', 'Дарья', 'Егор', 'Жанна', 'Захар', 'Ирина', 'Кирилл', 'Лия', 'Максим', 'Нина', 'Олег', 'Полина', 'Роман', diff --git a/lib/core/storage/app_database.dart b/lib/core/storage/app_database.dart index ce13def..d95b9d0 100644 --- a/lib/core/storage/app_database.dart +++ b/lib/core/storage/app_database.dart @@ -883,6 +883,20 @@ class AppDatabase { ); } + static Future?> loadContact( + int accountId, + int id, + ) async { + final db = await _instance; + final rows = await db.query( + 'contacts', + where: 'account_id = ? AND id = ?', + whereArgs: [accountId, id], + limit: 1, + ); + return rows.isEmpty ? null : rows.first; + } + static Future deleteContact(int accountId, int id) async { final db = await _instance; await db.delete( diff --git a/lib/frontend/screens/chats/chat_info_screen.dart b/lib/frontend/screens/chats/chat_info_screen.dart index 470ea71..27a9113 100644 --- a/lib/frontend/screens/chats/chat_info_screen.dart +++ b/lib/frontend/screens/chats/chat_info_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:komet/main.dart'; import 'package:material_symbols_icons/symbols.dart'; import '../contacts/edit_contact_sheet.dart'; +import '../../../backend/modules/contacts.dart'; import '../../../backend/modules/messages.dart' show ContactCache; import '../../../core/cache/info_cache.dart'; import '../../../core/config/app_show_extra_info.dart'; @@ -78,10 +79,10 @@ class _ChatInfoScreenState extends State { String _selectedTab = ''; bool _descExpanded = false; bool _showRealName = false; - String? _nameOverride; int? _otherId; ContactInfo? _contactData; + CachedContact? _localContact; int? _seenTime; bool _isOnline = false; int _presenceStatus = 0; @@ -187,6 +188,7 @@ class _ChatInfoScreenState extends State { } if (_otherId != null) { + _localContact = await ContactsModule.getContact(_myId, _otherId!); final contact = await ContactInfoFetch.get(_otherId!); if (contact != null) { _contactData = contact; @@ -322,12 +324,18 @@ class _ChatInfoScreenState extends State { return null; } - bool get _isContact => _nameEntry('CUSTOM') != null; + bool get _isContact => _localContact != null; String _joinName(String first, String last) => last.trim().isEmpty ? first.trim() : '${first.trim()} ${last.trim()}'; + String get _customName { + final c = _localContact; + if (c != null) return _joinName(c.firstName, c.lastName ?? ''); + return widget.name; + } + Widget _buildMoreButton(ColorScheme cs) { final canEdit = widget.chatType == 'DIALOG' && _isContact; if (!canEdit) { @@ -358,17 +366,17 @@ class _ChatInfoScreenState extends State { } Future _openEdit() async { - final custom = _nameEntry('CUSTOM'); final oneme = _nameEntry('ONEME'); + final local = _localContact; final peerId = _otherId ?? widget.dialogPeerId ?? 0; if (peerId == 0) return; final result = await showEditContactSheet( context, contactId: peerId, - avatarUrl: _contactData?.avatarUrl ?? widget.imageUrl, - customFirst: custom?.firstName ?? '', - customLast: custom?.lastName ?? '', + avatarUrl: _contactData?.avatarUrl ?? local?.baseUrl ?? widget.imageUrl, + customFirst: local?.firstName ?? '', + customLast: local?.lastName ?? '', onemeFirst: oneme?.firstName ?? '', onemeLast: oneme?.lastName ?? '', ); @@ -376,33 +384,17 @@ class _ChatInfoScreenState extends State { switch (result.action) { case EditContactAction.updated: - _applyUpdatedNames(result.firstName, result.lastName); + final fresh = await ContactsModule.getContact(_myId, peerId); + if (!mounted) return; + setState(() { + _localContact = fresh; + _showRealName = false; + }); case EditContactAction.removed: Navigator.of(context).pop(); } } - void _applyUpdatedNames(String first, String last) { - final data = _contactData; - final newCustom = ContactName( - type: 'CUSTOM', - name: first, - firstName: first, - lastName: last, - ); - final others = data == null - ? [] - : data.names.where((n) => n.type != 'CUSTOM').toList(); - setState(() { - _contactData = ContactInfo( - raw: data?.raw ?? const {}, - names: [newCustom, ...others], - ); - _nameOverride = _joinName(first, last); - _showRealName = false; - }); - } - String? get _realName { final data = _contactData; if (data == null) return null; @@ -426,7 +418,7 @@ class _ChatInfoScreenState extends State { fontSize: 22, fontWeight: FontWeight.w700, ); - final custom = _nameOverride ?? widget.name; + final custom = _customName; final real = _realName; final hasToggle = widget.chatType == 'DIALOG' && real != null && real != custom; diff --git a/lib/frontend/screens/contacts/add_contact_sheet.dart b/lib/frontend/screens/contacts/add_contact_sheet.dart index 2f91609..a9d5712 100644 --- a/lib/frontend/screens/contacts/add_contact_sheet.dart +++ b/lib/frontend/screens/contacts/add_contact_sheet.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -7,32 +5,16 @@ import 'package:komet/backend/modules/contacts.dart'; import 'package:komet/core/config/countries.dart'; import 'package:komet/frontend/screens/auth/phone_input_formatter.dart'; import 'package:komet/frontend/screens/auth/select_country_screen.dart'; +import 'package:komet/frontend/screens/contacts/contact_sheet_common.dart'; import 'package:komet/frontend/screens/contacts/open_contact_profile.dart'; import 'package:komet/frontend/widgets/custom_notification.dart'; import 'package:komet/l10n/app_localizations.dart'; import 'package:komet/main.dart'; Future showAddContactSheet(BuildContext context) { - return showGeneralDialog( - context: context, - barrierDismissible: true, - barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, - barrierColor: Colors.black.withValues(alpha: 0.28), - transitionDuration: const Duration(milliseconds: 260), - pageBuilder: (_, _, _) => const SizedBox.shrink(), - transitionBuilder: (dialogContext, anim, _, _) { - final t = Curves.easeOutCubic.transform(anim.value); - return BackdropFilter( - filter: ImageFilter.blur(sigmaX: 14 * t, sigmaY: 14 * t), - child: Opacity( - opacity: anim.value, - child: Transform.scale( - scale: 0.94 + 0.06 * t, - child: _AddContactCard(hostContext: context), - ), - ), - ); - }, + return showBlurredCard( + context, + (host) => _AddContactCard(hostContext: host), ); } @@ -181,19 +163,19 @@ class _AddContactCardState extends State<_AddContactCard> { ), const SizedBox(height: 20), _buildPhoneRow(cs), - _divider(cs), + contactSheetDivider(cs), _buildTextRow( cs, controller: _firstCtrl, hint: l10n.addContactFirstName, ), - _divider(cs), + contactSheetDivider(cs), _buildTextRow( cs, controller: _lastCtrl, hint: l10n.addContactLastName, ), - _divider(cs), + contactSheetDivider(cs), _buildSaveButton(cs, l10n), ], ), @@ -214,7 +196,7 @@ class _AddContactCardState extends State<_AddContactCard> { mainAxisSize: MainAxisSize.min, children: [ Text( - _flagEmoji(_country.code), + contactFlagEmoji(_country.code), style: const TextStyle(fontSize: 22), ), const SizedBox(width: 6), @@ -358,16 +340,4 @@ class _AddContactCardState extends State<_AddContactCard> { ); } - Widget _divider(ColorScheme cs) => - Divider(height: 1, thickness: 0.5, color: cs.outlineVariant); - - static String _flagEmoji(String code) { - if (code.length != 2) return '🏳️'; - final upper = code.toUpperCase(); - final a = upper.codeUnitAt(0); - final b = upper.codeUnitAt(1); - if (a < 65 || a > 90 || b < 65 || b > 90) return '🏳️'; - return String.fromCharCode(0x1F1E6 + (a - 65)) + - String.fromCharCode(0x1F1E6 + (b - 65)); - } } diff --git a/lib/frontend/screens/contacts/contact_sheet_common.dart b/lib/frontend/screens/contacts/contact_sheet_common.dart new file mode 100644 index 0000000..b2d1a3d --- /dev/null +++ b/lib/frontend/screens/contacts/contact_sheet_common.dart @@ -0,0 +1,43 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +Future showBlurredCard( + BuildContext context, + Widget Function(BuildContext hostContext) builder, +) { + return showGeneralDialog( + context: context, + barrierDismissible: true, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, + barrierColor: Colors.black.withValues(alpha: 0.28), + transitionDuration: const Duration(milliseconds: 260), + pageBuilder: (_, _, _) => const SizedBox.shrink(), + transitionBuilder: (_, anim, _, _) { + final t = Curves.easeOutCubic.transform(anim.value); + return BackdropFilter( + filter: ImageFilter.blur(sigmaX: 14 * t, sigmaY: 14 * t), + child: Opacity( + opacity: anim.value, + child: Transform.scale( + scale: 0.94 + 0.06 * t, + child: builder(context), + ), + ), + ); + }, + ); +} + +Widget contactSheetDivider(ColorScheme cs) => + Divider(height: 1, thickness: 0.5, color: cs.outlineVariant); + +String contactFlagEmoji(String code) { + if (code.length != 2) return '🏳️'; + final upper = code.toUpperCase(); + final a = upper.codeUnitAt(0); + final b = upper.codeUnitAt(1); + if (a < 65 || a > 90 || b < 65 || b > 90) return '🏳️'; + return String.fromCharCode(0x1F1E6 + (a - 65)) + + String.fromCharCode(0x1F1E6 + (b - 65)); +} diff --git a/lib/frontend/screens/contacts/edit_contact_sheet.dart b/lib/frontend/screens/contacts/edit_contact_sheet.dart index 0a0374b..55a653c 100644 --- a/lib/frontend/screens/contacts/edit_contact_sheet.dart +++ b/lib/frontend/screens/contacts/edit_contact_sheet.dart @@ -1,8 +1,7 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:komet/backend/modules/contacts.dart'; +import 'package:komet/frontend/screens/contacts/contact_sheet_common.dart'; import 'package:komet/frontend/widgets/custom_notification.dart'; import 'package:komet/frontend/widgets/komet_avatar.dart'; import 'package:komet/l10n/app_localizations.dart'; @@ -31,33 +30,16 @@ Future showEditContactSheet( required String onemeFirst, required String onemeLast, }) { - return showGeneralDialog( - context: context, - barrierDismissible: true, - barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, - barrierColor: Colors.black.withValues(alpha: 0.28), - transitionDuration: const Duration(milliseconds: 260), - pageBuilder: (_, _, _) => const SizedBox.shrink(), - transitionBuilder: (_, anim, _, _) { - final t = Curves.easeOutCubic.transform(anim.value); - return BackdropFilter( - filter: ImageFilter.blur(sigmaX: 14 * t, sigmaY: 14 * t), - child: Opacity( - opacity: anim.value, - child: Transform.scale( - scale: 0.94 + 0.06 * t, - child: _EditContactCard( - contactId: contactId, - avatarUrl: avatarUrl, - customFirst: customFirst, - customLast: customLast, - onemeFirst: onemeFirst, - onemeLast: onemeLast, - ), - ), - ), - ); - }, + return showBlurredCard( + context, + (_) => _EditContactCard( + contactId: contactId, + avatarUrl: avatarUrl, + customFirst: customFirst, + customLast: customLast, + onemeFirst: onemeFirst, + onemeLast: onemeLast, + ), ); } @@ -223,7 +205,7 @@ class _EditContactCardState extends State<_EditContactCard> { controller: _firstCtrl, hint: l10n.editContactFirstName, ), - _divider(cs), + contactSheetDivider(cs), _inputRow( cs, controller: _lastCtrl, @@ -341,6 +323,4 @@ class _EditContactCardState extends State<_EditContactCard> { ); } - Widget _divider(ColorScheme cs) => - Divider(height: 1, thickness: 0.5, color: cs.outlineVariant); }