Skip to content

Commit

Permalink
examples: add wasm example
Browse files Browse the repository at this point in the history
  • Loading branch information
dspicher committed Aug 4, 2023
1 parent 88b3b3f commit b0f2689
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ target

fuzz/hfuzz_target
fuzz/hfuzz_workspace

**/dist
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
- Added support for `no-std` environments. https://github.com/dspicher/ur-rs/pull/183
- Introduced a type-safe `ur::Type` enum and a `ur::Encoder::bytes` shorthand constructor. https://github.com/dspicher/ur-rs/pull/186
- Added `wasm` example. https://github.com/dspicher/ur-rs/pull/191

## [0.3.0] - 2023-01-07
- Added `ur::ur::decode` to the public API to decode a single `ur` URI. https://github.com/dspicher/ur-rs/pull/112
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["fuzz"]
members = ["examples/wasm", "fuzz"]

[package]
name = "ur"
Expand Down
16 changes: 16 additions & 0 deletions examples/wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "wasm"
license = "MIT"
version = "0.1.0"
authors = ["Dominik Spicher <dominikspicher@gmail.com>"]
edition = "2021"

[dependencies]
base64 = "0.21"
gloo = "0.9"
js-sys = "0.3"
qrcode-generator = "4"
ur = { path = "../.." }
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["Event","EventTarget","InputEvent"] }
yew = { version = "0.20", features = ["csr"] }
7 changes: 7 additions & 0 deletions examples/wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
WASM example
======================

[Install](https://trunkrs.dev/#install) `trunk` and run from this directory:
```shell
trunk serve --open
```
11 changes: 11 additions & 0 deletions examples/wasm/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<title>Uniform Resources Demo</title>
<link data-trunk rel="scss" href="index.scss"/>
<link data-trunk rel="rust" />
</head>
<body></body>
</html>
19 changes: 19 additions & 0 deletions examples/wasm/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
$font-stack: Roboto, sans-serif;
$primary-color: #008f53;

body {
font: 100% $font-stack;
color: white;
background-color: $primary-color;
text-align: center;
}

#buttons {
text-align: center;
margin-bottom: 10px;
}

#wrapper {
overflow: hidden;
width: 100%;
}
29 changes: 29 additions & 0 deletions examples/wasm/src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{Event, HtmlInputElement, InputEvent};
use yew::prelude::*;

#[derive(Clone, PartialEq, Properties)]
pub struct Props {
pub value: String,
pub on_change: Callback<String>,
}

fn get_value_from_input_event(e: InputEvent) -> String {
let event: Event = e.dyn_into().unwrap_throw();
let event_target = event.target().unwrap_throw();
let target: HtmlInputElement = event_target.dyn_into().unwrap_throw();
target.value()
}

#[function_component(TextInput)]
pub fn text_input(props: &Props) -> Html {
let Props { value, on_change } = props.clone();

let oninput = Callback::from(move |input_event: InputEvent| {
on_change.emit(get_value_from_input_event(input_event));
});

html! {
<input type="text" {value} {oninput} />
}
}
131 changes: 131 additions & 0 deletions examples/wasm/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
mod input;

use base64::Engine;
use gloo::console;
use gloo::timers::callback::Interval;
use qrcode_generator::QrCodeEcc;
use yew::prelude::*;

pub enum Msg {
StartInterval,
Cancel,
Tick,
SetInput(String),
}

pub struct App {
encoder: ur::Encoder,
interval: Option<Interval>,
current_part: Option<String>,
input: String,
}

impl App {
fn cancel(&mut self) {
self.interval = None;
self.current_part = None;
self.encoder = ur::Encoder::bytes(b"placeholder", MAX_FRAGMENT_SIZE).unwrap();
self.input = String::new();
}
}

const MAX_FRAGMENT_SIZE: usize = 50;

impl Component for App {
type Message = Msg;
type Properties = ();

fn create(_ctx: &Context<Self>) -> Self {
Self {
encoder: ur::Encoder::bytes(b"placeholder", MAX_FRAGMENT_SIZE).unwrap(),
interval: None,
current_part: None,
input: String::new(),
}
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::StartInterval => {
let handle = {
let link = ctx.link().clone();
Interval::new(1000, move || link.send_message(Msg::Tick))
};
self.interval = Some(handle);
true
}
Msg::Cancel => {
self.cancel();
console::warn!("Canceled!");
true
}
Msg::Tick => {
self.current_part = Some(self.encoder.next_part().unwrap());
true
}
Msg::SetInput(s) => {
self.encoder = ur::Encoder::bytes(s.as_bytes(), MAX_FRAGMENT_SIZE).unwrap();
self.input = s;
true
}
}
}

fn view(&self, ctx: &Context<Self>) -> Html {
let has_job = self.interval.is_some();
let qrcode_rendered = self.current_part.as_ref().map_or_else(
|| html! {},
|part| {
let qr = base64::prelude::BASE64_STANDARD
.encode(qrcode_generator::to_png_to_vec(part, QrCodeEcc::Low, 1024).unwrap());
html! {
<div id="wrapper">
<div id="qrcode">
<img src= { format!("data:image/png;base64,{qr}") } width=300 />
</div>
</div>
}
},
);
let part = self.current_part.as_ref().map_or_else(
|| {
html! {
<></>
}
},
|part| {
html! {
<div id="part">
<code>{ part.to_string() }</code>
</div>
}
},
);
let on_change = ctx.link().callback(Msg::SetInput);
html! {
<>
<h1>{ "Uniform Resources Demo" }</h1>
<h4>{ "Enter the text you would like to transmit and click Start" }</h4>
<div>
<crate::input::TextInput {on_change} value={self.input.clone()} />
<p></p>
</div>
<div id="buttons">
<button disabled={has_job} onclick={ctx.link().callback(|_| Msg::StartInterval)}>
{ "Start" }
</button>
<button disabled={!has_job} onclick={ctx.link().callback(|_| Msg::Cancel)}>
{ "Cancel" }
</button>
</div>
{ qrcode_rendered }
<p></p>
{ part }
</>
}
}
}

fn main() {
yew::Renderer::<App>::new().render();
}

0 comments on commit b0f2689

Please sign in to comment.