mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
commit
ce8e8f0d5b
25 changed files with 526 additions and 494 deletions
|
|
@ -3,6 +3,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
|
|||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../utils/platform_info.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class LinkHandler {
|
||||
static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) {
|
||||
|
|
@ -93,21 +94,19 @@ class LinkHandler {
|
|||
final uri = Uri.parse(url);
|
||||
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_couldNotOpenLink(url)),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_couldNotOpenLink(url)),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_invalidLink),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_invalidLink),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
lib/helpers/snack_bar_builder.dart
Normal file
56
lib/helpers/snack_bar_builder.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
// showDismissibleSnackBar shows a [SnackBar] with tap to dismiss
|
||||
// all other properties are default and optional
|
||||
void showDismissibleSnackBar(
|
||||
BuildContext context, {
|
||||
Key? key,
|
||||
required Widget content,
|
||||
Color? backgroundColor,
|
||||
double? elevation,
|
||||
EdgeInsetsGeometry? margin,
|
||||
EdgeInsetsGeometry? padding,
|
||||
double? width,
|
||||
ShapeBorder? shape,
|
||||
HitTestBehavior? hitTestBehavior,
|
||||
SnackBarBehavior? behavior,
|
||||
SnackBarAction? action,
|
||||
double? actionOverflowThreshold,
|
||||
bool? showCloseIcon,
|
||||
Color? closeIconColor,
|
||||
Duration? duration,
|
||||
bool? persist,
|
||||
Animation<double>? animation,
|
||||
void Function()? onVisible,
|
||||
DismissDirection? dismissDirection,
|
||||
Clip? clipBehavior,
|
||||
}) {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
key: key,
|
||||
content: GestureDetector(
|
||||
onTap: () => messenger.hideCurrentSnackBar(),
|
||||
child: content,
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
margin: margin,
|
||||
padding: padding,
|
||||
width: width,
|
||||
shape: shape,
|
||||
hitTestBehavior: hitTestBehavior,
|
||||
behavior: behavior,
|
||||
action: action,
|
||||
actionOverflowThreshold: actionOverflowThreshold,
|
||||
showCloseIcon: showCloseIcon,
|
||||
closeIconColor: closeIconColor,
|
||||
duration: duration ?? const Duration(seconds: 4),
|
||||
persist: persist,
|
||||
animation: animation,
|
||||
onVisible: onVisible,
|
||||
dismissDirection: dismissDirection ?? DismissDirection.down,
|
||||
clipBehavior: clipBehavior ?? Clip.hardEdge,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
|||
import '../l10n/l10n.dart';
|
||||
import '../services/app_debug_log_service.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class AppDebugLogScreen extends StatelessWidget {
|
||||
const AppDebugLogScreen({super.key});
|
||||
|
|
@ -34,8 +35,9 @@ class AppDebugLogScreen extends StatelessWidget {
|
|||
.join('\n');
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.debugLog_copied)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.debugLog_copied),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../services/app_settings_service.dart';
|
|||
import '../services/notification_service.dart';
|
||||
import '../services/translation_service.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'map_cache_screen.dart';
|
||||
|
||||
class AppSettingsScreen extends StatelessWidget {
|
||||
|
|
@ -151,13 +152,12 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
.requestPermissions();
|
||||
if (!granted) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.appSettings_notificationPermissionDenied,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.appSettings_notificationPermissionDenied,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
|
@ -166,15 +166,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
|
||||
await settingsService.setNotificationsEnabled(value);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_notificationsEnabled
|
||||
: context.l10n.appSettings_notificationsDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_notificationsEnabled
|
||||
: context.l10n.appSettings_notificationsDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
@ -301,15 +300,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
value: settingsService.settings.clearPathOnMaxRetry,
|
||||
onChanged: (value) {
|
||||
settingsService.setClearPathOnMaxRetry(value);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_pathsWillBeCleared
|
||||
: context.l10n.appSettings_pathsWillNotBeCleared,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_pathsWillBeCleared
|
||||
: context.l10n.appSettings_pathsWillNotBeCleared,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -329,15 +327,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
value: settingsService.settings.autoRouteRotationEnabled,
|
||||
onChanged: (value) {
|
||||
settingsService.setAutoRouteRotationEnabled(value);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_autoRouteRotationEnabled
|
||||
: context.l10n.appSettings_autoRouteRotationDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_autoRouteRotationEnabled
|
||||
: context.l10n.appSettings_autoRouteRotationDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -1164,8 +1161,9 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
String? id,
|
||||
}) async {
|
||||
if (sourceUrl.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.translation_enterUrlFirst)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.translation_enterUrlFirst),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1176,22 +1174,23 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
id: id,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.translation_modelDownloaded)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.translation_modelDownloaded),
|
||||
);
|
||||
await settingsService.setTranslationEnabled(true);
|
||||
} on TranslationDownloadCancelled {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.translation_downloadStopped)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.translation_downloadStopped),
|
||||
);
|
||||
} catch (error) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.translation_downloadFailed(error.toString()),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.translation_downloadFailed(error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1236,16 +1235,16 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
try {
|
||||
await translationService.removeModel(model);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
// TODO: l10n
|
||||
content: Text('Deleted ${translationModelFriendlyName(model)}.'),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
// TODO: l10n
|
||||
content: Text('Deleted ${translationModelFriendlyName(model)}.'),
|
||||
);
|
||||
} catch (error) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Delete failed: $error')),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text('Delete failed: $error'),
|
||||
); // TODO: l10n
|
||||
}
|
||||
}
|
||||
|
|
@ -1279,15 +1278,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
onChanged: (value) async {
|
||||
await settingsService.setAppDebugLogEnabled(value);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_appDebugLoggingEnabled
|
||||
: context.l10n.appSettings_appDebugLoggingDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
value
|
||||
? context.l10n.appSettings_appDebugLoggingEnabled
|
||||
: context.l10n.appSettings_appDebugLoggingDisabled,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import '../l10n/l10n.dart';
|
|||
import '../services/ble_debug_log_service.dart';
|
||||
import '../connector/meshcore_protocol.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
enum _BleLogView { frames, rawLogRx }
|
||||
|
||||
|
|
@ -52,10 +53,9 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
|
|||
.join('\n');
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.debugLog_bleCopied),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.debugLog_bleCopied),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import '../connector/meshcore_protocol.dart';
|
|||
import '../helpers/gif_helper.dart';
|
||||
import '../helpers/reaction_helper.dart';
|
||||
import '../helpers/utf8_length_limiter.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/channel.dart';
|
||||
import '../models/channel_message.dart';
|
||||
|
|
@ -144,11 +145,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
Future<void> _scrollToMessage(String messageId) async {
|
||||
final key = _messageKeys[messageId];
|
||||
if (key == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_originalMessageNotFound),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_originalMessageNotFound),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1151,9 +1151,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
final now = DateTime.now();
|
||||
if (_lastChannelSendAt != null &&
|
||||
now.difference(_lastChannelSendAt!) < const Duration(seconds: 1)) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown)));
|
||||
content: Text(context.l10n.chat_sendCooldown),
|
||||
);
|
||||
return;
|
||||
}
|
||||
_lastChannelSendAt = now;
|
||||
|
|
@ -1195,8 +1196,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
|
||||
final maxBytes = maxChannelMessageBytes(connector.selfName);
|
||||
if (utf8.encode(messageText).length > maxBytes) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_messageTooLong(maxBytes)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1323,17 +1325,19 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
|
||||
void _copyMessageText(String text) {
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied)));
|
||||
content: Text(context.l10n.chat_messageCopied),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _deleteMessage(ChannelMessage message) async {
|
||||
await context.read<MeshCoreConnector>().deleteChannelMessage(message);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted)));
|
||||
content: Text(context.l10n.chat_messageDeleted),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatPathPrefixes(Uint8List pathBytes) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import '../widgets/empty_state.dart';
|
|||
import '../widgets/qr_code_display.dart';
|
||||
import '../widgets/quick_switch_bar.dart';
|
||||
import '../widgets/unread_badge.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'channel_chat_screen.dart';
|
||||
import 'community_qr_scanner_screen.dart';
|
||||
import 'contacts_screen.dart';
|
||||
|
|
@ -809,15 +810,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
onPressed: () async {
|
||||
final name = nameController.text.trim();
|
||||
if (name.isEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -837,13 +835,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
nextIndex,
|
||||
);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(
|
||||
name,
|
||||
),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(name),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -897,15 +892,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
final name = nameController.text.trim();
|
||||
final pskHex = pskController.text.trim();
|
||||
if (name.isEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -914,15 +906,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
try {
|
||||
psk = Channel.parsePskHex(pskHex);
|
||||
} on FormatException {
|
||||
ScaffoldMessenger.of(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_pskMustBe32Hex,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_pskMustBe32Hex,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -930,13 +919,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
Navigator.pop(dialogContext);
|
||||
connector.setChannel(nextIndex, name, psk);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(
|
||||
name,
|
||||
),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(name),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -967,11 +953,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
Navigator.pop(dialogContext);
|
||||
connector.setChannel(nextIndex, 'Public', psk);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_publicChannelAdded,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_publicChannelAdded,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1097,15 +1082,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
onPressed: () async {
|
||||
var hashtag = hashtagController.text.trim();
|
||||
if (hashtag.isEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.channels_enterChannelName,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -1125,15 +1107,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
} else {
|
||||
// Community hashtag - HMAC derivation from community secret
|
||||
if (selectedCommunity == null) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.community_selectCommunity,
|
||||
),
|
||||
content: Text(
|
||||
dialogContext
|
||||
.l10n
|
||||
.community_selectCommunity,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -1159,12 +1138,11 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
psk,
|
||||
);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(
|
||||
channelName,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_channelAdded(
|
||||
channelName,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -1259,13 +1237,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
onPressed: () async {
|
||||
final name = nameController.text.trim();
|
||||
if (name.isEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
dialogContext,
|
||||
).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
dialogContext.l10n.community_enterName,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
dialogContext.l10n.community_enterName,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -1301,11 +1276,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.community_created(name),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.community_created(name),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
@ -1494,10 +1468,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
try {
|
||||
psk = Channel.parsePskHex(pskHex);
|
||||
} on FormatException {
|
||||
ScaffoldMessenger.of(dialogContext).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(dialogContext.l10n.channels_pskMustBe32Hex),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
dialogContext,
|
||||
content: Text(dialogContext.l10n.channels_pskMustBe32Hex),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1510,16 +1483,16 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
smazEnabled,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.channels_channelUpdated(name)),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.channels_channelUpdated(name)),
|
||||
);
|
||||
} catch (e, st) {
|
||||
debugPrint(st.toString());
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to update channel: $e')),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text('Failed to update channel: $e'),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
@ -1559,21 +1532,19 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
|
||||
if (!context.mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_channelDeleted(channel.name),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_channelDeleted(channel.name),
|
||||
),
|
||||
);
|
||||
} catch (e, st) {
|
||||
if (!context.mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.channels_channelDeleteFailed(channel.name),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.channels_channelDeleteFailed(channel.name),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
@ -1594,8 +1565,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
void _addPublicChannel(BuildContext context, MeshCoreConnector connector) {
|
||||
final psk = Channel.parsePskHex(Channel.publicChannelPsk);
|
||||
connector.setChannel(0, 'Public', psk);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.channels_publicChannelAdded)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.channels_publicChannelAdded),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1810,12 +1782,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.community_deleted(community.name),
|
||||
),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.community_deleted(community.name)),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import '../widgets/radio_stats_entry.dart';
|
|||
import '../widgets/translated_message_content.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'telemetry_screen.dart';
|
||||
|
||||
class ChatScreen extends StatefulWidget {
|
||||
|
|
@ -633,9 +634,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
final now = DateTime.now();
|
||||
if (_lastTextSendAt != null &&
|
||||
now.difference(_lastTextSendAt!) < const Duration(seconds: 1)) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown)));
|
||||
content: Text(context.l10n.chat_sendCooldown),
|
||||
);
|
||||
return;
|
||||
}
|
||||
_lastTextSendAt = now;
|
||||
|
|
@ -671,8 +673,9 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
}
|
||||
final maxBytes = maxContactMessageBytes();
|
||||
if (utf8.encode(outgoingText).length > maxBytes) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_messageTooLong(maxBytes)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -860,15 +863,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
_showFullPathDialog(context, path.pathBytes),
|
||||
onTap: () async {
|
||||
if (path.pathBytes.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context
|
||||
.l10n
|
||||
.chat_pathDetailsNotAvailable,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.chat_pathDetailsNotAvailable,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -952,11 +952,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
_resolveContact(connector),
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_pathCleared),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_pathCleared),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
|
|
@ -982,11 +981,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
pathLen: -1,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_floodModeEnabled),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_floodModeEnabled),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
|
|
@ -1020,11 +1018,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
|
||||
void _showFullPathDialog(BuildContext context, List<int> pathBytes) {
|
||||
if (pathBytes.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_pathDetailsNotAvailable),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_pathDetailsNotAvailable),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1137,11 +1134,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
: (verified
|
||||
? context.l10n.chat_pathDeviceConfirmed
|
||||
: context.l10n.chat_pathDeviceNotConfirmed);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.chat_pathSetHops(hopCount, status)),
|
||||
duration: const Duration(seconds: 3),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.chat_pathSetHops(hopCount, status)),
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1490,26 +1486,29 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
|
||||
void _copyMessageText(String text) {
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied)));
|
||||
content: Text(context.l10n.chat_messageCopied),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _deleteMessage(Message message) async {
|
||||
await context.read<MeshCoreConnector>().deleteMessage(message);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted)));
|
||||
content: Text(context.l10n.chat_messageDeleted),
|
||||
);
|
||||
}
|
||||
|
||||
void _retryMessage(Message message) {
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
// Retry using the contact's current path override setting
|
||||
connector.sendMessage(_resolveContact(connector), message.text);
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.chat_retryingMessage)));
|
||||
content: Text(context.l10n.chat_retryingMessage),
|
||||
);
|
||||
}
|
||||
|
||||
void _showEmojiPicker(Message message, Contact senderContact) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import '../models/community.dart';
|
|||
import '../storage/community_store.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../widgets/qr_scanner_widget.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
/// Screen for scanning community QR codes to join communities.
|
||||
///
|
||||
|
|
@ -76,11 +77,10 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
|
|||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -93,12 +93,11 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
|
|||
}
|
||||
|
||||
void _showInvalidQrError(BuildContext context) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.orange,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.orange,
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -229,11 +228,10 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
|
|||
}
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_joined(community.name)),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.community_joined(community.name)),
|
||||
backgroundColor: Colors.green,
|
||||
);
|
||||
|
||||
// Return to previous screen
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import '../widgets/quick_switch_bar.dart';
|
|||
import '../widgets/repeater_login_dialog.dart';
|
||||
import '../widgets/room_login_dialog.dart';
|
||||
import '../widgets/unread_badge.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'channels_screen.dart';
|
||||
import 'chat_screen.dart';
|
||||
import 'discovery_screen.dart';
|
||||
|
|
@ -150,9 +151,10 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
}
|
||||
|
||||
void _showGroupsUnavailableMessage(BuildContext context) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.common_loading)));
|
||||
content: Text(context.l10n.common_loading),
|
||||
);
|
||||
}
|
||||
|
||||
void _setupFrameListener() {
|
||||
|
|
@ -169,10 +171,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
// Validate packet has expected minimum size (98+ bytes per protocol)
|
||||
if (advertPacket.length < 98) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_invalidAdvertFormat),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_invalidAdvertFormat),
|
||||
);
|
||||
}
|
||||
_pendingOperations.remove(ContactOperationType.export);
|
||||
|
|
@ -187,24 +188,23 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
if (!mounted) return;
|
||||
|
||||
if (_pendingOperations.contains(ContactOperationType.import)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.contacts_contactImported)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_contactImported),
|
||||
);
|
||||
}
|
||||
|
||||
if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_zeroHopContactAdvertSent),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_zeroHopContactAdvertSent),
|
||||
);
|
||||
}
|
||||
|
||||
if (_pendingOperations.contains(ContactOperationType.export)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_contactAdvertCopied),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_contactAdvertCopied),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -216,25 +216,22 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
if (!mounted) return;
|
||||
|
||||
if (_pendingOperations.contains(ContactOperationType.import)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_contactImportFailed),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_contactImportFailed),
|
||||
);
|
||||
}
|
||||
|
||||
if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_zeroHopContactAdvertFailed),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_zeroHopContactAdvertFailed),
|
||||
);
|
||||
}
|
||||
if (_pendingOperations.contains(ContactOperationType.export)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_contactAdvertCopyFailed),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_contactAdvertCopyFailed),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -271,8 +268,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
final clipboardData = await Clipboard.getData('text/plain');
|
||||
if (clipboardData == null || clipboardData.text == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.contacts_clipboardEmpty)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_clipboardEmpty),
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
|
@ -280,8 +278,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
final text = clipboardData.text!.trim();
|
||||
if (!text.startsWith('meshcore://')) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_invalidAdvertFormat),
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
|
@ -294,8 +293,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
connector.importContact(importContactFrame);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_invalidAdvertFormat),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -330,10 +330,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
),
|
||||
onTap: () => {
|
||||
connector.sendSelfAdvert(flood: false),
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.settings_advertisementSent),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.settings_advertisementSent),
|
||||
),
|
||||
},
|
||||
),
|
||||
|
|
@ -347,10 +346,9 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
),
|
||||
onTap: () => {
|
||||
connector.sendSelfAdvert(flood: true),
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.settings_advertisementSent),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.settings_advertisementSent),
|
||||
),
|
||||
},
|
||||
),
|
||||
|
|
@ -1146,19 +1144,17 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
onPressed: () async {
|
||||
final name = nameController.text.trim();
|
||||
if (name.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_groupNameRequired),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_groupNameRequired),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (name.toLowerCase() ==
|
||||
contactsAllGroupsValue.toLowerCase()) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.contacts_groupNameReserved),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_groupNameReserved),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1167,11 +1163,10 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||
return g.name.toLowerCase() == name.toLowerCase();
|
||||
});
|
||||
if (exists) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.contacts_groupAlreadyExists(name),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.contacts_groupAlreadyExists(name),
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import '../utils/contact_search.dart';
|
|||
import '../utils/platform_info.dart';
|
||||
import '../widgets/app_bar.dart';
|
||||
import '../widgets/list_filter_widget.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
enum DiscoverySortOption { lastSeen, name, type }
|
||||
|
||||
|
|
@ -234,8 +235,9 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
|||
final hexString = pubKeyToHex(contact.rawPacket!);
|
||||
Clipboard.setData(ClipboardData(text: "meshcore://$hexString"));
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.contacts_contactAdvertCopied)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.contacts_contactAdvertCopied),
|
||||
);
|
||||
break;
|
||||
case 'delete_contact':
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
|
|||
import '../services/app_settings_service.dart';
|
||||
import '../services/map_tile_cache_service.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class MapCacheScreen extends StatefulWidget {
|
||||
const MapCacheScreen({super.key});
|
||||
|
|
@ -112,15 +113,17 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||
Future<void> _startDownload() async {
|
||||
final bounds = _selectedBounds;
|
||||
if (bounds == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.mapCache_selectAreaFirst)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.mapCache_selectAreaFirst),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_estimatedTiles == 0) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.mapCache_noTilesToDownload)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.mapCache_noTilesToDownload),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -182,9 +185,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||
result.failed,
|
||||
)
|
||||
: context.l10n.mapCache_cachedTiles(result.downloaded);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
showDismissibleSnackBar(context, content: Text(message));
|
||||
}
|
||||
|
||||
Future<void> _clearCache() async {
|
||||
|
|
@ -210,8 +211,9 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||
final cacheService = context.read<MapTileCacheService>();
|
||||
await cacheService.clearCache();
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.mapCache_offlineCacheCleared)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.mapCache_offlineCacheCleared),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import 'chat_screen.dart';
|
|||
import 'contacts_screen.dart';
|
||||
import '../widgets/repeater_login_dialog.dart';
|
||||
import '../widgets/room_login_dialog.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'repeater_hub_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
import 'line_of_sight_map_screen.dart';
|
||||
|
|
@ -1659,7 +1660,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||
);
|
||||
await connector.refreshDeviceInfo();
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(SnackBar(content: Text(successMsg)));
|
||||
showDismissibleSnackBar(
|
||||
messenger.context,
|
||||
content: Text(successMsg),
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
|
|
@ -1681,8 +1685,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||
required String flags,
|
||||
}) async {
|
||||
if (!connector.isConnected) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.map_connectToShareMarkers)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.map_connectToShareMarkers),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -2271,8 +2276,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||
_points.clear();
|
||||
_polylines.clear();
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.map_pathTraceCancelled)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.map_pathTraceCancelled),
|
||||
);
|
||||
},
|
||||
tooltip: l10n.common_cancel,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import '../connector/meshcore_protocol.dart';
|
|||
import '../services/repeater_command_service.dart';
|
||||
import '../widgets/path_management_dialog.dart';
|
||||
import '../widgets/snr_indicator.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class NeighborsScreen extends StatefulWidget {
|
||||
final Contact repeater;
|
||||
|
|
@ -163,11 +164,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
|
|||
_neighborCount = neighborCount;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_receivedData),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.neighbors_receivedData),
|
||||
backgroundColor: Colors.green,
|
||||
);
|
||||
_statusTimeout?.cancel();
|
||||
if (!mounted) return;
|
||||
|
|
@ -224,11 +224,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
|
|||
_isLoading = false;
|
||||
_isLoaded = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_requestTimedOut),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.neighbors_requestTimedOut),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
_recordStatusResult(false);
|
||||
});
|
||||
|
|
@ -239,11 +238,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
|
|||
_isLoaded = false;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_errorLoading(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.neighbors_errorLoading(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import '../connector/meshcore_protocol.dart';
|
|||
import '../widgets/debug_frame_viewer.dart';
|
||||
import '../services/repeater_command_service.dart';
|
||||
import '../widgets/path_management_dialog.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class RepeaterCliScreen extends StatefulWidget {
|
||||
final Contact repeater;
|
||||
|
|
@ -336,8 +337,9 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
|
|||
if (_commandController.text.trim().isNotEmpty) {
|
||||
_sendCommand(showDebug: true);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.repeater_enterCommandFirst)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.repeater_enterCommandFirst),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../services/app_debug_log_service.dart';
|
|||
import '../services/repeater_command_service.dart';
|
||||
import '../services/storage_service.dart';
|
||||
import '../widgets/path_management_dialog.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class RepeaterSettingsScreen extends StatefulWidget {
|
||||
final Contact repeater;
|
||||
|
|
@ -468,18 +469,16 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||
|
||||
if (mounted) {
|
||||
if (successCount > 0) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.repeater_refreshed(label)),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.repeater_refreshed(label)),
|
||||
backgroundColor: Colors.green,
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.repeater_errorRefreshing(label)),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.repeater_errorRefreshing(label)),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -666,11 +665,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||
});
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.repeater_settingsSaved),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.repeater_settingsSaved),
|
||||
backgroundColor: Colors.green,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -679,13 +677,12 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||
});
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.repeater_errorSavingSettings(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.repeater_errorSavingSettings(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1429,9 +1426,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||
|
||||
if (command == 'erase') {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.repeater_eraseSerialOnly)));
|
||||
content: Text(l10n.repeater_eraseSerialOnly),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1453,17 +1451,17 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||
await connector.sendFrame(frame);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.repeater_commandSent(command))),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.repeater_commandSent(command)),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.repeater_errorSendingCommand(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.repeater_errorSendingCommand(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import '../services/app_settings_service.dart';
|
|||
import '../services/repeater_command_service.dart';
|
||||
import '../utils/battery_utils.dart';
|
||||
import '../widgets/path_management_dialog.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class RepeaterStatusScreen extends StatefulWidget {
|
||||
final Contact repeater;
|
||||
|
|
@ -309,11 +310,10 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
|
|||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.repeater_statusRequestTimeout),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.repeater_statusRequestTimeout),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
_recordStatusResult(false);
|
||||
});
|
||||
|
|
@ -323,13 +323,10 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
|
|||
_isLoading = false;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.repeater_errorLoadingStatus(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.repeater_errorLoadingStatus(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
_recordStatusResult(false);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../services/linux_ble_error_classifier.dart';
|
|||
import '../utils/app_logger.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../widgets/device_tile.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'contacts_screen.dart';
|
||||
import 'tcp_screen.dart';
|
||||
import 'usb_screen.dart';
|
||||
|
|
@ -317,11 +318,10 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
|||
return;
|
||||
}
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.scanner_connectionFailed(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.scanner_connectionFailed(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import '../l10n/l10n.dart';
|
|||
import '../models/radio_settings.dart';
|
||||
import '../services/app_debug_log_service.dart';
|
||||
import '../widgets/app_bar.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'app_settings_screen.dart';
|
||||
import 'app_debug_log_screen.dart';
|
||||
import 'ble_debug_log_screen.dart';
|
||||
|
|
@ -513,8 +514,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
await connector.setNodeName(controller.text);
|
||||
await connector.refreshDeviceInfo();
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_nodeNameUpdated)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_nodeNameUpdated),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.common_save),
|
||||
|
|
@ -628,10 +630,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
final interval = int.tryParse(intervalText);
|
||||
if (interval == null || interval < 60 || interval >= 86400) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.settings_locationIntervalInvalid),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_locationIntervalInvalid),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -639,8 +640,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
await connector.setCustomVar("gps_interval:$interval");
|
||||
await connector.refreshDeviceInfo();
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_locationUpdated)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_locationUpdated),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -660,15 +662,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
: currentLon;
|
||||
if (lat == null || lon == null) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_locationBothRequired)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_locationBothRequired),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (lat < -90 || lat > 90 || lon < -180 || lon > 180) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_locationInvalid)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_locationInvalid),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -676,8 +680,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
await connector.setNodeLocation(lat: lat, lon: lon);
|
||||
await connector.refreshDeviceInfo();
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_locationUpdated)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_locationUpdated),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.common_save),
|
||||
|
|
@ -691,9 +696,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
void _syncTime(BuildContext context, MeshCoreConnector connector) {
|
||||
final l10n = context.l10n;
|
||||
connector.syncTime();
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.settings_timeSynchronized)));
|
||||
content: Text(l10n.settings_timeSynchronized),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmReboot(BuildContext context, MeshCoreConnector connector) {
|
||||
|
|
@ -758,23 +764,27 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
if (!mounted) return;
|
||||
switch (result) {
|
||||
case gpxExportSuccess:
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportSuccess)));
|
||||
content: Text(l10n.settings_gpxExportSuccess),
|
||||
);
|
||||
case gpxExportNoContacts:
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_gpxExportNoContacts)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_gpxExportNoContacts),
|
||||
);
|
||||
break;
|
||||
case gpxExportNotAvailable:
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_gpxExportNotAvailable)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_gpxExportNotAvailable),
|
||||
);
|
||||
break;
|
||||
case gpxExportFailed:
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportError)));
|
||||
content: Text(l10n.settings_gpxExportError),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1077,8 +1087,9 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) {
|
|||
);
|
||||
await connector.refreshDeviceInfo();
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_telemetryModeUpdated)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_telemetryModeUpdated),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.common_save),
|
||||
|
|
@ -1410,18 +1421,18 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
|
|||
final txPower = int.tryParse(_txPowerController.text);
|
||||
|
||||
if (freqMHz == null || freqMHz < 300 || freqMHz > 2500) {
|
||||
ScaffoldMessenger.of(
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.settings_frequencyInvalid)));
|
||||
content: Text(l10n.settings_frequencyInvalid),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final maxTxPower = widget.connector.maxTxPower ?? 22;
|
||||
if (txPower == null || txPower < 0 || txPower > maxTxPower) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1441,8 +1452,9 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
|
|||
if (knownRepeat) {
|
||||
const validRepeatFreqsKHz = {433000, 869000, 918000};
|
||||
if (_clientRepeat && !validRepeatFreqsKHz.contains(freqHz)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_clientRepeatFreqWarning)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_clientRepeatFreqWarning),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1472,14 +1484,16 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
|
|||
|
||||
if (!mounted) return;
|
||||
_logRadioSettingsState('Radio settings saved successfully');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_radioSettingsUpdated)),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_radioSettingsUpdated),
|
||||
);
|
||||
} catch (e) {
|
||||
_appLog.warn('Radio settings save failed: $e', tag: 'RadioSettings');
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.settings_error(e.toString()))),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.settings_error(e.toString())),
|
||||
);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
|
|||
import '../services/app_settings_service.dart';
|
||||
import '../utils/platform_info.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'contacts_screen.dart';
|
||||
import 'usb_screen.dart';
|
||||
|
||||
|
|
@ -270,8 +271,10 @@ class _TcpScreenState extends State<TcpScreen> {
|
|||
|
||||
void _showError(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(message), backgroundColor: Colors.red),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(message),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import '../utils/app_logger.dart';
|
|||
import '../widgets/path_management_dialog.dart';
|
||||
import '../helpers/cayenne_lpp.dart';
|
||||
import '../utils/battery_utils.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class TelemetryScreen extends StatefulWidget {
|
||||
final Contact contact;
|
||||
|
|
@ -86,11 +87,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||
_isLoading = false;
|
||||
_isLoaded = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.telemetry_requestTimeout),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.telemetry_requestTimeout),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
_recordTelemetryResult(false);
|
||||
});
|
||||
|
|
@ -137,11 +137,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||
_parsedTelemetry = parsedTelemetry;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.telemetry_receivedData),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.telemetry_receivedData),
|
||||
backgroundColor: Colors.green,
|
||||
);
|
||||
_statusTimeout?.cancel();
|
||||
if (!mounted) return;
|
||||
|
|
@ -182,11 +181,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||
_isLoaded = false;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.telemetry_errorLoading(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.telemetry_errorLoading(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../utils/app_logger.dart';
|
|||
import '../utils/platform_info.dart';
|
||||
import '../utils/usb_port_labels.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'contacts_screen.dart';
|
||||
import 'scanner_screen.dart';
|
||||
import 'tcp_screen.dart';
|
||||
|
|
@ -383,11 +384,10 @@ class _UsbScreenState extends State<UsbScreen> {
|
|||
|
||||
void _showError(Object error) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(_friendlyErrorMessage(error)),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(_friendlyErrorMessage(error)),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import '../l10n/l10n.dart';
|
|||
import '../models/contact.dart';
|
||||
import '../helpers/path_helper.dart';
|
||||
import '../services/path_history_service.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'path_selection_dialog.dart';
|
||||
|
||||
class PathManagementDialog {
|
||||
|
|
@ -65,11 +66,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
void _showFullPathDialog(BuildContext context, List<int> pathBytes) {
|
||||
final l10n = context.l10n;
|
||||
if (pathBytes.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.chat_pathDetailsNotAvailable),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.chat_pathDetailsNotAvailable),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -159,11 +159,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
);
|
||||
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.chat_hopsCount(result.length)),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.chat_hopsCount(result.length)),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -337,13 +336,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
_showFullPathDialog(context, path.pathBytes),
|
||||
onTap: () async {
|
||||
if (path.pathBytes.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
l10n.chat_pathDetailsNotAvailable,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
l10n.chat_pathDetailsNotAvailable,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -361,13 +359,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
l10n.path_usingHopsPath(path.hopCount),
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
l10n.path_usingHopsPath(path.hopCount),
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -459,11 +456,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
onTap: () async {
|
||||
await connector.clearContactPath(currentContact);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.chat_pathCleared),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.chat_pathCleared),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
|
|
@ -489,11 +485,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
|
|||
pathLen: -1,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.chat_floodModeEnabled),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.chat_floodModeEnabled),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
|
||||
class PathSelectionDialog extends StatefulWidget {
|
||||
final List<Contact> availableContacts;
|
||||
|
|
@ -138,26 +139,22 @@ class _PathSelectionDialogState extends State<PathSelectionDialog> {
|
|||
|
||||
// Show error for invalid prefixes
|
||||
if (invalidPrefixes.isNotEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
l10n.path_invalidHexPrefixes(invalidPrefixes.join(", ")),
|
||||
),
|
||||
duration: const Duration(seconds: 3),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.path_invalidHexPrefixes(invalidPrefixes.join(", "))),
|
||||
duration: const Duration(seconds: 3),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check max path length (64 hops)
|
||||
if (pathBytesList.length > 64) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.path_tooLong),
|
||||
duration: const Duration(seconds: 3),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(l10n.path_tooLong),
|
||||
duration: const Duration(seconds: 3),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../services/storage_service.dart';
|
|||
import '../connector/meshcore_connector.dart';
|
||||
import '../connector/meshcore_protocol.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'path_management_dialog.dart';
|
||||
|
||||
class RoomLoginDialog extends StatefulWidget {
|
||||
|
|
@ -175,11 +176,10 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
|
|||
setState(() {
|
||||
_isLoggingIn = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.login_failed(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.login_failed(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue