Commit a0ed54d6ab9a03fbccd9d15b4d802267d5175b33

Authored by Georg Hopp
1 parent 5b9ef0e3

Use a ListPatchModel to keep track of the uploads list

  1 +.upload {
  2 + float: left;
  3 + width: 400px;
  4 + padding: 1em;
  5 + border-radius: .5em;
  6 + border: 1px solid #ddd;
  7 + background: #f7f7f7;
  8 +}
  9 +
  10 +.upload ul {
  11 + padding-left: 5px;
  12 +}
  13 +
  14 +.upload > ul {
  15 + max-height: 15em;
  16 + width: calc(100% - 10px);
  17 + overflow-y: auto;
  18 + overflow-x: hidden;
  19 + background: #ffffff;
  20 + border-radius: .35em;
  21 + border: 2px solid #bbb;
  22 +}
  23 +
  24 +.upload > ul > li {
  25 + display: flex;
  26 + border: 1px solid black;
  27 + width: fit-content;
  28 +}
  29 +
  30 +.upload > ul > li > ul > li {
  31 + display: block;
  32 +}
  33 +
1 34 .markdown {
2 35 float: left;
3 36 padding: 1em;
... ...
1 1 pub(crate) mod markdown;
  2 +pub(crate) mod upload;
... ...
  1 +use std::fmt::Display;
  2 +
  3 +use super::super::error::*;
  4 +use super::super::client::Client;
  5 +
  6 +#[derive(Debug, Clone)]
  7 +pub struct Upload {
  8 + client: Client,
  9 +}
  10 +
  11 +impl Upload {
  12 + pub(crate) async fn new(name :&str) -> Result<()> {
  13 + Ok(())
  14 + /*
  15 + match response.status() {
  16 + 200 => Ok(self),
  17 + status => Err(Self::status_error(status)),
  18 + }
  19 + */
  20 + }
  21 +}
... ...
1 1 use js_sys::JsString;
2 2 use mogwai::prelude::*;
3 3 use wasm_bindgen::prelude::*;
4   -use web_sys::{Window, window, Response, Request, RequestInit, RequestMode};
  4 +use web_sys::{Window, window, Response, Request, RequestInit, RequestMode, ReadableStream};
5 5 use super::error::*;
6 6
7 7 use std::result::Result as StdResult;
... ... @@ -25,7 +25,7 @@ impl Client {
25 25
26 26 pub async fn get(&self, url :&str) -> Result<(Response, String)> {
27 27 let mut init = RequestInit::new();
28   - let request = REQUEST( &url
  28 + let request = REQUEST( url
29 29 , init . method("GET")
30 30 . mode(RequestMode::Cors) )?;
31 31
... ... @@ -46,7 +46,7 @@ impl Client {
46 46
47 47 pub async fn put(&self, url :&str, data :&str) -> Result<Response> {
48 48 let mut init = RequestInit::new();
49   - let request = REQUEST( &url
  49 + let request = REQUEST( url
50 50 , init . method("PUT")
51 51 . mode(RequestMode::Cors)
52 52 . body(Some(&data.into())) )?;
... ... @@ -61,4 +61,25 @@ impl Client {
61 61
62 62 Ok(response)
63 63 }
  64 +
  65 + pub async fn post_stream( &self
  66 + , url :&str
  67 + , mime_type :&str
  68 + , data :ReadableStream) -> Result<Response> {
  69 + let mut init = RequestInit::new();
  70 + let request = REQUEST( url
  71 + , init . method("POST")
  72 + . mode(RequestMode::Cors)
  73 + . body(Some(&data.into())) )?;
  74 +
  75 + request . headers()
  76 + . set("Content-Type", mime_type)?;
  77 +
  78 + let response = JsFuture::from( self.window
  79 + . fetch_with_request(&request))
  80 + . await?
  81 + . dyn_into::<Response>()?;
  82 +
  83 + Ok(response)
  84 + }
64 85 }
... ...
1 1 use mogwai::prelude::*;
2   -use web_sys::{HtmlInputElement, window, ImageBitmap, HtmlCanvasElement, CanvasRenderingContext2d};
  2 +use web_sys::{HtmlInputElement, ImageBitmap, HtmlCanvasElement, CanvasRenderingContext2d};
3 3
4   -use crate::component::upload::view::upload_preview_view;
  4 +use super::upload::Upload;
5 5
6   -async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
7   - , bitmap :ImageBitmap ) {
  6 +pub(super) async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
  7 + , upload :Upload ) {
8 8 while let Some(dom) = rx_canvas.next().await {
9 9 match dom.inner_read() {
10 10 Either::Left(c) => {
... ... @@ -12,10 +12,12 @@ async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
12 12 let context = canvas
13 13 . get_context("2d").unwrap().unwrap()
14 14 . dyn_into::<CanvasRenderingContext2d>().unwrap();
15   - context . draw_image_with_image_bitmap_and_dw_and_dh( &bitmap
16   - , 0.0, 0.0
17   - , 100.0, 100.0 )
18   - . unwrap();
  15 + context
  16 + . draw_image_with_image_bitmap_and_dw_and_dh(
  17 + &upload.bitmap()
  18 + , 0.0, 0.0
  19 + , canvas.width() as f64, canvas.height() as f64 )
  20 + . unwrap();
19 21 },
20 22 _ => (),
21 23 }
... ... @@ -25,36 +27,28 @@ async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
25 27 pub(super) async fn upload_logic( mut rx_logic: broadcast::Receiver<DomEvent>
26 28 , tx_previews: mpmc::Sender<ListPatch<ViewBuilder<Dom>>>
27 29 ) {
  30 + let mut uploads: ListPatchModel<Upload> = ListPatchModel::new();
  31 +
  32 + mogwai::spawn(uploads.stream().for_each(move |patch| {
  33 + let patch = patch.map(|u| u.into());
  34 + let tx_previews = tx_previews.clone();
  35 + async move {
  36 + tx_previews.send(patch).await.unwrap();
  37 + }
  38 + }));
  39 +
28 40 while let Some(msg) = rx_logic.next().await {
29 41 match msg.clone_inner() {
30 42 Either::Left(val) => {
31   - let event = val.dyn_into::<Event>().unwrap();
32   - let target = event.target().unwrap();
33   - let element = target.dyn_into::<HtmlInputElement>().unwrap();
34   - let filelist = element.files().unwrap();
  43 + let filelist = val.dyn_into::<Event>().unwrap()
  44 + . target().unwrap()
  45 + . dyn_into::<HtmlInputElement>().unwrap()
  46 + . files().unwrap();
35 47
36   - let mut previews = vec![];
37 48 for index in 0..filelist.length() {
38   - let (tx_canvas, rx_canvas) = broadcast::bounded(1);
39 49 let file = filelist.item(index).unwrap();
40   - let bitmap =
41   - JsFuture::from( window().unwrap()
42   - . create_image_bitmap_with_blob(&file.clone().into()).unwrap());
43   - let bitmap = bitmap
44   - . await.unwrap()
45   - . dyn_into::<ImageBitmap>().unwrap();
46   -
47   - let view = upload_preview_view( tx_canvas
48   - , file.name()
49   - , file.size()
50   - , file.type_()
51   - , file.last_modified() );
52   - let logic = upload_preview_logic(rx_canvas, bitmap);
53   -
54   - previews.push(Component::from(view).with_logic(logic).into());
  50 + uploads.list_patch_push(Upload::new(file).await);
55 51 }
56   - let previews = ListPatch::splice(.., previews.into_iter());
57   - tx_previews.send(previews).await.unwrap();
58 52 },
59 53 _ => (),
60 54 }
... ...
1   -mod view;
2 1 mod logic;
  2 +mod upload;
  3 +mod view;
3 4
4 5 use mogwai::prelude::*;
5 6
... ...
  1 +use mogwai::{prelude::*, utils::window};
  2 +use web_sys::{File, ImageBitmap, ReadableStream};
  3 +
  4 +use super::{view::upload_preview_view, logic::upload_preview_logic};
  5 +
  6 +#[derive(Clone, Debug)]
  7 +pub(super) struct Upload {
  8 + file :File,
  9 + bitmap :ImageBitmap,
  10 +}
  11 +
  12 +impl Upload {
  13 + pub(super) async fn new(file :File) -> Upload {
  14 + let bitmap = window()
  15 + . create_image_bitmap_with_blob(&file.clone().into())
  16 + . unwrap();
  17 + let bitmap = JsFuture::from(bitmap)
  18 + . await.unwrap()
  19 + . dyn_into::<ImageBitmap>().unwrap();
  20 +
  21 + Self { file, bitmap }
  22 + }
  23 +
  24 + pub(super) fn mime_type(&self) -> String {
  25 + self.file.type_()
  26 + }
  27 +
  28 + pub(super) fn data(&self) -> ReadableStream {
  29 + self.file.stream()
  30 + }
  31 +
  32 + pub(super) fn bitmap(&self) -> ImageBitmap {
  33 + self.to_owned().bitmap
  34 + }
  35 +}
  36 +
  37 +impl From<Upload> for Component<Dom> {
  38 + fn from(upload: Upload) -> Self {
  39 + let (tx_canvas, rx_canvas) = broadcast::bounded(1);
  40 +
  41 + let view = upload_preview_view( tx_canvas
  42 + , upload.file.name()
  43 + , upload.file.size()
  44 + , upload.file.type_()
  45 + , upload.file.last_modified() );
  46 + let logic = upload_preview_logic(rx_canvas, upload);
  47 +
  48 + Component::from(view).with_logic(logic)
  49 + }
  50 +}
  51 +
  52 +impl From<Upload> for ViewBuilder<Dom> {
  53 + fn from(upload: Upload) -> Self {
  54 + let component :Component<Dom> = upload.into();
  55 + component.into()
  56 + }
  57 +}
... ...
... ... @@ -11,18 +11,15 @@ pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom>
11 11
12 12 builder! {
13 13 <li style:display="flex">
14   - <div style:width="100px"
15   - style:height="100px">
16   - <canvas post:build=post_build />
17   - </div>
18   - <div style:width="fit-content">
19   - <ul>
20   - <li>{format!("filename: {}", filename)}</li>
21   - <li>{format!("size: {}", size)}</li>
22   - <li>{format!("mime type: {}", mime_type)}</li>
23   - <li>{format!("modification time: {}", mtime)}</li>
24   - </ul>
25   - </div>
  14 + <canvas width="75px"
  15 + height="75px"
  16 + post:build=post_build />
  17 + <ul>
  18 + <li>{format!("filename: {}", filename)}</li>
  19 + <li>{format!("size: {}", size)}</li>
  20 + <li>{format!("mime type: {}", mime_type)}</li>
  21 + <li>{format!("modification time: {}", mtime)}</li>
  22 + </ul>
26 23 </li>
27 24 }
28 25 }
... ... @@ -30,14 +27,15 @@ pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom>
30 27 pub(super) fn upload_view( tx_logic: broadcast::Sender<DomEvent>
31 28 , rx_previews: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>>
32 29 ) -> ViewBuilder<Dom> {
  30 +// <div class="spin"></div>
33 31 builder! {
34 32 <div class="upload">
35   - <div class="spin"></div>
36 33 <div>
37 34 <input type="file"
38 35 multiple="multiple"
39 36 accept="image/*"
40   - on:change=tx_logic.sink()/>
  37 + on:change=tx_logic.sink() />
  38 + <button>"Upload"</button>
41 39 </div>
42 40 <ul patch:children=rx_previews>
43 41 </ul>
... ...
Please register or login to post a comment