Parent and child posts
This commit is contained in:
parent
04f888c323
commit
54379b98e0
16 changed files with 334 additions and 58 deletions
|
|
@ -4,8 +4,11 @@ Sam's small image board. Currently a WIP.
|
|||
|
||||
## TODO
|
||||
|
||||
- [ ] Parent posts (including tags and stuff)
|
||||
- [ ] Edit tags in the middle of input
|
||||
- [ ] Pools
|
||||
- [ ] Video support
|
||||
- [ ] Cleanup/fixup background tasks
|
||||
- [ ] CSS
|
||||
- [ ] CLI, env vars, logging...
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,29 @@ impl MigrationTrait for Migration {
|
|||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(SameyTag::Table)
|
||||
.if_not_exists()
|
||||
.col(pk_auto(SameyTag::Id))
|
||||
.col(string_len(SameyTag::Name, 100))
|
||||
.col(string_len_uniq(SameyTag::NormalizedName, 100))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(SameyPool::Table)
|
||||
.if_not_exists()
|
||||
.col(pk_auto(SameyPool::Id))
|
||||
.col(string_len_uniq(SameyPool::Name, 100))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
|
|
@ -98,18 +121,6 @@ impl MigrationTrait for Migration {
|
|||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(SameyTag::Table)
|
||||
.if_not_exists()
|
||||
.col(pk_auto(SameyTag::Id))
|
||||
.col(string_len(SameyTag::Name, 100))
|
||||
.col(string_len_uniq(SameyTag::NormalizedName, 100))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
|
|
@ -142,6 +153,39 @@ impl MigrationTrait for Migration {
|
|||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(SameyPoolPost::Table)
|
||||
.if_not_exists()
|
||||
.col(pk_auto(SameyPoolPost::Id))
|
||||
.col(integer(SameyPoolPost::PoolId))
|
||||
.col(integer(SameyPoolPost::PostId))
|
||||
.col(float(SameyPoolPost::Position))
|
||||
.foreign_key(
|
||||
ForeignKeyCreateStatement::new()
|
||||
.name("fk-samey_pool_post-samey_pool-pool_id")
|
||||
.from(SameyPoolPost::Table, SameyPoolPost::PoolId)
|
||||
.to(SameyPool::Table, SameyPool::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKeyCreateStatement::new()
|
||||
.name("fk-samey_pool_post-samey_post-post_id")
|
||||
.from(SameyPoolPost::Table, SameyPoolPost::PostId)
|
||||
.to(SameyPost::Table, SameyPost::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.unique()
|
||||
.col(SameyPoolPost::PoolId)
|
||||
.col(SameyPoolPost::PostId),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -149,11 +193,11 @@ impl MigrationTrait for Migration {
|
|||
// Replace the sample below with your own migration scripts
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SameyTagPost::Table).to_owned())
|
||||
.drop_table(Table::drop().table(SameyPoolPost::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SameyTag::Table).to_owned())
|
||||
.drop_table(Table::drop().table(SameyTagPost::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
|
|
@ -164,6 +208,14 @@ impl MigrationTrait for Migration {
|
|||
.drop_table(Table::drop().table(SameyPost::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SameyPool::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SameyTag::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SameyUser::Table).to_owned())
|
||||
.await?;
|
||||
|
|
@ -255,3 +307,21 @@ enum SameyTagPost {
|
|||
TagId,
|
||||
PostId,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum SameyPool {
|
||||
#[sea_orm(iden = "samey_pool")]
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum SameyPoolPost {
|
||||
#[sea_orm(iden = "samey_pool_post")]
|
||||
Table,
|
||||
Id,
|
||||
PoolId,
|
||||
PostId,
|
||||
Position,
|
||||
}
|
||||
|
|
|
|||
21
src/auth.rs
21
src/auth.rs
|
|
@ -123,7 +123,7 @@ impl SessionStore for SessionStorage {
|
|||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp().to_string()),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp()),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&self.db)
|
||||
|
|
@ -141,7 +141,7 @@ impl SessionStore for SessionStorage {
|
|||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp().to_string()),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp()),
|
||||
..Default::default()
|
||||
})
|
||||
.filter(samey_session::Column::SessionId.eq(record.id.to_string()))
|
||||
|
|
@ -175,20 +175,9 @@ impl SessionStore for SessionStorage {
|
|||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
expiry_date: match session.expiry_date.parse() {
|
||||
Ok(timestamp) => {
|
||||
OffsetDateTime::from_unix_timestamp(timestamp).map_err(|_| {
|
||||
session_store::Error::Backend(
|
||||
"Invalid timestamp for expiry date".into(),
|
||||
)
|
||||
})?
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(session_store::Error::Backend(
|
||||
"Failed to parse session expiry date".into(),
|
||||
));
|
||||
}
|
||||
},
|
||||
expiry_date: OffsetDateTime::from_unix_timestamp(session.expiry_date).map_err(
|
||||
|_| session_store::Error::Backend("Invalid timestamp for expiry date".into()),
|
||||
)?,
|
||||
},
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod samey_pool;
|
||||
pub mod samey_pool_post;
|
||||
pub mod samey_post;
|
||||
pub mod samey_post_source;
|
||||
pub mod samey_session;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
pub use super::samey_pool::Entity as SameyPool;
|
||||
pub use super::samey_pool_post::Entity as SameyPoolPost;
|
||||
pub use super::samey_post::Entity as SameyPost;
|
||||
pub use super::samey_post_source::Entity as SameyPostSource;
|
||||
pub use super::samey_session::Entity as SameySession;
|
||||
|
|
|
|||
26
src/entities/samey_pool.rs
Normal file
26
src/entities/samey_pool.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "samey_pool")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::samey_pool_post::Entity")]
|
||||
SameyPoolPost,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPoolPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
48
src/entities/samey_pool_post.rs
Normal file
48
src/entities/samey_pool_post.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "samey_pool_post")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub pool_id: i32,
|
||||
pub post_id: i32,
|
||||
#[sea_orm(column_type = "Float")]
|
||||
pub position: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::samey_pool::Entity",
|
||||
from = "Column::PoolId",
|
||||
to = "super::samey_pool::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
SameyPool,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::samey_post::Entity",
|
||||
from = "Column::PostId",
|
||||
to = "super::samey_post::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
SameyPost,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPool.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::samey_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
@ -24,6 +24,8 @@ pub struct Model {
|
|||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::samey_pool_post::Entity")]
|
||||
SameyPoolPost,
|
||||
#[sea_orm(
|
||||
belongs_to = "Entity",
|
||||
from = "Column::ParentId",
|
||||
|
|
@ -46,6 +48,12 @@ pub enum Relation {
|
|||
SameyUser,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPoolPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::samey_post_source::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPostSource.def()
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ use sea_orm::entity::prelude::*;
|
|||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub session_id: String,
|
||||
pub data: Json,
|
||||
pub expiry_date: String,
|
||||
pub expiry_date: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
|||
37
src/query.rs
37
src/query.rs
|
|
@ -16,16 +16,17 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug, FromQueryResult)]
|
||||
pub(crate) struct SearchPost {
|
||||
pub(crate) struct PostOverview {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) thumbnail: String,
|
||||
pub(crate) tags: String,
|
||||
pub(crate) rating: String,
|
||||
}
|
||||
|
||||
pub(crate) fn search_posts(
|
||||
tags: Option<&Vec<&str>>,
|
||||
user: Option<User>,
|
||||
) -> Selector<SelectModel<SearchPost>> {
|
||||
user: Option<&User>,
|
||||
) -> Selector<SelectModel<PostOverview>> {
|
||||
let mut include_tags = HashSet::<String>::new();
|
||||
let mut exclude_tags = HashSet::<String>::new();
|
||||
let mut include_ratings = HashSet::<String>::new();
|
||||
|
|
@ -47,11 +48,12 @@ pub(crate) fn search_posts(
|
|||
}
|
||||
}
|
||||
|
||||
let mut query = if include_tags.is_empty() && exclude_tags.is_empty() {
|
||||
let query = if include_tags.is_empty() && exclude_tags.is_empty() {
|
||||
let mut query = SameyPost::find()
|
||||
.select_only()
|
||||
.column(samey_post::Column::Id)
|
||||
.column(samey_post::Column::Thumbnail)
|
||||
.column(samey_post::Column::Rating)
|
||||
.column_as(
|
||||
Expr::cust("GROUP_CONCAT(\"samey_tag\".\"name\", ' ')"),
|
||||
"tags",
|
||||
|
|
@ -73,6 +75,7 @@ pub(crate) fn search_posts(
|
|||
.select_only()
|
||||
.column(samey_post::Column::Id)
|
||||
.column(samey_post::Column::Thumbnail)
|
||||
.column(samey_post::Column::Rating)
|
||||
.column_as(
|
||||
Expr::cust("GROUP_CONCAT(\"samey_tag\".\"name\", ' ')"),
|
||||
"tags",
|
||||
|
|
@ -130,20 +133,10 @@ pub(crate) fn search_posts(
|
|||
query
|
||||
};
|
||||
|
||||
query = match user {
|
||||
None => query.filter(samey_post::Column::IsPublic.into_simple_expr()),
|
||||
Some(user) if !user.is_admin => query.filter(
|
||||
Condition::any()
|
||||
.add(samey_post::Column::IsPublic.into_simple_expr())
|
||||
.add(samey_post::Column::UploaderId.eq(user.id)),
|
||||
),
|
||||
_ => query,
|
||||
};
|
||||
|
||||
query
|
||||
filter_by_user(query, user)
|
||||
.group_by(samey_post::Column::Id)
|
||||
.order_by_desc(samey_post::Column::Id)
|
||||
.into_model::<SearchPost>()
|
||||
.into_model::<PostOverview>()
|
||||
}
|
||||
|
||||
pub(crate) fn get_tags_for_post(post_id: i32) -> Select<SameyTag> {
|
||||
|
|
@ -152,3 +145,15 @@ pub(crate) fn get_tags_for_post(post_id: i32) -> Select<SameyTag> {
|
|||
.filter(samey_tag_post::Column::PostId.eq(post_id))
|
||||
.order_by_asc(samey_tag::Column::Name)
|
||||
}
|
||||
|
||||
pub(crate) fn filter_by_user(query: Select<SameyPost>, user: Option<&User>) -> Select<SameyPost> {
|
||||
match user {
|
||||
None => query.filter(samey_post::Column::IsPublic.into_simple_expr()),
|
||||
Some(user) if !user.is_admin => query.filter(
|
||||
Condition::any()
|
||||
.add(samey_post::Column::IsPublic.into_simple_expr())
|
||||
.add(samey_post::Column::UploaderId.eq(user.id)),
|
||||
),
|
||||
_ => query,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
91
src/views.rs
91
src/views.rs
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
samey_post, samey_post_source, samey_tag, samey_tag_post,
|
||||
},
|
||||
error::SameyError,
|
||||
query::{SearchPost, get_tags_for_post, search_posts},
|
||||
query::{PostOverview, filter_by_user, get_tags_for_post, search_posts},
|
||||
};
|
||||
|
||||
const MAX_THUMBNAIL_DIMENSION: u32 = 192;
|
||||
|
|
@ -350,7 +350,7 @@ pub(crate) async fn select_tag(
|
|||
struct PostsTemplate<'a> {
|
||||
tags: Option<Vec<&'a str>>,
|
||||
tags_text: Option<String>,
|
||||
posts: Vec<SearchPost>,
|
||||
posts: Vec<PostOverview>,
|
||||
page: u32,
|
||||
page_count: u64,
|
||||
}
|
||||
|
|
@ -378,7 +378,7 @@ pub(crate) async fn posts_page(
|
|||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| tags.split_whitespace().collect::<Vec<_>>());
|
||||
let pagination = search_posts(tags.as_ref(), auth_session.user).paginate(&db, 50);
|
||||
let pagination = search_posts(tags.as_ref(), auth_session.user.as_ref()).paginate(&db, 50);
|
||||
let page_count = pagination.num_pages().await?;
|
||||
let posts = pagination.fetch_page(page.saturating_sub(1) as u64).await?;
|
||||
let posts = posts
|
||||
|
|
@ -386,7 +386,7 @@ pub(crate) async fn posts_page(
|
|||
.map(|post| {
|
||||
let mut tags_vec: Vec<_> = post.tags.split_ascii_whitespace().collect();
|
||||
tags_vec.sort();
|
||||
SearchPost {
|
||||
PostOverview {
|
||||
tags: tags_vec.into_iter().join(" "),
|
||||
..post
|
||||
}
|
||||
|
|
@ -412,8 +412,11 @@ pub(crate) async fn posts_page(
|
|||
struct ViewPostTemplate {
|
||||
post: samey_post::Model,
|
||||
tags: Vec<samey_tag::Model>,
|
||||
tags_text: String,
|
||||
sources: Vec<samey_post_source::Model>,
|
||||
can_edit: bool,
|
||||
parent_post: Option<PostOverview>,
|
||||
children_posts: Vec<PostOverview>,
|
||||
}
|
||||
|
||||
pub(crate) async fn view_post(
|
||||
|
|
@ -421,18 +424,64 @@ pub(crate) async fn view_post(
|
|||
auth_session: AuthSession,
|
||||
Path(post_id): Path<u32>,
|
||||
) -> Result<impl IntoResponse, SameyError> {
|
||||
let tags = get_tags_for_post(post_id as i32).all(&db).await?;
|
||||
let post_id = post_id as i32;
|
||||
let tags = get_tags_for_post(post_id).all(&db).await?;
|
||||
let tags_text = tags.iter().map(|tag| &tag.name).join(" ");
|
||||
|
||||
let sources = SameyPostSource::find()
|
||||
.filter(samey_post_source::Column::PostId.eq(post_id))
|
||||
.all(&db)
|
||||
.await?;
|
||||
|
||||
let post = SameyPost::find_by_id(post_id as i32)
|
||||
let post = SameyPost::find_by_id(post_id)
|
||||
.one(&db)
|
||||
.await?
|
||||
.ok_or(SameyError::NotFound)?;
|
||||
|
||||
let parent_post = if let Some(parent_id) = post.parent_id {
|
||||
match filter_by_user(SameyPost::find_by_id(parent_id), auth_session.user.as_ref())
|
||||
.one(&db)
|
||||
.await?
|
||||
{
|
||||
Some(parent_post) => Some(PostOverview {
|
||||
id: parent_id,
|
||||
thumbnail: parent_post.thumbnail,
|
||||
tags: get_tags_for_post(post_id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: parent_post.rating,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let children_posts_models = filter_by_user(
|
||||
SameyPost::find().filter(samey_post::Column::ParentId.eq(post_id)),
|
||||
auth_session.user.as_ref(),
|
||||
)
|
||||
.all(&db)
|
||||
.await?;
|
||||
let mut children_posts = Vec::with_capacity(children_posts_models.capacity());
|
||||
|
||||
for child_post in children_posts_models.into_iter() {
|
||||
children_posts.push(PostOverview {
|
||||
id: child_post.id,
|
||||
thumbnail: child_post.thumbnail,
|
||||
tags: get_tags_for_post(child_post.id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: child_post.rating,
|
||||
});
|
||||
}
|
||||
|
||||
let can_edit = match auth_session.user {
|
||||
None => false,
|
||||
Some(user) => user.is_admin || post.uploader_id == user.id,
|
||||
|
|
@ -446,8 +495,11 @@ pub(crate) async fn view_post(
|
|||
ViewPostTemplate {
|
||||
post,
|
||||
tags,
|
||||
tags_text,
|
||||
sources,
|
||||
can_edit,
|
||||
parent_post,
|
||||
children_posts,
|
||||
}
|
||||
.render()?,
|
||||
))
|
||||
|
|
@ -505,12 +557,14 @@ pub(crate) struct SubmitPostDetailsForm {
|
|||
#[serde(rename = "source")]
|
||||
sources: Option<Vec<String>>,
|
||||
tags: String,
|
||||
parent_post: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "submit_post_details.html")]
|
||||
struct SubmitPostDetailsTemplate {
|
||||
post: samey_post::Model,
|
||||
parent_post: Option<PostOverview>,
|
||||
sources: Vec<samey_post_source::Model>,
|
||||
tags: Vec<samey_tag::Model>,
|
||||
can_edit: bool,
|
||||
|
|
@ -529,7 +583,7 @@ pub(crate) async fn submit_post_details(
|
|||
.await?
|
||||
.ok_or(SameyError::NotFound)?;
|
||||
|
||||
match auth_session.user {
|
||||
match auth_session.user.as_ref() {
|
||||
None => return Err(SameyError::Forbidden),
|
||||
Some(user) => {
|
||||
if !user.is_admin && post.uploader_id != user.id {
|
||||
|
|
@ -546,6 +600,27 @@ pub(crate) async fn submit_post_details(
|
|||
description if description.is_empty() => None,
|
||||
description => Some(description.to_owned()),
|
||||
};
|
||||
let parent_post = if let Some(parent_id) = body.parent_post.trim().parse().ok() {
|
||||
match filter_by_user(SameyPost::find_by_id(parent_id), auth_session.user.as_ref())
|
||||
.one(&db)
|
||||
.await?
|
||||
{
|
||||
Some(parent_post) => Some(PostOverview {
|
||||
id: parent_id,
|
||||
thumbnail: parent_post.thumbnail,
|
||||
tags: get_tags_for_post(post_id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: parent_post.rating,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let is_public = body.is_public.is_some();
|
||||
let post = SameyPost::update(samey_post::ActiveModel {
|
||||
id: Set(post_id),
|
||||
|
|
@ -553,6 +628,7 @@ pub(crate) async fn submit_post_details(
|
|||
description: Set(description),
|
||||
is_public: Set(is_public),
|
||||
rating: Set(body.rating),
|
||||
parent_id: Set(parent_post.as_ref().map(|post| post.id)),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&db)
|
||||
|
|
@ -622,6 +698,7 @@ pub(crate) async fn submit_post_details(
|
|||
post,
|
||||
sources,
|
||||
tags: upload_tags,
|
||||
parent_post,
|
||||
can_edit: true,
|
||||
}
|
||||
.render()?,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
</ul>
|
||||
<button hx-post="/post_source" hx-target="#sources" hx-swap="beforeend">+</button>
|
||||
</div>
|
||||
<div>
|
||||
<label>Parent post</label>
|
||||
<input name="parent_post" type="text" value="{% if let Some(parent_id) = post.parent_id %}{{ parent_id }}{% endif %}" />
|
||||
</div>
|
||||
<div>
|
||||
<button>Submit</button>
|
||||
<button hx-get="/post_details/{{ post.id }}">Cancel</button>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
hx-trigger="input changed delay:400ms"
|
||||
hx-target="next .tags-autocomplete"
|
||||
hx-on::after-settle="this.focus(); this.setSelectionRange(-1, -1);"
|
||||
value=""
|
||||
autofocus
|
||||
/>
|
||||
<ul class="tags-autocomplete" id="search-autocomplete"></ul>
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
hx-trigger="input changed delay:400ms"
|
||||
hx-target="next .tags-autocomplete"
|
||||
hx-on::after-settle="this.focus(); this.setSelectionRange(-1, -1);"
|
||||
value=""
|
||||
/>
|
||||
<ul class="tags-autocomplete" id="upload-autocomplete"></ul>
|
||||
<input
|
||||
|
|
@ -52,6 +50,13 @@
|
|||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Create pool</h2>
|
||||
<form method="post" action="/pool">
|
||||
<input class="tags" type="text" id="pool-name" name="name" />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</article>
|
||||
<article>
|
||||
<a href="/logout">Log out</a>
|
||||
</article>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
title="{{ post.tags }}"
|
||||
>
|
||||
<img src="/files/{{ post.thumbnail }}" />
|
||||
<div>{{ post.rating }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,14 @@
|
|||
{% include "post_details.html" %}
|
||||
{% include "post_details.html" %} {% if let Some(parent_post) = parent_post %}
|
||||
<article id="parent-post" hx-swap-oob="outerHTML">
|
||||
<h2>Parent</h2>
|
||||
<a href="/view/{{ parent_post.id }}" title="{{ parent_post.tags }}">
|
||||
<img src="/files/{{ parent_post.thumbnail }}" />
|
||||
<div>{{ parent_post.rating }}</div>
|
||||
</a>
|
||||
</article>
|
||||
{% else %}
|
||||
<article id="parent-post" hx-swap-oob="outerHTML" hidden></article>
|
||||
{% endif %}
|
||||
<ul id="tags-list" hx-swap-oob="outerHTML">
|
||||
{% for tag in tags %}
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<meta property="og:image" content="/files/{{ post.media }}" />
|
||||
<meta property="og:image:width" content="{{ post.width }}" />
|
||||
<meta property="og:image:height" content="{{ post.height }}" />
|
||||
<!-- <meta property="og:image:alt" content="TO-DO" /> -->
|
||||
<meta property="og:image:alt" content="{{ tags_text }}" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="{% if let Some(description) = post.description %}{{ description }}{% endif %}"
|
||||
|
|
@ -34,6 +34,31 @@
|
|||
<h2>Details</h2>
|
||||
{% include "post_details.html" %}
|
||||
</article>
|
||||
{% if let Some(parent_post) = parent_post %}
|
||||
<article id="parent-post">
|
||||
<h2>Parent</h2>
|
||||
<a href="/view/{{ parent_post.id }}" title="{{ parent_post.tags }}">
|
||||
<img src="/files/{{ parent_post.thumbnail }}" />
|
||||
<div>{{ parent_post.rating }}</div>
|
||||
</a>
|
||||
</article>
|
||||
{% else %}
|
||||
<article id="parent-post" hidden></article>
|
||||
{% endif %} {% if !children_posts.is_empty() %}
|
||||
<article>
|
||||
<h2>Child posts</h2>
|
||||
<ul>
|
||||
{% for child_post in children_posts %}
|
||||
<li>
|
||||
<a href="/view/{{ child_post.id }}" title="{{ child_post.tags }}">
|
||||
<img src="/files/{{ child_post.thumbnail }}" />
|
||||
<div>{{ child_post.rating }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
{% endif %}
|
||||
<article>
|
||||
<h2>Tags</h2>
|
||||
<ul id="tags-list">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue