diff --git a/lib/screens/chat_detail_screen.dart b/lib/screens/chat_detail_screen.dart index d89e651..9ce9f2d 100644 --- a/lib/screens/chat_detail_screen.dart +++ b/lib/screens/chat_detail_screen.dart @@ -183,8 +183,7 @@ class _ChatDetailScreenState extends State { void _onScroll() { if (!_scrollController.hasClients) return; if (_isLoadingOlder || !_hasMoreMessages) return; - // Trigger when user scrolls near the top - if (_scrollController.position.pixels < 50) { + if (_scrollController.position.pixels < 200) { _loadOlder(); } } @@ -283,7 +282,7 @@ class _ChatDetailScreenState extends State { bool get _isNearBottom { if (!_scrollController.hasClients) return true; final pos = _scrollController.position; - return pos.pixels >= pos.maxScrollExtent - 100; + return pos.maxScrollExtent == 0 || pos.pixels >= pos.maxScrollExtent - 200; } void _onChatDataChanged() { diff --git a/lib/services/api/tf_api_client.dart b/lib/services/api/tf_api_client.dart index 79b416e..55186f4 100644 --- a/lib/services/api/tf_api_client.dart +++ b/lib/services/api/tf_api_client.dart @@ -645,7 +645,11 @@ class TfApiClient { } } - bool _parseBool(String? result) => result?.endsWith('True') ?? false; + bool _parseBool(String? result) { + if (result == null) return false; + final trimmed = result.trim().toLowerCase(); + return trimmed == 'true' || trimmed.endsWith('true'); + } Map? _parseJsonMap(String? result) { if (result == null) { diff --git a/lib/services/chat_data_service.dart b/lib/services/chat_data_service.dart index d03aa99..6efb37f 100644 --- a/lib/services/chat_data_service.dart +++ b/lib/services/chat_data_service.dart @@ -663,7 +663,6 @@ class ChatDataService extends ChangeNotifier { if (uid == null) return; final senderUid = info.senderUid; if (senderUid == null) return; - if (senderUid == uid && info.groupId == null && info.roomId == null) return; final roomId = _roomIdForNotification(info, uid); final msg = ChatMessage.fromNotification( @@ -683,7 +682,6 @@ class ChatDataService extends ChangeNotifier { if (uid == null) return; final senderUid = info.senderUid; if (senderUid == null) return; - if (senderUid == uid && info.groupId == null && info.roomId == null) return; final roomId = _roomIdForNotification(info, uid); final msg = ChatMessage.fromNotification( @@ -700,7 +698,7 @@ class ChatDataService extends ChangeNotifier { } void _addToCacheSilent(String roomId, ChatMessage msg) { - final cached = _messageCache[roomId] ?? []; + final cached = List.from(_messageCache[roomId] ?? []); final exists = _containsMessage(cached, msg); if (!exists) { cached.add(msg); @@ -734,7 +732,7 @@ class ChatDataService extends ChangeNotifier { } void _addToCache(String roomId, ChatMessage msg) { - final cached = _messageCache[roomId] ?? []; + final cached = List.from(_messageCache[roomId] ?? []); final exists = _containsMessage(cached, msg); if (!exists) { cached.add(msg); @@ -894,7 +892,7 @@ class ChatDataService extends ChangeNotifier { } void addSentMessage(String roomId, ChatMessage msg) { - final cached = _messageCache[roomId] ?? []; + final cached = List.from(_messageCache[roomId] ?? []); if (!_containsMessage(cached, msg)) { cached.add(msg); cached.sort((a, b) => a.timestamp.compareTo(b.timestamp)); diff --git a/lib/services/chat_ws_service.dart b/lib/services/chat_ws_service.dart index 33d5159..5763a03 100644 --- a/lib/services/chat_ws_service.dart +++ b/lib/services/chat_ws_service.dart @@ -416,11 +416,7 @@ class ChatWsService extends ChangeNotifier { try { final payload = { 'type': 'message.file', - 'content': { - 'send_to': 'U$sendToUid', - 'quote': quote, - 'file_hashes': fileHash, - }, + 'content': {'send_to': 'U$sendToUid', 'quote': quote, 'file_hashes': [fileHash]}, }; if (clientMid != null) payload['client_mid'] = clientMid; _sendEncrypted(jsonEncode(payload)); @@ -441,11 +437,7 @@ class ChatWsService extends ChangeNotifier { try { final payload = { 'type': 'message.file', - 'content': { - 'send_to': 'G$gid', - 'quote': quote, - 'file_hashes': fileHash, - }, + 'content': {'send_to': 'G$gid', 'quote': quote, 'file_hashes': [fileHash]}, }; if (clientMid != null) payload['client_mid'] = clientMid; _sendEncrypted(jsonEncode(payload)); diff --git a/lib/widgets/chat_input_bar.dart b/lib/widgets/chat_input_bar.dart index 18825e1..2206189 100644 --- a/lib/widgets/chat_input_bar.dart +++ b/lib/widgets/chat_input_bar.dart @@ -257,57 +257,52 @@ class _ChatInputBarState extends State style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 13), ), const SizedBox(height: 12), - PopupMenuButton( - onSelected: (value) async { - await _handleAttachment(value); + ElevatedButton.icon( + onPressed: () async { + final selected = await showMenu( + context: context, + position: const RelativeRect.fromLTRB(0, 0, 0, 0), + items: [ + PopupMenuItem( + value: 'image', + child: Row(children: [ + const Icon(Icons.image), + const SizedBox(width: 12), + Text(l10n.mediaPickImage), + ]), + ), + PopupMenuItem( + value: 'video', + child: Row(children: [ + const Icon(Icons.videocam), + const SizedBox(width: 12), + Text(l10n.mediaPickVideo), + ]), + ), + PopupMenuItem( + value: 'audio', + child: Row(children: [ + const Icon(Icons.audiotrack), + const SizedBox(width: 12), + Text(l10n.mediaPickAudio), + ]), + ), + PopupMenuItem( + value: 'file', + child: Row(children: [ + const Icon(Icons.file_upload), + const SizedBox(width: 12), + Text(l10n.chatInputUploadFile), + ]), + ), + ], + ); + if (selected != null) { + await _handleAttachment(selected); + } }, - itemBuilder: (context) => [ - PopupMenuItem( - value: 'image', - child: Row( - children: [ - const Icon(Icons.image), - const SizedBox(width: 12), - Text(l10n.mediaPickImage), - ], - ), - ), - PopupMenuItem( - value: 'video', - child: Row( - children: [ - const Icon(Icons.videocam), - const SizedBox(width: 12), - Text(l10n.mediaPickVideo), - ], - ), - ), - PopupMenuItem( - value: 'audio', - child: Row( - children: [ - const Icon(Icons.audiotrack), - const SizedBox(width: 12), - Text(l10n.mediaPickAudio), - ], - ), - ), - PopupMenuItem( - value: 'file', - child: Row( - children: [ - const Icon(Icons.file_upload), - const SizedBox(width: 12), - Text(l10n.chatInputUploadFile), - ], - ), - ), - ], - child: ElevatedButton.icon( - onPressed: null, - icon: const Icon(Icons.add, size: 18), - label: Text(l10n.chatFunctionPickFile), - ), + icon: const Icon(Icons.add, size: 18), + label: Text(l10n.chatFunctionPickFile), ), ], ),