Fix index mobile styles, add delete confirmations
This commit is contained in:
parent
dabff41156
commit
fbf10e2eea
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1334,7 +1334,7 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pict-rs-aggregator"
|
name = "pict-rs-aggregator"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pict-rs-aggregator"
|
name = "pict-rs-aggregator"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "src/build.rs"
|
build = "src/build.rs"
|
||||||
|
|
|
@ -51,6 +51,11 @@ article .content-group {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete-confirmation {
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -135,8 +140,11 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-space {
|
.button-group {
|
||||||
margin: 0 -8px;
|
margin: 0 -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-space {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +245,7 @@ ul {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-space {
|
.button-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -248,6 +256,9 @@ ul {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|
80
src/lib.rs
80
src/lib.rs
|
@ -89,8 +89,15 @@ impl State {
|
||||||
self.scoped(&format!("{}?token={}", id, token.token))
|
self.scoped(&format!("{}?token={}", id, token.token))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_collection_path(&self, id: Uuid, token: &ValidToken) -> String {
|
fn delete_collection_path(&self, id: Uuid, token: &ValidToken, confirmed: bool) -> String {
|
||||||
self.scoped(&format!("{}/delete?token={}", id, token.token))
|
if confirmed {
|
||||||
|
self.scoped(&format!(
|
||||||
|
"{}/delete?token={}&confirmed=true",
|
||||||
|
id, token.token
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
self.scoped(&format!("{}/delete?token={}", id, token.token))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public_collection_path(&self, id: Uuid) -> String {
|
fn public_collection_path(&self, id: Uuid) -> String {
|
||||||
|
@ -108,11 +115,24 @@ impl State {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_entry_path(&self, collection_id: Uuid, id: Uuid, token: &ValidToken) -> String {
|
fn delete_entry_path(
|
||||||
self.scoped(&format!(
|
&self,
|
||||||
"{}/entry/{}/delete?token={}",
|
collection_id: Uuid,
|
||||||
collection_id, id, token.token
|
id: Uuid,
|
||||||
))
|
token: &ValidToken,
|
||||||
|
confirmed: bool,
|
||||||
|
) -> String {
|
||||||
|
if confirmed {
|
||||||
|
self.scoped(&format!(
|
||||||
|
"{}/entry/{}/delete?token={}&confirmed=true",
|
||||||
|
collection_id, id, token.token
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
self.scoped(&format!(
|
||||||
|
"{}/entry/{}/delete?token={}",
|
||||||
|
collection_id, id, token.token
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statics_path(&self, file: &str) -> String {
|
fn statics_path(&self, file: &str) -> String {
|
||||||
|
@ -448,13 +468,7 @@ async fn view_collection(
|
||||||
|
|
||||||
let mut cursor = Cursor::new(vec![]);
|
let mut cursor = Cursor::new(vec![]);
|
||||||
|
|
||||||
self::templates::view_collection(
|
self::templates::view_collection(&mut cursor, path.collection, &collection, &entries, &state)?;
|
||||||
&mut cursor,
|
|
||||||
path.collection,
|
|
||||||
&collection,
|
|
||||||
&entries,
|
|
||||||
&state,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type(mime::TEXT_HTML.essence_str())
|
.content_type(mime::TEXT_HTML.essence_str())
|
||||||
|
@ -544,12 +558,36 @@ async fn update_entry(
|
||||||
Ok(to_edit_page(entry_path.collection, &token, &state))
|
Ok(to_edit_page(entry_path.collection, &token, &state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct ConfirmQuery {
|
||||||
|
confirmed: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_entry(
|
async fn delete_entry(
|
||||||
entry_path: web::Path<EntryPath>,
|
entry_path: web::Path<EntryPath>,
|
||||||
|
query: web::Query<ConfirmQuery>,
|
||||||
token: ValidToken,
|
token: ValidToken,
|
||||||
conn: web::Data<Connection>,
|
conn: web::Data<Connection>,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
|
if !query.confirmed.unwrap_or(false) {
|
||||||
|
let entry = state.store.entry(&entry_path).await?;
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(vec![]);
|
||||||
|
self::templates::confirm_entry_delete(
|
||||||
|
&mut cursor,
|
||||||
|
entry_path.collection,
|
||||||
|
entry_path.entry,
|
||||||
|
&entry,
|
||||||
|
&token,
|
||||||
|
&state,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
return Ok(HttpResponse::Ok()
|
||||||
|
.content_type(mime::TEXT_HTML.essence_str())
|
||||||
|
.body(cursor.into_inner()));
|
||||||
|
}
|
||||||
|
|
||||||
let entry = state.store.entry(&entry_path).await?;
|
let entry = state.store.entry(&entry_path).await?;
|
||||||
|
|
||||||
conn.delete(&entry.filename, &entry.delete_token).await?;
|
conn.delete(&entry.filename, &entry.delete_token).await?;
|
||||||
|
@ -565,10 +603,22 @@ async fn delete_entry(
|
||||||
|
|
||||||
async fn delete_collection(
|
async fn delete_collection(
|
||||||
path: web::Path<CollectionPath>,
|
path: web::Path<CollectionPath>,
|
||||||
_token: ValidToken,
|
query: web::Query<ConfirmQuery>,
|
||||||
|
token: ValidToken,
|
||||||
conn: web::Data<Connection>,
|
conn: web::Data<Connection>,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
|
if !query.confirmed.unwrap_or(false) {
|
||||||
|
let collection = state.store.collection(&path).await?;
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(vec![]);
|
||||||
|
self::templates::confirm_delete(&mut cursor, path.collection, &collection, &token, &state)?;
|
||||||
|
|
||||||
|
return Ok(HttpResponse::Ok()
|
||||||
|
.content_type(mime::TEXT_HTML.essence_str())
|
||||||
|
.body(cursor.into_inner()));
|
||||||
|
}
|
||||||
|
|
||||||
let entries = state.store.entries(path.entry_range()).await?;
|
let entries = state.store.entries(path.entry_range()).await?;
|
||||||
|
|
||||||
let future_vec = entries
|
let future_vec = entries
|
||||||
|
|
27
templates/confirm_delete.rs.html
Normal file
27
templates/confirm_delete.rs.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
@use crate::{ui::ButtonKind, Collection, State, ValidToken};
|
||||||
|
@use super::{layout, button_link, return_home};
|
||||||
|
@use uuid::Uuid;
|
||||||
|
|
||||||
|
@(id: Uuid, collection: &Collection, token: &ValidToken, state: &State)
|
||||||
|
|
||||||
|
@:layout(state, &format!("Delete: {}", collection.title), Some(&format!("Are you sure you want to delete {}", collection.title)), {
|
||||||
|
<meta property="og:url" content="@state.delete_collection_path(id, token, false)" />
|
||||||
|
}, {
|
||||||
|
<section>
|
||||||
|
<article>
|
||||||
|
<div class="content-group">
|
||||||
|
<h3>Delete @collection.title</h3>
|
||||||
|
</div>
|
||||||
|
<div class="content-group">
|
||||||
|
<p class="subtitle">Are you sure you want to delete @collection.title</p>
|
||||||
|
</div>
|
||||||
|
<div class="content-group">
|
||||||
|
<div class="button-group">
|
||||||
|
@:button_link("Delete Collection", &state.delete_collection_path(id, token, true), ButtonKind::Submit)
|
||||||
|
@:button_link("Cancel", &state.edit_collection_path(id, token), ButtonKind::Outline)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
@:return_home(state)
|
||||||
|
})
|
33
templates/confirm_entry_delete.rs.html
Normal file
33
templates/confirm_entry_delete.rs.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
@use crate::{ui::ButtonKind, Entry, State, ValidToken};
|
||||||
|
@use super::{layout, button_link, image, return_home};
|
||||||
|
@use uuid::Uuid;
|
||||||
|
|
||||||
|
@(collection_id: Uuid, id: Uuid, entry: &Entry, token: &ValidToken, state: &State)
|
||||||
|
|
||||||
|
@:layout(state, "Delete Image", Some("Are you sure you want to delete this image?"), {
|
||||||
|
<meta property="og:url" content="@state.delete_entry_path(collection_id, id, token, false)" />
|
||||||
|
}, {
|
||||||
|
<section>
|
||||||
|
<article>
|
||||||
|
<div class="content-group">
|
||||||
|
<h3>Delete Image</h3>
|
||||||
|
</div>
|
||||||
|
<div class="content-group">
|
||||||
|
<div class="edit-row">
|
||||||
|
<div class="edit-item">
|
||||||
|
@:image(entry, state)
|
||||||
|
</div>
|
||||||
|
<div class="edit-item">
|
||||||
|
<p class="delete-confirmation">Are you sure you want to delete this image?</p>
|
||||||
|
<div class="button-group button-space">
|
||||||
|
@:button_link("Delete Image", &state.delete_entry_path(collection_id, id, token, true), ButtonKind::Submit)
|
||||||
|
@:button_link("Cancel", &state.edit_collection_path(collection_id, token), ButtonKind::Outline)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
@:return_home(state)
|
||||||
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@use crate::{ui::ButtonKind, Collection, Entry, State, ValidToken};
|
@use crate::{ui::ButtonKind, Collection, Entry, State, ValidToken};
|
||||||
@use super::{button, button_link, image_preview, file_input, layout, text_area, text_input, statics::file_upload_js};
|
@use super::{button, button_link, image, file_input, layout, return_home, text_area, text_input, statics::file_upload_js};
|
||||||
@use uuid::Uuid;
|
@use uuid::Uuid;
|
||||||
|
|
||||||
@(collection: &Collection, collection_id: Uuid, entries: &[(Uuid, Entry)], token: &ValidToken, state: &State)
|
@(collection: &Collection, collection_id: Uuid, entries: &[(Uuid, Entry)], token: &ValidToken, state: &State)
|
||||||
|
@ -34,11 +34,11 @@
|
||||||
</article>
|
</article>
|
||||||
<article class="content-group">
|
<article class="content-group">
|
||||||
<form method="POST" action="@state.update_collection_path(collection_id, token)">
|
<form method="POST" action="@state.update_collection_path(collection_id, token)">
|
||||||
@:text_input("title", Some("Title"), Some(&collection.title))
|
@:text_input("title", Some("Collection Title"), Some(&collection.title))
|
||||||
@:text_area("description", Some("Description"), Some(&collection.description))
|
@:text_area("description", Some("Collection Description"), Some(&collection.description))
|
||||||
<div class="button-space">
|
<div class="button-group button-space">
|
||||||
@:button("Update Collection", ButtonKind::Submit)
|
@:button("Update Collection", ButtonKind::Submit)
|
||||||
@:button_link("Delete Collection", &state.delete_collection_path(collection_id, token), ButtonKind::Outline)
|
@:button_link("Delete Collection", &state.delete_collection_path(collection_id, token, false), ButtonKind::Outline)
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
@ -48,21 +48,17 @@
|
||||||
<article>
|
<article>
|
||||||
<div class="edit-row">
|
<div class="edit-row">
|
||||||
<div class="edit-item">
|
<div class="edit-item">
|
||||||
@:image_preview(entry, state)
|
@:image(entry, state)
|
||||||
<div class="image-meta">
|
|
||||||
<div class="image-title">@entry.title</div>
|
|
||||||
<div class="image-description">@entry.description</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-item">
|
<div class="edit-item">
|
||||||
<form method="POST" action="@state.update_entry_path(collection_id, *id, token)">
|
<form method="POST" action="@state.update_entry_path(collection_id, *id, token)">
|
||||||
@:text_input("title", Some("Title"), Some(&entry.title))
|
@:text_input("title", Some("Image Title"), Some(&entry.title))
|
||||||
@:text_area("description", Some("Description"), Some(&entry.description))
|
@:text_area("description", Some("Image Description"), Some(&entry.description))
|
||||||
<input type="hidden" name="filename" value="@entry.filename" />
|
<input type="hidden" name="filename" value="@entry.filename" />
|
||||||
<input type="hidden" name="delete_token" value="@entry.delete_token" />
|
<input type="hidden" name="delete_token" value="@entry.delete_token" />
|
||||||
<div class="button-space">
|
<div class="button-group button-space">
|
||||||
@:button("Update Image", ButtonKind::Submit)
|
@:button("Update Image", ButtonKind::Submit)
|
||||||
@:button_link("Delete Image", &state.delete_entry_path(collection_id, *id, token), ButtonKind::Outline)
|
@:button_link("Delete Image", &state.delete_entry_path(collection_id, *id, token, false), ButtonKind::Outline)
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,14 +79,15 @@
|
||||||
<h3><legend>Add Image</legend></h3>
|
<h3><legend>Add Image</legend></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-group" id="file-input-container">
|
<div class="content-group" id="file-input-container">
|
||||||
<div class="button-space">
|
<div class="button-group">
|
||||||
@:file_input("images[]", Some("Select Image"), Some(crate::accept()), false)
|
@:file_input("images[]", Some("Select Image"), Some(crate::accept()), false)
|
||||||
</div>
|
</div>
|
||||||
<div class="button-space">
|
<div class="button-group button-space">
|
||||||
@:button("Upload", ButtonKind::Submit)
|
@:button("Upload", ButtonKind::Submit)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
@:return_home(state)
|
||||||
})
|
})
|
||||||
|
|
10
templates/image.rs.html
Normal file
10
templates/image.rs.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
@use crate::{Entry, State};
|
||||||
|
@use super::image_preview;
|
||||||
|
|
||||||
|
@(entry: &Entry, state: &State)
|
||||||
|
|
||||||
|
@:image_preview(entry, state)
|
||||||
|
<div class="image-meta">
|
||||||
|
<div class="image-title">@entry.title</div>
|
||||||
|
<div class="image-description">@entry.description</div>
|
||||||
|
</div>
|
|
@ -15,7 +15,9 @@
|
||||||
@:text_area("description", Some("Description"), None)
|
@:text_area("description", Some("Description"), None)
|
||||||
</div>
|
</div>
|
||||||
<div class="content-group">
|
<div class="content-group">
|
||||||
@:button("Create Collection", ButtonKind::Submit)
|
<div class="button-group">
|
||||||
|
@:button("Create Collection", ButtonKind::Submit)
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
|
9
templates/return_home.rs.html
Normal file
9
templates/return_home.rs.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
@use crate::State;
|
||||||
|
|
||||||
|
@(state: &State)
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<article class="content-group">
|
||||||
|
<a href="@state.create_collection_path()">Return Home</a>
|
||||||
|
</article>
|
||||||
|
</section>
|
|
@ -1,5 +1,5 @@
|
||||||
@use crate::{Collection, Entry, State};
|
@use crate::{Collection, Entry, State};
|
||||||
@use super::{layout, image_preview};
|
@use super::{layout, image, return_home};
|
||||||
@use uuid::Uuid;
|
@use uuid::Uuid;
|
||||||
|
|
||||||
@(id: Uuid, collection: &Collection, entries: &[(Uuid, Entry)], state: &State)
|
@(id: Uuid, collection: &Collection, entries: &[(Uuid, Entry)], state: &State)
|
||||||
|
@ -23,15 +23,12 @@
|
||||||
@for (_, entry) in entries {
|
@for (_, entry) in entries {
|
||||||
<li class="content-group even">
|
<li class="content-group even">
|
||||||
<article>
|
<article>
|
||||||
@:image_preview(entry, state)
|
@:image(entry, state)
|
||||||
<div class="image-meta">
|
|
||||||
<div class="image-title">@entry.title</div>
|
|
||||||
<div class="image-description">@entry.description</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
@:return_home(state)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue