[feat](trx-client): support website_name in header
Add an optional website_name config field and prefer it over callsign for the linked web header title label. Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -39,6 +39,8 @@ pub struct GeneralConfig {
|
|||||||
pub callsign: Option<String>,
|
pub callsign: Option<String>,
|
||||||
/// Optional website URL to use as the web UI header title link.
|
/// Optional website URL to use as the web UI header title link.
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
|
/// Optional website name to use as the web UI header title label.
|
||||||
|
pub website_name: Option<String>,
|
||||||
/// Log level (trace, debug, info, warn, error)
|
/// Log level (trace, debug, info, warn, error)
|
||||||
pub log_level: Option<String>,
|
pub log_level: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -48,6 +50,7 @@ impl Default for GeneralConfig {
|
|||||||
Self {
|
Self {
|
||||||
callsign: Some("N0CALL".to_string()),
|
callsign: Some("N0CALL".to_string()),
|
||||||
website_url: None,
|
website_url: None,
|
||||||
|
website_name: None,
|
||||||
log_level: None,
|
log_level: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,6 +345,11 @@ impl ClientConfig {
|
|||||||
return Err("[general].website_url must not be empty when set".to_string());
|
return Err("[general].website_url must not be empty when set".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(name) = &self.general.website_name {
|
||||||
|
if name.trim().is_empty() {
|
||||||
|
return Err("[general].website_name must not be empty when set".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.frontends.http.enabled && self.frontends.http.port == 0 {
|
if self.frontends.http.enabled && self.frontends.http.port == 0 {
|
||||||
return Err("[frontends.http].port must be > 0 when enabled".to_string());
|
return Err("[frontends.http].port must be > 0 when enabled".to_string());
|
||||||
@@ -433,6 +441,7 @@ impl ClientConfig {
|
|||||||
general: GeneralConfig {
|
general: GeneralConfig {
|
||||||
callsign: Some("N0CALL".to_string()),
|
callsign: Some("N0CALL".to_string()),
|
||||||
website_url: Some("https://haxx.space".to_string()),
|
website_url: Some("https://haxx.space".to_string()),
|
||||||
|
website_name: Some("haxx.space".to_string()),
|
||||||
log_level: Some("info".to_string()),
|
log_level: Some("info".to_string()),
|
||||||
},
|
},
|
||||||
remote: RemoteConfig {
|
remote: RemoteConfig {
|
||||||
@@ -555,6 +564,7 @@ mod tests {
|
|||||||
assert_eq!(config.frontends.http_json.port, 0);
|
assert_eq!(config.frontends.http_json.port, 0);
|
||||||
assert!(config.remote.url.is_none());
|
assert!(config.remote.url.is_none());
|
||||||
assert!(config.general.website_url.is_none());
|
assert!(config.general.website_url.is_none());
|
||||||
|
assert!(config.general.website_name.is_none());
|
||||||
assert_eq!(config.remote.poll_interval_ms, 750);
|
assert_eq!(config.remote.poll_interval_ms, 750);
|
||||||
assert!(config.frontends.audio.enabled);
|
assert!(config.frontends.audio.enabled);
|
||||||
assert_eq!(config.frontends.audio.server_port, 4531);
|
assert_eq!(config.frontends.audio.server_port, 4531);
|
||||||
@@ -570,6 +580,7 @@ mod tests {
|
|||||||
[general]
|
[general]
|
||||||
callsign = "W1AW"
|
callsign = "W1AW"
|
||||||
website_url = "https://example.com"
|
website_url = "https://example.com"
|
||||||
|
website_name = "Example"
|
||||||
|
|
||||||
[remote]
|
[remote]
|
||||||
url = "192.168.1.100:9000"
|
url = "192.168.1.100:9000"
|
||||||
@@ -588,6 +599,7 @@ initial_map_zoom = 12
|
|||||||
let config: ClientConfig = toml::from_str(toml_str).unwrap();
|
let config: ClientConfig = toml::from_str(toml_str).unwrap();
|
||||||
assert_eq!(config.general.callsign, Some("W1AW".to_string()));
|
assert_eq!(config.general.callsign, Some("W1AW".to_string()));
|
||||||
assert_eq!(config.general.website_url, Some("https://example.com".to_string()));
|
assert_eq!(config.general.website_url, Some("https://example.com".to_string()));
|
||||||
|
assert_eq!(config.general.website_name, Some("Example".to_string()));
|
||||||
assert_eq!(config.remote.url, Some("192.168.1.100:9000".to_string()));
|
assert_eq!(config.remote.url, Some("192.168.1.100:9000".to_string()));
|
||||||
assert_eq!(config.remote.rig_id, Some("hf".to_string()));
|
assert_eq!(config.remote.rig_id, Some("hf".to_string()));
|
||||||
assert_eq!(config.remote.auth.token, Some("my-token".to_string()));
|
assert_eq!(config.remote.auth.token, Some("my-token".to_string()));
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ async fn async_init() -> DynResult<AppState> {
|
|||||||
.or_else(|| cfg.general.callsign.clone());
|
.or_else(|| cfg.general.callsign.clone());
|
||||||
frontend_runtime.owner_callsign = callsign.clone();
|
frontend_runtime.owner_callsign = callsign.clone();
|
||||||
frontend_runtime.owner_website_url = cfg.general.website_url.clone();
|
frontend_runtime.owner_website_url = cfg.general.website_url.clone();
|
||||||
|
frontend_runtime.owner_website_name = cfg.general.website_name.clone();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Starting trx-client (remote: {}, frontends: {})",
|
"Starting trx-client (remote: {}, frontends: {})",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@ pub struct FrontendRuntimeContext {
|
|||||||
pub owner_callsign: Option<String>,
|
pub owner_callsign: Option<String>,
|
||||||
/// Optional website URL for the web UI header title link.
|
/// Optional website URL for the web UI header title link.
|
||||||
pub owner_website_url: Option<String>,
|
pub owner_website_url: Option<String>,
|
||||||
|
/// Optional website name for the web UI header title label.
|
||||||
|
pub owner_website_name: Option<String>,
|
||||||
/// Latest spectrum frame from the active SDR rig; None for non-SDR backends.
|
/// Latest spectrum frame from the active SDR rig; None for non-SDR backends.
|
||||||
pub spectrum: Arc<Mutex<SharedSpectrum>>,
|
pub spectrum: Arc<Mutex<SharedSpectrum>>,
|
||||||
}
|
}
|
||||||
@@ -211,6 +213,7 @@ impl FrontendRuntimeContext {
|
|||||||
remote_rigs: Arc::new(Mutex::new(Vec::new())),
|
remote_rigs: Arc::new(Mutex::new(Vec::new())),
|
||||||
owner_callsign: None,
|
owner_callsign: None,
|
||||||
owner_website_url: None,
|
owner_website_url: None,
|
||||||
|
owner_website_name: None,
|
||||||
spectrum: Arc::new(Mutex::new(SharedSpectrum::default())),
|
spectrum: Arc::new(Mutex::new(SharedSpectrum::default())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1266,6 +1266,7 @@ let serverBuildDate = null;
|
|||||||
let serverCallsign = null;
|
let serverCallsign = null;
|
||||||
let ownerCallsign = null;
|
let ownerCallsign = null;
|
||||||
let ownerWebsiteUrl = null;
|
let ownerWebsiteUrl = null;
|
||||||
|
let ownerWebsiteName = null;
|
||||||
let serverRigs = [];
|
let serverRigs = [];
|
||||||
let serverActiveRigId = null;
|
let serverActiveRigId = null;
|
||||||
let serverLat = null;
|
let serverLat = null;
|
||||||
@@ -1284,7 +1285,7 @@ function updateTitle() {
|
|||||||
const titleEl = document.getElementById("rig-title");
|
const titleEl = document.getElementById("rig-title");
|
||||||
if (titleEl) {
|
if (titleEl) {
|
||||||
if (ownerWebsiteUrl) {
|
if (ownerWebsiteUrl) {
|
||||||
const label = ownerCallsign || displayLabelFromUrl(ownerWebsiteUrl);
|
const label = ownerWebsiteName || displayLabelFromUrl(ownerWebsiteUrl);
|
||||||
titleEl.innerHTML =
|
titleEl.innerHTML =
|
||||||
`<a class="title-link" href="${escapeMapHtml(ownerWebsiteUrl)}" target="_blank" rel="noopener">${escapeMapHtml(label)}</a>`;
|
`<a class="title-link" href="${escapeMapHtml(ownerWebsiteUrl)}" target="_blank" rel="noopener">${escapeMapHtml(label)}</a>`;
|
||||||
} else {
|
} else {
|
||||||
@@ -1314,6 +1315,9 @@ function render(update) {
|
|||||||
if (typeof update.owner_website_url === "string" && update.owner_website_url.length > 0) {
|
if (typeof update.owner_website_url === "string" && update.owner_website_url.length > 0) {
|
||||||
ownerWebsiteUrl = update.owner_website_url;
|
ownerWebsiteUrl = update.owner_website_url;
|
||||||
}
|
}
|
||||||
|
if (typeof update.owner_website_name === "string" && update.owner_website_name.length > 0) {
|
||||||
|
ownerWebsiteName = update.owner_website_name;
|
||||||
|
}
|
||||||
if (update.server_latitude != null) serverLat = update.server_latitude;
|
if (update.server_latitude != null) serverLat = update.server_latitude;
|
||||||
if (update.server_longitude != null) serverLon = update.server_longitude;
|
if (update.server_longitude != null) serverLon = update.server_longitude;
|
||||||
if (typeof update.initial_map_zoom === "number" && Number.isFinite(update.initial_map_zoom)) {
|
if (typeof update.initial_map_zoom === "number" && Number.isFinite(update.initial_map_zoom)) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ struct FrontendMeta {
|
|||||||
rig_ids: Vec<String>,
|
rig_ids: Vec<String>,
|
||||||
owner_callsign: Option<String>,
|
owner_callsign: Option<String>,
|
||||||
owner_website_url: Option<String>,
|
owner_website_url: Option<String>,
|
||||||
|
owner_website_name: Option<String>,
|
||||||
show_sdr_gain_control: bool,
|
show_sdr_gain_control: bool,
|
||||||
initial_map_zoom: u8,
|
initial_map_zoom: u8,
|
||||||
}
|
}
|
||||||
@@ -86,6 +87,9 @@ fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String {
|
|||||||
if let Some(url) = meta.owner_website_url {
|
if let Some(url) = meta.owner_website_url {
|
||||||
map.insert("owner_website_url".to_string(), serde_json::json!(url));
|
map.insert("owner_website_url".to_string(), serde_json::json!(url));
|
||||||
}
|
}
|
||||||
|
if let Some(name) = meta.owner_website_name {
|
||||||
|
map.insert("owner_website_name".to_string(), serde_json::json!(name));
|
||||||
|
}
|
||||||
map.insert(
|
map.insert(
|
||||||
"show_sdr_gain_control".to_string(),
|
"show_sdr_gain_control".to_string(),
|
||||||
serde_json::json!(meta.show_sdr_gain_control),
|
serde_json::json!(meta.show_sdr_gain_control),
|
||||||
@@ -110,6 +114,7 @@ fn frontend_meta_from_context(
|
|||||||
rig_ids: rig_ids_from_context(context),
|
rig_ids: rig_ids_from_context(context),
|
||||||
owner_callsign: owner_callsign_from_context(context),
|
owner_callsign: owner_callsign_from_context(context),
|
||||||
owner_website_url: owner_website_url_from_context(context),
|
owner_website_url: owner_website_url_from_context(context),
|
||||||
|
owner_website_name: owner_website_name_from_context(context),
|
||||||
show_sdr_gain_control: show_sdr_gain_control_from_context(context),
|
show_sdr_gain_control: show_sdr_gain_control_from_context(context),
|
||||||
initial_map_zoom: initial_map_zoom_from_context(context),
|
initial_map_zoom: initial_map_zoom_from_context(context),
|
||||||
}
|
}
|
||||||
@@ -149,6 +154,10 @@ fn owner_website_url_from_context(context: &FrontendRuntimeContext) -> Option<St
|
|||||||
context.owner_website_url.clone()
|
context.owner_website_url.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn owner_website_name_from_context(context: &FrontendRuntimeContext) -> Option<String> {
|
||||||
|
context.owner_website_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn show_sdr_gain_control_from_context(context: &FrontendRuntimeContext) -> bool {
|
fn show_sdr_gain_control_from_context(context: &FrontendRuntimeContext) -> bool {
|
||||||
context.http_show_sdr_gain_control
|
context.http_show_sdr_gain_control
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user