Merge pull request #365 from zjs81/rpt-guest

enh: make repeater admin guest aware
This commit is contained in:
zjs81
2026-04-14 20:44:14 -07:00
committed by GitHub
26 changed files with 403 additions and 134 deletions
+5 -2
View File
@@ -1038,8 +1038,8 @@
"login_enterPassword": "Enter password", "login_enterPassword": "Enter password",
"login_savePassword": "Save password", "login_savePassword": "Save password",
"login_savePasswordSubtitle": "Password will be stored securely on this device", "login_savePasswordSubtitle": "Password will be stored securely on this device",
"login_repeaterDescription": "Enter the repeater password to access settings and status.", "login_repeaterDescription": "Enter the repeater password for guest or admin access.",
"login_roomDescription": "Enter the room password to access settings and status.", "login_roomDescription": "Enter the room password for guest or admin access.",
"login_routing": "Routing", "login_routing": "Routing",
"login_routingMode": "Routing mode", "login_routingMode": "Routing mode",
"login_autoUseSavedPath": "Auto (use saved path)", "login_autoUseSavedPath": "Auto (use saved path)",
@@ -1105,7 +1105,10 @@
"path_setPath": "Set Path", "path_setPath": "Set Path",
"repeater_management": "Repeater Management", "repeater_management": "Repeater Management",
"room_management": "Room Server Management", "room_management": "Room Server Management",
"repeater_guest": "Repeater Information",
"room_guest": "Room Server Information",
"repeater_managementTools": "Management Tools", "repeater_managementTools": "Management Tools",
"repeater_guestTools": "Guest Tools",
"repeater_status": "Status", "repeater_status": "Status",
"repeater_statusSubtitle": "View repeater status, stats, and neighbors", "repeater_statusSubtitle": "View repeater status, stats, and neighbors",
"repeater_telemetry": "Telemetry", "repeater_telemetry": "Telemetry",
+20 -2
View File
@@ -3438,13 +3438,13 @@ abstract class AppLocalizations {
/// No description provided for @login_repeaterDescription. /// No description provided for @login_repeaterDescription.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Enter the repeater password to access settings and status.'** /// **'Enter the repeater password for guest or admin access.'**
String get login_repeaterDescription; String get login_repeaterDescription;
/// No description provided for @login_roomDescription. /// No description provided for @login_roomDescription.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Enter the room password to access settings and status.'** /// **'Enter the room password for guest or admin access.'**
String get login_roomDescription; String get login_roomDescription;
/// No description provided for @login_routing. /// No description provided for @login_routing.
@@ -3609,12 +3609,30 @@ abstract class AppLocalizations {
/// **'Room Server Management'** /// **'Room Server Management'**
String get room_management; String get room_management;
/// No description provided for @repeater_guest.
///
/// In en, this message translates to:
/// **'Repeater Information'**
String get repeater_guest;
/// No description provided for @room_guest.
///
/// In en, this message translates to:
/// **'Room Server Information'**
String get room_guest;
/// No description provided for @repeater_managementTools. /// No description provided for @repeater_managementTools.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Management Tools'** /// **'Management Tools'**
String get repeater_managementTools; String get repeater_managementTools;
/// No description provided for @repeater_guestTools.
///
/// In en, this message translates to:
/// **'Guest Tools'**
String get repeater_guestTools;
/// No description provided for @repeater_status. /// No description provided for @repeater_status.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
+9
View File
@@ -2019,9 +2019,18 @@ class AppLocalizationsBg extends AppLocalizations {
@override @override
String get room_management => 'Управление на сървъра за стая'; String get room_management => 'Управление на сървъра за стая';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Инструменти за управление'; String get repeater_managementTools => 'Инструменти за управление';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Статус'; String get repeater_status => 'Статус';
+9
View File
@@ -2017,9 +2017,18 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get room_management => 'Raum-Server-Verwaltung'; String get room_management => 'Raum-Server-Verwaltung';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Verwaltungs-Tools'; String get repeater_managementTools => 'Verwaltungs-Tools';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+11 -2
View File
@@ -1871,11 +1871,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get login_repeaterDescription => String get login_repeaterDescription =>
'Enter the repeater password to access settings and status.'; 'Enter the repeater password for guest or admin access.';
@override @override
String get login_roomDescription => String get login_roomDescription =>
'Enter the room password to access settings and status.'; 'Enter the room password for guest or admin access.';
@override @override
String get login_routing => 'Routing'; String get login_routing => 'Routing';
@@ -1979,9 +1979,18 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get room_management => 'Room Server Management'; String get room_management => 'Room Server Management';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Management Tools'; String get repeater_managementTools => 'Management Tools';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2015,9 +2015,18 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get room_management => 'Administración del Servidor de Habitación'; String get room_management => 'Administración del Servidor de Habitación';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Herramientas de Gestión'; String get repeater_managementTools => 'Herramientas de Gestión';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Estado'; String get repeater_status => 'Estado';
+9
View File
@@ -2026,9 +2026,18 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get room_management => 'Administrattion Room Server'; String get room_management => 'Administrattion Room Server';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Outils de Gestion'; String get repeater_managementTools => 'Outils de Gestion';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'État'; String get repeater_status => 'État';
+9
View File
@@ -2030,9 +2030,18 @@ class AppLocalizationsHu extends AppLocalizations {
@override @override
String get room_management => 'Szoba-szerver kezelés'; String get room_management => 'Szoba-szerver kezelés';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Menedzsmentes eszközök'; String get repeater_managementTools => 'Menedzsmentes eszközök';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Állapot'; String get repeater_status => 'Állapot';
+9
View File
@@ -2016,9 +2016,18 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get room_management => 'Gestione del Server di Camera'; String get room_management => 'Gestione del Server di Camera';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Strumenti di Gestione'; String get repeater_managementTools => 'Strumenti di Gestione';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Stato'; String get repeater_status => 'Stato';
+9
View File
@@ -1932,9 +1932,18 @@ class AppLocalizationsJa extends AppLocalizations {
@override @override
String get room_management => 'ルームサーバーの管理'; String get room_management => 'ルームサーバーの管理';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => '管理ツール'; String get repeater_managementTools => '管理ツール';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'ステータス'; String get repeater_status => 'ステータス';
+9
View File
@@ -1929,9 +1929,18 @@ class AppLocalizationsKo extends AppLocalizations {
@override @override
String get room_management => '방 서버 관리'; String get room_management => '방 서버 관리';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => '관리 도구'; String get repeater_managementTools => '관리 도구';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => '상태'; String get repeater_status => '상태';
+9
View File
@@ -2003,9 +2003,18 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get room_management => 'Beheer Server Kamer'; String get room_management => 'Beheer Server Kamer';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Beheerfuncties'; String get repeater_managementTools => 'Beheerfuncties';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2031,9 +2031,18 @@ class AppLocalizationsPl extends AppLocalizations {
@override @override
String get room_management => 'Zarządzanie Serwerem Pokoju'; String get room_management => 'Zarządzanie Serwerem Pokoju';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Narzędzia Zarządzania'; String get repeater_managementTools => 'Narzędzia Zarządzania';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2015,9 +2015,18 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get room_management => 'Gerenciamento de Servidor de Sala'; String get room_management => 'Gerenciamento de Servidor de Sala';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Ferramentas de Gerenciamento'; String get repeater_managementTools => 'Ferramentas de Gerenciamento';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2019,9 +2019,18 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get room_management => 'Управление сервером комнат'; String get room_management => 'Управление сервером комнат';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Инструменты управления'; String get repeater_managementTools => 'Инструменты управления';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Статус'; String get repeater_status => 'Статус';
+9
View File
@@ -2004,9 +2004,18 @@ class AppLocalizationsSk extends AppLocalizations {
@override @override
String get room_management => 'Správa servera miestnosti'; String get room_management => 'Správa servera miestnosti';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Nástroje na správu'; String get repeater_managementTools => 'Nástroje na správu';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2001,9 +2001,18 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get room_management => 'Upravljanje stremlišča'; String get room_management => 'Upravljanje stremlišča';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Upravne orodje'; String get repeater_managementTools => 'Upravne orodje';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -1990,9 +1990,18 @@ class AppLocalizationsSv extends AppLocalizations {
@override @override
String get room_management => 'Rumserverhantering'; String get room_management => 'Rumserverhantering';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Administrationsverktyg'; String get repeater_managementTools => 'Administrationsverktyg';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Status'; String get repeater_status => 'Status';
+9
View File
@@ -2014,9 +2014,18 @@ class AppLocalizationsUk extends AppLocalizations {
@override @override
String get room_management => 'Адміністрування сервера кімнати'; String get room_management => 'Адміністрування сервера кімнати';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => 'Інструменти керування'; String get repeater_managementTools => 'Інструменти керування';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => 'Статус'; String get repeater_status => 'Статус';
+9
View File
@@ -1890,9 +1890,18 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get room_management => '房间服务器管理'; String get room_management => '房间服务器管理';
@override
String get repeater_guest => 'Repeater Information';
@override
String get room_guest => 'Room Server Information';
@override @override
String get repeater_managementTools => '管理工具'; String get repeater_managementTools => '管理工具';
@override
String get repeater_guestTools => 'Guest Tools';
@override @override
String get repeater_status => '状态'; String get repeater_status => '状态';
+12 -5
View File
@@ -961,13 +961,16 @@ class _ContactsScreenState extends State<ContactsScreen>
context: context, context: context,
builder: (context) => RepeaterLoginDialog( builder: (context) => RepeaterLoginDialog(
repeater: repeater, repeater: repeater,
onLogin: (password) { onLogin: (password, isAdmin) {
// Navigate to repeater hub screen after successful login // Navigate to repeater hub screen after successful login
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => RepeaterHubScreen(
RepeaterHubScreen(repeater: repeater, password: password), repeater: repeater,
password: password,
isAdmin: isAdmin,
),
), ),
); );
}, },
@@ -984,14 +987,18 @@ class _ContactsScreenState extends State<ContactsScreen>
context: context, context: context,
builder: (context) => RoomLoginDialog( builder: (context) => RoomLoginDialog(
room: room, room: room,
onLogin: (password) { onLogin: (password, isAdmin) {
context.read<MeshCoreConnector>().markContactRead(room.publicKeyHex); context.read<MeshCoreConnector>().markContactRead(room.publicKeyHex);
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
destination == RoomLoginDestination.management destination == RoomLoginDestination.management
? RepeaterHubScreen(repeater: room, password: password) ? RepeaterHubScreen(
repeater: room,
password: password,
isAdmin: isAdmin,
)
: ChatScreen(contact: room), : ChatScreen(contact: room),
), ),
); );
+8 -4
View File
@@ -1367,13 +1367,16 @@ class _MapScreenState extends State<MapScreen> {
context: context, context: context,
builder: (context) => RepeaterLoginDialog( builder: (context) => RepeaterLoginDialog(
repeater: repeater, repeater: repeater,
onLogin: (password) { onLogin: (password, isAdmin) {
// Navigate to repeater hub screen after successful login // Navigate to repeater hub screen after successful login
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => RepeaterHubScreen(
RepeaterHubScreen(repeater: repeater, password: password), repeater: repeater,
password: password,
isAdmin: isAdmin,
),
), ),
); );
}, },
@@ -1386,7 +1389,8 @@ class _MapScreenState extends State<MapScreen> {
context: context, context: context,
builder: (context) => RoomLoginDialog( builder: (context) => RoomLoginDialog(
room: room, room: room,
onLogin: (password) { // onLogin(password, isAdmin) isAdmin not used for room caht screen
onLogin: (password, _) {
// Navigate to chat screen after successful login // Navigate to chat screen after successful login
context.read<MeshCoreConnector>().markContactRead(room.publicKeyHex); context.read<MeshCoreConnector>().markContactRead(room.publicKeyHex);
Navigator.push( Navigator.push(
+105 -91
View File
@@ -13,11 +13,13 @@ import 'neighbors_screen.dart';
class RepeaterHubScreen extends StatelessWidget { class RepeaterHubScreen extends StatelessWidget {
final Contact repeater; final Contact repeater;
final String password; final String password;
final bool isAdmin;
const RepeaterHubScreen({ const RepeaterHubScreen({
super.key, super.key,
required this.repeater, required this.repeater,
required this.password, required this.password,
required this.isAdmin,
}); });
@override @override
@@ -33,11 +35,18 @@ class RepeaterHubScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( if (isAdmin)
repeater.type == advTypeRepeater Text(
? l10n.repeater_management repeater.type == advTypeRepeater
: l10n.room_management, ? l10n.repeater_management
), : l10n.room_management,
),
if (!isAdmin)
Text(
repeater.type == advTypeRepeater
? l10n.repeater_guest
: l10n.room_guest,
),
Text( Text(
repeater.name, repeater.name,
style: const TextStyle( style: const TextStyle(
@@ -113,64 +122,67 @@ class RepeaterHubScreen extends StatelessWidget {
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Card( if (isAdmin)
child: Padding( Card(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 12), child: Padding(
child: Column( padding: const EdgeInsets.fromLTRB(16, 16, 16, 12),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Row( children: [
children: [ Row(
const Icon(Icons.battery_full), children: [
const SizedBox(width: 10), const Icon(Icons.battery_full),
Expanded( const SizedBox(width: 10),
child: Text( Expanded(
l10n.appSettings_batteryChemistry, child: Text(
style: const TextStyle( l10n.appSettings_batteryChemistry,
fontSize: 16, style: const TextStyle(
fontWeight: FontWeight.w600, fontSize: 16,
fontWeight: FontWeight.w600,
),
), ),
), ),
), ],
],
),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
initialValue: chemistry,
isExpanded: true,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
isDense: true,
), ),
onChanged: (value) { const SizedBox(height: 12),
if (value == null) return; DropdownButtonFormField<String>(
settingsService.setBatteryChemistryForRepeater( initialValue: chemistry,
repeater.publicKeyHex, isExpanded: true,
value, decoration: const InputDecoration(
); border: UnderlineInputBorder(),
}, isDense: true,
items: [
DropdownMenuItem(
value: 'nmc',
child: Text(l10n.appSettings_batteryNmc),
), ),
DropdownMenuItem( onChanged: (value) {
value: 'lifepo4', if (value == null) return;
child: Text(l10n.appSettings_batteryLifepo4), settingsService.setBatteryChemistryForRepeater(
), repeater.publicKeyHex,
DropdownMenuItem( value,
value: 'lipo', );
child: Text(l10n.appSettings_batteryLipo), },
), items: [
], DropdownMenuItem(
), value: 'nmc',
], child: Text(l10n.appSettings_batteryNmc),
),
DropdownMenuItem(
value: 'lifepo4',
child: Text(l10n.appSettings_batteryLifepo4),
),
DropdownMenuItem(
value: 'lipo',
child: Text(l10n.appSettings_batteryLipo),
),
],
),
],
),
), ),
), ),
),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
l10n.repeater_managementTools, isAdmin
? l10n.repeater_managementTools
: l10n.repeater_guestTools,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -210,26 +222,27 @@ class RepeaterHubScreen extends StatelessWidget {
); );
}, },
), ),
const SizedBox(height: 12), if (isAdmin) const SizedBox(height: 12),
// CLI button // CLI button
_buildManagementCard( if (isAdmin)
context, _buildManagementCard(
icon: Icons.terminal, context,
title: l10n.repeater_cli, icon: Icons.terminal,
subtitle: l10n.repeater_cliSubtitle, title: l10n.repeater_cli,
color: Colors.green, subtitle: l10n.repeater_cliSubtitle,
onTap: () { color: Colors.green,
Navigator.push( onTap: () {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => RepeaterCliScreen( MaterialPageRoute(
repeater: repeater, builder: (context) => RepeaterCliScreen(
password: password, repeater: repeater,
password: password,
),
), ),
), );
); },
}, ),
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Neighbors button // Neighbors button
_buildManagementCard( _buildManagementCard(
@@ -248,26 +261,27 @@ class RepeaterHubScreen extends StatelessWidget {
); );
}, },
), ),
const SizedBox(height: 12), if (isAdmin) const SizedBox(height: 12),
// Settings button // Settings button
_buildManagementCard( if (isAdmin)
context, _buildManagementCard(
icon: Icons.settings, context,
title: l10n.repeater_settings, icon: Icons.settings,
subtitle: l10n.repeater_settingsSubtitle, title: l10n.repeater_settings,
color: Colors.deepOrange, subtitle: l10n.repeater_settingsSubtitle,
onTap: () { color: Colors.deepOrange,
Navigator.push( onTap: () {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => RepeaterSettingsScreen( MaterialPageRoute(
repeater: repeater, builder: (context) => RepeaterSettingsScreen(
password: password, repeater: repeater,
password: password,
),
), ),
), );
); },
}, ),
),
], ],
), ),
), ),
+11 -6
View File
@@ -14,7 +14,7 @@ import 'path_management_dialog.dart';
class RepeaterLoginDialog extends StatefulWidget { class RepeaterLoginDialog extends StatefulWidget {
final Contact repeater; final Contact repeater;
final Function(String password) onLogin; final Function(String password, bool isAdmin) onLogin;
const RepeaterLoginDialog({ const RepeaterLoginDialog({
super.key, super.key,
@@ -119,6 +119,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
: '${selection.hopCount} hops'; : '${selection.hopCount} hops';
appLogger.info('Login routing: $selectionLabel', tag: 'RepeaterLogin'); appLogger.info('Login routing: $selectionLabel', tag: 'RepeaterLogin');
bool? loginResult; bool? loginResult;
bool isAdmin = false;
for (int attempt = 0; attempt < _maxAttempts; attempt++) { for (int attempt = 0; attempt < _maxAttempts; attempt++) {
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
@@ -131,7 +132,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
); );
await _connector.sendFrame(loginFrame); await _connector.sendFrame(loginFrame);
loginResult = await _awaitLoginResponse(timeout); (loginResult, isAdmin) = await _awaitLoginResponse(timeout);
if (loginResult == true) { if (loginResult == true) {
appLogger.info( appLogger.info(
'Login succeeded for ${repeater.name}', 'Login succeeded for ${repeater.name}',
@@ -212,7 +213,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
if (mounted) { if (mounted) {
Navigator.pop(context, password); Navigator.pop(context, password);
Future.microtask(() => widget.onLogin(password)); Future.microtask(() => widget.onLogin(password, isAdmin));
} }
} catch (e) { } catch (e) {
final repeater = _resolveRepeater(_connector); final repeater = _resolveRepeater(_connector);
@@ -229,17 +230,21 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
} }
} }
Future<bool?> _awaitLoginResponse(Duration timeout) async { // _awaitLoginResponse returns a record of bool, for success and if the client is an admin
Future<(bool?, bool)> _awaitLoginResponse(Duration timeout) async {
final completer = Completer<bool?>(); final completer = Completer<bool?>();
Timer? timer; Timer? timer;
StreamSubscription<Uint8List>? subscription; StreamSubscription<Uint8List>? subscription;
final targetPrefix = widget.repeater.publicKey.sublist(0, 6); final targetPrefix = widget.repeater.publicKey.sublist(0, 6);
bool isAdmin = false;
subscription = _connector.receivedFrames.listen((frame) { subscription = _connector.receivedFrames.listen((frame) {
if (frame.isEmpty) return; if (frame.isEmpty) return;
final code = frame[0]; final code = frame[0];
if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return; if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return;
if (frame.length < 8) return; if (frame.length < 8) return;
// NOTE: a bug in the repeater firmware only ever sends 1 or 0 back, not the
// expected client permissions
isAdmin = (frame[1] == 1);
final prefix = frame.sublist(2, 8); final prefix = frame.sublist(2, 8);
if (!listEquals(prefix, targetPrefix)) return; if (!listEquals(prefix, targetPrefix)) return;
@@ -258,7 +263,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
final result = await completer.future; final result = await completer.future;
timer.cancel(); timer.cancel();
await subscription.cancel(); await subscription.cancel();
return result; return (result, isAdmin);
} }
@override @override
+10 -5
View File
@@ -15,7 +15,7 @@ import 'path_management_dialog.dart';
class RoomLoginDialog extends StatefulWidget { class RoomLoginDialog extends StatefulWidget {
final Contact room; final Contact room;
final Function(String password) onLogin; final Function(String password, bool isAdmin) onLogin;
const RoomLoginDialog({super.key, required this.room, required this.onLogin}); const RoomLoginDialog({super.key, required this.room, required this.onLogin});
@@ -115,6 +115,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
: '${selection.hopCount} hops'; : '${selection.hopCount} hops';
appLogger.info('Login routing: $selectionLabel', tag: 'RoomLogin'); appLogger.info('Login routing: $selectionLabel', tag: 'RoomLogin');
bool? loginResult; bool? loginResult;
bool isAdmin = false;
for (int attempt = 0; attempt < _maxAttempts; attempt++) { for (int attempt = 0; attempt < _maxAttempts; attempt++) {
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
@@ -127,7 +128,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
); );
await _connector.sendFrame(loginFrame); await _connector.sendFrame(loginFrame);
loginResult = await _awaitLoginResponse(timeout); (loginResult, isAdmin) = await _awaitLoginResponse(timeout);
if (loginResult == true) { if (loginResult == true) {
appLogger.info('Login succeeded for ${room.name}', tag: 'RoomLogin'); appLogger.info('Login succeeded for ${room.name}', tag: 'RoomLogin');
break; break;
@@ -167,7 +168,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
if (mounted) { if (mounted) {
Navigator.pop(context, password); Navigator.pop(context, password);
Future.microtask(() => widget.onLogin(password)); Future.microtask(() => widget.onLogin(password, isAdmin));
} }
} catch (e) { } catch (e) {
final room = _resolveRepeater(_connector); final room = _resolveRepeater(_connector);
@@ -185,16 +186,20 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
} }
} }
Future<bool?> _awaitLoginResponse(Duration timeout) async { Future<(bool?, bool)> _awaitLoginResponse(Duration timeout) async {
final completer = Completer<bool?>(); final completer = Completer<bool?>();
Timer? timer; Timer? timer;
StreamSubscription<Uint8List>? subscription; StreamSubscription<Uint8List>? subscription;
final targetPrefix = widget.room.publicKey.sublist(0, 6); final targetPrefix = widget.room.publicKey.sublist(0, 6);
bool isAdmin = false;
subscription = _connector.receivedFrames.listen((frame) { subscription = _connector.receivedFrames.listen((frame) {
if (frame.isEmpty) return; if (frame.isEmpty) return;
final code = frame[0]; final code = frame[0];
if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return; if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return;
// NOTE: a bug in the repeater firmware only ever sends 1 or 0 back, not the
// expected client permissions
isAdmin = (frame[1] == 1);
if (frame.length < 8) return; if (frame.length < 8) return;
final prefix = frame.sublist(2, 8); final prefix = frame.sublist(2, 8);
if (!listEquals(prefix, targetPrefix)) return; if (!listEquals(prefix, targetPrefix)) return;
@@ -214,7 +219,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
final result = await completer.future; final result = await completer.future;
timer.cancel(); timer.cancel();
await subscription.cancel(); await subscription.cancel();
return result; return (result, isAdmin);
} }
@override @override
+68 -17
View File
@@ -1,69 +1,120 @@
{ {
"bg": [ "bg": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"de": [ "de": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"es": [ "es": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"fr": [ "fr": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"hu": [ "hu": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"it": [ "it": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"ja": [ "ja": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"ko": [ "ko": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"nl": [ "nl": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"pl": [ "pl": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"pt": [ "pt": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"ru": [ "ru": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"sk": [ "sk": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"sl": [ "sl": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"sv": [ "sv": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"uk": [ "uk": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
], ],
"zh": [ "zh": [
"chat_sendMessage" "chat_sendMessage",
"repeater_guest",
"room_guest",
"repeater_guestTools"
] ]
} }