mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-28 23:08:17 +00:00
Replace hardcoded English strings in 32 Dart source files with l10n references. Covers settings labels, UI actions, map/SAR features, device config, notifications, profiles, spectrum scan, and status messages for sl, de, hr, pl, es, fr, it, el, ru, tr, uk, zh, pt.
147 lines
4.2 KiB
Dart
147 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../models/message.dart';
|
|
import '../../l10n/app_localizations.dart';
|
|
import '../messages/message_bubble.dart';
|
|
|
|
/// Message overlay widget for displaying recent messages on the map
|
|
/// Only shown in fullscreen mode on large screens (>= 800px width)
|
|
class MapMessageOverlay extends StatefulWidget {
|
|
final List<Message> messages;
|
|
final VoidCallback? onNavigateToMessages;
|
|
final Function(String messageId)? onMessageTap;
|
|
|
|
const MapMessageOverlay({
|
|
super.key,
|
|
required this.messages,
|
|
this.onNavigateToMessages,
|
|
this.onMessageTap,
|
|
});
|
|
|
|
@override
|
|
State<MapMessageOverlay> createState() => _MapMessageOverlayState();
|
|
}
|
|
|
|
class _MapMessageOverlayState extends State<MapMessageOverlay> {
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Scroll to bottom on initial build
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_scrollToBottom(animate: false);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(MapMessageOverlay oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
// Auto-scroll to bottom when new messages arrive
|
|
if (widget.messages.length > oldWidget.messages.length) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_scrollToBottom(animate: true);
|
|
});
|
|
}
|
|
}
|
|
|
|
void _scrollToBottom({bool animate = true}) {
|
|
if (_scrollController.hasClients) {
|
|
if (animate) {
|
|
_scrollController.animateTo(
|
|
_scrollController.position.maxScrollExtent,
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.easeOut,
|
|
);
|
|
} else {
|
|
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_scrollController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (widget.messages.isEmpty) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.black.withValues(alpha: 0.75),
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: 0.3),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
// Header
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black.withValues(alpha: 0.3),
|
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Icon(
|
|
Icons.message,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
AppLocalizations.of(context)!.recentMessages,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
'${widget.messages.length}',
|
|
style: TextStyle(
|
|
color: Colors.white.withValues(alpha: 0.7),
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
// Message list
|
|
Expanded(
|
|
child: ListView.separated(
|
|
controller: _scrollController,
|
|
padding: const EdgeInsets.all(8),
|
|
itemCount: widget.messages.length,
|
|
separatorBuilder: (context, index) => const SizedBox(height: 4),
|
|
itemBuilder: (context, index) {
|
|
final message = widget.messages[index];
|
|
|
|
return MessageBubble(
|
|
message: message,
|
|
isCompact: true,
|
|
onTap: () {
|
|
widget.onMessageTap?.call(message.id);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|