Commit 4c4a7c346c1e54900fb853ba79b0142917272799

Authored by Georg Hopp
1 parent 7093450d

incomplete image selector

... ... @@ -8,27 +8,23 @@ pub enum Either<L, R> {
8 8
9 9 #[derive(Clone, Debug, Serialize, Deserialize)]
10 10 pub struct MarkdownJson {
11   - pub name: String,
12   - pub content: String,
13   - pub number_of_versions: i32,
14   - pub date_created: String,
15   - pub date_updated: String,
  11 + pub name :String,
  12 + pub content :String,
  13 + pub number_of_versions :i32,
  14 + pub date_created :String,
  15 + pub date_updated :String,
16 16 }
17 17
18 18 #[derive(Clone, Debug, Serialize, Deserialize)]
19 19 pub struct MarkdownDiffJson {
20   - pub id: i32,
21   - pub date_created: String,
  20 + pub id :i32,
  21 + pub date_created :String,
22 22 }
23 23
24 24 #[derive(Clone, Debug, Serialize, Deserialize)]
25 25 pub struct ImageJson {
26   - pub upload_uuid :Option<Vec<u8>>,
27   - pub uuid :Option<Vec<u8>>,
28   - pub size :i32,
29   - pub dim_x :Option<i32>,
30   - pub dim_y :Option<i32>,
31   - pub mime_type :String,
  26 + pub id :i32,
  27 + pub uuid :Vec<u8>,
32 28 pub date_created :String,
33 29 pub date_updated :String
34 30 }
... ...
... ... @@ -57,6 +57,9 @@ async fn main() -> std::io::Result<()> {
57 57 . service( web::resource("/upload")
58 58 . route(web::post().to(upload))
59 59 )
  60 + . service( web::resource("/images")
  61 + . route(web::get().to(get_images))
  62 + )
60 63 . service( web::resource("/images/{id}")
61 64 . route(web::get().to(get_image))
62 65 )
... ...
... ... @@ -240,6 +240,13 @@ pub(crate) fn finalize( pool :Arc<Pool>
240 240 }
241 241 }
242 242
  243 +pub(crate) fn get_images(pool: Arc<Pool>) -> Result<Vec<Image>>
  244 +{
  245 + use crate::schema::images::dsl::*;
  246 + let db_connection = pool.get()?;
  247 + Ok(images.load::<Image>(&db_connection)?)
  248 +}
  249 +
243 250 pub(crate) fn get_image( pool :Arc<Pool>
244 251 , ident :i32 ) -> Result<Image>
245 252 {
... ...
... ... @@ -2,7 +2,7 @@ use std::fmt::Display;
2 2
3 3 use crate::{models::image, AppData, error::Error};
4 4
5   -use actix_web::{Error as ActixError, web, http::StatusCode};
  5 +use actix_web::{Error as ActixError, web, HttpResponse, http::StatusCode};
6 6 use anyhow::Result;
7 7 use serde::{Deserialize, Serialize};
8 8
... ... @@ -35,6 +35,17 @@ impl Display for Size {
35 35 }
36 36 }
37 37
  38 +pub async fn get_images(app_data: web::Data<AppData>)
  39 + -> Result<HttpResponse, ActixError>
  40 +{
  41 + let pool = app_data.database_pool.clone();
  42 +
  43 + Ok(web::block(move || image::get_images(pool))
  44 + . await
  45 + . map(|images| HttpResponse::Ok().json(images))
  46 + . map_err(|_| HttpResponse::InternalServerError())?)
  47 +}
  48 +
38 49 pub async fn get_image( app_data: web::Data<AppData>
39 50 , ident: web::Path<i32>
40 51 , size: web::Query<SizeQuery>
... ...
... ... @@ -111,17 +111,17 @@ async fn save_resized( original :&DynamicImage
111 111 Err(e) if e.kind() == ErrorKind::NotFound =>
112 112 spawn_blocking(move || -> Result<(), Error> {
113 113 let mut scaled = original.resize(width, height, Lanczos3);
114   - overlay(&mut scaled, CONFIG.copyright_image(), 0_u32, 0_u32);
115 114
116 115 if let Size::Thumbnail = size {
117   - scaled.save_with_format(&path, Jpeg)?;
118 116 } else {
119   - let stegonography = CONFIG.copyright_steganography().as_bytes();
120   - let encoder = Encoder::new(stegonography, scaled);
121   - let scaled = encoder.encode_alpha();
122   - scaled.save_with_format(&path, Jpeg)?;
  117 + overlay(&mut scaled, CONFIG.copyright_image(), 0_u32, 0_u32);
123 118 }
124 119
  120 + let stegonography = CONFIG.copyright_steganography().as_bytes();
  121 + let encoder = Encoder::new(stegonography, scaled);
  122 + let scaled = encoder.encode_alpha();
  123 + scaled.save_with_format(&path, Jpeg)?;
  124 +
125 125 let exiv = Metadata::new_from_path(&path)?;
126 126 exiv.set_tag_string("Exif.Image.Copyright", CONFIG.copyright_exiv())?;
127 127 exiv.save_to_file(&path)?;
... ...
1   -.upload {
  1 +.application > div {
2 2 float: left;
3   - width: 400px;
  3 + width: 30%;
  4 +}
  5 +
  6 +.selector {
  7 + padding: 1em;
  8 + border-radius: .5em;
  9 + border: 1px solid #ddd;
  10 + background: #f7f7f7;
  11 +}
  12 +
  13 +.selector img {
  14 + max-width: 75px;
  15 + max-height: 75px;
  16 + width: auto;
  17 + height: auto;
  18 + vertical-align: middle;
  19 +}
  20 +
  21 +.selector > ul {
  22 + display: flex;
  23 + flex-wrap: wrap;
  24 + gap: 5px;
  25 + justify-content: space-between;
  26 + align-items: center;
  27 + max-height: 15em;
  28 + width: calc(100% - 15px);
  29 + overflow-y: auto;
  30 + overflow-x: auto;
  31 + background: #ffffff;
  32 + border-radius: .35em;
  33 + border: 2px solid #bbb;
  34 + cursor: default;
  35 + padding-left: 5px;
  36 + padding-right: 5px;
  37 + margin-top: 0;
  38 + margin-bottom: 0;
  39 +}
  40 +
  41 +.selector > ul > li {
  42 + display: unset;
  43 + border: 1px solid black;
  44 + width: fit-content;
  45 + height: fit-content;
  46 + margin-bottom: .15em;
  47 +}
  48 +
  49 +.selector > ul > li:last-child {
  50 + margin-bottom: 0;
  51 +}
  52 +
  53 +.upload {
4 54 padding: 1em;
5 55 border-radius: .5em;
6 56 border: 1px solid #ddd;
... ... @@ -55,7 +105,6 @@
55 105 }
56 106
57 107 .markdown {
58   - float: left;
59 108 padding: 1em;
60 109 border-radius: .5em;
61 110 border: 1px solid #ddd;
... ... @@ -76,7 +125,6 @@
76 125
77 126 .markdown > div:first-child {
78 127 position: fixed;
79   - width: inherit;
80 128 z-index: 10;
81 129 }
82 130
... ...
  1 +use std::fmt::Display;
  2 +
  3 +use artshop_common::types::ImageJson;
  4 +
  5 +use super::super::error::*;
  6 +use super::super::client::Client;
  7 +
  8 +#[derive(Debug, Clone)]
  9 +pub struct Image {
  10 + pub json: ImageJson,
  11 +}
  12 +
  13 +pub(crate) async fn images() -> Result<Vec<Image>> {
  14 + let client = Client::new()?;
  15 + let (response, data) = client.get("/api/v0/images").await?;
  16 +
  17 + match response.status() {
  18 + 200 => Ok ( serde_json::from_str(data.as_str())
  19 + . map(|images :Vec<ImageJson>| {
  20 + images.into_iter().map(|json| Image { json }).collect()
  21 + })? ),
  22 + status => Err(status_error(status)),
  23 + }
  24 +}
  25 +
  26 +fn status_error<I: Display>(status :I) -> Error {
  27 + let err_str = format!("Invalid response status: {}", status);
  28 + Error::from(err_str.as_str())
  29 +}
  30 +
  31 +// impl Image {
  32 +// }
... ...
  1 +pub(crate) mod image;
1 2 pub(crate) mod markdown;
2 3 pub(crate) mod upload;
... ...
  1 +use mogwai::prelude::*;
  2 +
  3 +use crate::api::image::{Image, images};
  4 +
  5 +use super::{PatchSender, view::image_preview_view};
  6 +
  7 +pub(super) async fn image_preview_logic() {
  8 +}
  9 +
  10 +pub(super) async fn selector_logic( mut rx_dom :broadcast::Receiver<Dom>
  11 + , tx_previews :PatchSender) {
  12 + let mut previews :ListPatchModel<Image> = ListPatchModel::new();
  13 +
  14 + mogwai::spawn(previews.stream().for_each(move |patch| {
  15 + let patch :ListPatch<ViewBuilder<Dom>> = patch.map(|i| {
  16 + let view = image_preview_view(
  17 + String::from(format!( "/api/v0/images/{}?size=thumbnail"
  18 + , i.json.id )));
  19 + let logic = image_preview_logic();
  20 +
  21 + Component::from(view).with_logic(logic).into()
  22 + });
  23 + let tx_previews = tx_previews.clone();
  24 +
  25 + async move {
  26 + tx_previews.send(patch).await.unwrap();
  27 + }
  28 + }));
  29 +
  30 + if let Some(_) = rx_dom.next().await {
  31 + for image in images().await.unwrap().into_iter() {
  32 + previews.list_patch_push(image);
  33 + }
  34 + }
  35 +}
... ...
  1 +pub(crate) mod logic;
  2 +mod view;
  3 +
  4 +use mogwai::prelude::*;
  5 +
  6 +use self::{view::selector_view, logic::selector_logic};
  7 +
  8 +type PatchSender = mpmc::Sender<ListPatch<ViewBuilder<Dom>>>;
  9 +type PatchReceiver = mpmc::Receiver<ListPatch<ViewBuilder<Dom>>>;
  10 +
  11 +pub(crate) async fn new() -> Component<Dom> {
  12 + let (tx_previews, rx_previews) = mpmc::bounded(1);
  13 + let (tx_dom, rx_dom) = broadcast::bounded(1);
  14 +
  15 + let view = selector_view(tx_dom, rx_previews);
  16 + let logic = selector_logic(rx_dom, tx_previews);
  17 +
  18 + Component::from(view).with_logic(logic)
  19 +}
... ...
  1 +use mogwai::prelude::*;
  2 +
  3 +use crate::component::imageselector::PatchReceiver;
  4 +
  5 +pub(super) fn image_preview_view(image_url :String) -> ViewBuilder<Dom> {
  6 + builder! {
  7 + <li><img src=image_url/></li>
  8 + }
  9 +}
  10 +
  11 +pub(super) fn selector_view( tx_dom :broadcast::Sender<Dom>
  12 + , rx_previews :PatchReceiver) -> ViewBuilder<Dom> {
  13 + let post_build = move |dom: &mut Dom| {
  14 + tx_dom.try_broadcast(dom.clone()).unwrap();
  15 + };
  16 +
  17 + builder! {
  18 + <div class="selector">
  19 + <ul patch:children=rx_previews
  20 + post:build=post_build>
  21 + </ul>
  22 + </div>
  23 + }
  24 +}
... ...
... ... @@ -58,7 +58,6 @@ pub(super) fn markdown_view( tx_logic: broadcast::Sender<MarkdownLogic>
58 58
59 59 builder! {
60 60 <div class="markdown"
61   - style:width="33%"
62 61 post:build=move |_: &mut Dom| {
63 62 tx_logic.try_broadcast(MarkdownLogic::Choose(None)).unwrap();
64 63 }
... ...
  1 +pub(crate) mod imageselector;
1 2 pub(crate) mod markdown;
2 3 pub(crate) mod upload;
... ...
... ... @@ -29,11 +29,13 @@ pub async fn main() -> Result<(), JsValue> {
29 29
30 30 let md = markdown::new().await;
31 31 let comp = upload::new().await;
  32 + let selector = imageselector::new().await;
32 33
33 34 let page = Component::from(builder! {
34   - <div>
  35 + <div class="application">
35 36 {comp}
36 37 {md}
  38 + {selector}
37 39 </div>
38 40 });
39 41 page.build()?.run()
... ...
Please register or login to post a comment