This commit is contained in:
euzu
2025-07-05 19:55:43 +02:00
parent a9cecb056b
commit 2b9a51fa17
22 changed files with 197 additions and 26 deletions
-1
View File
@@ -147,7 +147,6 @@ impl ConfigInputAliasDto {
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct ConfigInputDto {
+5 -4
View File
@@ -1,3 +1,4 @@
use std::rc::Rc;
use crate::error::{TuliproxError, TuliproxErrorKind};
use crate::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err};
use crate::foundation::filter::{get_filter, Filter};
@@ -86,10 +87,10 @@ pub struct HdHomeRunTargetOutputDto {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(deny_unknown_fields, tag = "type", rename_all = "lowercase")]
pub enum TargetOutputDto {
Xtream(XtreamTargetOutputDto),
M3u(M3uTargetOutputDto),
Strm(StrmTargetOutputDto),
HdHomeRun(HdHomeRunTargetOutputDto),
Xtream(Rc<XtreamTargetOutputDto>),
M3u(Rc<M3uTargetOutputDto>),
Strm(Rc<StrmTargetOutputDto>),
HdHomeRun(Rc<HdHomeRunTargetOutputDto>),
}
impl TargetOutputDto {
+5
View File
@@ -14,4 +14,9 @@ $media-size-mobile: 600px;
.tp__pulse {
animation: tp__pulse-bg 500ms ease-out;
}
.tp__error-text {
color: var(--attention-color);
font-size: 1rem;
}
+1
View File
@@ -18,3 +18,4 @@
@forward "dashboard_view";
@forward "playlist_view";
@forward "playlist_assistant";
@forward "reveal_content";
+1 -1
View File
@@ -4,7 +4,7 @@
overflow: hidden;
box-sizing: border-box;
gap: 6px;
.input-wrapper {
.tp__input-wrapper {
overflow: hidden;
box-sizing: border-box;
border: 1px solid var(--input-border-color);
-5
View File
@@ -60,10 +60,5 @@
gap: 20px;
padding-top: 20px;
}
.error-text {
color: var(--attention-color);
font-size: 1rem;
}
}
}
+15
View File
@@ -0,0 +1,15 @@
.tp__reveal_content {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
fill: var(--modest-text-color);
&:hover {
fill: var(--text-color);
}
.svg-icon {
height: 2rem;
pointer-events: none;
}
}
+2 -2
View File
@@ -39,8 +39,8 @@
--text-button-primary-hover-background-color: #2f68be;
--text-button-primary-hover-color: #ffffff;
--toggle-switch-on-background-color: var(--primary-color);
--toggle-switch-off-background-color: #888888;
--toggle-switch-on-background-color: #36c55a;
--toggle-switch-off-background-color: #b3b3b6;
--toggle-switch-toggle-color: #ffffff;
--table-header-background-color: #242427;
+7 -2
View File
@@ -1,4 +1,4 @@
$toggle-switch-width: 3.2rem;
$toggle-switch-width: 2.8rem;
$toggle-switch-height: 1.6rem;
$toggle-switch-toggle-padding: 3px;
$toggle-switch-toggle-width: calc($toggle-switch-height - $toggle-switch-toggle-padding * 2);
@@ -9,6 +9,11 @@ $toggle-switch-toggle-end-pos: calc($toggle-switch-width - ($toggle-switch-toggl
display: inline-block;
width: $toggle-switch-width;
height: $toggle-switch-height;
cursor: pointer;
&__readonly {
cursor: default;
}
input {
opacity: 0; width: 0; height: 0
}
@@ -20,7 +25,6 @@ $toggle-switch-toggle-end-pos: calc($toggle-switch-width - ($toggle-switch-toggl
}
&__track {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: var(--toggle-switch-off-background-color);
border-radius: $toggle-switch-toggle-width;
@@ -37,6 +41,7 @@ $toggle-switch-toggle-end-pos: calc($toggle-switch-width - ($toggle-switch-toggl
border-radius: 50%;
transition: 0.3s;
transform: translateX(0);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.8);
}
&__active {
+2 -2
View File
@@ -77,8 +77,8 @@ pub fn Login() -> Html {
<Input label={translation.t("LABEL.USERNAME")} input_ref={username_ref} input_type="text" name="username" autocomplete={true} autofocus={true}/>
<Input label={translation.t("LABEL.PASSWORD")} input_ref={password_ref} input_type="password" name="password" autocomplete={true} onkeydown={handle_key_down}/>
<div class="tp__login-view__form-action">
<TextButton style="tp__primary" name="login" title={ translation.t("LABEL.LOGIN")} onclick={handle_login}></TextButton>
<span class={if *auth_success { "tp__hidden" } else { "error-text" }}>{ "Failed to login" }</span>
<TextButton style="primary" name="login" title={ translation.t("LABEL.LOGIN")} onclick={handle_login}></TextButton>
<span class={if *auth_success { "tp__hidden" } else { "tp__error-text" }}>{ "Failed to login" }</span>
</div>
</div>
</form>
+1
View File
@@ -18,6 +18,7 @@ mod breadcrumbs;
mod playlist;
mod table;
mod toggle_switch;
mod reveal_content;
pub use self::svg_icon::*;
// pub use self::input::*;
+1 -1
View File
@@ -10,7 +10,7 @@ pub struct PanelProps {
#[function_component]
pub fn Panel(props: &PanelProps) -> Html {
html! {
<div class={classes!("panel", if props.value == props.active {""} else {"tp__hidden"} )}>
<div class={classes!("tp__panel", if props.value == props.active {""} else {"tp__hidden"} )}>
{ for props.children.iter() }
</div>
}
+3 -1
View File
@@ -4,10 +4,12 @@ mod create;
mod context;
mod page;
mod target_table;
mod target;
pub use self::assistant::*;
pub use self::list::*;
pub use self::create::*;
pub use self::context::*;
pub use self::page::*;
pub use self::target_table::*;
pub use self::target_table::*;
pub use self::target::*;
@@ -0,0 +1,10 @@
use yew::prelude::*;
#[function_component]
pub fn HdHomerunOutput() -> Html {
html! {
<div class="tp__hdhomerun_output">
</div>
}
}
@@ -0,0 +1,10 @@
use yew::prelude::*;
#[function_component]
pub fn M3uOutput() -> Html {
html! {
<div class="tp__m3u_output">
</div>
}
}
@@ -0,0 +1,11 @@
mod xtream_output;
mod m3u_output;
mod strm_output;
mod hdhomerun_output;
mod target_output;
pub use self::xtream_output::*;
pub use self::m3u_output::*;
pub use self::strm_output::*;
pub use self::hdhomerun_output::*;
pub use self::target_output::*;
@@ -0,0 +1,10 @@
use yew::prelude::*;
#[function_component]
pub fn StrmOutput() -> Html {
html! {
<div class="tp__strm_output">
</div>
}
}
@@ -0,0 +1,27 @@
use std::rc::Rc;
use yew::prelude::*;
use shared::model::{ConfigTargetDto, TargetOutputDto};
use crate::app::components::{HdHomerunOutput, M3uOutput, StrmOutput, XtreamOutput};
#[derive(Properties, Clone, PartialEq, Debug)]
pub struct TargetOutputProps {
pub target: Rc<ConfigTargetDto>,
}
#[function_component]
pub fn TargetOutput(props: &TargetOutputProps) -> Html {
html! {
<div class="tp__target_output">
{
props.target.output.iter().map(|output| {
match output {
TargetOutputDto::Xtream(xc) => html! { <XtreamOutput output={xc} /> },
TargetOutputDto::M3u(m3u) => html! { <M3uOutput /> },
TargetOutputDto::Strm(strm) => html! { <StrmOutput /> },
TargetOutputDto::HdHomeRun(hdhr) => html! { <HdHomerunOutput /> },
}
}).collect::<Html>()
}
</div>
}
}
@@ -0,0 +1,49 @@
use std::rc::Rc;
use yew::prelude::*;
use shared::model::XtreamTargetOutputDto;
#[derive(Properties, PartialEq, Clone)]
pub struct XtreamOutputProps {
pub output: XtreamTargetOutputDto,
}
#[function_component]
pub fn XtreamOutput(props: &XtreamOutputProps) -> Html {
html! {
<div class="tp__xtream_output">
</div>
}
}
/*
<div class="tp__tp__target_output_row">
<label>{translate.t("LABEL.SKIP_DIRECT_SOURCE")}</label>
<TagList>
<Tag style={if xc.skip_live_direct_source { "on" } else { "off "}} label={translate.t("LABEL.LIVE")}/>
<Tag style={if xc.skip_vod_direct_source { "on" } else { "off "}} label={translate.t("LABEL.VOD")}/>
<Tag style={if xc.skip_series_direct_source { "on" } else { "off "}} label={translate.t("LABEL.SERIES")}/>
</TagList>
</div>
<div class="tp__tp__target_output_row"><label>{translate.t("LABEL.RESOVE")}</label></div>
#[serde(default = "default_as_true")]
pub skip_live_direct_source: bool,
#[serde(default = "default_as_true")]
pub skip_video_direct_source: bool,
#[serde(default = "default_as_true")]
pub skip_series_direct_source: bool,
#[serde(default)]
pub resolve_series: bool,
#[serde(default = "default_resolve_delay_secs")]
pub resolve_series_delay: u16,
#[serde(default)]
pub resolve_vod: bool,
#[serde(default = "default_resolve_delay_secs")]
pub resolve_vod_delay: u16,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trakt: Option<TraktConfigDto>,
<span>{"xc"}</span>
},
*/
@@ -5,7 +5,8 @@ use yew::prelude::*;
use yew::suspense::use_future;
use yew_i18n::use_translation;
use shared::model::{ConfigTargetDto};
use crate::app::components::{PlaylistContext, Table, TableDefinition, ToggleSwitch};
use crate::app::components::{PlaylistContext, Table, TableDefinition, TargetOutput, ToggleSwitch};
use crate::app::components::reveal_content::RevealContent;
use crate::hooks::use_service_context;
const HEADERS: [&str; 11] = [
@@ -47,12 +48,12 @@ pub fn TargetTable() -> Html {
move |(_row, col, dto): (usize, usize, Rc<ConfigTargetDto>)| {
match col {
0 => html! { &dto.id.to_string() },
1 => html! { <ToggleSwitch value={&dto.enabled} /> },
1 => html! { <ToggleSwitch readonly={false} value={&dto.enabled} /> },
2 => html! { &dto.name.to_string() },
3 => html! { "options" },
4 => html! { dto.sort.as_ref().map_or_else(String::new, |s| format!("{s:?}")) },
3 => html! { <RevealContent>{"Hello"}</RevealContent> },
4 => html! { <RevealContent>{dto.sort.as_ref().map_or_else(String::new, |s| format!("{s:?}"))}</RevealContent> },
5 => html! { &dto.filter.clone() },
6 => html! { "output" },
6 => html! { <TargetOutput target={Rc::clone(&dto)} /> },
7 => html! { "rename" },
8 => html! { dto.mapping.as_ref().map_or_else(String::new, |m| format!("{m:?}")) },
9 => html! { &dto.processing_order.to_string() },
@@ -0,0 +1,19 @@
use yew::prelude::*;
use crate::app::components::AppIcon;
#[derive(Properties, Clone, PartialEq, Debug)]
pub struct RevealContentProps {
#[prop_or_default]
pub icon: String,
pub children: Children,
}
#[function_component]
pub fn RevealContent(props: &RevealContentProps) -> Html {
html! {
<div class={"tp__reveal_content"}>
<AppIcon name={if props.icon.is_empty() {"Visibility".to_string()} else {props.icon.to_string()} } />
// { for props.children.iter() }
</div>
}
}
+12 -2
View File
@@ -1,9 +1,12 @@
use log::info;
use yew::prelude::*;
#[derive(Properties, Clone, PartialEq, Debug)]
pub struct ToggleSwitchProps {
#[prop_or_default]
pub value: bool,
#[prop_or_default]
pub readonly: bool,
}
#[function_component]
@@ -12,11 +15,18 @@ pub fn ToggleSwitch(props: &ToggleSwitchProps) -> Html {
let onclick = {
let toggled = toggled.clone();
Callback::from(move |_| toggled.set(!*toggled))
let readonly = props.readonly;
Callback::from(move |e: MouseEvent| {
if readonly {
e.prevent_default();
return;
}
toggled.set(!*toggled)
})
};
html! {
<label class="tp__toggle-switch">
<label class={classes!("tp__toggle-switch", if props.readonly { "tp__toggle-switch__readonly" } else {""})}>
<input type="checkbox"
checked={*toggled}
onclick={onclick}/>