Skip to content

Commit

Permalink
Merge branch 'master' into polled-stream
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/yew/src/server_renderer.rs
  • Loading branch information
futursolo committed Aug 29, 2022
2 parents 6ae5a65 + fc96543 commit f310575
Show file tree
Hide file tree
Showing 24 changed files with 989 additions and 192 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/post-size-cmp.yml
Expand Up @@ -8,7 +8,7 @@ on:
- completed

jobs:
size-cmp:
post-size-cmp:
name: Post Comment on Pull Request
runs-on: ubuntu-latest

Expand Down
30 changes: 30 additions & 0 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/file_upload/Cargo.toml
Expand Up @@ -8,8 +8,9 @@ license = "MIT OR Apache-2.0"
[dependencies]
js-sys = "0.3"
yew = { path = "../../packages/yew", features = ["csr"] }
base64 = "0.13.0"
gloo = "0.8"

[dependencies.web-sys]
version = "0.3"
features = ["File"]
features = ["File", "DragEvent", "DataTransfer"]
5 changes: 5 additions & 0 deletions examples/file_upload/index.html
Expand Up @@ -5,6 +5,11 @@
<title>Yew • File Upload</title>

<link data-trunk rel="rust" />
<link data-trunk rel="css" href="./styles.css" />
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"
/>
</head>

<body></body>
Expand Down
157 changes: 89 additions & 68 deletions examples/file_upload/src/main.rs
@@ -1,24 +1,27 @@
extern crate base64;
use std::collections::HashMap;

use base64::encode;
use gloo::file::callbacks::FileReader;
use gloo::file::File;
use web_sys::{Event, HtmlInputElement};
use web_sys::{DragEvent, Event, FileList, HtmlInputElement};
use yew::html::TargetCast;
use yew::{html, Component, Context, Html};
use yew::{html, Callback, Component, Context, Html};

type Chunks = bool;
struct FileDetails {
name: String,
file_type: String,
data: Vec<u8>,
}

pub enum Msg {
Loaded(String, String),
LoadedBytes(String, Vec<u8>),
Files(Vec<File>, Chunks),
ToggleReadBytes,
Loaded(String, String, Vec<u8>),
Files(Vec<File>),
}

pub struct App {
readers: HashMap<String, FileReader>,
files: Vec<String>,
read_bytes: bool,
files: Vec<FileDetails>,
}

impl Component for App {
Expand All @@ -28,98 +31,116 @@ impl Component for App {
fn create(_ctx: &Context<Self>) -> Self {
Self {
readers: HashMap::default(),
files: vec![],
read_bytes: false,
files: Vec::default(),
}
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Loaded(file_name, data) => {
let info = format!("file_name: {}, data: {:?}", file_name, data);
self.files.push(info);
Msg::Loaded(file_name, file_type, data) => {
self.files.push(FileDetails {
data,
file_type,
name: file_name.clone(),
});
self.readers.remove(&file_name);
true
}
Msg::LoadedBytes(file_name, data) => {
let info = format!("file_name: {}, data: {:?}", file_name, data);
self.files.push(info);
self.readers.remove(&file_name);
true
}
Msg::Files(files, bytes) => {
Msg::Files(files) => {
for file in files.into_iter() {
let file_name = file.name();
let file_type = file.raw_mime_type();

let task = {
let file_name = file_name.clone();
let link = ctx.link().clone();
let file_name = file_name.clone();

if bytes {
gloo::file::callbacks::read_as_bytes(&file, move |res| {
link.send_message(Msg::LoadedBytes(
file_name,
res.expect("failed to read file"),
))
})
} else {
gloo::file::callbacks::read_as_text(&file, move |res| {
link.send_message(Msg::Loaded(
file_name,
res.unwrap_or_else(|e| e.to_string()),
))
})
}
gloo::file::callbacks::read_as_bytes(&file, move |res| {
link.send_message(Msg::Loaded(
file_name,
file_type,
res.expect("failed to read file"),
))
})
};
self.readers.insert(file_name, task);
}
true
}
Msg::ToggleReadBytes => {
self.read_bytes = !self.read_bytes;
true
}
}
}

fn view(&self, ctx: &Context<Self>) -> Html {
let flag = self.read_bytes;
html! {
<div>
<div>
<p>{ "Choose a file to upload to see the uploaded bytes" }</p>
<input type="file" multiple=true onchange={ctx.link().callback(move |e: Event| {
let mut result = Vec::new();
let input: HtmlInputElement = e.target_unchecked_into();

if let Some(files) = input.files() {
let files = js_sys::try_iter(&files)
.unwrap()
.unwrap()
.map(|v| web_sys::File::from(v.unwrap()))
.map(File::from);
result.extend(files);
}
Msg::Files(result, flag)
<div id="wrapper">
<p id="title">{ "Upload Your Files To The Cloud" }</p>
<label for="file-upload">
<div
id="drop-container"
ondrop={ctx.link().callback(|event: DragEvent| {
event.prevent_default();
let files = event.data_transfer().unwrap().files();
Self::upload_files(files)
})}
/>
</div>
<div>
<label>{ "Read bytes" }</label>
<input type="checkbox" checked={flag} onclick={ctx.link().callback(|_| Msg::ToggleReadBytes)} />
ondragover={Callback::from(|event: DragEvent| {
event.prevent_default();
})}
ondragenter={Callback::from(|event: DragEvent| {
event.prevent_default();
})}
>
<i class="fa fa-cloud-upload"></i>
<p>{"Drop your images here or click to select"}</p>
</div>
</label>
<input
id="file-upload"
type="file"
accept="image/*,video/*"
multiple={true}
onchange={ctx.link().callback(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
Self::upload_files(input.files())
})}
/>
<div id="preview-area">
{ for self.files.iter().map(Self::view_file) }
</div>
<ul>
{ for self.files.iter().map(|f| Self::view_file(f)) }
</ul>
</div>
}
}
}

impl App {
fn view_file(data: &str) -> Html {
fn view_file(file: &FileDetails) -> Html {
html! {
<li>{ data }</li>
<div class="preview-tile">
<p class="preview-name">{ format!("{}", file.name) }</p>
<div class="preview-media">
if file.file_type.contains("image") {
<img src={format!("data:{};base64,{}", file.file_type, encode(&file.data))} />
} else if file.file_type.contains("video") {
<video controls={true}>
<source src={format!("data:{};base64,{}", file.file_type, encode(&file.data))} type={file.file_type.clone()}/>
</video>
}
</div>
</div>
}
}

fn upload_files(files: Option<FileList>) -> Msg {
let mut result = Vec::new();

if let Some(files) = files {
let files = js_sys::try_iter(&files)
.unwrap()
.unwrap()
.map(|v| web_sys::File::from(v.unwrap()))
.map(File::from);
result.extend(files);
}
Msg::Files(result)
}
}

Expand Down
78 changes: 78 additions & 0 deletions examples/file_upload/styles.css
@@ -0,0 +1,78 @@
* {
box-sizing: border-box;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}

html,
body {
margin: 0;
padding: 0;
background: #2d3131;
color: #fcfcfc;
}

img,
video {
max-width: 300px;
max-height: 500px;
}

p {
text-align: center;
}

label {
cursor: pointer;
}

input#file-upload {
visibility: hidden;
}

#wrapper {
width: 70%;
margin: auto;
}

#title {
font-size: 2rem;
text-align: center;
}

#drop-container {
padding: 4rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #3d4141;
border: 1px dashed #fcfcfc;
border-radius: 1rem;
}

#drop-container i {
font-size: 4rem;
}

#preview-area {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: stretch;
}

.preview-tile {
display: flex;
flex-direction: column;
padding: 2rem;
margin: 1rem;
background: #313737;
border-radius: 1rem;
}

.preview-media {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
3 changes: 1 addition & 2 deletions examples/simple_ssr/src/bin/simple_ssr_server.rs
Expand Up @@ -25,7 +25,7 @@ async fn render(

Box::new(
stream::once(async move { index_html_before })
.chain(renderer.render_stream().await)
.chain(renderer.render_stream())
.chain(stream::once(async move { index_html_after }))
.map(|m| Result::<_, BoxedError>::Ok(m.into())),
)
Expand Down Expand Up @@ -54,6 +54,5 @@ async fn main() {
let routes = html.or(warp::fs::dir(opts.dir));

println!("You can view the website at: http://localhost:8080/");

warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}

0 comments on commit f310575

Please sign in to comment.