From ef1daff89606ec2f0cdbf287d6a73bfe5c064402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=88=D0=B5=20=D0=98=D0=BC=D1=8F?= Date: Sat, 11 Apr 2026 00:22:32 +0700 Subject: [PATCH] =?UTF-8?q?=D0=B0=D0=B2=D0=BE=D1=82=D0=B0=D1=80=D0=BE4?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B2=20=D0=BF=D0=B5=D1=80=D0=B5=D1=81=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/backend/modules/messages.dart | 18 +++++++-- lib/frontend/screens/chats/chat_screen.dart | 3 ++ lib/frontend/widgets/message_bubble.dart | 44 ++++++++++++++++++++- lib/l10n/app_localizations.dart | 37 ++++++++--------- lib/l10n/app_localizations_en.dart | 9 ++--- lib/l10n/app_localizations_ru.dart | 15 +++---- lib/models/attachment.dart | 3 ++ 7 files changed, 87 insertions(+), 42 deletions(-) diff --git a/lib/backend/modules/messages.dart b/lib/backend/modules/messages.dart index 13f8af2..00dd1c0 100644 --- a/lib/backend/modules/messages.dart +++ b/lib/backend/modules/messages.dart @@ -6,13 +6,21 @@ import '../../core/storage/app_database.dart'; import '../../models/attachment.dart'; class ContactCache { - static final Map _cache = {}; + static final Map _nameCache = {}; + static final Map _avatarCache = {}; static void put(int id, String name) { - _cache[id] = name; + _nameCache[id] = name; } - static String? get(int id) => _cache[id]; + static void putAvatar(int id, String? baseUrl) { + if (baseUrl != null) { + _avatarCache[id] = baseUrl; + } + } + + static String? get(int id) => _nameCache[id]; + static String? getAvatar(int id) => _avatarCache[id]; } class CachedMessage { @@ -371,6 +379,10 @@ class MessagesModule { ? '$firstName $lastName' : firstName; ContactCache.put(contactId, fullName); + + final baseUrl = contact['baseUrl'] as String?; + ContactCache.putAvatar(contactId, baseUrl); + return fullName; } } diff --git a/lib/frontend/screens/chats/chat_screen.dart b/lib/frontend/screens/chats/chat_screen.dart index 1968d3b..a05c853 100644 --- a/lib/frontend/screens/chats/chat_screen.dart +++ b/lib/frontend/screens/chats/chat_screen.dart @@ -6,6 +6,7 @@ import '../../../backend/api.dart'; import '../../../backend/modules/messages.dart'; import '../../../core/storage/app_database.dart'; import '../../../models/attachment.dart'; +import '../../../backend/modules/messages.dart' show ContactCache; import '../../widgets/message_bubble.dart'; class ChatScreen extends StatefulWidget { @@ -183,6 +184,7 @@ class _ChatScreenState extends State for (final id in forwardIds) { final name = await messagesModule.searchContactById(id); + final avatar = ContactCache.getAvatar(id); if (name != null && mounted) { setState(() { for (var i = 0; i < _messages.length; i++) { @@ -195,6 +197,7 @@ class _ChatScreenState extends State return ForwardedMessageAttachment( originalSenderId: id, originalSenderName: name, + originalSenderAvatar: avatar, originalMessageId: a.originalMessageId, originalTime: a.originalTime, originalText: a.originalText, diff --git a/lib/frontend/widgets/message_bubble.dart b/lib/frontend/widgets/message_bubble.dart index 352435d..647da92 100644 --- a/lib/frontend/widgets/message_bubble.dart +++ b/lib/frontend/widgets/message_bubble.dart @@ -360,12 +360,14 @@ class MessageBubble extends StatelessWidget { ForwardedMessageAttachment forwarded, Color textColor, ) { + final cs = Theme.of(context).colorScheme; final headerColor = isMe ? Colors.white.withValues(alpha: 0.7) - : Theme.of(context).colorScheme.onSurfaceVariant; + : cs.onSurfaceVariant; final senderName = forwarded.originalSenderName; final displaySender = senderName ?? forwarded.originalSenderId.toString(); + final senderAvatar = forwarded.originalSenderAvatar; final origText = forwarded.originalText; final hasOrigText = origText != null && origText.isNotEmpty; @@ -378,6 +380,24 @@ class MessageBubble extends StatelessWidget { children: [ Icon(Symbols.forward, size: 14, color: headerColor), const SizedBox(width: 4), + if (senderAvatar != null && senderAvatar.isNotEmpty) + CircleAvatar( + radius: 10, + backgroundImage: NetworkImage(senderAvatar), + backgroundColor: cs.primaryContainer, + ) + else + CircleAvatar( + radius: 10, + backgroundColor: cs.primaryContainer, + child: Text( + displaySender.isNotEmpty + ? displaySender[0].toUpperCase() + : '?', + style: TextStyle(fontSize: 9, color: cs.onPrimaryContainer), + ), + ), + const SizedBox(width: 6), Text( displaySender, style: TextStyle( @@ -580,11 +600,13 @@ class MessageBubble extends StatelessWidget { ForwardedMessageAttachment forwarded, List photos, ) { + final cs = Theme.of(context).colorScheme; final headerColor = isMe ? Colors.white.withValues(alpha: 0.7) - : Theme.of(context).colorScheme.onSurfaceVariant; + : cs.onSurfaceVariant; final senderName = forwarded.originalSenderName; final displaySender = senderName ?? forwarded.originalSenderId.toString(); + final senderAvatar = forwarded.originalSenderAvatar; final hasCaption = message.text != null && message.text!.isNotEmpty; return Column( @@ -598,6 +620,24 @@ class MessageBubble extends StatelessWidget { children: [ Icon(Symbols.forward, size: 14, color: headerColor), const SizedBox(width: 4), + if (senderAvatar != null && senderAvatar.isNotEmpty) + CircleAvatar( + radius: 10, + backgroundImage: NetworkImage(senderAvatar), + backgroundColor: cs.primaryContainer, + ) + else + CircleAvatar( + radius: 10, + backgroundColor: cs.primaryContainer, + child: Text( + displaySender.isNotEmpty + ? displaySender[0].toUpperCase() + : '?', + style: TextStyle(fontSize: 9, color: cs.onPrimaryContainer), + ), + ), + const SizedBox(width: 6), Text( displaySender, style: TextStyle( diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index c029ed3..6741f70 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -62,8 +62,7 @@ import 'app_localizations_ru.dart'; /// be consistent with the languages listed in the AppLocalizations.supportedLocales /// property. abstract class AppLocalizations { - AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -71,8 +70,7 @@ abstract class AppLocalizations { return Localizations.of(context, AppLocalizations); } - static const LocalizationsDelegate delegate = - _AppLocalizationsDelegate(); + static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); /// A list of this localizations delegate along with the default localizations /// delegates. @@ -84,18 +82,17 @@ abstract class AppLocalizations { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = - >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + static const List> localizationsDelegates = >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('en'), - Locale('ru'), + Locale('ru') ]; /// No description provided for @loginTitle. @@ -381,8 +378,7 @@ abstract class AppLocalizations { String get proxyInvalidHostOrPort; } -class _AppLocalizationsDelegate - extends LocalizationsDelegate { +class _AppLocalizationsDelegate extends LocalizationsDelegate { const _AppLocalizationsDelegate(); @override @@ -391,26 +387,25 @@ class _AppLocalizationsDelegate } @override - bool isSupported(Locale locale) => - ['en', 'ru'].contains(locale.languageCode); + bool isSupported(Locale locale) => ['en', 'ru'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; } AppLocalizations lookupAppLocalizations(Locale locale) { + + // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'en': - return AppLocalizationsEn(); - case 'ru': - return AppLocalizationsRu(); + case 'en': return AppLocalizationsEn(); + case 'ru': return AppLocalizationsRu(); } throw FlutterError( 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.', + 'that was used.' ); } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index f7bad3e..68d2d21 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -12,8 +12,7 @@ class AppLocalizationsEn extends AppLocalizations { String get loginTitle => 'Sign in to Komet'; @override - String get loginSubtitle => - 'Check your country code and enter your\nphone number.'; + String get loginSubtitle => 'Check your country code and enter your\nphone number.'; @override String get loginCountry => 'Country'; @@ -103,8 +102,7 @@ class AppLocalizationsEn extends AppLocalizations { String get selectCountrySearchHint => 'Search countries…'; @override - String get codeConfirmationSmsSent => - 'We sent an SMS with a verification code to your phone number.'; + String get codeConfirmationSmsSent => 'We sent an SMS with a verification code to your phone number.'; @override String codeResendInSeconds(int seconds) { @@ -151,6 +149,5 @@ class AppLocalizationsEn extends AppLocalizations { String get proxySettingsSaved => 'Proxy settings applied'; @override - String get proxyInvalidHostOrPort => - 'Enter a valid proxy host and port (1–65535)'; + String get proxyInvalidHostOrPort => 'Enter a valid proxy host and port (1–65535)'; } diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 0524bca..7f58d45 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -12,8 +12,7 @@ class AppLocalizationsRu extends AppLocalizations { String get loginTitle => 'Войдите в Komet'; @override - String get loginSubtitle => - 'Проверьте код страны и введите свой\nномер телефона.'; + String get loginSubtitle => 'Проверьте код страны и введите свой\nномер телефона.'; @override String get loginCountry => 'Страна'; @@ -43,8 +42,7 @@ class AppLocalizationsRu extends AppLocalizations { String get loginDone => 'Готово'; @override - String get loginReadTermsNotification => - 'Сначала прочитайте условия использования'; + String get loginReadTermsNotification => 'Сначала прочитайте условия использования'; @override String get loginSpoofRedacted => 'Подделка спуфа'; @@ -71,8 +69,7 @@ class AppLocalizationsRu extends AppLocalizations { String get serverUseDefault => 'Сбросить к умолчанию'; @override - String get serverInvalidHostOrPort => - 'Укажите корректный хост и порт (1–65535)'; + String get serverInvalidHostOrPort => 'Укажите корректный хост и порт (1–65535)'; @override String get serverSettingsSaved => 'Настройки сервера применены'; @@ -105,8 +102,7 @@ class AppLocalizationsRu extends AppLocalizations { String get selectCountrySearchHint => 'Поиск страны…'; @override - String get codeConfirmationSmsSent => - 'Мы отправили SMS с кодом подтверждения на ваш номер телефона.'; + String get codeConfirmationSmsSent => 'Мы отправили SMS с кодом подтверждения на ваш номер телефона.'; @override String codeResendInSeconds(int seconds) { @@ -153,6 +149,5 @@ class AppLocalizationsRu extends AppLocalizations { String get proxySettingsSaved => 'Настройки прокси применены'; @override - String get proxyInvalidHostOrPort => - 'Укажите корректный хост и порт прокси (1–65535)'; + String get proxyInvalidHostOrPort => 'Укажите корректный хост и порт прокси (1–65535)'; } diff --git a/lib/models/attachment.dart b/lib/models/attachment.dart index 3da1153..2c43222 100644 --- a/lib/models/attachment.dart +++ b/lib/models/attachment.dart @@ -436,6 +436,7 @@ class ControlAttachment extends MessageAttachment { class ForwardedMessageAttachment extends MessageAttachment { final int originalSenderId; final String? originalSenderName; + final String? originalSenderAvatar; final String? originalMessageId; final int? originalTime; final String? originalText; @@ -445,6 +446,7 @@ class ForwardedMessageAttachment extends MessageAttachment { const ForwardedMessageAttachment({ required this.originalSenderId, this.originalSenderName, + this.originalSenderAvatar, this.originalMessageId, this.originalTime, this.originalText, @@ -493,6 +495,7 @@ class ForwardedMessageAttachment extends MessageAttachment { '_type': 'FORWARD', 'originalSenderId': originalSenderId, 'originalSenderName': originalSenderName, + 'originalSenderAvatar': originalSenderAvatar, 'originalMessageId': originalMessageId, 'originalTime': originalTime, 'originalText': originalText,