mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-09 18:22:48 +10:00
Merge branch 'zjs81:main' into main
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
|
|
||||||
|
class LosIcon extends StatelessWidget {
|
||||||
|
final double size;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
const LosIcon({super.key, this.size = 24, this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final iconTheme = IconTheme.of(context);
|
||||||
|
final iconColor =
|
||||||
|
color ??
|
||||||
|
iconTheme.color ??
|
||||||
|
theme.iconTheme.color ??
|
||||||
|
theme.colorScheme.onSurface;
|
||||||
|
|
||||||
|
return Icon(Symbols.elevation, size: size, color: iconColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Неуспешно изтриване на канала \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "bg",
|
"@@locale": "bg",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Контакти",
|
"nav_contacts": "Контакти",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Име на точката",
|
"losPointName": "Име на точката",
|
||||||
"losShowPanelTooltip": "Показване на LOS панел",
|
"losShowPanelTooltip": "Показване на LOS панел",
|
||||||
"losHidePanelTooltip": "Скриване на LOS панела",
|
"losHidePanelTooltip": "Скриване на LOS панела",
|
||||||
"losElevationAttribution": "Данни за надморска височина: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Данни за надморска височина: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Радиохоризонт",
|
||||||
|
"losLegendLosBeam": "Линия на видимост",
|
||||||
|
"losLegendTerrain": "Терен",
|
||||||
|
"losFrequencyLabel": "Честота",
|
||||||
|
"losFrequencyInfoTooltip": "Преглед на детайли за изчислението",
|
||||||
|
"losFrequencyDialogTitle": "Изчисляване на радиохоризонта",
|
||||||
|
"losFrequencyDialogDescription": "Започвайки от k={baselineK} при {baselineFreq} MHz, изчислението коригира k-фактора за текущата {frequencyMHz} MHz лента, която определя границата на извития радиохоризонт.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Kanal {name} konnte nicht gelöscht werden",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "de",
|
"@@locale": "de",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Kontakte",
|
"nav_contacts": "Kontakte",
|
||||||
@@ -1746,5 +1748,29 @@
|
|||||||
"losPointName": "Punktname",
|
"losPointName": "Punktname",
|
||||||
"losShowPanelTooltip": "LOS-Panel anzeigen",
|
"losShowPanelTooltip": "LOS-Panel anzeigen",
|
||||||
"losHidePanelTooltip": "LOS-Panel ausblenden",
|
"losHidePanelTooltip": "LOS-Panel ausblenden",
|
||||||
"losElevationAttribution": "Höhendaten: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Höhendaten: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Funkhorizont",
|
||||||
|
"losLegendLosBeam": "Sichtlinie",
|
||||||
|
"losLegendTerrain": "Gelände",
|
||||||
|
"losFrequencyLabel": "Frequenz",
|
||||||
|
"losFrequencyInfoTooltip": "Details zur Berechnung anzeigen",
|
||||||
|
"losFrequencyDialogTitle": "Berechnung des Funkhorizonts",
|
||||||
|
"losFrequencyDialogDescription": "Ausgehend von k={baselineK} bei {baselineFreq} MHz passt die Berechnung den k-Faktor für das aktuelle {frequencyMHz} MHz-Band an, das die gekrümmte Funkhorizontobergrenze definiert.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -365,6 +365,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"channels_channelDeleteFailed": "Failed to delete channel \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"name": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"channels_channelDeleted": "Channel \"{name}\" deleted",
|
"channels_channelDeleted": "Channel \"{name}\" deleted",
|
||||||
"@channels_channelDeleted": {
|
"@channels_channelDeleted": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -1667,6 +1675,30 @@
|
|||||||
"losShowPanelTooltip": "Show LOS panel",
|
"losShowPanelTooltip": "Show LOS panel",
|
||||||
"losHidePanelTooltip": "Hide LOS panel",
|
"losHidePanelTooltip": "Hide LOS panel",
|
||||||
"losElevationAttribution": "Elevation data: Open-Meteo (CC BY 4.0)",
|
"losElevationAttribution": "Elevation data: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Radio horizon",
|
||||||
|
"losLegendLosBeam": "LOS beam",
|
||||||
|
"losLegendTerrain": "Terrain",
|
||||||
|
"losFrequencyLabel": "Frequency",
|
||||||
|
"losFrequencyInfoTooltip": "View calculation details",
|
||||||
|
"losFrequencyDialogTitle": "Radio horizon calculation",
|
||||||
|
"losFrequencyDialogDescription": "Starting from k={baselineK} at {baselineFreq} MHz, the calculation adjusts the k-factor for the current {frequencyMHz} MHz band, which defines the curved radio horizon cap.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"contacts_pathTrace": "Path Trace",
|
"contacts_pathTrace": "Path Trace",
|
||||||
"contacts_ping": "Ping",
|
"contacts_ping": "Ping",
|
||||||
"contacts_repeaterPathTrace": "Path trace to repeater",
|
"contacts_repeaterPathTrace": "Path trace to repeater",
|
||||||
|
|||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "No se pudo eliminar el canal \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "es",
|
"@@locale": "es",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Contactos",
|
"nav_contacts": "Contactos",
|
||||||
@@ -1746,5 +1748,29 @@
|
|||||||
"losPointName": "Nombre del punto",
|
"losPointName": "Nombre del punto",
|
||||||
"losShowPanelTooltip": "Mostrar panel LOS",
|
"losShowPanelTooltip": "Mostrar panel LOS",
|
||||||
"losHidePanelTooltip": "Ocultar panel LOS",
|
"losHidePanelTooltip": "Ocultar panel LOS",
|
||||||
"losElevationAttribution": "Datos de elevación: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Datos de elevación: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Horizonte radioeléctrico",
|
||||||
|
"losLegendLosBeam": "Línea de visión",
|
||||||
|
"losLegendTerrain": "Terreno",
|
||||||
|
"losFrequencyLabel": "Frecuencia",
|
||||||
|
"losFrequencyInfoTooltip": "Ver detalles del cálculo",
|
||||||
|
"losFrequencyDialogTitle": "Cálculo del horizonte radioeléctrico",
|
||||||
|
"losFrequencyDialogDescription": "A partir de k={baselineK} en {baselineFreq} MHz, el cálculo ajusta el factor k para la banda actual de {frequencyMHz} MHz, que define el límite curvo del horizonte de radio.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Échec de la suppression de la chaîne \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "fr",
|
"@@locale": "fr",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Contacts",
|
"nav_contacts": "Contacts",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Nom du point",
|
"losPointName": "Nom du point",
|
||||||
"losShowPanelTooltip": "Afficher le panneau LOS",
|
"losShowPanelTooltip": "Afficher le panneau LOS",
|
||||||
"losHidePanelTooltip": "Masquer le panneau LOS",
|
"losHidePanelTooltip": "Masquer le panneau LOS",
|
||||||
"losElevationAttribution": "Données d'altitude : Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Données d’altitude : Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Horizon radio",
|
||||||
|
"losLegendLosBeam": "Ligne de visée",
|
||||||
|
"losLegendTerrain": "Terrain",
|
||||||
|
"losFrequencyLabel": "Fréquence",
|
||||||
|
"losFrequencyInfoTooltip": "Voir les détails du calcul",
|
||||||
|
"losFrequencyDialogTitle": "Calcul de l’horizon radio",
|
||||||
|
"losFrequencyDialogDescription": "À partir de k={baselineK} à {baselineFreq} MHz, le calcul ajuste le facteur k pour la bande actuelle de {frequencyMHz} MHz, ce qui définit la limite incurvée de l'horizon radio.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Impossibile eliminare il canale \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "it",
|
"@@locale": "it",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Contatti",
|
"nav_contacts": "Contatti",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Nome del punto",
|
"losPointName": "Nome del punto",
|
||||||
"losShowPanelTooltip": "Mostra il pannello LOS",
|
"losShowPanelTooltip": "Mostra il pannello LOS",
|
||||||
"losHidePanelTooltip": "Nascondi il pannello LOS",
|
"losHidePanelTooltip": "Nascondi il pannello LOS",
|
||||||
"losElevationAttribution": "Dati di elevazione: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Dati di elevazione: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Orizzonte radio",
|
||||||
|
"losLegendLosBeam": "Linea di vista",
|
||||||
|
"losLegendTerrain": "Terreno",
|
||||||
|
"losFrequencyLabel": "Frequenza",
|
||||||
|
"losFrequencyInfoTooltip": "Visualizza i dettagli del calcolo",
|
||||||
|
"losFrequencyDialogTitle": "Calcolo dell’orizzonte radio",
|
||||||
|
"losFrequencyDialogDescription": "Partendo da k={baselineK} a {baselineFreq} MHz, il calcolo regola il fattore k per l'attuale banda {frequencyMHz} MHz, che definisce il limite curvo dell'orizzonte radio.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1582,6 +1582,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Delete \"{name}\"? This cannot be undone.'**
|
/// **'Delete \"{name}\"? This cannot be undone.'**
|
||||||
String channels_deleteChannelConfirm(String name);
|
String channels_deleteChannelConfirm(String name);
|
||||||
|
|
||||||
|
/// No description provided for @channels_channelDeleteFailed.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Failed to delete channel \"{name}\"'**
|
||||||
|
String channels_channelDeleteFailed(String name);
|
||||||
|
|
||||||
/// No description provided for @channels_channelDeleted.
|
/// No description provided for @channels_channelDeleted.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -5004,6 +5010,53 @@ abstract class AppLocalizations {
|
|||||||
/// **'Elevation data: Open-Meteo (CC BY 4.0)'**
|
/// **'Elevation data: Open-Meteo (CC BY 4.0)'**
|
||||||
String get losElevationAttribution;
|
String get losElevationAttribution;
|
||||||
|
|
||||||
|
/// No description provided for @losLegendRadioHorizon.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Radio horizon'**
|
||||||
|
String get losLegendRadioHorizon;
|
||||||
|
|
||||||
|
/// No description provided for @losLegendLosBeam.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS beam'**
|
||||||
|
String get losLegendLosBeam;
|
||||||
|
|
||||||
|
/// No description provided for @losLegendTerrain.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Terrain'**
|
||||||
|
String get losLegendTerrain;
|
||||||
|
|
||||||
|
/// No description provided for @losFrequencyLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Frequency'**
|
||||||
|
String get losFrequencyLabel;
|
||||||
|
|
||||||
|
/// No description provided for @losFrequencyInfoTooltip.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'View calculation details'**
|
||||||
|
String get losFrequencyInfoTooltip;
|
||||||
|
|
||||||
|
/// No description provided for @losFrequencyDialogTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Radio horizon calculation'**
|
||||||
|
String get losFrequencyDialogTitle;
|
||||||
|
|
||||||
|
/// Explain how the calculation uses the baseline frequency and derived k-factor.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Starting from k={baselineK} at {baselineFreq} MHz, the calculation adjusts the k-factor for the current {frequencyMHz} MHz band, which defines the curved radio horizon cap.'**
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
);
|
||||||
|
|
||||||
/// No description provided for @contacts_pathTrace.
|
/// No description provided for @contacts_pathTrace.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -820,6 +820,11 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
return 'Изтрий \"$name\"? Това не може да бъде отменено.';
|
return 'Изтрий \"$name\"? Това не може да бъде отменено.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Неуспешно изтриване на канала \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Каналът \"$name\" е изтрит';
|
return 'Каналът \"$name\" е изтрит';
|
||||||
@@ -2867,6 +2872,34 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Данни за надморска височина: Open-Meteo (CC BY 4.0)';
|
'Данни за надморска височина: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Радиохоризонт';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Линия на видимост';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Терен';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Честота';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Преглед на детайли за изчислението';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Изчисляване на радиохоризонта';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Започвайки от k=$baselineK при $baselineFreq MHz, изчислението коригира k-фактора за текущата $frequencyMHz MHz лента, която определя границата на извития радиохоризонт.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Пътен проследяване';
|
String get contacts_pathTrace => 'Пътен проследяване';
|
||||||
|
|
||||||
|
|||||||
@@ -817,6 +817,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
return 'Löschen von \"$name\"? Dies kann nicht rückgängig gemacht werden.';
|
return 'Löschen von \"$name\"? Dies kann nicht rückgängig gemacht werden.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Kanal $name konnte nicht gelöscht werden';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanal \"$name\" gelöscht';
|
return 'Kanal \"$name\" gelöscht';
|
||||||
@@ -2873,6 +2878,34 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get losElevationAttribution => 'Höhendaten: Open-Meteo (CC BY 4.0)';
|
String get losElevationAttribution => 'Höhendaten: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Funkhorizont';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Sichtlinie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Gelände';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frequenz';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Details zur Berechnung anzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Berechnung des Funkhorizonts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Ausgehend von k=$baselineK bei $baselineFreq MHz passt die Berechnung den k-Faktor für das aktuelle $frequencyMHz MHz-Band an, das die gekrümmte Funkhorizontobergrenze definiert.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Pfadverfolgung';
|
String get contacts_pathTrace => 'Pfadverfolgung';
|
||||||
|
|
||||||
|
|||||||
@@ -808,6 +808,11 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
return 'Delete \"$name\"? This cannot be undone.';
|
return 'Delete \"$name\"? This cannot be undone.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Failed to delete channel \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Channel \"$name\" deleted';
|
return 'Channel \"$name\" deleted';
|
||||||
@@ -2824,6 +2829,34 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Elevation data: Open-Meteo (CC BY 4.0)';
|
'Elevation data: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Radio horizon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'LOS beam';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terrain';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frequency';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'View calculation details';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Radio horizon calculation';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Starting from k=$baselineK at $baselineFreq MHz, the calculation adjusts the k-factor for the current $frequencyMHz MHz band, which defines the curved radio horizon cap.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Path Trace';
|
String get contacts_pathTrace => 'Path Trace';
|
||||||
|
|
||||||
|
|||||||
@@ -818,6 +818,11 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
return 'Eliminar \"$name\"? Esto no se puede deshacer.';
|
return 'Eliminar \"$name\"? Esto no se puede deshacer.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'No se pudo eliminar el canal \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Canal \"$name\" eliminado';
|
return 'Canal \"$name\" eliminado';
|
||||||
@@ -2867,6 +2872,34 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Datos de elevación: Open-Meteo (CC BY 4.0)';
|
'Datos de elevación: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Horizonte radioeléctrico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Línea de visión';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terreno';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frecuencia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Ver detalles del cálculo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Cálculo del horizonte radioeléctrico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'A partir de k=$baselineK en $baselineFreq MHz, el cálculo ajusta el factor k para la banda actual de $frequencyMHz MHz, que define el límite curvo del horizonte de radio.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Rastreo de caminos';
|
String get contacts_pathTrace => 'Rastreo de caminos';
|
||||||
|
|
||||||
|
|||||||
@@ -820,6 +820,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
return 'Supprimer $name? Cela ne peut pas être annulé.';
|
return 'Supprimer $name? Cela ne peut pas être annulé.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Échec de la suppression de la chaîne \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Le canal \"$name\" a été supprimé';
|
return 'Le canal \"$name\" a été supprimé';
|
||||||
@@ -2880,7 +2885,35 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Données d\'altitude : Open-Meteo (CC BY 4.0)';
|
'Données d’altitude : Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Horizon radio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Ligne de visée';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terrain';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Fréquence';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Voir les détails du calcul';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Calcul de l’horizon radio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'À partir de k=$baselineK à $baselineFreq MHz, le calcul ajuste le facteur k pour la bande actuelle de $frequencyMHz MHz, ce qui définit la limite incurvée de l\'horizon radio.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traçage de chemin';
|
String get contacts_pathTrace => 'Traçage de chemin';
|
||||||
|
|||||||
@@ -816,6 +816,11 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
return 'Eliminare \"$name\"? Non può essere annullato.';
|
return 'Eliminare \"$name\"? Non può essere annullato.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Impossibile eliminare il canale \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Canale \"$name\" eliminato';
|
return 'Canale \"$name\" eliminato';
|
||||||
@@ -2867,6 +2872,34 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Dati di elevazione: Open-Meteo (CC BY 4.0)';
|
'Dati di elevazione: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Orizzonte radio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Linea di vista';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terreno';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frequenza';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Visualizza i dettagli del calcolo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Calcolo dell’orizzonte radio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Partendo da k=$baselineK a $baselineFreq MHz, il calcolo regola il fattore k per l\'attuale banda $frequencyMHz MHz, che definisce il limite curvo dell\'orizzonte radio.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traccia Percorso';
|
String get contacts_pathTrace => 'Traccia Percorso';
|
||||||
|
|
||||||
|
|||||||
@@ -813,6 +813,11 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
return 'Verwijderen \"$name\"? Dit kan niet worden teruggedraaid.';
|
return 'Verwijderen \"$name\"? Dit kan niet worden teruggedraaid.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Kan kanaal $name niet verwijderen';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanaal \"$name\" verwijderd';
|
return 'Kanaal \"$name\" verwijderd';
|
||||||
@@ -2857,6 +2862,34 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Hoogtegegevens: Open-Meteo (CC BY 4.0)';
|
'Hoogtegegevens: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Radiohorizon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Zichtlijn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terrein';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frequentie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Bekijk details van de berekening';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Berekening van de radiohorizon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Beginnend met k=$baselineK bij $baselineFreq MHz, wordt bij de berekening de k-factor aangepast voor de huidige $frequencyMHz MHz-band, die de gebogen radiohorizonkap definieert.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Pad Traceren';
|
String get contacts_pathTrace => 'Pad Traceren';
|
||||||
|
|
||||||
|
|||||||
@@ -818,6 +818,11 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
return 'Usuń \"$name\"? Nie można tego cofnąć.';
|
return 'Usuń \"$name\"? Nie można tego cofnąć.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Nie udało się usunąć kanału \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanał \"$name\" usunięto';
|
return 'Kanał \"$name\" usunięto';
|
||||||
@@ -2863,6 +2868,34 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)';
|
'Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Horyzont radiowy';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Linia widoczności';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Teren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Częstotliwość';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Zobacz szczegóły obliczenia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Obliczanie horyzontu radiowego';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Zaczynając od k=$baselineK przy $baselineFreq MHz, obliczenia korygują współczynnik k dla bieżącego pasma $frequencyMHz MHz, które definiuje zakrzywiony limit horyzontu radiowego.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Śledzenie Ścieżek';
|
String get contacts_pathTrace => 'Śledzenie Ścieżek';
|
||||||
|
|
||||||
|
|||||||
@@ -819,6 +819,11 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
return 'Excluir \"$name\"? Não pode ser desfeito.';
|
return 'Excluir \"$name\"? Não pode ser desfeito.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Falha ao excluir o canal \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Canal \"$name\" excluído';
|
return 'Canal \"$name\" excluído';
|
||||||
@@ -2866,6 +2871,34 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Dados de elevação: Open-Meteo (CC BY 4.0)';
|
'Dados de elevação: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Horizonte de rádio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Linha de visada';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terreno';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frequência';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Ver detalhes do cálculo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Cálculo do horizonte de rádio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Começando em k=$baselineK em $baselineFreq MHz, o cálculo ajusta o fator k para a banda atual de $frequencyMHz MHz, que define o limite do horizonte de rádio curvo.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traçado de Caminho';
|
String get contacts_pathTrace => 'Traçado de Caminho';
|
||||||
|
|
||||||
|
|||||||
@@ -817,6 +817,11 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
return 'Удалить \"$name\"? Это действие нельзя отменить.';
|
return 'Удалить \"$name\"? Это действие нельзя отменить.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Не удалось удалить канал $name.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Канал \"$name\" удалён';
|
return 'Канал \"$name\" удалён';
|
||||||
@@ -2869,6 +2874,34 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Данные о высоте: Open-Meteo (CC BY 4.0)';
|
'Данные о высоте: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Радиогоризонт';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Линия прямой видимости';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Рельеф';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Частота';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Просмотреть детали расчёта';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Расчёт радиогоризонта';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Начиная с k=$baselineK на частоте $baselineFreq МГц, расчет корректирует коэффициент k для текущего диапазона $frequencyMHz МГц, который определяет изогнутую границу радиогоризонта.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Трассировка пути';
|
String get contacts_pathTrace => 'Трассировка пути';
|
||||||
|
|
||||||
|
|||||||
@@ -813,6 +813,11 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
return 'Odstrániť \"$name\"? To sa nedá zrušiť.';
|
return 'Odstrániť \"$name\"? To sa nedá zrušiť.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Kanál \"$name\" sa nepodarilo odstrániť';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanál \"$name\" bol odstránený';
|
return 'Kanál \"$name\" bol odstránený';
|
||||||
@@ -2851,6 +2856,34 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)';
|
'Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Rádiový horizont';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Priama viditeľnosť';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terén';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frekvencia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Zobraziť podrobnosti výpočtu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Výpočet rádiového horizontu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Počnúc od k=$baselineK pri $baselineFreq MHz výpočet upraví k-faktor pre aktuálne pásmo $frequencyMHz MHz, ktorý definuje zakrivený strop rádiového horizontu.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Sledovanie lúčov';
|
String get contacts_pathTrace => 'Sledovanie lúčov';
|
||||||
|
|
||||||
|
|||||||
@@ -811,6 +811,11 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
return 'Izbrišem \"$name\"? To se ne da povrniti.';
|
return 'Izbrišem \"$name\"? To se ne da povrniti.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Kanala $name ni bilo mogoče izbrisati';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanal \"$name\" izbrisan.';
|
return 'Kanal \"$name\" izbrisan.';
|
||||||
@@ -2854,6 +2859,34 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Podatki o višini: Open-Meteo (CC BY 4.0)';
|
'Podatki o višini: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Radijski horizont';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Linija vidnosti';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Teren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frekvenca';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Prikaži podrobnosti izračuna';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Izračun radijskega horizonta';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Začenši od k=$baselineK pri $baselineFreq MHz, izračun prilagodi k-faktor za trenutni pas $frequencyMHz MHz, ki določa ukrivljeno zgornjo mejo radijskega horizonta.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Sledenje poti';
|
String get contacts_pathTrace => 'Sledenje poti';
|
||||||
|
|
||||||
|
|||||||
@@ -807,6 +807,11 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
return 'Radera \"$name\"? Detta kan inte ångras.';
|
return 'Radera \"$name\"? Detta kan inte ångras.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Det gick inte att ta bort kanalen \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Kanalen \"$name\" raderad';
|
return 'Kanalen \"$name\" raderad';
|
||||||
@@ -2837,6 +2842,34 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get losElevationAttribution => 'Höjddata: Open-Meteo (CC BY 4.0)';
|
String get losElevationAttribution => 'Höjddata: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Radiohorisont';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Siktlinje';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Terräng';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Frekvens';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Visa detaljer om beräkningen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Beräkning av radiohorisonten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Med start från k=$baselineK vid $baselineFreq MHz, justerar beräkningen k-faktorn för det aktuella $frequencyMHz MHz-bandet, som definierar den böjda radiohorisonten.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Path Trace';
|
String get contacts_pathTrace => 'Path Trace';
|
||||||
|
|
||||||
|
|||||||
@@ -815,6 +815,11 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
return 'Видалити $name? Це не можна скасувати.';
|
return 'Видалити $name? Це не можна скасувати.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return 'Не вдалося видалити канал \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return 'Канал «$name» видалено';
|
return 'Канал «$name» видалено';
|
||||||
@@ -2877,6 +2882,34 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
String get losElevationAttribution =>
|
String get losElevationAttribution =>
|
||||||
'Дані про висоту: Open-Meteo (CC BY 4.0)';
|
'Дані про висоту: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => 'Радіогоризонт';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => 'Лінія прямої видимості';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => 'Рельєф';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => 'Частота';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => 'Переглянути деталі розрахунку';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => 'Розрахунок радіогоризонту';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return 'Починаючи з k=$baselineK на $baselineFreq МГц, обчислення коригує k-фактор для поточного діапазону $frequencyMHz МГц, який визначає викривлену межу радіогоризонту.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Трасування шляхів';
|
String get contacts_pathTrace => 'Трасування шляхів';
|
||||||
|
|
||||||
|
|||||||
@@ -775,6 +775,11 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
return 'Delete \"$name\"? This cannot be undone.';
|
return 'Delete \"$name\"? This cannot be undone.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String channels_channelDeleteFailed(String name) {
|
||||||
|
return '无法删除频道 \"$name\"';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channels_channelDeleted(String name) {
|
String channels_channelDeleted(String name) {
|
||||||
return '删除频道 \"$name\"';
|
return '删除频道 \"$name\"';
|
||||||
@@ -2715,6 +2720,34 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get losElevationAttribution => '高程数据:Open-Meteo (CC BY 4.0)';
|
String get losElevationAttribution => '高程数据:Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendRadioHorizon => '无线电地平线';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendLosBeam => '视距波束';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losLegendTerrain => '地形';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyLabel => '频率';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyInfoTooltip => '查看计算详情';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losFrequencyDialogTitle => '无线电地平线计算';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losFrequencyDialogDescription(
|
||||||
|
double baselineK,
|
||||||
|
double baselineFreq,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
return '从 $baselineFreq MHz 处的 k=$baselineK 开始,计算调整当前 $frequencyMHz MHz 频段的 k 因子,该因子定义了弯曲的无线电范围上限。';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => '路径追踪';
|
String get contacts_pathTrace => '路径追踪';
|
||||||
|
|
||||||
|
|||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Kan kanaal {name} niet verwijderen",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "nl",
|
"@@locale": "nl",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Contacten",
|
"nav_contacts": "Contacten",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Puntnaam",
|
"losPointName": "Puntnaam",
|
||||||
"losShowPanelTooltip": "Toon LOS-paneel",
|
"losShowPanelTooltip": "Toon LOS-paneel",
|
||||||
"losHidePanelTooltip": "LOS-paneel verbergen",
|
"losHidePanelTooltip": "LOS-paneel verbergen",
|
||||||
"losElevationAttribution": "Hoogtegegevens: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Hoogtegegevens: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Radiohorizon",
|
||||||
|
"losLegendLosBeam": "Zichtlijn",
|
||||||
|
"losLegendTerrain": "Terrein",
|
||||||
|
"losFrequencyLabel": "Frequentie",
|
||||||
|
"losFrequencyInfoTooltip": "Bekijk details van de berekening",
|
||||||
|
"losFrequencyDialogTitle": "Berekening van de radiohorizon",
|
||||||
|
"losFrequencyDialogDescription": "Beginnend met k={baselineK} bij {baselineFreq} MHz, wordt bij de berekening de k-factor aangepast voor de huidige {frequencyMHz} MHz-band, die de gebogen radiohorizonkap definieert.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Nie udało się usunąć kanału \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "pl",
|
"@@locale": "pl",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Kontakty",
|
"nav_contacts": "Kontakty",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Nazwa punktu",
|
"losPointName": "Nazwa punktu",
|
||||||
"losShowPanelTooltip": "Pokaż panel LOS",
|
"losShowPanelTooltip": "Pokaż panel LOS",
|
||||||
"losHidePanelTooltip": "Ukryj panel LOS",
|
"losHidePanelTooltip": "Ukryj panel LOS",
|
||||||
"losElevationAttribution": "Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Horyzont radiowy",
|
||||||
|
"losLegendLosBeam": "Linia widoczności",
|
||||||
|
"losLegendTerrain": "Teren",
|
||||||
|
"losFrequencyLabel": "Częstotliwość",
|
||||||
|
"losFrequencyInfoTooltip": "Zobacz szczegóły obliczenia",
|
||||||
|
"losFrequencyDialogTitle": "Obliczanie horyzontu radiowego",
|
||||||
|
"losFrequencyDialogDescription": "Zaczynając od k={baselineK} przy {baselineFreq} MHz, obliczenia korygują współczynnik k dla bieżącego pasma {frequencyMHz} MHz, które definiuje zakrzywiony limit horyzontu radiowego.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Falha ao excluir o canal \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "pt",
|
"@@locale": "pt",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Contactos",
|
"nav_contacts": "Contactos",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Nome do ponto",
|
"losPointName": "Nome do ponto",
|
||||||
"losShowPanelTooltip": "Mostrar painel LOS",
|
"losShowPanelTooltip": "Mostrar painel LOS",
|
||||||
"losHidePanelTooltip": "Ocultar painel LOS",
|
"losHidePanelTooltip": "Ocultar painel LOS",
|
||||||
"losElevationAttribution": "Dados de elevação: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Dados de elevação: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Horizonte de rádio",
|
||||||
|
"losLegendLosBeam": "Linha de visada",
|
||||||
|
"losLegendTerrain": "Terreno",
|
||||||
|
"losFrequencyLabel": "Frequência",
|
||||||
|
"losFrequencyInfoTooltip": "Ver detalhes do cálculo",
|
||||||
|
"losFrequencyDialogTitle": "Cálculo do horizonte de rádio",
|
||||||
|
"losFrequencyDialogDescription": "Começando em k={baselineK} em {baselineFreq} MHz, o cálculo ajusta o fator k para a banda atual de {frequencyMHz} MHz, que define o limite do horizonte de rádio curvo.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Не удалось удалить канал {name}.",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "ru",
|
"@@locale": "ru",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Контакты",
|
"nav_contacts": "Контакты",
|
||||||
@@ -958,5 +960,29 @@
|
|||||||
"losPointName": "Имя точки",
|
"losPointName": "Имя точки",
|
||||||
"losShowPanelTooltip": "Показать панель LOS",
|
"losShowPanelTooltip": "Показать панель LOS",
|
||||||
"losHidePanelTooltip": "Скрыть панель LOS",
|
"losHidePanelTooltip": "Скрыть панель LOS",
|
||||||
"losElevationAttribution": "Данные о высоте: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Данные о высоте: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Радиогоризонт",
|
||||||
|
"losLegendLosBeam": "Линия прямой видимости",
|
||||||
|
"losLegendTerrain": "Рельеф",
|
||||||
|
"losFrequencyLabel": "Частота",
|
||||||
|
"losFrequencyInfoTooltip": "Просмотреть детали расчёта",
|
||||||
|
"losFrequencyDialogTitle": "Расчёт радиогоризонта",
|
||||||
|
"losFrequencyDialogDescription": "Начиная с k={baselineK} на частоте {baselineFreq} МГц, расчет корректирует коэффициент k для текущего диапазона {frequencyMHz} МГц, который определяет изогнутую границу радиогоризонта.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Kanál \"{name}\" sa nepodarilo odstrániť",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "sk",
|
"@@locale": "sk",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Kontakty",
|
"nav_contacts": "Kontakty",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Názov bodu",
|
"losPointName": "Názov bodu",
|
||||||
"losShowPanelTooltip": "Zobraziť panel LOS",
|
"losShowPanelTooltip": "Zobraziť panel LOS",
|
||||||
"losHidePanelTooltip": "Skryť panel LOS",
|
"losHidePanelTooltip": "Skryť panel LOS",
|
||||||
"losElevationAttribution": "Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Rádiový horizont",
|
||||||
|
"losLegendLosBeam": "Priama viditeľnosť",
|
||||||
|
"losLegendTerrain": "Terén",
|
||||||
|
"losFrequencyLabel": "Frekvencia",
|
||||||
|
"losFrequencyInfoTooltip": "Zobraziť podrobnosti výpočtu",
|
||||||
|
"losFrequencyDialogTitle": "Výpočet rádiového horizontu",
|
||||||
|
"losFrequencyDialogDescription": "Počnúc od k={baselineK} pri {baselineFreq} MHz výpočet upraví k-faktor pre aktuálne pásmo {frequencyMHz} MHz, ktorý definuje zakrivený strop rádiového horizontu.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Kanala {name} ni bilo mogoče izbrisati",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "sl",
|
"@@locale": "sl",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Stiki",
|
"nav_contacts": "Stiki",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Ime točke",
|
"losPointName": "Ime točke",
|
||||||
"losShowPanelTooltip": "Pokaži ploščo LOS",
|
"losShowPanelTooltip": "Pokaži ploščo LOS",
|
||||||
"losHidePanelTooltip": "Skrij ploščo LOS",
|
"losHidePanelTooltip": "Skrij ploščo LOS",
|
||||||
"losElevationAttribution": "Podatki o višini: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Podatki o višini: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Radijski horizont",
|
||||||
|
"losLegendLosBeam": "Linija vidnosti",
|
||||||
|
"losLegendTerrain": "Teren",
|
||||||
|
"losFrequencyLabel": "Frekvenca",
|
||||||
|
"losFrequencyInfoTooltip": "Prikaži podrobnosti izračuna",
|
||||||
|
"losFrequencyDialogTitle": "Izračun radijskega horizonta",
|
||||||
|
"losFrequencyDialogDescription": "Začenši od k={baselineK} pri {baselineFreq} MHz, izračun prilagodi k-faktor za trenutni pas {frequencyMHz} MHz, ki določa ukrivljeno zgornjo mejo radijskega horizonta.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Det gick inte att ta bort kanalen \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "sv",
|
"@@locale": "sv",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Kontakter",
|
"nav_contacts": "Kontakter",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Punktnamn",
|
"losPointName": "Punktnamn",
|
||||||
"losShowPanelTooltip": "Visa LOS-panelen",
|
"losShowPanelTooltip": "Visa LOS-panelen",
|
||||||
"losHidePanelTooltip": "Dölj LOS-panelen",
|
"losHidePanelTooltip": "Dölj LOS-panelen",
|
||||||
"losElevationAttribution": "Höjddata: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Höjddata: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Radiohorisont",
|
||||||
|
"losLegendLosBeam": "Siktlinje",
|
||||||
|
"losLegendTerrain": "Terräng",
|
||||||
|
"losFrequencyLabel": "Frekvens",
|
||||||
|
"losFrequencyInfoTooltip": "Visa detaljer om beräkningen",
|
||||||
|
"losFrequencyDialogTitle": "Beräkning av radiohorisonten",
|
||||||
|
"losFrequencyDialogDescription": "Med start från k={baselineK} vid {baselineFreq} MHz, justerar beräkningen k-faktorn för det aktuella {frequencyMHz} MHz-bandet, som definierar den böjda radiohorisonten.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "Не вдалося видалити канал \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "uk",
|
"@@locale": "uk",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "Контакти",
|
"nav_contacts": "Контакти",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "Назва точки",
|
"losPointName": "Назва точки",
|
||||||
"losShowPanelTooltip": "Показати панель LOS",
|
"losShowPanelTooltip": "Показати панель LOS",
|
||||||
"losHidePanelTooltip": "Приховати панель LOS",
|
"losHidePanelTooltip": "Приховати панель LOS",
|
||||||
"losElevationAttribution": "Дані про висоту: Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "Дані про висоту: Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "Радіогоризонт",
|
||||||
|
"losLegendLosBeam": "Лінія прямої видимості",
|
||||||
|
"losLegendTerrain": "Рельєф",
|
||||||
|
"losFrequencyLabel": "Частота",
|
||||||
|
"losFrequencyInfoTooltip": "Переглянути деталі розрахунку",
|
||||||
|
"losFrequencyDialogTitle": "Розрахунок радіогоризонту",
|
||||||
|
"losFrequencyDialogDescription": "Починаючи з k={baselineK} на {baselineFreq} МГц, обчислення коригує k-фактор для поточного діапазону {frequencyMHz} МГц, який визначає викривлену межу радіогоризонту.",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"channels_channelDeleteFailed": "无法删除频道 \"{name}\"",
|
||||||
|
"@channels_channelDeleteFailed": { "placeholders": { "name": { "type": "String" } } },
|
||||||
"@@locale": "zh",
|
"@@locale": "zh",
|
||||||
"appTitle": "MeshCore Open",
|
"appTitle": "MeshCore Open",
|
||||||
"nav_contacts": "联系方式",
|
"nav_contacts": "联系方式",
|
||||||
@@ -1718,5 +1720,29 @@
|
|||||||
"losPointName": "点名称",
|
"losPointName": "点名称",
|
||||||
"losShowPanelTooltip": "显示 LOS 面板",
|
"losShowPanelTooltip": "显示 LOS 面板",
|
||||||
"losHidePanelTooltip": "隐藏 LOS 面板",
|
"losHidePanelTooltip": "隐藏 LOS 面板",
|
||||||
"losElevationAttribution": "高程数据:Open-Meteo (CC BY 4.0)"
|
"losElevationAttribution": "高程数据:Open-Meteo (CC BY 4.0)",
|
||||||
|
"losLegendRadioHorizon": "无线电地平线",
|
||||||
|
"losLegendLosBeam": "视距波束",
|
||||||
|
"losLegendTerrain": "地形",
|
||||||
|
"losFrequencyLabel": "频率",
|
||||||
|
"losFrequencyInfoTooltip": "查看计算详情",
|
||||||
|
"losFrequencyDialogTitle": "无线电地平线计算",
|
||||||
|
"losFrequencyDialogDescription": "从 {baselineFreq} MHz 处的 k={baselineK} 开始,计算调整当前 {frequencyMHz} MHz 频段的 k 因子,该因子定义了弯曲的无线电范围上限。",
|
||||||
|
"@losFrequencyDialogDescription": {
|
||||||
|
"description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
|
||||||
|
"placeholders": {
|
||||||
|
"baselineK": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"baselineFreq": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"frequencyMHz": {
|
||||||
|
"type": "double"
|
||||||
|
},
|
||||||
|
"kFactor": {
|
||||||
|
"type": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ import 'services/ble_debug_log_service.dart';
|
|||||||
import 'services/app_debug_log_service.dart';
|
import 'services/app_debug_log_service.dart';
|
||||||
import 'services/background_service.dart';
|
import 'services/background_service.dart';
|
||||||
import 'services/map_tile_cache_service.dart';
|
import 'services/map_tile_cache_service.dart';
|
||||||
|
import 'services/chat_text_scale_service.dart';
|
||||||
import 'storage/prefs_manager.dart';
|
import 'storage/prefs_manager.dart';
|
||||||
import 'utils/app_logger.dart';
|
import 'utils/app_logger.dart';
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ void main() async {
|
|||||||
final appDebugLogService = AppDebugLogService();
|
final appDebugLogService = AppDebugLogService();
|
||||||
final backgroundService = BackgroundService();
|
final backgroundService = BackgroundService();
|
||||||
final mapTileCacheService = MapTileCacheService();
|
final mapTileCacheService = MapTileCacheService();
|
||||||
|
final chatTextScaleService = ChatTextScaleService();
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
await appSettingsService.loadSettings();
|
await appSettingsService.loadSettings();
|
||||||
@@ -50,6 +52,8 @@ void main() async {
|
|||||||
await backgroundService.initialize();
|
await backgroundService.initialize();
|
||||||
_registerThirdPartyLicenses();
|
_registerThirdPartyLicenses();
|
||||||
|
|
||||||
|
await chatTextScaleService.initialize();
|
||||||
|
|
||||||
// Wire up connector with services
|
// Wire up connector with services
|
||||||
connector.initialize(
|
connector.initialize(
|
||||||
retryService: retryService,
|
retryService: retryService,
|
||||||
@@ -78,6 +82,7 @@ void main() async {
|
|||||||
bleDebugLogService: bleDebugLogService,
|
bleDebugLogService: bleDebugLogService,
|
||||||
appDebugLogService: appDebugLogService,
|
appDebugLogService: appDebugLogService,
|
||||||
mapTileCacheService: mapTileCacheService,
|
mapTileCacheService: mapTileCacheService,
|
||||||
|
chatTextScaleService: chatTextScaleService,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -112,6 +117,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
final BleDebugLogService bleDebugLogService;
|
final BleDebugLogService bleDebugLogService;
|
||||||
final AppDebugLogService appDebugLogService;
|
final AppDebugLogService appDebugLogService;
|
||||||
final MapTileCacheService mapTileCacheService;
|
final MapTileCacheService mapTileCacheService;
|
||||||
|
final ChatTextScaleService chatTextScaleService;
|
||||||
|
|
||||||
const MeshCoreApp({
|
const MeshCoreApp({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -123,6 +129,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
required this.bleDebugLogService,
|
required this.bleDebugLogService,
|
||||||
required this.appDebugLogService,
|
required this.appDebugLogService,
|
||||||
required this.mapTileCacheService,
|
required this.mapTileCacheService,
|
||||||
|
required this.chatTextScaleService,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -135,6 +142,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
ChangeNotifierProvider.value(value: appSettingsService),
|
ChangeNotifierProvider.value(value: appSettingsService),
|
||||||
ChangeNotifierProvider.value(value: bleDebugLogService),
|
ChangeNotifierProvider.value(value: bleDebugLogService),
|
||||||
ChangeNotifierProvider.value(value: appDebugLogService),
|
ChangeNotifierProvider.value(value: appDebugLogService),
|
||||||
|
ChangeNotifierProvider.value(value: chatTextScaleService),
|
||||||
Provider.value(value: storage),
|
Provider.value(value: storage),
|
||||||
Provider.value(value: mapTileCacheService),
|
Provider.value(value: mapTileCacheService),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ import '../l10n/l10n.dart';
|
|||||||
import '../models/channel.dart';
|
import '../models/channel.dart';
|
||||||
import '../models/channel_message.dart';
|
import '../models/channel_message.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
|
import '../services/chat_text_scale_service.dart';
|
||||||
import '../utils/emoji_utils.dart';
|
import '../utils/emoji_utils.dart';
|
||||||
|
import '../widgets/chat_zoom_wrapper.dart';
|
||||||
import '../widgets/emoji_picker.dart';
|
import '../widgets/emoji_picker.dart';
|
||||||
import '../widgets/gif_message.dart';
|
import '../widgets/gif_message.dart';
|
||||||
import '../widgets/jump_to_bottom_button.dart';
|
import '../widgets/jump_to_bottom_button.dart';
|
||||||
@@ -219,37 +221,50 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
ListView.builder(
|
ChatZoomWrapper(
|
||||||
reverse: true, // List grows from bottom up
|
child: ListView.builder(
|
||||||
controller: _scrollController,
|
reverse: true, // List grows from bottom up
|
||||||
padding: const EdgeInsets.all(8),
|
controller: _scrollController,
|
||||||
itemCount: itemCount,
|
padding: const EdgeInsets.all(8),
|
||||||
itemBuilder: (context, index) {
|
itemCount: itemCount,
|
||||||
// Loading indicator now appears at end (bottom) of reversed list
|
itemBuilder: (context, index) {
|
||||||
if (_isLoadingOlder && index == itemCount - 1) {
|
// Loading indicator now appears at end (bottom) of reversed list
|
||||||
return const Padding(
|
if (_isLoadingOlder && index == itemCount - 1) {
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
return const Padding(
|
||||||
child: Center(
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
child: SizedBox(
|
child: Center(
|
||||||
width: 20,
|
child: SizedBox(
|
||||||
height: 20,
|
width: 20,
|
||||||
child: CircularProgressIndicator(
|
height: 20,
|
||||||
strokeWidth: 2,
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final messageIndex = index;
|
||||||
|
final message = reversedMessages[messageIndex];
|
||||||
|
if (!_messageKeys.containsKey(message.messageId)) {
|
||||||
|
_messageKeys[message.messageId] = GlobalKey();
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
key: _messageKeys[message.messageId]!,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final textScale = context
|
||||||
|
.select<ChatTextScaleService, double>(
|
||||||
|
(service) => service.scale,
|
||||||
|
);
|
||||||
|
return _buildMessageBubble(
|
||||||
|
message,
|
||||||
|
textScale,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
final messageIndex = index;
|
),
|
||||||
final message = reversedMessages[messageIndex];
|
|
||||||
if (!_messageKeys.containsKey(message.messageId)) {
|
|
||||||
_messageKeys[message.messageId] = GlobalKey();
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
key: _messageKeys[message.messageId]!,
|
|
||||||
child: _buildMessageBubble(message),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
JumpToBottomButton(scrollController: _scrollController),
|
JumpToBottomButton(scrollController: _scrollController),
|
||||||
],
|
],
|
||||||
@@ -264,7 +279,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageBubble(ChannelMessage message) {
|
Widget _buildMessageBubble(ChannelMessage message, double textScale) {
|
||||||
final settingsService = context.watch<AppSettingsService>();
|
final settingsService = context.watch<AppSettingsService>();
|
||||||
final enableTracing = settingsService.settings.enableMessageTracing;
|
final enableTracing = settingsService.settings.enableMessageTracing;
|
||||||
final isOutgoing = message.isOutgoing;
|
final isOutgoing = message.isOutgoing;
|
||||||
@@ -278,6 +293,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
|
|
||||||
const maxSwipeOffset = 64.0;
|
const maxSwipeOffset = 64.0;
|
||||||
const replySwipeThreshold = 64.0;
|
const replySwipeThreshold = 64.0;
|
||||||
|
const bodyFontSize = 14.0;
|
||||||
final messageBody = Column(
|
final messageBody = Column(
|
||||||
crossAxisAlignment: isOutgoing
|
crossAxisAlignment: isOutgoing
|
||||||
? CrossAxisAlignment.end
|
? CrossAxisAlignment.end
|
||||||
@@ -334,7 +350,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
if (gifId == null) const SizedBox(height: 4),
|
if (gifId == null) const SizedBox(height: 4),
|
||||||
],
|
],
|
||||||
if (message.replyToMessageId != null) ...[
|
if (message.replyToMessageId != null) ...[
|
||||||
_buildReplyPreview(message),
|
_buildReplyPreview(message, textScale),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
if (poi != null)
|
if (poi != null)
|
||||||
@@ -342,6 +358,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
context,
|
context,
|
||||||
poi,
|
poi,
|
||||||
isOutgoing,
|
isOutgoing,
|
||||||
|
textScale,
|
||||||
trailing: (!enableTracing && isOutgoing)
|
trailing: (!enableTracing && isOutgoing)
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
@@ -415,9 +432,11 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Linkify(
|
child: Linkify(
|
||||||
text: message.text,
|
text: message.text,
|
||||||
style: const TextStyle(fontSize: 14),
|
style: TextStyle(
|
||||||
linkStyle: const TextStyle(
|
fontSize: bodyFontSize * textScale,
|
||||||
fontSize: 14,
|
),
|
||||||
|
linkStyle: TextStyle(
|
||||||
|
fontSize: bodyFontSize * textScale,
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
@@ -595,7 +614,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildReplyPreview(ChannelMessage message) {
|
Widget _buildReplyPreview(ChannelMessage message, double textScale) {
|
||||||
final connector = context.read<MeshCoreConnector>();
|
final connector = context.read<MeshCoreConnector>();
|
||||||
final isOwnNode = message.replyToSenderName == connector.selfName;
|
final isOwnNode = message.replyToSenderName == connector.selfName;
|
||||||
final replyText = message.replyToText ?? '';
|
final replyText = message.replyToText ?? '';
|
||||||
@@ -623,7 +642,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
context.l10n.chat_location,
|
context.l10n.chat_location,
|
||||||
style: TextStyle(fontSize: 12, color: previewTextColor),
|
style: TextStyle(fontSize: 12 * textScale, color: previewTextColor),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -633,7 +652,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12 * textScale,
|
||||||
color: previewTextColor,
|
color: previewTextColor,
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
@@ -657,7 +676,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
Text(
|
Text(
|
||||||
context.l10n.chat_replyTo(message.replyToSenderName ?? ''),
|
context.l10n.chat_replyTo(message.replyToSenderName ?? ''),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11 * textScale,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: isOwnNode
|
color: isOwnNode
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
@@ -736,7 +755,8 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
Widget _buildPoiMessage(
|
Widget _buildPoiMessage(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
_PoiInfo poi,
|
_PoiInfo poi,
|
||||||
bool isOutgoing, {
|
bool isOutgoing,
|
||||||
|
double textScale, {
|
||||||
Widget? trailing,
|
Widget? trailing,
|
||||||
}) {
|
}) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
@@ -774,12 +794,16 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
context.l10n.chat_poiShared,
|
context.l10n.chat_poiShared,
|
||||||
style: TextStyle(color: textColor, fontWeight: FontWeight.w600),
|
style: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 14 * textScale,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (poi.label.isNotEmpty)
|
if (poi.label.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
poi.label,
|
poi.label,
|
||||||
style: TextStyle(color: metaColor, fontSize: 12),
|
style: TextStyle(color: metaColor, fontSize: 12 * textScale),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -849,7 +873,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
return colors[hash.abs() % colors.length];
|
return colors[hash.abs() % colors.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildReplyBanner() {
|
Widget _buildReplyBanner(double textScale) {
|
||||||
final message = _replyingToMessage!;
|
final message = _replyingToMessage!;
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@@ -875,7 +899,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
Text(
|
Text(
|
||||||
context.l10n.chat_replyingTo(message.senderName),
|
context.l10n.chat_replyingTo(message.senderName),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12 * textScale,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
),
|
),
|
||||||
@@ -885,7 +909,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11 * textScale,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.onSecondaryContainer.withValues(alpha: 0.7),
|
).colorScheme.onSecondaryContainer.withValues(alpha: 0.7),
|
||||||
@@ -912,7 +936,15 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (_replyingToMessage != null) _buildReplyBanner(),
|
if (_replyingToMessage != null)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final textScale = context.select<ChatTextScaleService, double>(
|
||||||
|
(service) => service.scale,
|
||||||
|
);
|
||||||
|
return _buildReplyBanner(textScale);
|
||||||
|
},
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|||||||
@@ -489,12 +489,13 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
ChannelMessageStore channelMessageStore,
|
ChannelMessageStore channelMessageStore,
|
||||||
Channel channel,
|
Channel channel,
|
||||||
) {
|
) {
|
||||||
|
final parentContext = context;
|
||||||
final settingsService = context.read<AppSettingsService>();
|
final settingsService = context.read<AppSettingsService>();
|
||||||
final isMuted = settingsService.isChannelMuted(channel.name);
|
final isMuted = settingsService.isChannelMuted(channel.name);
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: parentContext,
|
||||||
builder: (context) => SafeArea(
|
builder: (sheetContext) => SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -502,10 +503,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
leading: const Icon(Icons.edit_outlined),
|
leading: const Icon(Icons.edit_outlined),
|
||||||
title: Text(context.l10n.channels_editChannel),
|
title: Text(context.l10n.channels_editChannel),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
if (context.mounted) {
|
if (parentContext.mounted) {
|
||||||
_showEditChannelDialog(context, connector, channel);
|
_showEditChannelDialog(parentContext, connector, channel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -521,7 +522,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
: context.l10n.channels_muteChannel,
|
: context.l10n.channels_muteChannel,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
if (isMuted) {
|
if (isMuted) {
|
||||||
await settingsService.unmuteChannel(channel.name);
|
await settingsService.unmuteChannel(channel.name);
|
||||||
} else {
|
} else {
|
||||||
@@ -536,9 +537,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
if (context.mounted) {
|
if (parentContext.mounted) {
|
||||||
_confirmDeleteChannel(
|
_confirmDeleteChannel(
|
||||||
context,
|
context,
|
||||||
connector,
|
connector,
|
||||||
@@ -1454,7 +1455,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
child: Text(dialogContext.l10n.common_cancel),
|
child: Text(dialogContext.l10n.common_cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
final name = nameController.text.trim();
|
final name = nameController.text.trim();
|
||||||
final pskHex = pskController.text.trim();
|
final pskHex = pskController.text.trim();
|
||||||
|
|
||||||
@@ -1471,13 +1472,25 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Navigator.pop(dialogContext);
|
Navigator.pop(dialogContext);
|
||||||
connector.setChannel(channel.index, name, psk);
|
try {
|
||||||
connector.setChannelSmazEnabled(channel.index, smazEnabled);
|
await connector.setChannel(channel.index, name, psk);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
await connector.setChannelSmazEnabled(
|
||||||
SnackBar(
|
channel.index,
|
||||||
content: Text(context.l10n.channels_channelUpdated(name)),
|
smazEnabled,
|
||||||
),
|
);
|
||||||
);
|
if (!context.mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
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')),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(dialogContext.l10n.common_save),
|
child: Text(dialogContext.l10n.common_save),
|
||||||
),
|
),
|
||||||
@@ -1506,17 +1519,36 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
child: Text(dialogContext.l10n.common_cancel),
|
child: Text(dialogContext.l10n.common_cancel),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
Navigator.pop(dialogContext);
|
Navigator.pop(dialogContext);
|
||||||
connector.deleteChannel(channel.index);
|
try {
|
||||||
channelMessageStore.clearChannelMessages(channel.index);
|
await connector.deleteChannel(channel.index);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
channelMessageStore.clearChannelMessages(channel.index);
|
||||||
content: Text(
|
|
||||||
context.l10n.channels_channelDeleted(channel.name),
|
if (!context.mounted) return;
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Preserve existing logging (if it was there)
|
||||||
|
debugPrint('Failed to delete channel: $e\n$st');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
dialogContext.l10n.common_delete,
|
dialogContext.l10n.common_delete,
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import '../models/contact.dart';
|
|||||||
import '../models/message.dart';
|
import '../models/message.dart';
|
||||||
import '../models/path_history.dart';
|
import '../models/path_history.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
|
import '../services/chat_text_scale_service.dart';
|
||||||
import '../services/path_history_service.dart';
|
import '../services/path_history_service.dart';
|
||||||
|
import '../widgets/chat_zoom_wrapper.dart';
|
||||||
import '../widgets/elements_ui.dart';
|
import '../widgets/elements_ui.dart';
|
||||||
import 'channel_message_path_screen.dart';
|
import 'channel_message_path_screen.dart';
|
||||||
import 'map_screen.dart';
|
import 'map_screen.dart';
|
||||||
@@ -270,52 +272,62 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
_scrollController.scrollToBottomIfAtBottom();
|
_scrollController.scrollToBottomIfAtBottom();
|
||||||
});
|
});
|
||||||
|
|
||||||
return ListView.builder(
|
return ChatZoomWrapper(
|
||||||
reverse: true, // List grows from bottom up
|
child: ListView.builder(
|
||||||
controller: _scrollController,
|
reverse: true, // List grows from bottom up
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
controller: _scrollController,
|
||||||
itemCount: itemCount,
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||||
itemBuilder: (context, index) {
|
itemCount: itemCount,
|
||||||
// Loading indicator now appears at end (bottom) of reversed list
|
itemBuilder: (context, index) {
|
||||||
if (_isLoadingOlder && index == itemCount - 1) {
|
// Loading indicator now appears at end (bottom) of reversed list
|
||||||
return const Padding(
|
if (_isLoadingOlder && index == itemCount - 1) {
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
return const Padding(
|
||||||
child: Center(
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
child: SizedBox(
|
child: Center(
|
||||||
width: 20,
|
child: SizedBox(
|
||||||
height: 20,
|
width: 20,
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
final messageIndex = index;
|
||||||
final messageIndex = index;
|
Contact contact = widget.contact;
|
||||||
Contact contact = widget.contact;
|
final message = reversedMessages[messageIndex];
|
||||||
final message = reversedMessages[messageIndex];
|
String fourByteHex = '';
|
||||||
String fourByteHex = '';
|
if (widget.contact.type == advTypeRoom) {
|
||||||
if (widget.contact.type == advTypeRoom) {
|
contact = _resolveContactFrom4Bytes(
|
||||||
contact = _resolveContactFrom4Bytes(
|
connector,
|
||||||
connector,
|
message.fourByteRoomContactKey.isEmpty
|
||||||
message.fourByteRoomContactKey.isEmpty
|
? Uint8List.fromList([0, 0, 0, 0])
|
||||||
? Uint8List.fromList([0, 0, 0, 0])
|
: message.fourByteRoomContactKey,
|
||||||
: message.fourByteRoomContactKey,
|
);
|
||||||
);
|
fourByteHex = message.fourByteRoomContactKey
|
||||||
fourByteHex = message.fourByteRoomContactKey
|
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
.join()
|
||||||
.join()
|
.toUpperCase();
|
||||||
.toUpperCase();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return _MessageBubble(
|
return Builder(
|
||||||
message: message,
|
builder: (context) {
|
||||||
senderName: widget.contact.type == advTypeRoom
|
final textScale = context.select<ChatTextScaleService, double>(
|
||||||
? "${contact.name} [$fourByteHex]"
|
(service) => service.scale,
|
||||||
: contact.name,
|
);
|
||||||
isRoomServer: widget.contact.type == advTypeRoom,
|
return _MessageBubble(
|
||||||
onTap: () => _openMessagePath(message, contact),
|
message: message,
|
||||||
onLongPress: () => _showMessageActions(message, contact),
|
senderName: widget.contact.type == advTypeRoom
|
||||||
);
|
? "${contact.name} [$fourByteHex]"
|
||||||
},
|
: contact.name,
|
||||||
|
isRoomServer: widget.contact.type == advTypeRoom,
|
||||||
|
textScale: textScale,
|
||||||
|
onTap: () => _openMessagePath(message, contact),
|
||||||
|
onLongPress: () => _showMessageActions(message, contact),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1163,11 +1175,13 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
final bool isRoomServer;
|
final bool isRoomServer;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
final VoidCallback? onLongPress;
|
final VoidCallback? onLongPress;
|
||||||
|
final double textScale;
|
||||||
|
|
||||||
const _MessageBubble({
|
const _MessageBubble({
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.senderName,
|
required this.senderName,
|
||||||
required this.isRoomServer,
|
required this.isRoomServer,
|
||||||
|
required this.textScale,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
});
|
});
|
||||||
@@ -1190,6 +1204,7 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
? colorScheme.onErrorContainer
|
? colorScheme.onErrorContainer
|
||||||
: (isOutgoing ? colorScheme.onPrimary : colorScheme.onSurface);
|
: (isOutgoing ? colorScheme.onPrimary : colorScheme.onSurface);
|
||||||
final metaColor = textColor.withValues(alpha: 0.7);
|
final metaColor = textColor.withValues(alpha: 0.7);
|
||||||
|
const bodyFontSize = 14.0;
|
||||||
String messageText = message.text;
|
String messageText = message.text;
|
||||||
if (isRoomServer && !isOutgoing) {
|
if (isRoomServer && !isOutgoing) {
|
||||||
messageText = message.text.substring(4.clamp(0, message.text.length));
|
messageText = message.text.substring(4.clamp(0, message.text.length));
|
||||||
@@ -1258,6 +1273,7 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
poi,
|
poi,
|
||||||
textColor,
|
textColor,
|
||||||
metaColor,
|
metaColor,
|
||||||
|
textScale,
|
||||||
trailing: (!enableTracing && isOutgoing)
|
trailing: (!enableTracing && isOutgoing)
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
@@ -1321,10 +1337,14 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Linkify(
|
child: Linkify(
|
||||||
text: messageText,
|
text: messageText,
|
||||||
style: TextStyle(color: textColor),
|
style: TextStyle(
|
||||||
linkStyle: const TextStyle(
|
color: textColor,
|
||||||
|
fontSize: bodyFontSize * textScale,
|
||||||
|
),
|
||||||
|
linkStyle: TextStyle(
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
|
fontSize: bodyFontSize * textScale,
|
||||||
),
|
),
|
||||||
options: const LinkifyOptions(
|
options: const LinkifyOptions(
|
||||||
humanize: false,
|
humanize: false,
|
||||||
@@ -1464,7 +1484,8 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
_PoiInfo poi,
|
_PoiInfo poi,
|
||||||
Color textColor,
|
Color textColor,
|
||||||
Color metaColor, {
|
Color metaColor,
|
||||||
|
double textScale, {
|
||||||
Widget? trailing,
|
Widget? trailing,
|
||||||
}) {
|
}) {
|
||||||
return Row(
|
return Row(
|
||||||
@@ -1493,12 +1514,16 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
context.l10n.chat_poiShared,
|
context.l10n.chat_poiShared,
|
||||||
style: TextStyle(color: textColor, fontWeight: FontWeight.w600),
|
style: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 14 * textScale,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (poi.label.isNotEmpty)
|
if (poi.label.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
poi.label,
|
poi.label,
|
||||||
style: TextStyle(color: metaColor, fontSize: 12),
|
style: TextStyle(color: metaColor, fontSize: 12 * textScale),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import '../services/app_settings_service.dart';
|
|||||||
import '../services/line_of_sight_service.dart';
|
import '../services/line_of_sight_service.dart';
|
||||||
import '../services/map_tile_cache_service.dart';
|
import '../services/map_tile_cache_service.dart';
|
||||||
import '../utils/route_transitions.dart';
|
import '../utils/route_transitions.dart';
|
||||||
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../widgets/app_bar.dart';
|
import '../widgets/app_bar.dart';
|
||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
|
import '../icons/los_icon.dart';
|
||||||
|
|
||||||
class LineOfSightEndpoint {
|
class LineOfSightEndpoint {
|
||||||
final String label;
|
final String label;
|
||||||
@@ -71,6 +73,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
bool _showMarkerLabels = true;
|
bool _showMarkerLabels = true;
|
||||||
bool _didReceivePositionUpdate = false;
|
bool _didReceivePositionUpdate = false;
|
||||||
int _losRequestNonce = 0;
|
int _losRequestNonce = 0;
|
||||||
|
bool _initialLosScheduled = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -81,7 +84,16 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
_end = widget.candidates[1];
|
_end = widget.candidates[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_runLos();
|
_scheduleInitialRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleInitialRun() {
|
||||||
|
if (_initialLosScheduled) return;
|
||||||
|
_initialLosScheduled = true;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
_runLos();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -110,10 +122,13 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final connector = context.read<MeshCoreConnector>();
|
||||||
|
final frequencyMHz = _normalizeFrequencyMHz(connector.currentFreqHz);
|
||||||
final result = await _lineOfSightService.analyzePath(
|
final result = await _lineOfSightService.analyzePath(
|
||||||
[start.point, end.point],
|
[start.point, end.point],
|
||||||
startAntennaHeightMeters: startAntenna,
|
startAntennaHeightMeters: startAntenna,
|
||||||
endAntennaHeightMeters: endAntenna,
|
endAntennaHeightMeters: endAntenna,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (!_isRunRequestCurrent(
|
if (!_isRunRequestCurrent(
|
||||||
@@ -424,6 +439,12 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
Widget _buildControlPanel(bool isImperial) {
|
Widget _buildControlPanel(bool isImperial) {
|
||||||
_sanitizeSelection();
|
_sanitizeSelection();
|
||||||
final segment = _primarySegmentResult();
|
final segment = _primarySegmentResult();
|
||||||
|
final connector = context.read<MeshCoreConnector>();
|
||||||
|
final reportedFrequencyMHz = _normalizeFrequencyMHz(
|
||||||
|
connector.currentFreqHz,
|
||||||
|
);
|
||||||
|
final displayFrequencyMHz = segment?.frequencyMHz ?? reportedFrequencyMHz;
|
||||||
|
final kFactorUsed = segment?.usedKFactor;
|
||||||
final endpoints = _visibleEndpoints();
|
final endpoints = _visibleEndpoints();
|
||||||
final distanceUnit = isImperial ? 'mi' : 'km';
|
final distanceUnit = isImperial ? 'mi' : 'km';
|
||||||
final heightUnit = isImperial ? 'ft' : 'm';
|
final heightUnit = isImperial ? 'ft' : 'm';
|
||||||
@@ -461,6 +482,9 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
|
terrainLabel: context.l10n.losLegendTerrain,
|
||||||
|
losBeamLabel: context.l10n.losLegendLosBeam,
|
||||||
|
radioHorizonLabel: context.l10n.losLegendRadioHorizon,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -474,6 +498,14 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (segment != null) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_LosLegend(
|
||||||
|
terrainLabel: context.l10n.losLegendTerrain,
|
||||||
|
losBeamLabel: context.l10n.losLegendLosBeam,
|
||||||
|
radioHorizonLabel: context.l10n.losLegendRadioHorizon,
|
||||||
|
),
|
||||||
|
],
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
segment != null
|
segment != null
|
||||||
@@ -488,6 +520,52 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
if (displayFrequencyMHz != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2, bottom: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.l10n.losFrequencyLabel,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'${displayFrequencyMHz.toStringAsFixed(3)} MHz',
|
||||||
|
style: TextStyle(fontSize: 11, color: Colors.grey[700]),
|
||||||
|
),
|
||||||
|
if (kFactorUsed != null) ...[
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'k=${kFactorUsed.toStringAsFixed(3)}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(Icons.info_outline, size: 16),
|
||||||
|
color: Colors.grey[600],
|
||||||
|
tooltip: context.l10n.losFrequencyInfoTooltip,
|
||||||
|
onPressed: () {
|
||||||
|
_showFrequencyInfoDialog(
|
||||||
|
context,
|
||||||
|
displayFrequencyMHz,
|
||||||
|
kFactorUsed,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
context.l10n.losElevationAttribution,
|
context.l10n.losElevationAttribution,
|
||||||
style: TextStyle(fontSize: 10, color: Colors.grey[700]),
|
style: TextStyle(fontSize: 10, color: Colors.grey[700]),
|
||||||
@@ -642,7 +720,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: _loading ? null : _runLos,
|
onPressed: _loading ? null : _runLos,
|
||||||
icon: const Icon(Icons.visibility),
|
icon: const LosIcon(),
|
||||||
label: Text(context.l10n.losRun),
|
label: Text(context.l10n.losRun),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -896,6 +974,40 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showFrequencyInfoDialog(
|
||||||
|
BuildContext context,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
final baselineFreq = LineOfSightService.baselineFrequencyMHz;
|
||||||
|
final baselineK = LineOfSightService.baselineKFactor;
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => AlertDialog(
|
||||||
|
title: Text(context.l10n.losFrequencyDialogTitle),
|
||||||
|
content: Text(
|
||||||
|
context.l10n.losFrequencyDialogDescription(
|
||||||
|
baselineK,
|
||||||
|
baselineFreq,
|
||||||
|
frequencyMHz,
|
||||||
|
kFactor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext),
|
||||||
|
child: Text(context.l10n.common_ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _normalizeFrequencyMHz(int? frequencyKHz) {
|
||||||
|
if (frequencyKHz == null || frequencyKHz <= 0) return null;
|
||||||
|
return frequencyKHz / 1000.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LosProfilePainter extends CustomPainter {
|
class _LosProfilePainter extends CustomPainter {
|
||||||
@@ -903,12 +1015,18 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
final String distanceUnit;
|
final String distanceUnit;
|
||||||
final String heightUnit;
|
final String heightUnit;
|
||||||
final TextStyle badgeTextStyle;
|
final TextStyle badgeTextStyle;
|
||||||
|
final String terrainLabel;
|
||||||
|
final String losBeamLabel;
|
||||||
|
final String radioHorizonLabel;
|
||||||
|
|
||||||
const _LosProfilePainter({
|
const _LosProfilePainter({
|
||||||
required this.samples,
|
required this.samples,
|
||||||
required this.distanceUnit,
|
required this.distanceUnit,
|
||||||
required this.heightUnit,
|
required this.heightUnit,
|
||||||
required this.badgeTextStyle,
|
required this.badgeTextStyle,
|
||||||
|
required this.terrainLabel,
|
||||||
|
required this.losBeamLabel,
|
||||||
|
required this.radioHorizonLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -920,44 +1038,108 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
if (samples.length < 2) return;
|
if (samples.length < 2) return;
|
||||||
|
|
||||||
final minY = samples
|
final minY = samples
|
||||||
.map((s) => math.min(s.terrainMeters, s.lineHeightMeters))
|
.map(
|
||||||
|
(s) => math.min(
|
||||||
|
math.min(s.terrainMeters, s.lineHeightMeters),
|
||||||
|
s.refractedHeightMeters,
|
||||||
|
),
|
||||||
|
)
|
||||||
.reduce(math.min);
|
.reduce(math.min);
|
||||||
final maxY = samples
|
final maxY = samples
|
||||||
.map((s) => math.max(s.terrainMeters, s.lineHeightMeters))
|
.map(
|
||||||
|
(s) => math.max(
|
||||||
|
math.max(s.terrainMeters, s.lineHeightMeters),
|
||||||
|
s.refractedHeightMeters,
|
||||||
|
),
|
||||||
|
)
|
||||||
.reduce(math.max);
|
.reduce(math.max);
|
||||||
final ySpan = math.max(1.0, maxY - minY);
|
final ySpan = math.max(1.0, maxY - minY);
|
||||||
final maxDist = math.max(1.0, samples.last.distanceMeters);
|
final maxDist = math.max(1.0, samples.last.distanceMeters);
|
||||||
|
const horizontalPadding = 12.0;
|
||||||
|
const verticalPadding = 12.0;
|
||||||
|
final chartWidth = math.max(1.0, size.width - horizontalPadding * 2);
|
||||||
|
final chartHeight = math.max(1.0, size.height - verticalPadding * 2);
|
||||||
|
|
||||||
Offset mapPoint(double x, double y) {
|
Offset mapPoint(double x, double y) {
|
||||||
final px = (x / maxDist) * size.width;
|
final px = horizontalPadding + (x / maxDist) * chartWidth;
|
||||||
final py = size.height - ((y - minY) / ySpan) * size.height;
|
final py =
|
||||||
|
size.height - verticalPadding - ((y - minY) / ySpan) * chartHeight;
|
||||||
return Offset(px, py);
|
return Offset(px, py);
|
||||||
}
|
}
|
||||||
|
|
||||||
final terrainPath = ui.Path();
|
final firstTerrainPoint = mapPoint(
|
||||||
terrainPath.moveTo(0, size.height);
|
samples.first.distanceMeters,
|
||||||
for (final s in samples) {
|
samples.first.terrainMeters,
|
||||||
final p = mapPoint(s.distanceMeters, s.terrainMeters);
|
);
|
||||||
|
final lastTerrainPoint = mapPoint(
|
||||||
|
samples.last.distanceMeters,
|
||||||
|
samples.last.terrainMeters,
|
||||||
|
);
|
||||||
|
|
||||||
|
double distanceForCanvasX(double x) {
|
||||||
|
final normalized = ((x - horizontalPadding) / chartWidth).clamp(0.0, 1.0);
|
||||||
|
return normalized * maxDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
double elevationToPixel(double elevation) {
|
||||||
|
final normalized = ((elevation - minY) / ySpan).clamp(0.0, 1.0);
|
||||||
|
return size.height - verticalPadding - normalized * chartHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
double extrapolateTerrain(double distance, bool isLeft) {
|
||||||
|
final samplesForSlope = isLeft
|
||||||
|
? samples.sublist(0, math.min(2, samples.length))
|
||||||
|
: samples.sublist(samples.length - math.min(2, samples.length));
|
||||||
|
if (samplesForSlope.length < 2) {
|
||||||
|
return samplesForSlope.first.terrainMeters;
|
||||||
|
}
|
||||||
|
final a = samplesForSlope.first;
|
||||||
|
final b = samplesForSlope.last;
|
||||||
|
final dx = b.distanceMeters - a.distanceMeters;
|
||||||
|
if (dx.abs() < 1e-6) return a.terrainMeters;
|
||||||
|
final slope = (b.terrainMeters - a.terrainMeters) / dx;
|
||||||
|
return a.terrainMeters + slope * (distance - a.distanceMeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
final leftDistance = distanceForCanvasX(0.0);
|
||||||
|
final rightDistance = distanceForCanvasX(size.width);
|
||||||
|
final leftEdgeTerrain = extrapolateTerrain(leftDistance, true);
|
||||||
|
final rightEdgeTerrain = extrapolateTerrain(rightDistance, false);
|
||||||
|
final leftEdgePoint = Offset(0.0, elevationToPixel(leftEdgeTerrain));
|
||||||
|
final rightEdgePoint = Offset(
|
||||||
|
size.width,
|
||||||
|
elevationToPixel(rightEdgeTerrain),
|
||||||
|
);
|
||||||
|
|
||||||
|
final terrainPath = ui.Path()
|
||||||
|
..moveTo(0, size.height)
|
||||||
|
..lineTo(leftEdgePoint.dx, leftEdgePoint.dy)
|
||||||
|
..lineTo(firstTerrainPoint.dx, firstTerrainPoint.dy);
|
||||||
|
for (final sample in samples) {
|
||||||
|
final p = mapPoint(sample.distanceMeters, sample.terrainMeters);
|
||||||
terrainPath.lineTo(p.dx, p.dy);
|
terrainPath.lineTo(p.dx, p.dy);
|
||||||
}
|
}
|
||||||
terrainPath.lineTo(size.width, size.height);
|
terrainPath
|
||||||
terrainPath.close();
|
..lineTo(lastTerrainPoint.dx, lastTerrainPoint.dy)
|
||||||
|
..lineTo(rightEdgePoint.dx, rightEdgePoint.dy)
|
||||||
|
..lineTo(size.width, size.height)
|
||||||
|
..close();
|
||||||
|
|
||||||
canvas.drawPath(terrainPath, Paint()..color = const Color(0xCC7C6F5D));
|
const terrainFillColor = Color(0xCC7C6F5D);
|
||||||
|
const terrainLineColor = Color(0xFF9FE870);
|
||||||
|
const losLineColor = Color(0xFFE0E7FF);
|
||||||
|
canvas.drawPath(terrainPath, Paint()..color = terrainFillColor);
|
||||||
|
|
||||||
final terrainLine = ui.Path();
|
final terrainLine = ui.Path()..moveTo(leftEdgePoint.dx, leftEdgePoint.dy);
|
||||||
for (int i = 0; i < samples.length; i++) {
|
for (final sample in samples) {
|
||||||
final p = mapPoint(samples[i].distanceMeters, samples[i].terrainMeters);
|
final p = mapPoint(sample.distanceMeters, sample.terrainMeters);
|
||||||
if (i == 0) {
|
terrainLine.lineTo(p.dx, p.dy);
|
||||||
terrainLine.moveTo(p.dx, p.dy);
|
|
||||||
} else {
|
|
||||||
terrainLine.lineTo(p.dx, p.dy);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
terrainLine.lineTo(rightEdgePoint.dx, rightEdgePoint.dy);
|
||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
terrainLine,
|
terrainLine,
|
||||||
Paint()
|
Paint()
|
||||||
..color = const Color(0xFF9FE870)
|
..color = terrainLineColor
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 2,
|
..strokeWidth = 2,
|
||||||
);
|
);
|
||||||
@@ -977,10 +1159,59 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
losLine,
|
losLine,
|
||||||
Paint()
|
Paint()
|
||||||
..color = const Color(0xFFE0E7FF)
|
..color = losLineColor
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 2,
|
..strokeWidth = 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const refractedLineColor = Color(0xFFFFD57F);
|
||||||
|
final refractedLine = ui.Path();
|
||||||
|
for (int i = 0; i < samples.length; i++) {
|
||||||
|
final p = mapPoint(
|
||||||
|
samples[i].distanceMeters,
|
||||||
|
samples[i].refractedHeightMeters,
|
||||||
|
);
|
||||||
|
if (i == 0) {
|
||||||
|
refractedLine.moveTo(p.dx, p.dy);
|
||||||
|
} else {
|
||||||
|
refractedLine.lineTo(p.dx, p.dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.drawPath(
|
||||||
|
refractedLine,
|
||||||
|
Paint()
|
||||||
|
..color = refractedLineColor
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
final capPath = ui.Path();
|
||||||
|
for (int i = 0; i < samples.length; i++) {
|
||||||
|
final p = mapPoint(
|
||||||
|
samples[i].distanceMeters,
|
||||||
|
samples[i].refractedHeightMeters,
|
||||||
|
);
|
||||||
|
if (i == 0) {
|
||||||
|
capPath.moveTo(p.dx, p.dy);
|
||||||
|
} else {
|
||||||
|
capPath.lineTo(p.dx, p.dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = samples.length - 1; i >= 0; i--) {
|
||||||
|
final p = mapPoint(
|
||||||
|
samples[i].distanceMeters,
|
||||||
|
samples[i].lineHeightMeters,
|
||||||
|
);
|
||||||
|
capPath.lineTo(p.dx, p.dy);
|
||||||
|
}
|
||||||
|
capPath.close();
|
||||||
|
const horizonFillColor = Color(0x40FFD57F);
|
||||||
|
canvas.drawPath(
|
||||||
|
capPath,
|
||||||
|
Paint()
|
||||||
|
..color = horizonFillColor
|
||||||
|
..style = PaintingStyle.fill,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -988,7 +1219,10 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
return oldDelegate.samples != samples ||
|
return oldDelegate.samples != samples ||
|
||||||
oldDelegate.distanceUnit != distanceUnit ||
|
oldDelegate.distanceUnit != distanceUnit ||
|
||||||
oldDelegate.heightUnit != heightUnit ||
|
oldDelegate.heightUnit != heightUnit ||
|
||||||
oldDelegate.badgeTextStyle != badgeTextStyle;
|
oldDelegate.badgeTextStyle != badgeTextStyle ||
|
||||||
|
oldDelegate.terrainLabel != terrainLabel ||
|
||||||
|
oldDelegate.losBeamLabel != losBeamLabel ||
|
||||||
|
oldDelegate.radioHorizonLabel != radioHorizonLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawUnitBadge(Canvas canvas, Size size) {
|
void _drawUnitBadge(Canvas canvas, Size size) {
|
||||||
@@ -1001,3 +1235,73 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
painter.paint(canvas, Offset(size.width - painter.width - 8, 8));
|
painter.paint(canvas, Offset(size.width - painter.width - 8, 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _LosLegend extends StatelessWidget {
|
||||||
|
static const _terrainColor = Color(0xFF9FE870);
|
||||||
|
static const _losColor = Color(0xFFE0E7FF);
|
||||||
|
static const _radioColor = Color(0xFFFFD57F);
|
||||||
|
|
||||||
|
final String terrainLabel;
|
||||||
|
final String losBeamLabel;
|
||||||
|
final String radioHorizonLabel;
|
||||||
|
|
||||||
|
const _LosLegend({
|
||||||
|
required this.terrainLabel,
|
||||||
|
required this.losBeamLabel,
|
||||||
|
required this.radioHorizonLabel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textStyle =
|
||||||
|
Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
) ??
|
||||||
|
const TextStyle(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
);
|
||||||
|
|
||||||
|
final entries = [
|
||||||
|
_LegendEntry(terrainLabel, _terrainColor),
|
||||||
|
_LegendEntry(losBeamLabel, _losColor),
|
||||||
|
_LegendEntry(radioHorizonLabel, _radioColor),
|
||||||
|
];
|
||||||
|
|
||||||
|
const swatchSize = 10.0;
|
||||||
|
|
||||||
|
return Wrap(
|
||||||
|
spacing: 16,
|
||||||
|
runSpacing: 6,
|
||||||
|
children: entries
|
||||||
|
.map(
|
||||||
|
(entry) => Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: swatchSize,
|
||||||
|
height: swatchSize,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: entry.color,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(entry.label, style: textStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LegendEntry {
|
||||||
|
final String label;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const _LegendEntry(this.label, this.color);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import '../services/map_tile_cache_service.dart';
|
|||||||
import '../utils/contact_search.dart';
|
import '../utils/contact_search.dart';
|
||||||
import '../utils/route_transitions.dart';
|
import '../utils/route_transitions.dart';
|
||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
|
import '../icons/los_icon.dart';
|
||||||
import 'channels_screen.dart';
|
import 'channels_screen.dart';
|
||||||
import 'chat_screen.dart';
|
import 'chat_screen.dart';
|
||||||
import 'contacts_screen.dart';
|
import 'contacts_screen.dart';
|
||||||
@@ -280,7 +281,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
if (!_isBuildingPathTrace)
|
if (!_isBuildingPathTrace)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.visibility),
|
icon: const LosIcon(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final candidates = <LineOfSightEndpoint>[];
|
final candidates = <LineOfSightEndpoint>[];
|
||||||
if (connector.selfLatitude != null &&
|
if (connector.selfLatitude != null &&
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import '../storage/prefs_manager.dart';
|
||||||
|
|
||||||
|
/// Client-side accessibility/UI service that exposes a persistent shared text scale
|
||||||
|
/// factor. No MeshCoreConnector/RoomServer or protocol interaction occurs, and the
|
||||||
|
/// value is saved locally via SharedPreferences so it can be reused in Markdown
|
||||||
|
/// viewers, log panels, or other text-heavy widgets without redundant network
|
||||||
|
/// dependencies.
|
||||||
|
///
|
||||||
|
/// Widgets should scope rebuilds using the snippet below so only the scaled text
|
||||||
|
/// is rebuilt instead of the entire chat list:
|
||||||
|
/// ```dart
|
||||||
|
/// context.select<ChatTextScaleService, double>(
|
||||||
|
/// (service) => service.scale,
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
class ChatTextScaleService extends ChangeNotifier {
|
||||||
|
static const _prefKey = 'chat_text_scale';
|
||||||
|
static const double _minScale = 0.8;
|
||||||
|
static const double _maxScale = 1.8;
|
||||||
|
|
||||||
|
double _scale = 1.0;
|
||||||
|
Timer? _saveTimer;
|
||||||
|
|
||||||
|
double get scale => _scale;
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
final stored = PrefsManager.instance.getDouble(_prefKey);
|
||||||
|
if (stored != null) {
|
||||||
|
_scale = _clamp(stored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScale(double value, {bool persistImmediately = false}) {
|
||||||
|
final next = _clamp(value);
|
||||||
|
if (next == _scale) return;
|
||||||
|
_scale = next;
|
||||||
|
notifyListeners();
|
||||||
|
if (persistImmediately) {
|
||||||
|
_commitScale();
|
||||||
|
} else {
|
||||||
|
_scheduleSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
setScale(1.0, persistImmediately: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void persist() => _commitScale();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_saveTimer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleSave() {
|
||||||
|
_saveTimer?.cancel();
|
||||||
|
_saveTimer = Timer(const Duration(milliseconds: 250), _commitScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _commitScale() {
|
||||||
|
_saveTimer?.cancel();
|
||||||
|
PrefsManager.instance.setDouble(_prefKey, _scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _clamp(double value) => value.clamp(_minScale, _maxScale).toDouble();
|
||||||
|
}
|
||||||
@@ -12,12 +12,14 @@ class LineOfSightSample {
|
|||||||
final double distanceMeters;
|
final double distanceMeters;
|
||||||
final double terrainMeters;
|
final double terrainMeters;
|
||||||
final double lineHeightMeters;
|
final double lineHeightMeters;
|
||||||
|
final double refractedHeightMeters;
|
||||||
final double clearanceMeters;
|
final double clearanceMeters;
|
||||||
|
|
||||||
const LineOfSightSample({
|
const LineOfSightSample({
|
||||||
required this.distanceMeters,
|
required this.distanceMeters,
|
||||||
required this.terrainMeters,
|
required this.terrainMeters,
|
||||||
required this.lineHeightMeters,
|
required this.lineHeightMeters,
|
||||||
|
required this.refractedHeightMeters,
|
||||||
required this.clearanceMeters,
|
required this.clearanceMeters,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -30,6 +32,8 @@ class LineOfSightResult {
|
|||||||
final double? firstObstructionDistanceMeters;
|
final double? firstObstructionDistanceMeters;
|
||||||
final List<LineOfSightSample> samples;
|
final List<LineOfSightSample> samples;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
final double usedKFactor;
|
||||||
|
final double? frequencyMHz;
|
||||||
|
|
||||||
const LineOfSightResult({
|
const LineOfSightResult({
|
||||||
required this.hasData,
|
required this.hasData,
|
||||||
@@ -38,12 +42,16 @@ class LineOfSightResult {
|
|||||||
required this.maxObstructionMeters,
|
required this.maxObstructionMeters,
|
||||||
required this.firstObstructionDistanceMeters,
|
required this.firstObstructionDistanceMeters,
|
||||||
required this.samples,
|
required this.samples,
|
||||||
|
required this.usedKFactor,
|
||||||
|
this.frequencyMHz,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
const LineOfSightResult.error({
|
const LineOfSightResult.error({
|
||||||
required this.totalDistanceMeters,
|
required this.totalDistanceMeters,
|
||||||
required this.errorMessage,
|
required this.errorMessage,
|
||||||
|
this.usedKFactor = 4.0 / 3.0,
|
||||||
|
this.frequencyMHz,
|
||||||
}) : hasData = false,
|
}) : hasData = false,
|
||||||
isClear = false,
|
isClear = false,
|
||||||
maxObstructionMeters = 0,
|
maxObstructionMeters = 0,
|
||||||
@@ -89,6 +97,11 @@ class LineOfSightService {
|
|||||||
static const Duration _cacheTtl = Duration(hours: 24);
|
static const Duration _cacheTtl = Duration(hours: 24);
|
||||||
static const int _maxFetchAttempts = 4; // initial try + 3 retries
|
static const int _maxFetchAttempts = 4; // initial try + 3 retries
|
||||||
static const Duration _initialBackoff = Duration(milliseconds: 300);
|
static const Duration _initialBackoff = Duration(milliseconds: 300);
|
||||||
|
static const double _baselineFrequencyMHz = 915.0;
|
||||||
|
static const double _baselineKFactor = 4.0 / 3.0;
|
||||||
|
|
||||||
|
static double get baselineFrequencyMHz => _baselineFrequencyMHz;
|
||||||
|
static double get baselineKFactor => _baselineKFactor;
|
||||||
|
|
||||||
final http.Client _httpClient;
|
final http.Client _httpClient;
|
||||||
final bool _ownsHttpClient;
|
final bool _ownsHttpClient;
|
||||||
@@ -106,7 +119,7 @@ class LineOfSightService {
|
|||||||
List<LatLng> points, {
|
List<LatLng> points, {
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) async {
|
}) async {
|
||||||
if (points.length < 2) {
|
if (points.length < 2) {
|
||||||
@@ -123,6 +136,7 @@ class LineOfSightService {
|
|||||||
var blockedSegments = 0;
|
var blockedSegments = 0;
|
||||||
var unknownSegments = 0;
|
var unknownSegments = 0;
|
||||||
|
|
||||||
|
final kFactor = _kFactorForFrequency(frequencyMHz);
|
||||||
for (int i = 0; i < points.length - 1; i++) {
|
for (int i = 0; i < points.length - 1; i++) {
|
||||||
final result = await analyzeLink(
|
final result = await analyzeLink(
|
||||||
points[i],
|
points[i],
|
||||||
@@ -130,6 +144,7 @@ class LineOfSightService {
|
|||||||
startAntennaHeightMeters: startAntennaHeightMeters,
|
startAntennaHeightMeters: startAntennaHeightMeters,
|
||||||
endAntennaHeightMeters: endAntennaHeightMeters,
|
endAntennaHeightMeters: endAntennaHeightMeters,
|
||||||
kFactor: kFactor,
|
kFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
obstructionToleranceMeters: obstructionToleranceMeters,
|
obstructionToleranceMeters: obstructionToleranceMeters,
|
||||||
);
|
);
|
||||||
segments.add(
|
segments.add(
|
||||||
@@ -163,7 +178,8 @@ class LineOfSightService {
|
|||||||
LatLng end, {
|
LatLng end, {
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
required double kFactor,
|
||||||
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) async {
|
}) async {
|
||||||
final totalDistanceMeters = _distance.as(LengthUnit.Meter, start, end);
|
final totalDistanceMeters = _distance.as(LengthUnit.Meter, start, end);
|
||||||
@@ -175,6 +191,8 @@ class LineOfSightService {
|
|||||||
maxObstructionMeters: 0,
|
maxObstructionMeters: 0,
|
||||||
firstObstructionDistanceMeters: null,
|
firstObstructionDistanceMeters: null,
|
||||||
samples: const [],
|
samples: const [],
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +203,8 @@ class LineOfSightService {
|
|||||||
return LineOfSightResult.error(
|
return LineOfSightResult.error(
|
||||||
totalDistanceMeters: totalDistanceMeters,
|
totalDistanceMeters: totalDistanceMeters,
|
||||||
errorMessage: errorElevationUnavailable,
|
errorMessage: errorElevationUnavailable,
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +214,7 @@ class LineOfSightService {
|
|||||||
startAntennaHeightMeters: startAntennaHeightMeters,
|
startAntennaHeightMeters: startAntennaHeightMeters,
|
||||||
endAntennaHeightMeters: endAntennaHeightMeters,
|
endAntennaHeightMeters: endAntennaHeightMeters,
|
||||||
kFactor: kFactor,
|
kFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
obstructionToleranceMeters: obstructionToleranceMeters,
|
obstructionToleranceMeters: obstructionToleranceMeters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -203,13 +224,16 @@ class LineOfSightService {
|
|||||||
required List<double> elevations,
|
required List<double> elevations,
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
required double kFactor,
|
||||||
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) {
|
}) {
|
||||||
if (points.length < 2 || elevations.length != points.length) {
|
if (points.length < 2 || elevations.length != points.length) {
|
||||||
return const LineOfSightResult.error(
|
return LineOfSightResult.error(
|
||||||
totalDistanceMeters: 0,
|
totalDistanceMeters: 0,
|
||||||
errorMessage: errorInvalidInput,
|
errorMessage: errorInvalidInput,
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +262,10 @@ class LineOfSightService {
|
|||||||
(2 * effectiveEarthRadius);
|
(2 * effectiveEarthRadius);
|
||||||
final terrainHeight = elevations[i] + earthBulge;
|
final terrainHeight = elevations[i] + earthBulge;
|
||||||
final clearance = lineHeight - terrainHeight;
|
final clearance = lineHeight - terrainHeight;
|
||||||
|
final unrefBulge =
|
||||||
|
(distanceFromStart * (totalDistanceMeters - distanceFromStart)) /
|
||||||
|
(2 * _earthRadiusMeters);
|
||||||
|
final refractedHeight = lineHeight + (unrefBulge - earthBulge);
|
||||||
|
|
||||||
if (clearance < -obstructionToleranceMeters) {
|
if (clearance < -obstructionToleranceMeters) {
|
||||||
isClear = false;
|
isClear = false;
|
||||||
@@ -253,6 +281,7 @@ class LineOfSightService {
|
|||||||
distanceMeters: distanceFromStart,
|
distanceMeters: distanceFromStart,
|
||||||
terrainMeters: terrainHeight,
|
terrainMeters: terrainHeight,
|
||||||
lineHeightMeters: lineHeight,
|
lineHeightMeters: lineHeight,
|
||||||
|
refractedHeightMeters: refractedHeight,
|
||||||
clearanceMeters: clearance,
|
clearanceMeters: clearance,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -265,9 +294,20 @@ class LineOfSightService {
|
|||||||
maxObstructionMeters: maxObstructionMeters,
|
maxObstructionMeters: maxObstructionMeters,
|
||||||
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
|
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
|
||||||
samples: samples,
|
samples: samples,
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double _kFactorForFrequency(double? frequencyMHz) {
|
||||||
|
if (frequencyMHz == null) return _baselineKFactor;
|
||||||
|
final delta =
|
||||||
|
(frequencyMHz - _baselineFrequencyMHz) / _baselineFrequencyMHz;
|
||||||
|
final adjustment = delta * 0.15;
|
||||||
|
final scaled = _baselineKFactor * (1 + adjustment);
|
||||||
|
return scaled.clamp(1.1, 1.6).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
List<LatLng> _buildSamplePoints(
|
List<LatLng> _buildSamplePoints(
|
||||||
LatLng start,
|
LatLng start,
|
||||||
LatLng end,
|
LatLng end,
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../services/chat_text_scale_service.dart';
|
||||||
|
|
||||||
|
/// Gesture wrapper that exposes two-finger pinch-to-zoom for chat scrollables.
|
||||||
|
/// Double-tap resets the scale. Only the wrapper itself listens to gestures;
|
||||||
|
/// child scrollables keep their normal touch handling.
|
||||||
|
class ChatZoomWrapper extends StatefulWidget {
|
||||||
|
const ChatZoomWrapper({super.key, required this.child, this.onDoubleTap});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final VoidCallback? onDoubleTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatZoomWrapper> createState() => _ChatZoomWrapperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatZoomWrapperState extends State<ChatZoomWrapper> {
|
||||||
|
double? _startScale;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final service = context.read<ChatTextScaleService>();
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onDoubleTap: () {
|
||||||
|
service.reset();
|
||||||
|
service.persist();
|
||||||
|
widget.onDoubleTap?.call();
|
||||||
|
},
|
||||||
|
onScaleStart: (details) {
|
||||||
|
if (details.pointerCount != 2) return;
|
||||||
|
_startScale = service.scale;
|
||||||
|
},
|
||||||
|
onScaleUpdate: (details) {
|
||||||
|
if (details.pointerCount != 2) return;
|
||||||
|
final baseScale = _startScale ?? service.scale;
|
||||||
|
service.setScale(baseScale * details.scale);
|
||||||
|
},
|
||||||
|
onScaleEnd: (_) {
|
||||||
|
_startScale = null;
|
||||||
|
service.persist();
|
||||||
|
},
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
-15
@@ -5,10 +5,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.7"
|
version: "4.0.9"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -347,6 +347,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.2.2"
|
version: "8.2.2"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -401,10 +409,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c"
|
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.2"
|
version: "4.8.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -417,10 +425,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df"
|
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.10.0"
|
version: "4.11.0"
|
||||||
latlong2:
|
latlong2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -509,6 +517,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.0"
|
version: "0.13.0"
|
||||||
|
material_symbols_icons:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: material_symbols_icons
|
||||||
|
sha256: c62b15f2b3de98d72cbff0148812f5ef5159f05e61fc9f9a089ec2bb234df082
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2906.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -537,10 +553,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobile_scanner
|
name: mobile_scanner
|
||||||
sha256: c6184bf2913dd66be244108c9c27ca04b01caf726321c44b0e7a7a1e32d41044
|
sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.1.4"
|
version: "7.2.0"
|
||||||
native_toolchain_c:
|
native_toolchain_c:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -597,6 +613,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -649,10 +673,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
|
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.2"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -681,10 +705,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: posix
|
name: posix
|
||||||
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.5.0"
|
||||||
proj4dart:
|
proj4dart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1006,10 +1030,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.2"
|
version: "4.5.3"
|
||||||
|
vector_graphics:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics
|
||||||
|
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.19"
|
||||||
|
vector_graphics_codec:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_codec
|
||||||
|
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.13"
|
||||||
|
vector_graphics_compiler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_compiler
|
||||||
|
sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1043,7 +1091,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ dependencies:
|
|||||||
gpx: ^2.3.0
|
gpx: ^2.3.0
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
share_plus: ^12.0.1
|
share_plus: ^12.0.1
|
||||||
|
material_symbols_icons: ^4.2906.0
|
||||||
web: ^1.1.1
|
web: ^1.1.1
|
||||||
flutter_svg: ^2.0.10+1
|
flutter_svg: ^2.0.10+1
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ void main() {
|
|||||||
elevations: elevations,
|
elevations: elevations,
|
||||||
startAntennaHeightMeters: 2,
|
startAntennaHeightMeters: 2,
|
||||||
endAntennaHeightMeters: 2,
|
endAntennaHeightMeters: 2,
|
||||||
|
kFactor: 4.0 / 3.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.hasData, isTrue);
|
expect(result.hasData, isTrue);
|
||||||
@@ -36,6 +37,7 @@ void main() {
|
|||||||
elevations: elevations,
|
elevations: elevations,
|
||||||
startAntennaHeightMeters: 1.5,
|
startAntennaHeightMeters: 1.5,
|
||||||
endAntennaHeightMeters: 1.5,
|
endAntennaHeightMeters: 1.5,
|
||||||
|
kFactor: 4.0 / 3.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.hasData, isTrue);
|
expect(result.hasData, isTrue);
|
||||||
|
|||||||
Reference in New Issue
Block a user