clickable htmx button for adding search results to your list

This commit is contained in:
Joe Ardent 2023-12-31 13:55:16 -08:00
parent 1c304e1184
commit c4976a3efc
4 changed files with 55 additions and 56 deletions

View file

@ -8,7 +8,7 @@ table {
}
th {
background-color: darkgray;
background-color: ghostwhite;
}
th, td {
@ -16,7 +16,7 @@ th, td {
padding: 8px;
}
tr:nth-child(odd) {background-color: ghostwhite;}
tr:nth-child(even) {background-color: ghostwhite;}
#header {
text-align: end;

View file

@ -3,6 +3,7 @@ use axum::{
http::StatusCode,
response::{IntoResponse, Redirect, Response},
};
use http::HeaderValue;
use julid::Julid;
use serde::Deserialize;
use sqlx::{query, query_as, query_scalar, SqlitePool};
@ -37,22 +38,22 @@ const EMPTY_SEARCH_QUERY_STRUCT: SearchQuery = SearchQuery {
//-************************************************************************
#[Error]
pub struct WatchAddError(#[from] WatchAddErrorKind);
pub struct AddError(#[from] AddErrorKind);
#[Error]
#[non_exhaustive]
pub enum WatchAddErrorKind {
pub enum AddErrorKind {
UnknownDBError,
NotSignedIn,
}
impl IntoResponse for WatchAddError {
impl IntoResponse for AddError {
fn into_response(self) -> Response {
match &self.0 {
WatchAddErrorKind::UnknownDBError => {
AddErrorKind::UnknownDBError => {
(StatusCode::INTERNAL_SERVER_ERROR, format!("{self}")).into_response()
}
WatchAddErrorKind::NotSignedIn => (
AddErrorKind::NotSignedIn => (
StatusCode::OK,
"Ope, you need to sign in first!".to_string(),
)
@ -94,7 +95,7 @@ pub struct PostAddNewWatch {
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
pub struct PostAddExistingWatch {
pub id: String,
pub watch: String,
pub public: bool,
pub watched_already: bool,
}
@ -107,18 +108,12 @@ pub async fn get_add_new_watch(auth: AuthSession) -> impl IntoResponse {
AddNewWatchPage { user: auth.user }
}
struct QuestQuest {
pub user: Julid,
pub is_public: bool,
pub already_watched: bool,
}
/// Add a Watch to your watchlist (side effects system-add)
pub async fn post_add_new_watch(
auth: AuthSession,
State(pool): State<SqlitePool>,
Form(form): Form<PostAddNewWatch>,
) -> Result<impl IntoResponse, WatchAddError> {
) -> Result<impl IntoResponse, AddError> {
if let Some(user) = auth.user {
{
let release_date = year_to_epoch(form.year.as_deref());
@ -130,31 +125,27 @@ pub async fn post_add_new_watch(
added_by: user.id,
..Default::default()
};
let quest = QuestQuest {
let watch_id = add_new_watch_impl(&pool, &watch).await?;
let quest = WatchQuest {
user: user.id,
is_public: !form.private,
already_watched: form.watched_already,
watch: watch_id,
};
let watch_id = add_new_watch_impl(&pool, &watch, Some(quest)).await?;
add_watch_quest_impl(&pool, &quest)
.await
.map_err(|_| AddErrorKind::UnknownDBError)?;
let location = format!("/watch/{watch_id}");
Ok(Redirect::to(&location))
}
} else {
Err(WatchAddErrorKind::NotSignedIn.into())
Err(AddErrorKind::NotSignedIn.into())
}
}
async fn add_new_watch_impl(
db_pool: &SqlitePool,
watch: &Watch,
quest: Option<QuestQuest>,
) -> Result<Julid, WatchAddError> {
let mut tx = db_pool
.begin()
.await
.map_err(|_| WatchAddErrorKind::UnknownDBError)?;
async fn add_new_watch_impl(db_pool: &SqlitePool, watch: &Watch) -> Result<Julid, AddError> {
let watch_id: Julid = query_scalar(ADD_WATCH_QUERY)
.bind(&watch.title)
.bind(watch.kind)
@ -162,44 +153,43 @@ async fn add_new_watch_impl(
.bind(&watch.metadata_url)
.bind(watch.added_by)
.bind(watch.length)
.fetch_one(&mut *tx)
.fetch_one(db_pool)
.await
.map_err(|err| {
tracing::error!("Got error: {err}");
WatchAddErrorKind::UnknownDBError
AddErrorKind::UnknownDBError
})?;
if let Some(quest) = quest {
query(ADD_WATCH_QUEST_QUERY)
.bind(quest.user)
.bind(watch_id)
.bind(quest.is_public)
.bind(quest.already_watched)
.execute(&mut *tx)
.await
.map_err(|err| {
tracing::error!("Got error: {err}");
WatchAddErrorKind::UnknownDBError
})?;
}
tx.commit().await.map_err(|err| {
tracing::error!("Got error: {err}");
WatchAddErrorKind::UnknownDBError
})?;
Ok(watch_id)
}
/// Add a Watch to your watchlist by selecting it with a checkbox
pub async fn post_add_watch_quest(
_auth: AuthSession,
State(_pool): State<SqlitePool>,
Form(_form): Form<PostAddExistingWatch>,
) -> impl IntoResponse {
todo!()
auth: AuthSession,
State(pool): State<SqlitePool>,
Form(form): Form<PostAddExistingWatch>,
) -> Result<impl IntoResponse, AddError> {
if let Some(user) = auth.user {
let quest = WatchQuest {
user: user.id,
watch: Julid::from_string(&form.watch).unwrap(),
is_public: form.public,
already_watched: form.watched_already,
};
add_watch_quest_impl(&pool, &quest)
.await
.map_err(|_| AddErrorKind::UnknownDBError)?;
let resp = "<style=\"background-color:green\">&#10003;</style>";
Ok(resp.into_response())
} else {
let resp = Redirect::to("/login");
let mut resp = resp.into_response();
resp.headers_mut()
.insert("HX-Redirect", HeaderValue::from_str("/login").unwrap());
Ok(resp)
}
}
pub async fn _add_watch_quest_impl(pool: &SqlitePool, quest: &WatchQuest) -> Result<(), ()> {
pub async fn add_watch_quest_impl(pool: &SqlitePool, quest: &WatchQuest) -> Result<(), ()> {
query(ADD_WATCH_QUEST_QUERY)
.bind(quest.user)
.bind(quest.watch)

View file

@ -13,7 +13,7 @@
</div>
</div>
{% when None %} <!-- this is for the `when` statement -->
{% when None %}
<div class="header_logged_out">
<a href="/login">log in</a> or <a href="/signup">sign up</a>?
</div>

View file

@ -2,4 +2,13 @@
<td><span class="watchtitle">{{watch.title}}</span></td>
<td>{{watch.kind}}</td>
<td> {% call m::get_or_default(watch.year(), "when??") -%}</td>
<td>
<form id="add-watch-{{watch.id}}">
<input type="hidden" name="watch" value="{{watch.id}}">
<input type="hidden" name="public" value="true">
<input type="hidden" name="watched_already" value="false">
<button hx-post="/add/watch" hx-target="#add-watch-{{watch.id}}" hx-trigger="click"
hx-swap="OuterHTML">wanna watch?</button>
</form>
</td>
</tr>