diff --git a/frontend/src/app/components/config/api_config_view.rs b/frontend/src/app/components/config/api_config_view.rs index e080e8b65..0b62d3869 100644 --- a/frontend/src/app/components/config/api_config_view.rs +++ b/frontend/src/app/components/config/api_config_view.rs @@ -34,9 +34,9 @@ pub fn ApiConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - let deps = (form_state.form.clone(), form_state.modified); - use_effect_with(deps, move |(form, modified)| { - on_form_change.emit(ConfigForm::Api(*modified, form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::Api(*modified, state.form.clone())); || () }); } diff --git a/frontend/src/app/components/config/config_view.rs b/frontend/src/app/components/config/config_view.rs index d81fd0f3f..10ea2c23a 100644 --- a/frontend/src/app/components/config/config_view.rs +++ b/frontend/src/app/components/config/config_view.rs @@ -70,12 +70,12 @@ pub fn ConfigView() -> Html { let translate = translate.clone(); let edit_mode = edit_mode.clone(); - use_memo((form_state, edit_mode), move |(forms, edit)| { - let modified = collect_modified!(forms, [ - main, api, schedules, video, messaging, web_ui, - reverse_proxy, hd_homerun, proxy, ipcheck - ]) - .iter() + use_memo((form_state, edit_mode, translate), move |(forms, edit, translate)| { + let forms: &ConfigFormState = forms; + let modified_pages = collect_modified!(forms, [ + main, api, schedules, video, messaging, web_ui, + reverse_proxy, hd_homerun, proxy, ipcheck + ]).iter() .filter_map(|maybe_form| maybe_form.as_ref().map(config_form_to_config_page)) .collect::>(); @@ -94,7 +94,7 @@ pub fn ConfigView() -> Html { let editing = **edit; tab_configs.into_iter().map(|(page, label, children, icon)| { - let is_modified = editing && modified.contains(&page); + let is_modified = editing && modified_pages.contains(&page); TabItem { id: page.to_string(), title: translate.t(label), diff --git a/frontend/src/app/components/config/hdhomerun_config_view.rs b/frontend/src/app/components/config/hdhomerun_config_view.rs index 83fe96fe2..11eb51753 100644 --- a/frontend/src/app/components/config/hdhomerun_config_view.rs +++ b/frontend/src/app/components/config/hdhomerun_config_view.rs @@ -31,8 +31,9 @@ pub fn HdHomerunConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - use_effect_with(form_state.clone(), move |state| { - on_form_change.emit(ConfigForm::HdHomerun(state.modified, state.form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::HdHomerun(*modified, state.form.clone())); }); } diff --git a/frontend/src/app/components/config/ipcheck_config_view.rs b/frontend/src/app/components/config/ipcheck_config_view.rs index 12ab1c9cf..765d63198 100644 --- a/frontend/src/app/components/config/ipcheck_config_view.rs +++ b/frontend/src/app/components/config/ipcheck_config_view.rs @@ -37,8 +37,9 @@ pub fn IpCheckConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - use_effect_with(form_state.clone(), move |state| { - on_form_change.emit(ConfigForm::IpCheck(state.modified, state.form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::IpCheck(*modified, state.form.clone())); }); } diff --git a/frontend/src/app/components/config/main_config_view.rs b/frontend/src/app/components/config/main_config_view.rs index 8ec44f28f..48aa3ad91 100644 --- a/frontend/src/app/components/config/main_config_view.rs +++ b/frontend/src/app/components/config/main_config_view.rs @@ -49,8 +49,9 @@ pub fn MainConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - use_effect_with(form_state.clone(), move |state| { - on_form_change.emit(ConfigForm::Main(state.modified, state.form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::Main(*modified, state.form.clone())); }); } diff --git a/frontend/src/app/components/config/messaging_config_view.rs b/frontend/src/app/components/config/messaging_config_view.rs index 1d113b804..aadaf6f81 100644 --- a/frontend/src/app/components/config/messaging_config_view.rs +++ b/frontend/src/app/components/config/messaging_config_view.rs @@ -1,15 +1,20 @@ -use std::rc::Rc; -use std::str::FromStr; -use crate::app::components::{Card, Chip, RadioButtonGroup}; -use crate::app::context::ConfigContext; -use crate::{config_field, config_field_child, config_field_empty, config_field_hide, config_field_optional, edit_field_list, edit_field_text, edit_field_text_option, generate_form_reducer}; -use shared::model::{MessagingConfigDto, MsgKind, PushoverMessagingConfigDto, RestMessagingConfigDto, - TelegramMessagingConfigDto}; -use yew::prelude::*; -use yew_i18n::use_translation; use crate::app::components::config::config_page::ConfigForm; use crate::app::components::config::config_view_context::ConfigViewContext; use crate::app::components::config::macros::HasFormData; +use crate::app::components::{Card, Chip, RadioButtonGroup}; +use crate::app::context::ConfigContext; +use crate::{ + config_field, config_field_child, config_field_empty, config_field_hide, config_field_optional, + edit_field_list, edit_field_text, edit_field_text_option, generate_form_reducer, +}; +use shared::model::{ + MessagingConfigDto, MsgKind, PushoverMessagingConfigDto, RestMessagingConfigDto, + TelegramMessagingConfigDto, +}; +use std::rc::Rc; +use std::str::FromStr; +use yew::prelude::*; +use yew_i18n::use_translation; const LABEL_NOTIFY_ON: &str = "LABEL.NOTIFY_ON"; const LABEL_TELEGRAM: &str = "LABEL.TELEGRAM"; @@ -56,34 +61,43 @@ generate_form_reducer!( } ); - #[function_component] pub fn MessagingConfigView() -> Html { let translate = use_translation(); let config_ctx = use_context::().expect("ConfigContext not found"); let config_view_ctx = use_context::().expect("ConfigViewContext not found"); - let telegram_state: UseReducerHandle = use_reducer(|| { - TelegramMessagingConfigFormState { form: TelegramMessagingConfigDto::default(), modified: false } - }); - let rest_state: UseReducerHandle = use_reducer(|| { - RestMessagingConfigFormState { form: RestMessagingConfigDto::default(), modified: false } - }); + let telegram_state: UseReducerHandle = + use_reducer(|| TelegramMessagingConfigFormState { + form: TelegramMessagingConfigDto::default(), + modified: false, + }); + let rest_state: UseReducerHandle = + use_reducer(|| RestMessagingConfigFormState { + form: RestMessagingConfigDto::default(), + modified: false, + }); - let pushover_state: UseReducerHandle = use_reducer(|| { - PushoverMessagingConfigFormState { form: PushoverMessagingConfigDto::default(), modified: false } - }); + let pushover_state: UseReducerHandle = + use_reducer(|| PushoverMessagingConfigFormState { + form: PushoverMessagingConfigDto::default(), + modified: false, + }); - let messaging_state: UseReducerHandle = use_reducer(|| { - MessagingConfigFormState { form: MessagingConfigDto::default(), modified: false } - }); + let messaging_state: UseReducerHandle = + use_reducer(|| MessagingConfigFormState { + form: MessagingConfigDto::default(), + modified: false, + }); - let notify_on_options = use_memo((), |_| vec![ - MsgKind::Info.to_string(), - MsgKind::Stats.to_string(), - MsgKind::Error.to_string(), - MsgKind::Watch.to_string(), - ]); + let notify_on_options = use_memo((), |_| { + vec![ + MsgKind::Info.to_string(), + MsgKind::Stats.to_string(), + MsgKind::Error.to_string(), + MsgKind::Watch.to_string(), + ] + }); { let on_form_change = config_view_ctx.on_form_change.clone(); @@ -92,87 +106,88 @@ pub fn MessagingConfigView() -> Html { let rest_state = rest_state.clone(); let pushover_state = pushover_state.clone(); - use_effect_with( - (messaging_state, telegram_state, rest_state, pushover_state), - move |(m, t, r, p)| { - let mut form = m.form.clone(); - form.telegram = Some(t.form.clone()); - form.rest = Some(r.form.clone()); - form.pushover = Some(p.form.clone()); - - let modified = m.modified || t.modified || r.modified || p.modified; - on_form_change.emit(ConfigForm::Messaging(modified, form)); - }, + let deps = ( + messaging_state.modified, + telegram_state.modified, + rest_state.modified, + pushover_state.modified, + messaging_state, + telegram_state, + rest_state, + pushover_state, ); + use_effect_with(deps, move |(mm, tm, rm, pm, m, t, r, p)| { + let mut form = m.form.clone(); + form.telegram = Some(t.form.clone()); + form.rest = Some(r.form.clone()); + form.pushover = Some(p.form.clone()); + + let modified = *mm || *tm || *rm || *pm; + on_form_change.emit(ConfigForm::Messaging(modified, form)); + }); } - let render_telegram = |telegram: Option<&TelegramMessagingConfigDto>| { - match telegram { - Some(entry) => html! { - -

{translate.t("LABEL.TELEGRAM")}

- { config_field!(entry, translate.t(LABEL_BOT_TOKEN), bot_token) } - { config_field_child!(translate.t(LABEL_CHAT_IDS), { - html! { -
- { - if entry.chat_ids.is_empty() { - html! {} - } else { - html! { for entry.chat_ids.iter().map(|t| html! { }) } - } - } -
- } - })} -
- }, - None => html! { - -

{translate.t(LABEL_TELEGRAM)}

- { config_field_empty!(translate.t(LABEL_BOT_TOKEN)) } - { config_field_empty!(translate.t(LABEL_CHAT_IDS)) } -
- }, - } + let render_telegram = |telegram: Option<&TelegramMessagingConfigDto>| match telegram { + Some(entry) => html! { + +

{translate.t("LABEL.TELEGRAM")}

+ { config_field!(entry, translate.t(LABEL_BOT_TOKEN), bot_token) } + { config_field_child!(translate.t(LABEL_CHAT_IDS), { + html! { +
+ { + if entry.chat_ids.is_empty() { + html! {} + } else { + html! { for entry.chat_ids.iter().map(|t| html! { }) } + } + } +
+ } + })} +
+ }, + None => html! { + +

{translate.t(LABEL_TELEGRAM)}

+ { config_field_empty!(translate.t(LABEL_BOT_TOKEN)) } + { config_field_empty!(translate.t(LABEL_CHAT_IDS)) } +
+ }, }; - let render_rest = |rest: Option<&RestMessagingConfigDto>| { - match rest { - Some(entry) => html! { - -

{translate.t(LABEL_REST)}

- { config_field!(entry, translate.t(LABEL_URL), url) } -
- }, - None => html! { - -

{translate.t(LABEL_REST)}

- { config_field_empty!(translate.t(LABEL_URL)) } -
- }, - } + let render_rest = |rest: Option<&RestMessagingConfigDto>| match rest { + Some(entry) => html! { + +

{translate.t(LABEL_REST)}

+ { config_field!(entry, translate.t(LABEL_URL), url) } +
+ }, + None => html! { + +

{translate.t(LABEL_REST)}

+ { config_field_empty!(translate.t(LABEL_URL)) } +
+ }, }; - let render_pushover = |pushover: Option<&PushoverMessagingConfigDto>| { - match pushover { - Some(entry) => html! { - -

{translate.t(LABEL_PUSHOVER)}

- { config_field_optional!(entry, translate.t(LABEL_URL), url) } - { config_field_hide!(entry, translate.t(LABEL_TOKEN), token) } - { config_field!(entry, translate.t(LABEL_USER), user) } -
- }, - None => html! { - -

{translate.t(LABEL_PUSHOVER)}

- { config_field_empty!(translate.t(LABEL_URL)) } - { config_field_empty!(translate.t(LABEL_TOKEN)) } - { config_field_empty!(translate.t(LABEL_USER)) } -
- }, - } + let render_pushover = |pushover: Option<&PushoverMessagingConfigDto>| match pushover { + Some(entry) => html! { + +

{translate.t(LABEL_PUSHOVER)}

+ { config_field_optional!(entry, translate.t(LABEL_URL), url) } + { config_field_hide!(entry, translate.t(LABEL_TOKEN), token) } + { config_field!(entry, translate.t(LABEL_USER), user) } +
+ }, + None => html! { + +

{translate.t(LABEL_PUSHOVER)}

+ { config_field_empty!(translate.t(LABEL_URL)) } + { config_field_empty!(translate.t(LABEL_TOKEN)) } + { config_field_empty!(translate.t(LABEL_USER)) } +
+ }, }; let render_empty = || { @@ -194,21 +209,21 @@ pub fn MessagingConfigView() -> Html { if let Some(config) = &config_ctx.config { if let Some(messaging) = &config.config.messaging { html! { - <> -
- { config_field_child!(translate.t(LABEL_NOTIFY_ON), { - html! {
- { for messaging.notify_on.iter().map(|t| html! { }) } -
} - })} -
-
- {render_telegram(messaging.telegram.as_ref())} - {render_rest(messaging.rest.as_ref())} - {render_pushover(messaging.pushover.as_ref())} -
- - } + <> +
+ { config_field_child!(translate.t(LABEL_NOTIFY_ON), { + html! {
+ { for messaging.notify_on.iter().map(|t| html! { }) } +
} + })} +
+
+ {render_telegram(messaging.telegram.as_ref())} + {render_rest(messaging.rest.as_ref())} + {render_pushover(messaging.pushover.as_ref())} +
+ + } } else { render_empty() } @@ -219,47 +234,55 @@ pub fn MessagingConfigView() -> Html { let render_edit_mode = || { let msg_state = messaging_state.clone(); - let notify_on_selections = Rc::new(msg_state.form.notify_on.iter().map(ToString::to_string).collect()); + let notify_on_selections = Rc::new( + msg_state + .form + .notify_on + .iter() + .map(ToString::to_string) + .collect(), + ); html! { - <> -
- { config_field_child!(translate.t("LABEL.NOTIFY_ON"), { - html! { >| { - msg_state.dispatch(MessagingConfigFormAction::NotifyOn( - selections.iter().filter_map(|s| MsgKind::from_str(s).ok()).collect())); - })} - options={notify_on_options.clone()} - selected={notify_on_selections} - /> - }})} -
-
- -

{translate.t(LABEL_TELEGRAM)}

- { edit_field_text!(telegram_state, translate.t(LABEL_BOT_TOKEN), bot_token, TelegramMessagingConfigFormAction::BotToken) } - { edit_field_list!(telegram_state, translate.t(LABEL_CHAT_IDS), chat_ids, TelegramMessagingConfigFormAction::ChatIds, translate.t("LABEL.ADD_CHAT_ID")) } -
+ <> +
+ { config_field_child!(translate.t("LABEL.NOTIFY_ON"), { + html! { >| { + msg_state.dispatch(MessagingConfigFormAction::NotifyOn( + selections.iter().filter_map(|s| MsgKind::from_str(s).ok()).collect())); + })} + options={notify_on_options.clone()} + selected={notify_on_selections} + /> + }})} +
+
+ +

{translate.t(LABEL_TELEGRAM)}

+ { edit_field_text!(telegram_state, translate.t(LABEL_BOT_TOKEN), bot_token, TelegramMessagingConfigFormAction::BotToken) } + { edit_field_list!(telegram_state, translate.t(LABEL_CHAT_IDS), chat_ids, TelegramMessagingConfigFormAction::ChatIds, translate.t("LABEL.ADD_CHAT_ID")) } +
- -

{translate.t(LABEL_REST)}

- { edit_field_text!(rest_state, translate.t(LABEL_URL), url, RestMessagingConfigFormAction::Url) } -
+ +

{translate.t(LABEL_REST)}

+ { edit_field_text!(rest_state, translate.t(LABEL_URL), url, RestMessagingConfigFormAction::Url) } +
- -

{translate.t(LABEL_PUSHOVER)}

- { edit_field_text_option!(pushover_state, translate.t(LABEL_URL), url, PushoverMessagingConfigFormAction::Url) } - { edit_field_text!(pushover_state, translate.t(LABEL_TOKEN), token, PushoverMessagingConfigFormAction::Token, true) } - { edit_field_text!(pushover_state, translate.t(LABEL_USER), user, PushoverMessagingConfigFormAction::User) } -
-
- - }}; + +

{translate.t(LABEL_PUSHOVER)}

+ { edit_field_text_option!(pushover_state, translate.t(LABEL_URL), url, PushoverMessagingConfigFormAction::Url) } + { edit_field_text!(pushover_state, translate.t(LABEL_TOKEN), token, PushoverMessagingConfigFormAction::Token, true) } + { edit_field_text!(pushover_state, translate.t(LABEL_USER), user, PushoverMessagingConfigFormAction::User) } +
+
+ + } + }; html! {
{ if *config_view_ctx.edit_mode { render_edit_mode() } else { render_view_mode() } }
} -} \ No newline at end of file +} diff --git a/frontend/src/app/components/config/proxy_config_view.rs b/frontend/src/app/components/config/proxy_config_view.rs index 402ba306d..2e2bf23b1 100644 --- a/frontend/src/app/components/config/proxy_config_view.rs +++ b/frontend/src/app/components/config/proxy_config_view.rs @@ -36,8 +36,9 @@ pub fn ProxyConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - use_effect_with(form_state.clone(), move |state| { - on_form_change.emit(ConfigForm::Proxy(state.modified, state.form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::Proxy(*modified, state.form.clone())); }); } diff --git a/frontend/src/app/components/config/reverse_proxy_config_view.rs b/frontend/src/app/components/config/reverse_proxy_config_view.rs index 9afc89365..248de5189 100644 --- a/frontend/src/app/components/config/reverse_proxy_config_view.rs +++ b/frontend/src/app/components/config/reverse_proxy_config_view.rs @@ -123,23 +123,9 @@ pub fn ReverseProxyConfigView() -> Html { use_effect_with((reverse_proxy_cfg, config_view_ctx.edit_mode.clone()), move |(cfg, _mode)| { if let Some(rp) = cfg { reverse_proxy_state.dispatch(ReverseProxyConfigFormAction::SetAll((*rp).clone())); - - if let Some(cache) = &rp.cache { - cache_state.dispatch(CacheConfigFormAction::SetAll(cache.clone())); - } else { - cache_state.dispatch(CacheConfigFormAction::SetAll(CacheConfigDto::default())); - } - if let Some(rl) = &rp.rate_limit { - rate_limit_state.dispatch(RateLimitConfigFormAction::SetAll(rl.clone())); - } else { - rate_limit_state.dispatch(RateLimitConfigFormAction::SetAll(RateLimitConfigDto::default())); - } - if let Some(stream) = &rp.stream { - stream_state.dispatch(StreamConfigFormAction::SetAll(stream.clone())); - } else { - stream_state.dispatch(StreamConfigFormAction::SetAll(StreamConfigDto::default())); - } - + cache_state.dispatch(CacheConfigFormAction::SetAll(rp.cache.as_ref().map_or_else(CacheConfigDto::default, |c| c.clone()))); + rate_limit_state.dispatch(RateLimitConfigFormAction::SetAll(rp.rate_limit.as_ref().map_or_else(RateLimitConfigDto::default, |rl| rl.clone()))); + stream_state.dispatch(StreamConfigFormAction::SetAll(rp.stream.as_ref().map_or_else(StreamConfigDto::default, |s| s.clone()))); } else { reverse_proxy_state.dispatch(ReverseProxyConfigFormAction::SetAll(ReverseProxyConfigDto::default())); cache_state.dispatch(CacheConfigFormAction::SetAll(CacheConfigDto::default())); diff --git a/frontend/src/app/components/config/schedules_config_view.rs b/frontend/src/app/components/config/schedules_config_view.rs index 707c4fb7c..c4e44b014 100644 --- a/frontend/src/app/components/config/schedules_config_view.rs +++ b/frontend/src/app/components/config/schedules_config_view.rs @@ -61,8 +61,9 @@ pub fn SchedulesConfigView() -> Html { { let on_form_change = config_view_ctx.on_form_change.clone(); - use_effect_with(form_state.clone(), move |state| { - on_form_change.emit(ConfigForm::Schedules(state.modified, state.form.clone())); + let deps = (form_state.clone(), form_state.modified); + use_effect_with(deps, move |(state, modified)| { + on_form_change.emit(ConfigForm::Schedules(*modified, state.form.clone())); }); } diff --git a/frontend/src/app/components/config/video_config_view.rs b/frontend/src/app/components/config/video_config_view.rs index cf1a7cb0d..b564e41d5 100644 --- a/frontend/src/app/components/config/video_config_view.rs +++ b/frontend/src/app/components/config/video_config_view.rs @@ -79,7 +79,8 @@ pub fn VideoConfigView() -> Html { let on_form_change = config_view_ctx.on_form_change.clone(); let download_state = download_state.clone(); let video_state = video_state.clone(); - use_effect_with((video_state.modified, download_state.modified, video_state, download_state), + let deps = (video_state.modified, download_state.modified, video_state, download_state); + use_effect_with(deps, move |(vm, dm, v, d)| { let mut form = v.form.clone(); form.download = if *dm { Some(d.form.clone()) } else { form.download }; diff --git a/frontend/src/app/components/config/webui_config_view.rs b/frontend/src/app/components/config/webui_config_view.rs index 61560659b..82cd671a5 100644 --- a/frontend/src/app/components/config/webui_config_view.rs +++ b/frontend/src/app/components/config/webui_config_view.rs @@ -79,8 +79,8 @@ pub fn WebUiConfigView() -> Html { let webui_state = webui_state.clone(); let auth_state = auth_state.clone(); let csp_state = csp_state.clone(); - - use_effect_with((webui_state.modified, auth_state.modified, csp_state.modified, webui_state, auth_state, csp_state), + let deps = (webui_state.modified, auth_state.modified, csp_state.modified, webui_state, auth_state, csp_state); + use_effect_with(deps, move |(wm, am, cm, w, a, c)| { let mut form = w.form.clone(); form.auth = Some(a.form.clone()); diff --git a/frontend/src/app/components/tabset.rs b/frontend/src/app/components/tabset.rs index f2dcacac7..08a861bce 100644 --- a/frontend/src/app/components/tabset.rs +++ b/frontend/src/app/components/tabset.rs @@ -12,6 +12,19 @@ pub struct TabItem { pub inactive_class: Option, } +// impl TabItem { +// pub fn new(id: String, title: String, icon: String, children: Html) -> Self { +// Self { +// id, +// title, +// icon, +// children, +// active_class: None, +// inactive_class: None, +// } +// } +// } + #[derive(Properties, Clone, PartialEq)] pub struct TabSetProps { pub tabs: Rc>,