mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-08 00:42:32 +02:00
Filter Input and Filter editor started
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
"authUrl": "/auth/"
|
||||
},
|
||||
"discord": "https://discord.gg/gkzCmWw9Tf",
|
||||
"documentation": "https://github.com/euzu/tuliprox/blob/master/README.md",
|
||||
"documentation": "https://euzu.github.io/tuliprox-docs/",
|
||||
"wsUrl" : "/ws",
|
||||
"protocolVersion": 1
|
||||
}
|
||||
@@ -81,3 +81,4 @@
|
||||
@forward "components/source_editor/input_form";
|
||||
@forward "components/cluster_flags";
|
||||
@forward "components/titled_card";
|
||||
@forward "components/filter_input";
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
.bracket-3 { color: var(--code-bracket-3-color); }
|
||||
}
|
||||
.tp__filter__inline {
|
||||
overflow: hidden;
|
||||
.tp__filter__code {
|
||||
padding: 0;
|
||||
margin-block: 0;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
.tp__filter-input {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
fill: var(--modest-text-color);
|
||||
|
||||
.tp__input-wrapper {
|
||||
height: 2.5rem;
|
||||
padding: 0 var(--padding-mini);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
fill: var(--text-color);
|
||||
cursor: pointer;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
&__preview {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-height: 2rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
flex-flow: column;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
gap: 6px;
|
||||
gap: var(--gap-small);
|
||||
.tp__input-wrapper {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
use crate::app::components::{AppIcon, FilterView};
|
||||
use crate::services::DialogService;
|
||||
use yew::platform::spawn_local;
|
||||
use yew::prelude::*;
|
||||
use shared::foundation::filter::get_filter;
|
||||
use crate::app::{ConfigContext};
|
||||
|
||||
#[derive(Properties, Clone, PartialEq, Debug)]
|
||||
pub struct FilterInputProps {
|
||||
#[prop_or_default]
|
||||
pub icon: String,
|
||||
#[prop_or_default]
|
||||
pub filter: Option<String>,
|
||||
#[prop_or_default]
|
||||
pub on_change: Callback<Option<String>>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn FilterInput(props: &FilterInputProps) -> Html {
|
||||
let config_ctx = use_context::<ConfigContext>().expect("Config context not found");
|
||||
let dialog = use_context::<DialogService>().expect("Dialog service not found");
|
||||
|
||||
let filter_state = use_state(|| None);
|
||||
let parsed_filter_state = use_state(|| None);
|
||||
let templates_state = use_state(|| None);
|
||||
|
||||
{
|
||||
let templates = templates_state.clone();
|
||||
let cfg_templates = config_ctx.config.as_ref().and_then(|c| c.sources.templates.clone());
|
||||
use_effect_with(cfg_templates, move |templ| {
|
||||
templates.set(templ.clone());
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let filter = filter_state.clone();
|
||||
let parsed_filter = parsed_filter_state.clone();
|
||||
let templates = templates_state.clone();
|
||||
use_effect_with(props.filter.clone(), move |flt| {
|
||||
filter.set(flt.clone());
|
||||
let parsed = if let Some(new_fltr) = flt.as_ref() {
|
||||
get_filter(new_fltr, (*templates).as_ref()).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
parsed_filter.set(parsed);
|
||||
});
|
||||
}
|
||||
|
||||
let handle_click = {
|
||||
let dialog = dialog.clone();
|
||||
let current_filter = filter_state.clone();
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.prevent_default();
|
||||
let dlg = dialog.clone();
|
||||
spawn_local(async move {
|
||||
//let filter_view = html!{<FilterView pretty={true} filter={current_filter} />};
|
||||
//let _result = dlg.content(filter_view, None).await;
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class={"tp__filter-input tp__input"} onclick={handle_click}>
|
||||
<div class={"tp__input-wrapper"}>
|
||||
<span class="tp__filter-input__preview">
|
||||
{
|
||||
match (*parsed_filter_state).as_ref() {
|
||||
None => html! {},
|
||||
Some(preview) => html! {
|
||||
<FilterView inline={true} filter={preview.clone()} />
|
||||
}
|
||||
}
|
||||
}
|
||||
</span>
|
||||
<AppIcon name={if props.icon.is_empty() { "Edit".to_owned() } else { props.icon.clone()} } />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod filter_editor;
|
||||
mod filter_input;
|
||||
|
||||
pub use filter_editor::*;
|
||||
pub use filter_input::*;
|
||||
@@ -50,9 +50,12 @@ mod floating_background;
|
||||
mod source_editor;
|
||||
mod cluster_flags_input;
|
||||
mod title_card;
|
||||
mod filter;
|
||||
// pub use self::input::*;
|
||||
// pub use self::menu_item::*;
|
||||
// pub use self::popup_menu::*;
|
||||
//pub use self::number_input::*;
|
||||
//pub use self::date_input::*;
|
||||
|
||||
pub(crate) use self::cluster_flags_input::*;
|
||||
pub(crate) use self::toggle_switch::*;
|
||||
@@ -94,6 +97,4 @@ pub(crate) use self::loading_screen::*;
|
||||
pub(crate) use self::websocket_status::*;
|
||||
pub(crate) use self::source_editor::*;
|
||||
pub(crate) use self::title_card::*;
|
||||
|
||||
//pub use self::number_input::*;
|
||||
//pub use self::date_input::*;
|
||||
pub(crate) use self::filter::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::app::components::config::HasFormData;
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, EditMode, SourceEditorContext, TextButton};
|
||||
use crate::{edit_field_bool, edit_field_text_option, generate_form_reducer};
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, EditMode, FilterInput, SourceEditorContext, TextButton};
|
||||
use crate::{config_field_child, edit_field_bool, edit_field_text_option, generate_form_reducer};
|
||||
use shared::model::{M3uTargetOutputDto, TargetOutputDto};
|
||||
use std::rc::Rc;
|
||||
use yew::{function_component, html, use_context, use_effect_with, use_reducer, Callback, Html, Properties, UseReducerHandle};
|
||||
@@ -54,12 +54,19 @@ pub fn M3uTargetOutputView(props: &M3uTargetOutputViewProps) -> Html {
|
||||
}
|
||||
|
||||
let render_output = || {
|
||||
let output_form_state_1 = output_form_state.clone();
|
||||
html! {
|
||||
<Card class="tp__config-view__card">
|
||||
{ edit_field_text_option!(output_form_state, translate.t(LABEL_FILENAME), filename, M3uTargetOutputFormAction::Filename) }
|
||||
{ edit_field_bool!(output_form_state, translate.t(LABEL_INCLUDE_TYPE_IN_URL), include_type_in_url, M3uTargetOutputFormAction::IncludeTypeInUrl) }
|
||||
{ edit_field_bool!(output_form_state, translate.t(LABEL_MASK_REDIRECT_URL), mask_redirect_url, M3uTargetOutputFormAction::MaskRedirectUrl) }
|
||||
{ edit_field_text_option!(output_form_state, translate.t(LABEL_FILTER), filter, M3uTargetOutputFormAction::Filter) }
|
||||
{ config_field_child!(translate.t(LABEL_FILTER), {
|
||||
html! {
|
||||
<FilterInput filter={output_form_state_1.form.filter.clone()} on_change={Callback::from(move |new_filter| {
|
||||
output_form_state_1.dispatch(M3uTargetOutputFormAction::Filter(new_filter));
|
||||
})} />
|
||||
}
|
||||
})}
|
||||
</Card>
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::app::components::config::HasFormData;
|
||||
use crate::app::components::select::Select;
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, DropDownOption, DropDownSelection, EditMode, Panel, SourceEditorContext, TextButton};
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, DropDownOption, DropDownSelection, EditMode, FilterInput, Panel, SourceEditorContext, TextButton};
|
||||
use crate::{config_field_child, edit_field_bool, edit_field_list_option, edit_field_text, edit_field_text_option, generate_form_reducer};
|
||||
use shared::model::{StrmExportStyle, StrmTargetOutputDto, TargetOutputDto};
|
||||
use std::fmt::Display;
|
||||
@@ -106,6 +106,7 @@ pub fn StrmTargetOutputView(props: &StrmTargetOutputViewProps) -> Html {
|
||||
|
||||
let render_main = || {
|
||||
let output_form_state_1 = output_form_state.clone();
|
||||
let output_form_state_2 = output_form_state.clone();
|
||||
html! {
|
||||
<Card class="tp__config-view__card">
|
||||
{ edit_field_text!(output_form_state, translate.t(LABEL_DIRECTORY), directory, StrmTargetOutputFormAction::Directory) }
|
||||
@@ -134,7 +135,13 @@ pub fn StrmTargetOutputView(props: &StrmTargetOutputViewProps) -> Html {
|
||||
/>
|
||||
}
|
||||
})}
|
||||
{ edit_field_text_option!(output_form_state, translate.t(LABEL_FILTER), filter, StrmTargetOutputFormAction::Filter) }
|
||||
{ config_field_child!(translate.t(LABEL_FILTER), {
|
||||
html! {
|
||||
<FilterInput filter={output_form_state_2.form.filter.clone()} on_change={Callback::from(move |new_filter| {
|
||||
output_form_state_2.dispatch(StrmTargetOutputFormAction::Filter(new_filter));
|
||||
})} />
|
||||
}
|
||||
})}
|
||||
</Card>
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::app::components::config::HasFormData;
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, EditMode, IconButton, Panel, SourceEditorContext, TextButton, TraktListItemForm, TitledCard};
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, EditMode, IconButton, Panel, SourceEditorContext, TextButton, TraktListItemForm, TitledCard, FilterInput};
|
||||
use crate::{config_field_child, edit_field_bool, edit_field_number_u16, edit_field_text, edit_field_text_option, generate_form_reducer};
|
||||
use shared::model::{TargetOutputDto, TraktApiConfigDto, TraktConfigDto, TraktContentType, TraktListConfigDto, XtreamTargetOutputDto};
|
||||
use std::fmt::Display;
|
||||
@@ -165,6 +165,7 @@ pub fn XtreamTargetOutputView(props: &XtreamTargetOutputViewProps) -> Html {
|
||||
};
|
||||
|
||||
let render_output = || {
|
||||
let output_form_state_1 = output_form_state.clone();
|
||||
html! {
|
||||
<Card class="tp__config-view__card">
|
||||
<TitledCard title={translate.t(LABEL_SKIP_DIRECT_SOURCE)}>
|
||||
@@ -186,7 +187,13 @@ pub fn XtreamTargetOutputView(props: &XtreamTargetOutputViewProps) -> Html {
|
||||
{ edit_field_number_u16!(output_form_state, translate.t(LABEL_SERIES), resolve_series_delay, XtreamTargetOutputFormAction::ResolveSeriesDelay) }
|
||||
</div>
|
||||
</TitledCard>
|
||||
{ edit_field_text_option!(output_form_state, translate.t(LABEL_FILTER), filter, XtreamTargetOutputFormAction::Filter) }
|
||||
{ config_field_child!(translate.t(LABEL_FILTER), {
|
||||
html! {
|
||||
<FilterInput filter={output_form_state_1.form.filter.clone()} on_change={Callback::from(move |new_filter| {
|
||||
output_form_state_1.dispatch(XtreamTargetOutputFormAction::Filter(new_filter));
|
||||
})} />
|
||||
}
|
||||
})}
|
||||
</Card>
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::app::components::config::HasFormData;
|
||||
use crate::app::components::select::Select;
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, ClusterFlagsInput, DropDownOption, DropDownSelection, EditMode, Panel, SourceEditorContext, TextButton};
|
||||
use crate::app::components::{BlockId, BlockInstance, Card, ClusterFlagsInput, DropDownOption, DropDownSelection, EditMode, FilterInput, Panel, SourceEditorContext, TextButton};
|
||||
use crate::{config_field_child, edit_field_bool, edit_field_list_option, edit_field_text, generate_form_reducer};
|
||||
use shared::model::{ClusterFlags, ConfigTargetDto, ConfigTargetOptions, ProcessingOrder};
|
||||
use std::fmt::Display;
|
||||
@@ -160,6 +160,7 @@ pub fn ConfigTargetView(props: &ConfigTargetViewProps) -> Html {
|
||||
|
||||
let render_target = || {
|
||||
let target_form_state_1 = target_form_state.clone();
|
||||
let target_form_state_2 = target_form_state.clone();
|
||||
html! {
|
||||
<Card class="tp__config-view__card">
|
||||
<div class="tp__config-view__cols-2">
|
||||
@@ -167,7 +168,13 @@ pub fn ConfigTargetView(props: &ConfigTargetViewProps) -> Html {
|
||||
{ edit_field_bool!(target_form_state, translate.t(LABEL_USE_MEMORY_CACHE), use_memory_cache, ConfigTargetFormAction::UseMemoryCache) }
|
||||
</div>
|
||||
{ edit_field_text!(target_form_state, translate.t(LABEL_NAME), name, ConfigTargetFormAction::Name) }
|
||||
{ edit_field_text!(target_form_state, translate.t(LABEL_FILTER), filter, ConfigTargetFormAction::Filter) }
|
||||
{ config_field_child!(translate.t(LABEL_FILTER), {
|
||||
html! {
|
||||
<FilterInput filter={target_form_state_2.form.filter.clone()} on_change={Callback::from(move |new_filter: Option<String>| {
|
||||
target_form_state_2.dispatch(ConfigTargetFormAction::Filter(new_filter.unwrap_or_else(String::new)));
|
||||
})} />
|
||||
}
|
||||
})}
|
||||
|
||||
{ config_field_child!(translate.t(LABEL_PROCESSING_ORDER), {
|
||||
html! {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
pub fn default_documentation_url() -> String { String::from("https://euzu.github.io/tuliprox-docs/") }
|
||||
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Default)]
|
||||
pub struct ApiConfig {
|
||||
#[serde(alias = "apiUrl")]
|
||||
@@ -24,7 +27,7 @@ pub struct WebConfig {
|
||||
pub discord: String,
|
||||
#[serde(default)]
|
||||
pub github: String,
|
||||
#[serde(default)]
|
||||
#[serde(default = "default_documentation_url")]
|
||||
pub documentation: String,
|
||||
#[serde(alias = "wsUrl")]
|
||||
pub ws_url: String,
|
||||
|
||||
Reference in New Issue
Block a user