diff --git a/Cargo.lock b/Cargo.lock index 109c477..39d59ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,6 +1290,7 @@ dependencies = [ "minify-html", "opentelemetry", "opentelemetry-otlp", + "qrcodegen", "ructe", "serde", "serde_json", @@ -1433,6 +1434,12 @@ dependencies = [ "prost", ] +[[package]] +name = "qrcodegen" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135e6754eed8ca897dd70584d895e72e36860b3e163b6bcedce48571cbaef343" + [[package]] name = "quote" version = "1.0.15" diff --git a/Cargo.toml b/Cargo.toml index dd3dffc..0bd95cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ mime = "0.3" minify-html = "0.8.0" opentelemetry = { version = "0.17", features = ["rt-tokio"] } opentelemetry-otlp = "0.10" +qrcodegen = "1.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sled = { version = "0.34.7", features = ["zstd"] } diff --git a/scss/layout.scss b/scss/layout.scss index 0756831..f36bc38 100644 --- a/scss/layout.scss +++ b/scss/layout.scss @@ -19,6 +19,16 @@ section { border-radius: 3px; } +.qr { + display: flex; + flex-direction: row; + justify-content: center; + + svg { + height: 200px; + } +} + .content-group { padding: 16px 32px; border-bottom: 1px solid #e5e5e5; diff --git a/src/lib.rs b/src/lib.rs index 0aa5060..34fc96e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -632,9 +632,10 @@ async fn collection( path: web::Path, token: Option, state: web::Data, + req: HttpRequest, ) -> Result { match token { - Some(token) => edit_collection(path, token, state.clone()) + Some(token) => edit_collection(path, token, state.clone(), req) .await .stateful(&state), None => view_collection(path, state.clone()).await.stateful(&state), @@ -668,7 +669,10 @@ async fn edit_collection( path: web::Path, token: ValidToken, state: web::Data, + req: HttpRequest, ) -> Result { + let qr = qr(&req, &path, &state); + let collection = match state.store.collection(&path).await? { Some(collection) => collection, None => return Ok(to_404(&state)), @@ -687,6 +691,7 @@ async fn edit_collection( &entries, &token, &state, + &qr, ) }, HttpResponse::Ok(), @@ -824,6 +829,48 @@ async fn delete_entry( Ok(to_edit_page(entry_path.collection, &token, &state)) } +fn qr(req: &HttpRequest, path: &web::Path, state: &web::Data) -> String { + let host = req.head().headers().get("host").unwrap(); + + let url = format!( + "https://{}{}", + host.to_str().unwrap(), + state.public_collection_path(path.collection) + ); + + let code = qrcodegen::QrCode::encode_text(&url, qrcodegen::QrCodeEcc::Low).unwrap(); + + to_svg_string(&code, 4) +} + +fn to_svg_string(qr: &qrcodegen::QrCode, border: i32) -> String { + assert!(border >= 0, "Border must be non-negative"); + let mut result = String::new(); + // result += "\n"; + // result += "\n"; + let dimension = qr + .size() + .checked_add(border.checked_mul(2).unwrap()) + .unwrap(); + result += &format!( + "\n", dimension); + result += "\t\n"; + result += "\t\n"; + result += "\n"; + result +} + #[tracing::instrument(name = "Delete Collection")] async fn delete_collection( path: web::Path, diff --git a/templates/edit_collection.rs.html b/templates/edit_collection.rs.html index 6e92272..83e01d6 100644 --- a/templates/edit_collection.rs.html +++ b/templates/edit_collection.rs.html @@ -1,103 +1,104 @@ @use crate::{ui::ButtonKind, Collection, Direction, Entry, State, ValidToken}; -@use super::{button, button_link, image, file_input, layout, return_home, 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) +@(collection: &Collection, collection_id: Uuid, entries: &[(Uuid, Entry)], token: &ValidToken, state: &State, qr: &str) @:layout(state, "Edit Collection", None, { - + }, {
-
-

Share Collection

-
- +
+

Share Collection

+
+ +
+
+ @Html(qr) +
+
-
-

Edit Collection

-
- -
-
- @:text_input("title", Some("Collection Title"), Some(&collection.title)) - @:text_area("description", Some("Collection Description"), Some(&collection.description)) -
- @:button("Update Collection", ButtonKind::Submit) - @:button_link("Delete Collection", &state.delete_collection_path(collection_id, token, false), ButtonKind::Outline) -
-
-
-
    - @for (i, (id, entry)) in entries.iter().enumerate() { -
  • -
    -
    -
    - @:image(entry, state) -
    -
    -
    - @:text_input("title", Some("Image Title"), Some(&entry.title)) - @:text_area("description", Some("Image Description"), Some(&entry.description)) - - -
    - @:button("Update Image", ButtonKind::Submit) - @:button_link("Delete Image", &state.delete_entry_path(collection_id, *id, token, false), ButtonKind::Outline) -
    +
    +

    Edit Collection

    +
    + +
    + + @:text_input("title", Some("Collection Title"), Some(&collection.title)) + @:text_area("description", Some("Collection Description"), Some(&collection.description)) +
    + @:button("Update Collection", ButtonKind::Submit) + @:button_link("Delete Collection", &state.delete_collection_path(collection_id, token, false), + ButtonKind::Outline) +
    + +
    +
      + @for (i, (id, entry)) in entries.iter().enumerate() { +
    • +
      +
      +
      + @:image(entry, state) +
      +
      +
      + @:text_input("title", Some("Image Title"), Some(&entry.title)) + @:text_area("description", Some("Image Description"), Some(&entry.description)) + + +
      + @:button("Update Image", ButtonKind::Submit) + @:button_link("Delete Image", &state.delete_entry_path(collection_id, *id, token, false), + ButtonKind::Outline) +
      -
      - @if i != 0 { - @:button_link("Move Up", &state.move_entry_path(collection_id, *id, token, Direction::Up), ButtonKind::Outline) - } +
      + @if i != 0 { + @:button_link("Move Up", &state.move_entry_path(collection_id, *id, token, Direction::Up), + ButtonKind::Outline) + } - @if (i + 1) != entries.len() { - @:button_link("Move Down", &state.move_entry_path(collection_id, *id, token, Direction::Down), ButtonKind::Outline) - } -
      - -
      -
      -
      -
    • - } -
    + @if (i + 1) != entries.len() { + @:button_link("Move Down", &state.move_entry_path(collection_id, *id, token, Direction::Down), + ButtonKind::Outline) + } +
    + +
    + +
    +
  • + } +
-
-
-
-

Add Image

-
-
-
- @:file_input("images[]", Some("Select Image"), Some(crate::accept()), false) -
-
- @:button("Upload", ButtonKind::Submit) -
-
-
-
+
+
+
+

+ Add Image +

+
+
+
+ @:file_input("images[]", Some("Select Image"), Some(crate::accept()), false) +
+
+ @:button("Upload", ButtonKind::Submit) +
+
+
+
@:return_home(state) })