use anyhow::Result;
use iocraft::prelude::*;
use std::sync::Arc;
use crate::ui::components::create_form::CreateForm;
use crate::ui::components::delete_confirm::DeleteConfirm;
use crate::ui::components::ip_selector::IpSelector;
use crate::ui::components::record_list::RecordList;
use crate::ui::hooks::*;
use crate::ui::state::{AppProps, AppState, AppView};
use crate::ui::status::{StatusMessage, generate_contextual_status};
use crate::utils::format_selector;
#[component]
pub fn App(props: &AppProps, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let (_width, _height) = hooks.use_terminal_size();
let records_display = hooks.use_state(|| "Loading DNS records...".to_string());
let status = hooks.use_state(|| "Initializing...".to_string());
let should_exit = hooks.use_state(|| false);
let mut system = hooks.use_context_mut::<SystemContext>();
let view = hooks.use_state(|| AppView::List);
let form_focus = hooks.use_state(|| 1);
let form_type = hooks.use_state(|| "A".to_string());
let form_name = hooks.use_state(|| "".to_string());
let form_content = hooks.use_state(|| "".to_string());
let form_ttl = hooks.use_state(|| "1".to_string());
let form_proxied = hooks.use_state(|| "false".to_string());
let is_submitting = hooks.use_state(|| false);
#[allow(clippy::redundant_closure)]
let editing_record_id = hooks.use_state(|| String::new());
let ip_sel_idx = hooks.use_state(|| 0);
let ip_sel_open = hooks.use_state(|| false);
let list_sel_idx = hooks.use_state(|| 0);
let is_deleting = hooks.use_state(|| false);
let is_refreshing = hooks.use_state(|| false);
let ctx = AppCtx {
view,
should_exit,
form_focus,
form_type,
form_name,
form_content,
form_ttl,
form_proxied,
is_submitting,
editing_record_id,
ip_sel_idx,
ip_sel_open,
list_sel_idx,
is_deleting,
is_refreshing,
records_display,
status,
state: props.state.clone(),
};
use_app_events(&mut hooks, &ctx);
if should_exit.get() {
system.exit();
}
let records = props.state.records.lock().unwrap().clone();
let ips = props.state.existing_ips.lock().unwrap().clone();
let zone_name = props.state.zone_name.lock().unwrap().clone();
let domain_suffix = format!(".{}", &zone_name);
let sel_text = format_selector(&ips, ip_sel_idx.get());
let lsi = list_sel_idx.get();
let rec_name: String = if lsi < records.len() {
format!("{} ({})", records[lsi].name, records[lsi].record_type)
} else {
"Unknown".to_string()
};
let is_editing = matches!(view.get(), AppView::Edit);
let status_val = status.to_string();
let is_transient = StatusMessage::is_transient(&status_val);
let status_text = if is_transient {
status_val
} else {
let rec_name_opt = if lsi < records.len() {
Some(records[lsi].name.as_str())
} else {
None
};
let status_msg = generate_contextual_status(
&view.get(),
form_focus.get() as usize,
&form_type.to_string(),
&form_proxied.to_string(),
is_editing,
records.len(),
lsi,
rec_name_opt,
);
status_msg.render()
};
match view.get() {
AppView::Delete => element! {
DeleteConfirm(rec_name: rec_name, deleting: is_deleting.get(), status: status_text, zone_name: zone_name.clone())
}
.into_any(),
AppView::IpSelect => element! {
IpSelector(sel_text: sel_text, status: status_text, zone_name: zone_name.clone())
}
.into_any(),
AppView::Create | AppView::Edit => {
let title = if is_editing {
" Edit DNS Record "
} else {
" Create DNS Record "
};
let hint = "↑↓: navigate | esc: cancel";
element! {
CreateForm(
form_type: form_type,
form_name: form_name,
form_content: form_content,
form_ttl: form_ttl,
form_proxied: form_proxied,
form_focus: form_focus.get(),
status: status_text,
title: title.to_string(),
hint: hint.to_string(),
submit_label: if is_editing { "Save" } else { "Create" },
zone_name: zone_name.clone(),
domain_suffix: domain_suffix.clone(),
is_editing: is_editing,
)
}
.into_any()
}
AppView::List => element! {
RecordList(
records: records,
selected_idx: lsi as i32,
status: status_text,
zone_name: zone_name,
)
}
.into_any(),
}
}
pub fn run_app(api_token: String, zone_id: String) -> Result<()> {
let state = Arc::new(AppState::new(api_token, zone_id));
smol::block_on(element!(App(state: state.clone())).fullscreen())?;
Ok(())
}