feat: Update project version to 19, add repeaters filter and theme support, and enhance contact list display

This commit is contained in:
Janez T
2025-10-16 21:43:19 +02:00
parent 58d8ef0dae
commit e74ebda54d
15 changed files with 312 additions and 95 deletions

View File

@@ -489,7 +489,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -529,7 +529,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -545,7 +545,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -676,7 +676,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -699,7 +699,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 18; CURRENT_PROJECT_VERSION = 19;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -21,7 +21,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>18</string> <string>19</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>

View File

@@ -5,17 +5,22 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000203"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.000227">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.287931"> <testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.240778">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: build_app" time="31.633362"> <testcase classname="fastlane.lanes" name="2: build_app" time="91.431808">
</testcase>
<testcase classname="fastlane.lanes" name="3: upload_to_testflight" time="328.922766">
</testcase> </testcase>

View File

@@ -1097,6 +1097,11 @@
"description": "Filter option for contacts" "description": "Filter option for contacts"
}, },
"repeatersFilter": "Repeaters",
"@repeatersFilter": {
"description": "Filter option for repeaters"
},
"sarMarkers": "SAR Markers", "sarMarkers": "SAR Markers",
"@sarMarkers": { "@sarMarkers": {
"description": "SAR markers section header" "description": "SAR markers section header"

View File

@@ -371,6 +371,8 @@
"contactsFilter": "Kontakti", "contactsFilter": "Kontakti",
"repeatersFilter": "Repetitori",
"sarMarkers": "SAR markeri", "sarMarkers": "SAR markeri",
"foundPerson": "Pronađena osoba", "foundPerson": "Pronađena osoba",

View File

@@ -1215,6 +1215,12 @@ abstract class AppLocalizations {
/// **'Contacts'** /// **'Contacts'**
String get contactsFilter; String get contactsFilter;
/// Filter option for repeaters
///
/// In en, this message translates to:
/// **'Repeaters'**
String get repeatersFilter;
/// SAR markers section header /// SAR markers section header
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -643,6 +643,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get contactsFilter => 'Contacts'; String get contactsFilter => 'Contacts';
@override
String get repeatersFilter => 'Repeaters';
@override @override
String get sarMarkers => 'SAR Markers'; String get sarMarkers => 'SAR Markers';

View File

@@ -643,6 +643,9 @@ class AppLocalizationsHr extends AppLocalizations {
@override @override
String get contactsFilter => 'Kontakti'; String get contactsFilter => 'Kontakti';
@override
String get repeatersFilter => 'Repetitori';
@override @override
String get sarMarkers => 'SAR markeri'; String get sarMarkers => 'SAR markeri';

View File

@@ -643,6 +643,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get contactsFilter => 'Stiki'; String get contactsFilter => 'Stiki';
@override
String get repeatersFilter => 'Ponavljalniki';
@override @override
String get sarMarkers => 'SAR označevalci'; String get sarMarkers => 'SAR označevalci';

View File

@@ -371,6 +371,8 @@
"contactsFilter": "Stiki", "contactsFilter": "Stiki",
"repeatersFilter": "Ponavljalniki",
"sarMarkers": "SAR označevalci", "sarMarkers": "SAR označevalci",
"foundPerson": "Najdena oseba", "foundPerson": "Najdena oseba",

View File

@@ -772,6 +772,30 @@ class _SettingsScreenState extends State<SettingsScreen> {
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
RadioListTile<AppThemeMode>(
title: Row(
children: [
const Text('SAR Navy Blue'),
const SizedBox(width: 8),
Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: const Color(0xFF5C9FFF),
shape: BoxShape.circle,
border: Border.all(color: Colors.black26),
),
),
],
),
subtitle: const Text('Professional/Operations Mode'),
value: AppThemeMode.sarNavyBlue,
groupValue: _selectedTheme,
onChanged: (value) {
_handleThemeChange(value);
Navigator.pop(context);
},
),
const Divider(), const Divider(),
RadioListTile<AppThemeMode>( RadioListTile<AppThemeMode>(
title: Text(AppLocalizations.of(context)!.autoSystem), title: Text(AppLocalizations.of(context)!.autoSystem),

View File

@@ -5,6 +5,7 @@ enum AppThemeMode {
dark, dark,
sarRed, sarRed,
sarGreen, sarGreen,
sarNavyBlue,
system, system,
} }
@@ -181,6 +182,65 @@ class AppTheme {
); );
} }
// SAR Navy Blue theme (Professional/Operations tones)
static ThemeData get sarNavyBlueTheme {
return ThemeData(
useMaterial3: true,
colorScheme: const ColorScheme.dark(
brightness: Brightness.dark,
primary: Color(0xFF5C9FFF), // Bright navy blue
onPrimary: Color(0xFF000000),
primaryContainer: Color(0xFF003366), // Dark navy
onPrimaryContainer: Color(0xFFBBDEFF),
secondary: Color(0xFF80B3FF),
onSecondary: Color(0xFF000000),
secondaryContainer: Color(0xFF002244),
onSecondaryContainer: Color(0xFFBBDEFF),
tertiary: Color(0xFF4DB8FF),
onTertiary: Color(0xFF000000),
error: Color(0xFFCF6679),
onError: Color(0xFF000000),
surface: Color(0xFF00111C), // Very dark blue-tinted
onSurface: Color(0xFFE3F2FD),
surfaceContainerHighest: Color(0xFF001A2D),
onSurfaceVariant: Color(0xFFBBDEFF),
outline: Color(0xFF5C9FFF),
),
scaffoldBackgroundColor: const Color(0xFF00111C),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
backgroundColor: Color(0xFF001A2D),
foregroundColor: Color(0xFFE3F2FD),
),
cardTheme: CardThemeData(
elevation: 2,
color: const Color(0xFF001A2D),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(
color: Color(0xFF5C9FFF),
width: 1,
),
),
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF5C9FFF)),
),
filled: true,
fillColor: const Color(0xFF001A2D),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF5C9FFF),
foregroundColor: const Color(0xFF000000),
),
),
);
}
// Get theme by mode // Get theme by mode
static ThemeData getTheme(AppThemeMode mode, Brightness systemBrightness) { static ThemeData getTheme(AppThemeMode mode, Brightness systemBrightness) {
switch (mode) { switch (mode) {
@@ -192,6 +252,8 @@ class AppTheme {
return sarRedTheme; return sarRedTheme;
case AppThemeMode.sarGreen: case AppThemeMode.sarGreen:
return sarGreenTheme; return sarGreenTheme;
case AppThemeMode.sarNavyBlue:
return sarNavyBlueTheme;
case AppThemeMode.system: case AppThemeMode.system:
return systemBrightness == Brightness.dark ? darkTheme : lightTheme; return systemBrightness == Brightness.dark ? darkTheme : lightTheme;
} }
@@ -208,6 +270,8 @@ class AppTheme {
return 'SAR Red (Alert)'; return 'SAR Red (Alert)';
case AppThemeMode.sarGreen: case AppThemeMode.sarGreen:
return 'SAR Green (Safe)'; return 'SAR Green (Safe)';
case AppThemeMode.sarNavyBlue:
return 'SAR Navy Blue (Ops)';
case AppThemeMode.system: case AppThemeMode.system:
return 'Auto (System)'; return 'Auto (System)';
} }

View File

@@ -6,11 +6,14 @@ import '../../../models/contact.dart';
/// Contact list section for the compass dialog. /// Contact list section for the compass dialog.
/// Shows all contacts with location sorted by distance with bearing information. /// Shows all contacts with location sorted by distance with bearing information.
/// Splits contacts by type: Persons/Team, Repeaters, and Rooms.
class CompassContactList extends StatelessWidget { class CompassContactList extends StatelessWidget {
final List<Contact> contacts; final List<Contact> contacts;
final Position? position; final Position? position;
final double? heading; final double? heading;
final Contact? selectedContact; final Contact? selectedContact;
final bool showContacts;
final bool showRepeaters;
final ValueChanged<Contact?> onContactTap; final ValueChanged<Contact?> onContactTap;
const CompassContactList({ const CompassContactList({
@@ -19,6 +22,8 @@ class CompassContactList extends StatelessWidget {
required this.position, required this.position,
this.heading, this.heading,
required this.selectedContact, required this.selectedContact,
required this.showContacts,
required this.showRepeaters,
required this.onContactTap, required this.onContactTap,
}); });
@@ -32,9 +37,16 @@ class CompassContactList extends StatelessWidget {
return Text(AppLocalizations.of(context)!.locationUnavailable); return Text(AppLocalizations.of(context)!.locationUnavailable);
} }
// Calculate bearings and distances final l10n = AppLocalizations.of(context)!;
final contactsWithBearing = contacts.map((contact) {
if (contact.displayLocation == null) return null; // Split contacts by type
final persons = <Map<String, dynamic>>[];
final repeaters = <Map<String, dynamic>>[];
final rooms = <Map<String, dynamic>>[];
// Calculate bearings and distances for each contact
for (final contact in contacts) {
if (contact.displayLocation == null) continue;
final bearing = _calculateBearing( final bearing = _calculateBearing(
position!.latitude, position!.latitude,
@@ -50,101 +62,160 @@ class CompassContactList extends StatelessWidget {
contact.displayLocation!.longitude, contact.displayLocation!.longitude,
); );
return { final item = {
'contact': contact, 'contact': contact,
'bearing': bearing, 'bearing': bearing,
'distance': distance, 'distance': distance,
}; };
}).whereType<Map<String, dynamic>>().toList();
// Sort by distance if (contact.isRepeater) {
contactsWithBearing.sort((a, b) => repeaters.add(item);
(a['distance'] as double).compareTo(b['distance'] as double)); } else if (contact.isRoom) {
rooms.add(item);
} else {
persons.add(item);
}
}
// Sort each list by distance
persons.sort((a, b) => (a['distance'] as double).compareTo(b['distance'] as double));
repeaters.sort((a, b) => (a['distance'] as double).compareTo(b['distance'] as double));
rooms.sort((a, b) => (a['distance'] as double).compareTo(b['distance'] as double));
final l10n = AppLocalizations.of(context)!;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( // Persons/Team section
padding: const EdgeInsets.only(left: 16, bottom: 8), if (showContacts && persons.isNotEmpty) ...[
child: Text( Padding(
l10n.nearbyContacts, padding: const EdgeInsets.only(left: 16, bottom: 8, top: 4),
style: Theme.of(context).textTheme.titleMedium?.copyWith( child: Text(
fontWeight: FontWeight.bold, l10n.teamMembers,
), style: Theme.of(context).textTheme.titleMedium?.copyWith(
), fontWeight: FontWeight.bold,
),
...contactsWithBearing.map((item) {
final contact = item['contact'] as Contact;
final bearing = item['bearing'] as double;
final distance = item['distance'] as double;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: selectedContact == contact
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
border: selectedContact == contact
? Border.all(
color: Theme.of(context).colorScheme.primary,
width: 2,
)
: null,
),
child: ListTile(
dense: true,
leading: contact.roleEmoji != null
? Text(
contact.roleEmoji!,
style: const TextStyle(fontSize: 24),
)
: Icon(
Icons.person,
color: Theme.of(context).colorScheme.primary,
size: 24,
),
title: Text(contact.displayName),
subtitle: Text(
'${_bearingToCardinal(bearing)}${_formatDistance(distance)}',
style: Theme.of(context).textTheme.bodySmall,
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${bearing.round()}°',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.bold,
),
), ),
if (heading != null)
Text(
_formatRelativeBearing(bearing, heading!, context),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Colors.grey,
),
),
],
),
onTap: () {
if (selectedContact == contact) {
// Deselect if already selected
onContactTap(null);
} else {
// Select this contact
onContactTap(contact);
}
},
), ),
); ),
}), ...persons.map((item) => _buildContactTile(
context,
item,
Icons.groups,
Theme.of(context).colorScheme.primary,
)),
],
// Repeaters section
if (showRepeaters && repeaters.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.only(left: 16, bottom: 8, top: 12),
child: Text(
l10n.repeaters,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
...repeaters.map((item) => _buildContactTile(
context,
item,
Icons.router,
Colors.purple,
)),
],
// Rooms section
if (rooms.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.only(left: 16, bottom: 8, top: 12),
child: Text(
l10n.rooms,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
...rooms.map((item) => _buildContactTile(
context,
item,
Icons.meeting_room,
Colors.teal,
)),
],
], ],
); );
} }
Widget _buildContactTile(
BuildContext context,
Map<String, dynamic> item,
IconData defaultIcon,
Color iconColor,
) {
final contact = item['contact'] as Contact;
final bearing = item['bearing'] as double;
final distance = item['distance'] as double;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: selectedContact == contact
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
border: selectedContact == contact
? Border.all(
color: Theme.of(context).colorScheme.primary,
width: 2,
)
: null,
),
child: ListTile(
dense: true,
leading: contact.roleEmoji != null
? Text(
contact.roleEmoji!,
style: const TextStyle(fontSize: 24),
)
: Icon(
defaultIcon,
color: iconColor,
size: 24,
),
title: Text(contact.displayName),
subtitle: Text(
'${_bearingToCardinal(bearing)}${_formatDistance(distance)}',
style: Theme.of(context).textTheme.bodySmall,
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${bearing.round()}°',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
if (heading != null)
Text(
_formatRelativeBearing(bearing, heading!, context),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Colors.grey,
),
),
],
),
onTap: () {
if (selectedContact == contact) {
// Deselect if already selected
onContactTap(null);
} else {
// Select this contact
onContactTap(contact);
}
},
),
);
}
// Calculate bearing between two points (in degrees) // Calculate bearing between two points (in degrees)
double _calculateBearing( double _calculateBearing(
double lat1, double lon1, double lat2, double lon2) { double lat1, double lon1, double lat2, double lon2) {

View File

@@ -5,10 +5,12 @@ import '../../../l10n/app_localizations.dart';
/// Allows filtering of contacts and SAR marker types. /// Allows filtering of contacts and SAR marker types.
class CompassFilters extends StatefulWidget { class CompassFilters extends StatefulWidget {
final bool showContacts; final bool showContacts;
final bool showRepeaters;
final bool showFoundPerson; final bool showFoundPerson;
final bool showFire; final bool showFire;
final bool showStagingArea; final bool showStagingArea;
final ValueChanged<bool> onShowContactsChanged; final ValueChanged<bool> onShowContactsChanged;
final ValueChanged<bool> onShowRepeatersChanged;
final ValueChanged<bool> onShowFoundPersonChanged; final ValueChanged<bool> onShowFoundPersonChanged;
final ValueChanged<bool> onShowFireChanged; final ValueChanged<bool> onShowFireChanged;
final ValueChanged<bool> onShowStagingAreaChanged; final ValueChanged<bool> onShowStagingAreaChanged;
@@ -17,10 +19,12 @@ class CompassFilters extends StatefulWidget {
const CompassFilters({ const CompassFilters({
super.key, super.key,
required this.showContacts, required this.showContacts,
required this.showRepeaters,
required this.showFoundPerson, required this.showFoundPerson,
required this.showFire, required this.showFire,
required this.showStagingArea, required this.showStagingArea,
required this.onShowContactsChanged, required this.onShowContactsChanged,
required this.onShowRepeatersChanged,
required this.onShowFoundPersonChanged, required this.onShowFoundPersonChanged,
required this.onShowFireChanged, required this.onShowFireChanged,
required this.onShowStagingAreaChanged, required this.onShowStagingAreaChanged,
@@ -62,6 +66,18 @@ class _CompassFiltersState extends State<CompassFilters> {
}, },
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
// Repeaters filter
_CompactFilterItem(
icon: Icons.router,
color: Colors.purple,
label: l10n.repeatersFilter,
value: widget.showRepeaters,
onChanged: (value) {
widget.onShowRepeatersChanged(value);
setDialogState(() {});
},
),
const SizedBox(height: 4),
const Divider(height: 8), const Divider(height: 8),
const SizedBox(height: 4), const SizedBox(height: 4),
// SAR Markers section // SAR Markers section

View File

@@ -65,6 +65,7 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
// Visibility toggles // Visibility toggles
bool _showContacts = true; bool _showContacts = true;
bool _showRepeaters = false; // Hide repeaters by default
bool _showFoundPerson = true; bool _showFoundPerson = true;
bool _showFire = true; bool _showFire = true;
bool _showStagingArea = true; bool _showStagingArea = true;
@@ -308,6 +309,7 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
), ),
CompassFilters( CompassFilters(
showContacts: _showContacts, showContacts: _showContacts,
showRepeaters: _showRepeaters,
showFoundPerson: _showFoundPerson, showFoundPerson: _showFoundPerson,
showFire: _showFire, showFire: _showFire,
showStagingArea: _showStagingArea, showStagingArea: _showStagingArea,
@@ -316,6 +318,11 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
_showContacts = value; _showContacts = value;
}); });
}, },
onShowRepeatersChanged: (value) {
setState(() {
_showRepeaters = value;
});
},
onShowFoundPersonChanged: (value) { onShowFoundPersonChanged: (value) {
setState(() { setState(() {
_showFoundPerson = value; _showFoundPerson = value;
@@ -334,6 +341,7 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
onShowAll: () { onShowAll: () {
setState(() { setState(() {
_showContacts = true; _showContacts = true;
_showRepeaters = true;
_showFoundPerson = true; _showFoundPerson = true;
_showFire = true; _showFire = true;
_showStagingArea = true; _showStagingArea = true;
@@ -363,7 +371,10 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
? [_selectedContact!] ? [_selectedContact!]
: (_selectedSarMarker != null : (_selectedSarMarker != null
? [] ? []
: (_showContacts ? widget.contacts : [])), : widget.contacts.where((c) =>
(_showContacts && !c.isRepeater && !c.isRoom) ||
(_showRepeaters && c.isRepeater)
).toList()),
sarMarkers: _selectedSarMarker != null sarMarkers: _selectedSarMarker != null
? [_selectedSarMarker!] ? [_selectedSarMarker!]
: (_selectedContact != null : (_selectedContact != null
@@ -381,12 +392,14 @@ class _DetailedCompassDialogState extends State<DetailedCompassDialog> {
_buildSelectedItemDetail(context, heading, position), _buildSelectedItemDetail(context, heading, position),
const SizedBox(height: 12), const SizedBox(height: 12),
// Contacts list // Contacts list
if (_showContacts && widget.contacts.isNotEmpty) if (widget.contacts.isNotEmpty)
CompassContactList( CompassContactList(
contacts: widget.contacts, contacts: widget.contacts,
position: position, position: position,
heading: heading, heading: heading,
selectedContact: _selectedContact, selectedContact: _selectedContact,
showContacts: _showContacts,
showRepeaters: _showRepeaters,
onContactTap: (contact) { onContactTap: (contact) {
setState(() { setState(() {
_selectedContact = contact; _selectedContact = contact;