Fix index mobile styles, add delete confirmations

This commit is contained in:
asonix 2020-12-11 21:36:07 -06:00
parent dabff41156
commit fbf10e2eea
11 changed files with 178 additions and 42 deletions

2
Cargo.lock generated
View File

@ -1334,7 +1334,7 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pict-rs-aggregator"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"actix-web",
"anyhow",

View File

@ -1,6 +1,6 @@
[package]
name = "pict-rs-aggregator"
version = "0.1.2"
version = "0.1.3"
authors = ["asonix <asonix@asonix.dog>"]
edition = "2018"
build = "src/build.rs"

View File

@ -51,6 +51,11 @@ article .content-group {
margin: 0;
}
.delete-confirmation {
margin: 0;
padding: 16px 0 0;
}
h3 {
margin: 0;
}
@ -135,8 +140,11 @@ a {
}
}
.button-space {
.button-group {
margin: 0 -8px;
}
.button-space {
padding-top: 16px;
}
@ -237,7 +245,7 @@ ul {
width: 100%;
}
.button-space {
.button-group {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
@ -248,6 +256,9 @@ ul {
flex: 1;
min-width: 150px;
text-align: center;
display: flex;
justify-content: space-around;
align-items: center;
}
.title {

View File

@ -89,8 +89,15 @@ impl State {
self.scoped(&format!("{}?token={}", id, token.token))
}
fn delete_collection_path(&self, id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!("{}/delete?token={}", id, token.token))
fn delete_collection_path(&self, id: Uuid, token: &ValidToken, confirmed: bool) -> String {
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 {
@ -108,11 +115,24 @@ impl State {
))
}
fn delete_entry_path(&self, collection_id: Uuid, id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!(
"{}/entry/{}/delete?token={}",
collection_id, id, token.token
))
fn delete_entry_path(
&self,
collection_id: Uuid,
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 {
@ -448,13 +468,7 @@ async fn view_collection(
let mut cursor = Cursor::new(vec![]);
self::templates::view_collection(
&mut cursor,
path.collection,
&collection,
&entries,
&state,
)?;
self::templates::view_collection(&mut cursor, path.collection, &collection, &entries, &state)?;
Ok(HttpResponse::Ok()
.content_type(mime::TEXT_HTML.essence_str())
@ -544,12 +558,36 @@ async fn update_entry(
Ok(to_edit_page(entry_path.collection, &token, &state))
}
#[derive(serde::Deserialize)]
struct ConfirmQuery {
confirmed: Option<bool>,
}
async fn delete_entry(
entry_path: web::Path<EntryPath>,
query: web::Query<ConfirmQuery>,
token: ValidToken,
conn: web::Data<Connection>,
state: web::Data<State>,
) -> 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?;
conn.delete(&entry.filename, &entry.delete_token).await?;
@ -565,10 +603,22 @@ async fn delete_entry(
async fn delete_collection(
path: web::Path<CollectionPath>,
_token: ValidToken,
query: web::Query<ConfirmQuery>,
token: ValidToken,
conn: web::Data<Connection>,
state: web::Data<State>,
) -> 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 future_vec = entries

View 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)
})

View 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)
})

View File

@ -1,5 +1,5 @@
@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;
@(collection: &Collection, collection_id: Uuid, entries: &[(Uuid, Entry)], token: &ValidToken, state: &State)
@ -34,11 +34,11 @@
</article>
<article class="content-group">
<form method="POST" action="@state.update_collection_path(collection_id, token)">
@:text_input("title", Some("Title"), Some(&collection.title))
@:text_area("description", Some("Description"), Some(&collection.description))
<div class="button-space">
@:text_input("title", Some("Collection Title"), Some(&collection.title))
@:text_area("description", Some("Collection Description"), Some(&collection.description))
<div class="button-group button-space">
@: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>
</form>
</article>
@ -48,21 +48,17 @@
<article>
<div class="edit-row">
<div class="edit-item">
@:image_preview(entry, state)
<div class="image-meta">
<div class="image-title">@entry.title</div>
<div class="image-description">@entry.description</div>
</div>
@:image(entry, state)
</div>
<div class="edit-item">
<form method="POST" action="@state.update_entry_path(collection_id, *id, token)">
@:text_input("title", Some("Title"), Some(&entry.title))
@:text_area("description", Some("Description"), Some(&entry.description))
@:text_input("title", Some("Image Title"), Some(&entry.title))
@:text_area("description", Some("Image Description"), Some(&entry.description))
<input type="hidden" name="filename" value="@entry.filename" />
<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_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>
</form>
</div>
@ -83,14 +79,15 @@
<h3><legend>Add Image</legend></h3>
</div>
<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)
</div>
<div class="button-space">
<div class="button-group button-space">
@:button("Upload", ButtonKind::Submit)
</div>
</div>
</form>
</article>
</section>
@:return_home(state)
})

10
templates/image.rs.html Normal file
View 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>

View File

@ -15,7 +15,9 @@
@:text_area("description", Some("Description"), None)
</div>
<div class="content-group">
@:button("Create Collection", ButtonKind::Submit)
<div class="button-group">
@:button("Create Collection", ButtonKind::Submit)
</button>
</div>
</form>
</article>

View 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>

View File

@ -1,5 +1,5 @@
@use crate::{Collection, Entry, State};
@use super::{layout, image_preview};
@use super::{layout, image, return_home};
@use uuid::Uuid;
@(id: Uuid, collection: &Collection, entries: &[(Uuid, Entry)], state: &State)
@ -23,15 +23,12 @@
@for (_, entry) in entries {
<li class="content-group even">
<article>
@:image_preview(entry, state)
<div class="image-meta">
<div class="image-title">@entry.title</div>
<div class="image-description">@entry.description</div>
</div>
@:image(entry, state)
</article>
</li>
}
</ul>
</section>
@:return_home(state)
})