авотаро4ки в пересланных сообщениях
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 (1–65535)';
|
||||||
'Enter a valid proxy host and port (1–65535)';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user