meshcore-open/lib/main.dart
zach 02ca7801ea 🔄 Changes
Core Features
Unread Message Tracking: Added persistent unread counts for contacts and channels with visual badges
Message Deletion: Users can now long-press to delete individual messages in chats and channels
SMAZ Compression: Added per-contact compression settings (previously only channels)
UTF-8 Length Limiting: Text inputs now enforce protocol byte limits correctly
Channel Message Paths: New screen to visualize packet routing through repeater network with map view
Protocol Updates
Added maxContactMessageBytes() and maxChannelMessageBytes() helpers for message length validation
Changed channel PSK format from Base64 to Hexadecimal (breaking change)
Added app version field to connection handshake frame
UI Improvements
Unread badges on all contact and channel list items
Enhanced message bubbles with path visualization for channel messages
Character count displays in message input fields
Improved repeater CLI screen functionality
New Files
lib/storage/unread_store.dart - Unread tracking persistence
lib/storage/contact_settings_store.dart - Per-contact SMAZ settings
lib/widgets/unread_badge.dart - Unread count indicator
lib/helpers/utf8_length_limiter.dart - Byte-aware text input formatter
lib/screens/channel_message_path_screen.dart - Packet path visualization
2025-12-26 13:33:03 -07:00

119 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'connector/meshcore_connector.dart';
import 'screens/scanner_screen.dart';
import 'services/storage_service.dart';
import 'services/message_retry_service.dart';
import 'services/path_history_service.dart';
import 'services/app_settings_service.dart';
import 'services/notification_service.dart';
import 'services/ble_debug_log_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize services
final storage = StorageService();
final connector = MeshCoreConnector();
final pathHistoryService = PathHistoryService(storage);
final retryService = MessageRetryService(storage);
final appSettingsService = AppSettingsService();
final bleDebugLogService = BleDebugLogService();
// Load settings
await appSettingsService.loadSettings();
// Initialize notification service
final notificationService = NotificationService();
await notificationService.initialize();
// Wire up connector with services
connector.initialize(
retryService: retryService,
pathHistoryService: pathHistoryService,
appSettingsService: appSettingsService,
bleDebugLogService: bleDebugLogService,
);
await connector.loadContactCache();
await connector.loadChannelSettings();
// Load persisted channel messages
await connector.loadAllChannelMessages();
await connector.loadUnreadState();
runApp(MeshCoreApp(
connector: connector,
retryService: retryService,
pathHistoryService: pathHistoryService,
storage: storage,
appSettingsService: appSettingsService,
bleDebugLogService: bleDebugLogService,
));
}
class MeshCoreApp extends StatelessWidget {
final MeshCoreConnector connector;
final MessageRetryService retryService;
final PathHistoryService pathHistoryService;
final StorageService storage;
final AppSettingsService appSettingsService;
final BleDebugLogService bleDebugLogService;
const MeshCoreApp({
super.key,
required this.connector,
required this.retryService,
required this.pathHistoryService,
required this.storage,
required this.appSettingsService,
required this.bleDebugLogService,
});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: connector),
ChangeNotifierProvider.value(value: retryService),
ChangeNotifierProvider.value(value: pathHistoryService),
ChangeNotifierProvider.value(value: appSettingsService),
ChangeNotifierProvider.value(value: bleDebugLogService),
Provider.value(value: storage),
],
child: Consumer<AppSettingsService>(
builder: (context, settingsService, child) {
return MaterialApp(
title: 'MeshCore Open',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
useMaterial3: true,
),
themeMode: _themeModeFromSetting(settingsService.settings.themeMode),
home: const ScannerScreen(),
);
},
),
);
}
ThemeMode _themeModeFromSetting(String value) {
switch (value) {
case 'light':
return ThemeMode.light;
case 'dark':
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
}