Commit a0ed54d6ab9a03fbccd9d15b4d802267d5175b33
1 parent
5b9ef0e3
Use a ListPatchModel to keep track of the uploads list
Showing
8 changed files
with
175 additions
and
49 deletions
| 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; | ... | ... |
ui/src/api/upload.rs
0 → 100644
| 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 | } | ... | ... |
ui/src/component/upload/upload.rs
0 → 100644
| 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