diff --git a/migrations/20240907190149_add-links.sql b/migrations/20240907190149_add-links.sql index 67d3ad5..3e40a46 100644 --- a/migrations/20240907190149_add-links.sql +++ b/migrations/20240907190149_add-links.sql @@ -1,3 +1,4 @@ alter table "public"."series" add column source text not null; +alter table "public"."series" add column source_url text not null; alter table "public"."series" add column imdb text null; alter table "public"."series" add column wikipedia text null diff --git a/sql/insert_series.sql b/sql/insert_series.sql index 661d629..6349a77 100644 --- a/sql/insert_series.sql +++ b/sql/insert_series.sql @@ -1 +1 @@ -insert into series (name, source, imdb, wikipedia) values ($1, $2, $3, $4); +insert into series (name, source, source_url, imdb, wikipedia) values ($1, $2, $3, $4, $5); diff --git a/src/app/series/add.rs b/src/app/series/add.rs index 99d84da..4d02f08 100644 --- a/src/app/series/add.rs +++ b/src/app/series/add.rs @@ -1,3 +1,5 @@ +use crate::app::series::Source; + use leptos::*; use leptos_router::*; @@ -25,9 +27,21 @@ pub fn AddSeries() -> impl IntoView { />
- + + +
+
+
@@ -60,7 +74,8 @@ pub fn AddSeries() -> impl IntoView { #[server(AddSeries)] pub async fn add_series( name: String, - source: String, + source: Source, + source_url: String, imdb: String, wikipedia: String, ) -> Result<(), ServerFnError> { @@ -72,7 +87,8 @@ pub async fn add_series( sqlx::query(&s) .persistent(true) .bind(name) - .bind(source) + .bind(source.to_string()) + .bind(source_url) .bind(if imdb.is_empty() { None } else { Some(imdb) }) .bind(if wikipedia.is_empty() { None diff --git a/src/app/series/mod.rs b/src/app/series/mod.rs index b0cc1fe..3464942 100644 --- a/src/app/series/mod.rs +++ b/src/app/series/mod.rs @@ -1,9 +1,10 @@ +use std::fmt::Display; + use self::add::AddSeries; + use leptos::*; use leptos_router::*; use serde::{Deserialize, Serialize}; -#[cfg(feature = "ssr")] -use sqlx::FromRow; use uuid::Uuid; pub mod add; @@ -102,16 +103,92 @@ pub fn format_errors(errors: RwSignal) -> impl IntoView { } } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "ssr", derive(FromRow))] +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum Source { + Disney, + Hulu, + Paramount, + Prime, + Max, + Netflix, + Shudder, +} + +impl Display for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Source::Disney => f.write_str("Disney"), + Source::Hulu => f.write_str("Hulu"), + Source::Paramount => f.write_str("Paramount"), + Source::Prime => f.write_str("Prime"), + Source::Max => f.write_str("Max"), + Source::Netflix => f.write_str("Netflix"), + Source::Shudder => f.write_str("Shudder"), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Series { id: Uuid, name: String, - source: String, + source: Source, + source_url: String, imdb: Option, wikipedia: Option, } +#[cfg(feature = "ssr")] +mod ssr { + use super::{Series, Source}; + use sqlx::{postgres::PgRow, prelude::*}; + + impl<'r> FromRow<'r, PgRow> for Series { + fn from_row(row: &'r PgRow) -> Result { + let id = row.try_get("id")?; + let name = row.try_get("name")?; + let source: String = row.try_get("source")?; + + let source_enum: Source = + source + .try_into() + .map_err(|e: String| sqlx::Error::ColumnDecode { + index: "source".to_string(), + source: e.into(), + })?; + let source_url = row.try_get("source_url")?; + let imdb = row.try_get("imdb")?; + let wikipedia = row.try_get("wikipedia")?; + + Ok(Series { + id, + name, + source_url, + imdb, + wikipedia, + source: source_enum, + }) + } + } + + impl TryFrom for Source { + type Error = String; + + fn try_from(value: String) -> Result { + match value.to_lowercase().as_str() { + "disney" => Ok(Self::Disney), + "hulu" => Ok(Self::Hulu), + "paramount" => Ok(Self::Paramount), + "prime" => Ok(Self::Prime), + "max" => Ok(Self::Max), + "netflix" => Ok(Self::Netflix), + "shudder" => Ok(Self::Shudder), + _ => Err(format!("Unrecognized source: {value}")), + } + } + } +} + #[server] pub async fn get_all() -> Result, ServerFnError> { use crate::db::load_statement; diff --git a/src/app/series/view.rs b/src/app/series/view.rs index 82d0f60..e9e3b76 100644 --- a/src/app/series/view.rs +++ b/src/app/series/view.rs @@ -14,25 +14,13 @@ pub fn ViewSeries() -> impl IntoView { let id = move || { ps.with(|ps| { ps.as_ref() - .map(|ps| ps.id.clone()) + .map(|ps| ps.id) .expect("Failed to parse Id value from query string!") }) }; - let details = create_resource( - move || id(), - |id| async move { get_series_details(id).await }, - ); + let details = create_resource(id, |id| async move { get_series_details(id).await }); view! {
-
- - x - -

View Series

-
{details.get().map(format_series_details)} @@ -44,10 +32,20 @@ pub fn ViewSeries() -> impl IntoView { fn loading() -> impl IntoView { view! { - {format_field("Name", view!())} - {format_field("Source", view!())} - {format_field("IMDB", view!())} - {format_field("Wikpedia", view!())} +
+ + x + +

+
+ + + +
+
} } @@ -57,22 +55,44 @@ fn format_series_details( Ok(view! { {details.map(|details| view! { - {format_field("Name", details.name)} - {format_field("Source", details.source)} - {format_field("IMDB", details.imdb)} - {format_field("Wikipedia", details.wikipedia)} +
+ + x + +

{details.name}

+
+ }) }
}) } -fn format_field(label: &'static str, f: F) -> impl IntoView { +#[component] +fn OptionalUrlButton(label: &'static str, opt_url: Option) -> impl IntoView { view! { -
-
{label}
-
{f}
-
+ + {opt_url.as_ref().map(|url| view! { + + {label} + + })} + } }