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 .markdown { 34 .markdown {
2 float: left; 35 float: left;
3 padding: 1em; 36 padding: 1em;
1 pub(crate) mod markdown; 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 use js_sys::JsString; 1 use js_sys::JsString;
2 use mogwai::prelude::*; 2 use mogwai::prelude::*;
3 use wasm_bindgen::prelude::*; 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 use super::error::*; 5 use super::error::*;
6 6
7 use std::result::Result as StdResult; 7 use std::result::Result as StdResult;
@@ -25,7 +25,7 @@ impl Client { @@ -25,7 +25,7 @@ impl Client {
25 25
26 pub async fn get(&self, url :&str) -> Result<(Response, String)> { 26 pub async fn get(&self, url :&str) -> Result<(Response, String)> {
27 let mut init = RequestInit::new(); 27 let mut init = RequestInit::new();
28 - let request = REQUEST( &url 28 + let request = REQUEST( url
29 , init . method("GET") 29 , init . method("GET")
30 . mode(RequestMode::Cors) )?; 30 . mode(RequestMode::Cors) )?;
31 31
@@ -46,7 +46,7 @@ impl Client { @@ -46,7 +46,7 @@ impl Client {
46 46
47 pub async fn put(&self, url :&str, data :&str) -> Result<Response> { 47 pub async fn put(&self, url :&str, data :&str) -> Result<Response> {
48 let mut init = RequestInit::new(); 48 let mut init = RequestInit::new();
49 - let request = REQUEST( &url 49 + let request = REQUEST( url
50 , init . method("PUT") 50 , init . method("PUT")
51 . mode(RequestMode::Cors) 51 . mode(RequestMode::Cors)
52 . body(Some(&data.into())) )?; 52 . body(Some(&data.into())) )?;
@@ -61,4 +61,25 @@ impl Client { @@ -61,4 +61,25 @@ impl Client {
61 61
62 Ok(response) 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 use mogwai::prelude::*; 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 while let Some(dom) = rx_canvas.next().await { 8 while let Some(dom) = rx_canvas.next().await {
9 match dom.inner_read() { 9 match dom.inner_read() {
10 Either::Left(c) => { 10 Either::Left(c) => {
@@ -12,10 +12,12 @@ async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom> @@ -12,10 +12,12 @@ async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
12 let context = canvas 12 let context = canvas
13 . get_context("2d").unwrap().unwrap() 13 . get_context("2d").unwrap().unwrap()
14 . dyn_into::<CanvasRenderingContext2d>().unwrap(); 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,36 +27,28 @@ async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom>
25 pub(super) async fn upload_logic( mut rx_logic: broadcast::Receiver<DomEvent> 27 pub(super) async fn upload_logic( mut rx_logic: broadcast::Receiver<DomEvent>
26 , tx_previews: mpmc::Sender<ListPatch<ViewBuilder<Dom>>> 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 while let Some(msg) = rx_logic.next().await { 40 while let Some(msg) = rx_logic.next().await {
29 match msg.clone_inner() { 41 match msg.clone_inner() {
30 Either::Left(val) => { 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 for index in 0..filelist.length() { 48 for index in 0..filelist.length() {
38 - let (tx_canvas, rx_canvas) = broadcast::bounded(1);  
39 let file = filelist.item(index).unwrap(); 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 mod logic; 1 mod logic;
  2 +mod upload;
  3 +mod view;
3 4
4 use mogwai::prelude::*; 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,18 +11,15 @@ pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom>
11 11
12 builder! { 12 builder! {
13 <li style:display="flex"> 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 </li> 23 </li>
27 } 24 }
28 } 25 }
@@ -30,14 +27,15 @@ pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom> @@ -30,14 +27,15 @@ pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom>
30 pub(super) fn upload_view( tx_logic: broadcast::Sender<DomEvent> 27 pub(super) fn upload_view( tx_logic: broadcast::Sender<DomEvent>
31 , rx_previews: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>> 28 , rx_previews: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>>
32 ) -> ViewBuilder<Dom> { 29 ) -> ViewBuilder<Dom> {
  30 +// <div class="spin"></div>
33 builder! { 31 builder! {
34 <div class="upload"> 32 <div class="upload">
35 - <div class="spin"></div>  
36 <div> 33 <div>
37 <input type="file" 34 <input type="file"
38 multiple="multiple" 35 multiple="multiple"
39 accept="image/*" 36 accept="image/*"
40 - on:change=tx_logic.sink()/> 37 + on:change=tx_logic.sink() />
  38 + <button>"Upload"</button>
41 </div> 39 </div>
42 <ul patch:children=rx_previews> 40 <ul patch:children=rx_previews>
43 </ul> 41 </ul>
Please register or login to post a comment