авотаро4ки в пересланных сообщениях

This commit is contained in:
Ваше Имя
2026-04-11 00:22:32 +07:00
parent 78635dd327
commit ef1daff896
7 changed files with 87 additions and 42 deletions
+15 -3
View File
@@ -6,13 +6,21 @@ import '../../core/storage/app_database.dart';
import '../../models/attachment.dart'; import '../../models/attachment.dart';
class ContactCache { class ContactCache {
static final Map<int, String> _cache = {}; static final Map<int, String> _nameCache = {};
static final Map<int, String> _avatarCache = {};
static void put(int id, String name) { 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 { class CachedMessage {
@@ -371,6 +379,10 @@ class MessagesModule {
? '$firstName $lastName' ? '$firstName $lastName'
: firstName; : firstName;
ContactCache.put(contactId, fullName); ContactCache.put(contactId, fullName);
final baseUrl = contact['baseUrl'] as String?;
ContactCache.putAvatar(contactId, baseUrl);
return fullName; return fullName;
} }
} }
@@ -6,6 +6,7 @@ import '../../../backend/api.dart';
import '../../../backend/modules/messages.dart'; import '../../../backend/modules/messages.dart';
import '../../../core/storage/app_database.dart'; import '../../../core/storage/app_database.dart';
import '../../../models/attachment.dart'; import '../../../models/attachment.dart';
import '../../../backend/modules/messages.dart' show ContactCache;
import '../../widgets/message_bubble.dart'; import '../../widgets/message_bubble.dart';
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
@@ -183,6 +184,7 @@ class _ChatScreenState extends State<ChatScreen>
for (final id in forwardIds) { for (final id in forwardIds) {
final name = await messagesModule.searchContactById(id); final name = await messagesModule.searchContactById(id);
final avatar = ContactCache.getAvatar(id);
if (name != null && mounted) { if (name != null && mounted) {
setState(() { setState(() {
for (var i = 0; i < _messages.length; i++) { for (var i = 0; i < _messages.length; i++) {
@@ -195,6 +197,7 @@ class _ChatScreenState extends State<ChatScreen>
return ForwardedMessageAttachment( return ForwardedMessageAttachment(
originalSenderId: id, originalSenderId: id,
originalSenderName: name, originalSenderName: name,
originalSenderAvatar: avatar,
originalMessageId: a.originalMessageId, originalMessageId: a.originalMessageId,
originalTime: a.originalTime, originalTime: a.originalTime,
originalText: a.originalText, originalText: a.originalText,
+42 -2
View File
@@ -360,12 +360,14 @@ class MessageBubble extends StatelessWidget {
ForwardedMessageAttachment forwarded, ForwardedMessageAttachment forwarded,
Color textColor, Color textColor,
) { ) {
final cs = Theme.of(context).colorScheme;
final headerColor = isMe final headerColor = isMe
? Colors.white.withValues(alpha: 0.7) ? Colors.white.withValues(alpha: 0.7)
: Theme.of(context).colorScheme.onSurfaceVariant; : cs.onSurfaceVariant;
final senderName = forwarded.originalSenderName; final senderName = forwarded.originalSenderName;
final displaySender = senderName ?? forwarded.originalSenderId.toString(); final displaySender = senderName ?? forwarded.originalSenderId.toString();
final senderAvatar = forwarded.originalSenderAvatar;
final origText = forwarded.originalText; final origText = forwarded.originalText;
final hasOrigText = origText != null && origText.isNotEmpty; final hasOrigText = origText != null && origText.isNotEmpty;
@@ -378,6 +380,24 @@ class MessageBubble extends StatelessWidget {
children: [ children: [
Icon(Symbols.forward, size: 14, color: headerColor), Icon(Symbols.forward, size: 14, color: headerColor),
const SizedBox(width: 4), 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( Text(
displaySender, displaySender,
style: TextStyle( style: TextStyle(
@@ -580,11 +600,13 @@ class MessageBubble extends StatelessWidget {
ForwardedMessageAttachment forwarded, ForwardedMessageAttachment forwarded,
List<PhotoAttachment> photos, List<PhotoAttachment> photos,
) { ) {
final cs = Theme.of(context).colorScheme;
final headerColor = isMe final headerColor = isMe
? Colors.white.withValues(alpha: 0.7) ? Colors.white.withValues(alpha: 0.7)
: Theme.of(context).colorScheme.onSurfaceVariant; : cs.onSurfaceVariant;
final senderName = forwarded.originalSenderName; final senderName = forwarded.originalSenderName;
final displaySender = senderName ?? forwarded.originalSenderId.toString(); final displaySender = senderName ?? forwarded.originalSenderId.toString();
final senderAvatar = forwarded.originalSenderAvatar;
final hasCaption = message.text != null && message.text!.isNotEmpty; final hasCaption = message.text != null && message.text!.isNotEmpty;
return Column( return Column(
@@ -598,6 +620,24 @@ class MessageBubble extends StatelessWidget {
children: [ children: [
Icon(Symbols.forward, size: 14, color: headerColor), Icon(Symbols.forward, size: 14, color: headerColor),
const SizedBox(width: 4), 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( Text(
displaySender, displaySender,
style: TextStyle( style: TextStyle(
+16 -21
View File
@@ -62,8 +62,7 @@ import 'app_localizations_ru.dart';
/// be consistent with the languages listed in the AppLocalizations.supportedLocales /// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property. /// property.
abstract class AppLocalizations { abstract class AppLocalizations {
AppLocalizations(String locale) AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName; final String localeName;
@@ -71,8 +70,7 @@ abstract class AppLocalizations {
return Localizations.of<AppLocalizations>(context, AppLocalizations); return Localizations.of<AppLocalizations>(context, AppLocalizations);
} }
static const LocalizationsDelegate<AppLocalizations> delegate = static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
_AppLocalizationsDelegate();
/// A list of this localizations delegate along with the default localizations /// A list of this localizations delegate along with the default localizations
/// delegates. /// delegates.
@@ -84,18 +82,17 @@ abstract class AppLocalizations {
/// Additional delegates can be added by appending to this list in /// 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 /// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required. /// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
<LocalizationsDelegate<dynamic>>[ delegate,
delegate, GlobalMaterialLocalizations.delegate,
GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, ];
];
/// A list of this localizations delegate's supported locales. /// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[ static const List<Locale> supportedLocales = <Locale>[
Locale('en'), Locale('en'),
Locale('ru'), Locale('ru')
]; ];
/// No description provided for @loginTitle. /// No description provided for @loginTitle.
@@ -381,8 +378,7 @@ abstract class AppLocalizations {
String get proxyInvalidHostOrPort; String get proxyInvalidHostOrPort;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate(); const _AppLocalizationsDelegate();
@override @override
@@ -391,26 +387,25 @@ class _AppLocalizationsDelegate
} }
@override @override
bool isSupported(Locale locale) => bool isSupported(Locale locale) => <String>['en', 'ru'].contains(locale.languageCode);
<String>['en', 'ru'].contains(locale.languageCode);
@override @override
bool shouldReload(_AppLocalizationsDelegate old) => false; bool shouldReload(_AppLocalizationsDelegate old) => false;
} }
AppLocalizations lookupAppLocalizations(Locale locale) { AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when only language code is specified. // Lookup logic when only language code is specified.
switch (locale.languageCode) { switch (locale.languageCode) {
case 'en': case 'en': return AppLocalizationsEn();
return AppLocalizationsEn(); case 'ru': return AppLocalizationsRu();
case 'ru':
return AppLocalizationsRu();
} }
throw FlutterError( throw FlutterError(
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue ' 'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration ' 'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.', 'that was used.'
); );
} }
+3 -6
View File
@@ -12,8 +12,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get loginTitle => 'Sign in to Komet'; String get loginTitle => 'Sign in to Komet';
@override @override
String get loginSubtitle => String get loginSubtitle => 'Check your country code and enter your\nphone number.';
'Check your country code and enter your\nphone number.';
@override @override
String get loginCountry => 'Country'; String get loginCountry => 'Country';
@@ -103,8 +102,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get selectCountrySearchHint => 'Search countries…'; String get selectCountrySearchHint => 'Search countries…';
@override @override
String get codeConfirmationSmsSent => String get codeConfirmationSmsSent => 'We sent an SMS with a verification code to your phone number.';
'We sent an SMS with a verification code to your phone number.';
@override @override
String codeResendInSeconds(int seconds) { String codeResendInSeconds(int seconds) {
@@ -151,6 +149,5 @@ class AppLocalizationsEn extends AppLocalizations {
String get proxySettingsSaved => 'Proxy settings applied'; String get proxySettingsSaved => 'Proxy settings applied';
@override @override
String get proxyInvalidHostOrPort => String get proxyInvalidHostOrPort => 'Enter a valid proxy host and port (165535)';
'Enter a valid proxy host and port (165535)';
} }
+5 -10
View File
@@ -12,8 +12,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get loginTitle => 'Войдите в Komet'; String get loginTitle => 'Войдите в Komet';
@override @override
String get loginSubtitle => String get loginSubtitle => 'Проверьте код страны и введите свой\nномер телефона.';
'Проверьте код страны и введите свой\nномер телефона.';
@override @override
String get loginCountry => 'Страна'; String get loginCountry => 'Страна';
@@ -43,8 +42,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get loginDone => 'Готово'; String get loginDone => 'Готово';
@override @override
String get loginReadTermsNotification => String get loginReadTermsNotification => 'Сначала прочитайте условия использования';
'Сначала прочитайте условия использования';
@override @override
String get loginSpoofRedacted => 'Подделка спуфа'; String get loginSpoofRedacted => 'Подделка спуфа';
@@ -71,8 +69,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get serverUseDefault => 'Сбросить к умолчанию'; String get serverUseDefault => 'Сбросить к умолчанию';
@override @override
String get serverInvalidHostOrPort => String get serverInvalidHostOrPort => 'Укажите корректный хост и порт (1–65535)';
'Укажите корректный хост и порт (1–65535)';
@override @override
String get serverSettingsSaved => 'Настройки сервера применены'; String get serverSettingsSaved => 'Настройки сервера применены';
@@ -105,8 +102,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get selectCountrySearchHint => 'Поиск страны…'; String get selectCountrySearchHint => 'Поиск страны…';
@override @override
String get codeConfirmationSmsSent => String get codeConfirmationSmsSent => 'Мы отправили SMS с кодом подтверждения на ваш номер телефона.';
'Мы отправили SMS с кодом подтверждения на ваш номер телефона.';
@override @override
String codeResendInSeconds(int seconds) { String codeResendInSeconds(int seconds) {
@@ -153,6 +149,5 @@ class AppLocalizationsRu extends AppLocalizations {
String get proxySettingsSaved => 'Настройки прокси применены'; String get proxySettingsSaved => 'Настройки прокси применены';
@override @override
String get proxyInvalidHostOrPort => String get proxyInvalidHostOrPort => 'Укажите корректный хост и порт прокси (1–65535)';
'Укажите корректный хост и порт прокси (1–65535)';
} }
+3
View File
@@ -436,6 +436,7 @@ class ControlAttachment extends MessageAttachment {
class ForwardedMessageAttachment extends MessageAttachment { class ForwardedMessageAttachment extends MessageAttachment {
final int originalSenderId; final int originalSenderId;
final String? originalSenderName; final String? originalSenderName;
final String? originalSenderAvatar;
final String? originalMessageId; final String? originalMessageId;
final int? originalTime; final int? originalTime;
final String? originalText; final String? originalText;
@@ -445,6 +446,7 @@ class ForwardedMessageAttachment extends MessageAttachment {
const ForwardedMessageAttachment({ const ForwardedMessageAttachment({
required this.originalSenderId, required this.originalSenderId,
this.originalSenderName, this.originalSenderName,
this.originalSenderAvatar,
this.originalMessageId, this.originalMessageId,
this.originalTime, this.originalTime,
this.originalText, this.originalText,
@@ -493,6 +495,7 @@ class ForwardedMessageAttachment extends MessageAttachment {
'_type': 'FORWARD', '_type': 'FORWARD',
'originalSenderId': originalSenderId, 'originalSenderId': originalSenderId,
'originalSenderName': originalSenderName, 'originalSenderName': originalSenderName,
'originalSenderAvatar': originalSenderAvatar,
'originalMessageId': originalMessageId, 'originalMessageId': originalMessageId,
'originalTime': originalTime, 'originalTime': originalTime,
'originalText': originalText, 'originalText': originalText,