This commit is contained in:
parent
95c4cf6fe1
commit
a0e7c5d3c1
8 changed files with 111 additions and 23 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -158,7 +158,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
|||
[[package]]
|
||||
name = "based"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.hydrar.de/jmarya/based#291949b8c65e90aaacead7d108f77366c742125a"
|
||||
source = "git+https://git.hydrar.de/jmarya/based#a89c5661208585b2a9f09d5ee337ad1c60ea9a49"
|
||||
dependencies = [
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
|
|
9
migrations/0005_history.sql
Normal file
9
migrations/0005_history.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
CREATE TABLE IF NOT EXISTS video_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(255) NOT NULL,
|
||||
video_id UUID NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
FOREIGN KEY (username) REFERENCES users (username) ON DELETE CASCADE,
|
||||
FOREIGN KEY (video_id) REFERENCES videos (id) ON DELETE CASCADE
|
||||
);
|
43
src/library/history.rs
Normal file
43
src/library/history.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use based::{auth::User, get_pg};
|
||||
|
||||
use super::Video;
|
||||
|
||||
pub trait VideoHistory {
|
||||
async fn last_video(&self) -> Option<uuid::Uuid>;
|
||||
async fn insert_history(&self, vid: uuid::Uuid);
|
||||
async fn history_of(&self, n: i64) -> Vec<Video>;
|
||||
}
|
||||
|
||||
impl VideoHistory for User {
|
||||
async fn last_video(&self) -> Option<uuid::Uuid> {
|
||||
let res: Option<(uuid::Uuid,)> = sqlx::query_as("SELECT video_id FROM video_history WHERE username = $1 ORDER BY timestamp DESC LIMIT 1")
|
||||
.bind(&self.username)
|
||||
.fetch_optional(get_pg!())
|
||||
.await.unwrap();
|
||||
|
||||
res.map(|x| x.0)
|
||||
}
|
||||
|
||||
async fn insert_history(&self, vid: uuid::Uuid) {
|
||||
if let Some(prev) = self.last_video().await {
|
||||
if prev == vid {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sqlx::query("INSERT INTO video_history (username, video_id) VALUES ($1, $2)")
|
||||
.bind(&self.username)
|
||||
.bind(vid)
|
||||
.execute(get_pg!())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn history_of(&self, n: i64) -> Vec<Video> {
|
||||
sqlx::query_as("SELECT v.* FROM video_history vh JOIN videos v ON vh.video_id = v.id WHERE vh.username = $1 ORDER BY vh.timestamp DESC LIMIT $2")
|
||||
.bind(&self.username)
|
||||
.bind(n)
|
||||
.fetch_all(get_pg!())
|
||||
.await.unwrap()
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use based::auth::User;
|
||||
use based::get_pg;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
@ -9,6 +11,7 @@ pub use video::Video;
|
|||
|
||||
use crate::meta;
|
||||
mod func;
|
||||
pub mod history;
|
||||
mod video;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -66,7 +66,8 @@ async fn launch() -> _ {
|
|||
pages::index::index_page,
|
||||
pages::watch::watch_page,
|
||||
pages::user::login,
|
||||
pages::user::login_post
|
||||
pages::user::login_post,
|
||||
pages::user::history_page
|
||||
],
|
||||
)
|
||||
.attach(cors)
|
||||
|
|
|
@ -13,30 +13,38 @@ pub async fn render_page(
|
|||
title: &str,
|
||||
user: Option<User>,
|
||||
) -> StringResponse {
|
||||
based::page::render_page(content, title, ctx, &based::page::Shell::new(
|
||||
html! {
|
||||
script src="https://cdn.tailwindcss.com" {};
|
||||
script src="/assets/htmx.min.js" {};
|
||||
meta name="viewport" content="width=device-width, initial-scale=1.0";
|
||||
},
|
||||
html! {
|
||||
header class="bg-gray-800 text-white shadow-md py-2" {
|
||||
(script(include_str!("../scripts/header.js")));
|
||||
based::page::render_page(
|
||||
content,
|
||||
title,
|
||||
ctx,
|
||||
&based::page::Shell::new(
|
||||
html! {
|
||||
script src="https://cdn.tailwindcss.com" {};
|
||||
script src="/assets/htmx.min.js" {};
|
||||
meta name="viewport" content="width=device-width, initial-scale=1.0";
|
||||
},
|
||||
html! {
|
||||
header class="bg-gray-800 text-white shadow-md py-2" {
|
||||
(script(include_str!("../scripts/header.js")));
|
||||
|
||||
div class="flex justify-between px-6" {
|
||||
a href="/" class="flex items-center space-x-2" {
|
||||
img src="/favicon" alt="Logo" class="w-10 h-10 rounded-md";
|
||||
span class="font-semibold text-xl" { "WatchDogs" };
|
||||
};
|
||||
div class="flex justify-between px-6" {
|
||||
a href="/" class="flex items-center space-x-2" {
|
||||
img src="/favicon" alt="Logo" class="w-10 h-10 rounded-md";
|
||||
span class="font-semibold text-xl" { "WatchDogs" };
|
||||
};
|
||||
|
||||
@if user.is_some() {
|
||||
p { (user.unwrap().username) };
|
||||
};
|
||||
@if user.is_some() {
|
||||
p { (user.unwrap().username) };
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}, Some(String::from("bg-black text-white")))).await
|
||||
};
|
||||
},
|
||||
Some(String::from("bg-black text-white")),
|
||||
),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn loading_spinner() -> PreEscaped<String> {
|
||||
|
|
|
@ -5,6 +5,9 @@ use maud::html;
|
|||
use rocket::http::CookieJar;
|
||||
use rocket::{form::Form, get, http::Cookie, post, response::Redirect, FromForm};
|
||||
|
||||
use crate::library::history::VideoHistory;
|
||||
use crate::pages::components::video_element_wide;
|
||||
|
||||
use super::components::render_page;
|
||||
|
||||
#[get("/login")]
|
||||
|
@ -43,3 +46,17 @@ pub async fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) ->
|
|||
|
||||
Some(Redirect::to("/"))
|
||||
}
|
||||
|
||||
#[get("/history")]
|
||||
pub async fn history_page(ctx: RequestContext, user: User) -> StringResponse {
|
||||
let content = html! {
|
||||
h1 class="text-center text-4xl font-extrabold leading-tight mt-4 mb-2" { "History" };
|
||||
div class="p-6" {
|
||||
@for mut vid in user.history_of(10).await {
|
||||
( video_element_wide(&mut vid).await );
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
render_page(ctx, content, "History", Some(user)).await
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ use based::{
|
|||
use maud::html;
|
||||
use rocket::{get, State};
|
||||
|
||||
use crate::{library::Library, pages::components::video_element_wide};
|
||||
use crate::{
|
||||
library::{history::VideoHistory, Library},
|
||||
pages::components::video_element_wide,
|
||||
};
|
||||
|
||||
use super::components::render_page;
|
||||
|
||||
|
@ -24,6 +27,10 @@ pub async fn watch_page(
|
|||
library.get_video_by_youtube_id(&v).await.unwrap()
|
||||
};
|
||||
|
||||
if let Some(user) = user.user() {
|
||||
user.insert_history(video.id).await;
|
||||
}
|
||||
|
||||
let content = html!(
|
||||
main class="container mx-auto mt-6 flex flex-col lg:flex-row gap-6" {
|
||||
div class="lg:w-10/12 mt-10" {
|
||||
|
|
Loading…
Add table
Reference in a new issue