-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into platform-crate
# Conflicts: # packages/yew/Cargo.toml
- Loading branch information
Showing
34 changed files
with
3,315 additions
and
493 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "async_clock" | ||
version = "0.0.1" | ||
authors = ["Marcel Ibes <[email protected]>"] | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
|
||
[dependencies] | ||
yew = { path = "../../packages/yew", features = ["csr"] } | ||
chrono = "0.4" | ||
futures = "0.3" | ||
gloo-net = "0.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Asynchronous coding in Yew | ||
|
||
An example of using asynchronous tasks in a component. This example creates a clock in the background and | ||
continuously awaits clock-ticks. When the clock updates the new time is sent to the UI component to display. | ||
In parallel it fetches online jokes to make the clock more entertaining to watch. | ||
|
||
Its main purpose is to demonstrate various ways of using async code in a yew component. It uses the following async | ||
features: | ||
- send_future | ||
- send_stream | ||
- spawn_local | ||
- mpsc::unbounded channels | ||
|
||
## Running | ||
|
||
Run this application: | ||
|
||
```bash | ||
trunk serve --open | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>Yew • Async Examples</title> | ||
<link data-trunk rel="rust" /> | ||
<link data-trunk rel="sass" href="index.scss" /> | ||
</head> | ||
|
||
<body></body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
body { | ||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; | ||
font-size: 16pt; | ||
} | ||
|
||
.app { | ||
display: flex; | ||
justify-content: center; | ||
flex-direction: row; | ||
} | ||
|
||
.time-display { | ||
display: flex; | ||
justify-content: center; | ||
color: darkblue; | ||
font-size: 24pt; | ||
font-weight: bold; | ||
margin-bottom: 15px; | ||
} | ||
|
||
.joke-display { | ||
display: flex; | ||
justify-content: center; | ||
color: orangered; | ||
max-width: 75%; | ||
border-color: darkblue; | ||
border-style: dashed; | ||
border-width: 1px; | ||
padding: 10px; | ||
} | ||
|
||
.fun-score-display { | ||
font-style: italic; | ||
} | ||
|
||
|
||
.clock { | ||
display: flex; | ||
flex-direction: column; | ||
row-gap: 15px; | ||
align-items: center; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
use chrono::{DateTime, Local}; | ||
use futures::{FutureExt, StreamExt}; | ||
use services::compute_fun_score; | ||
use yew::platform::pinned::mpsc::UnboundedSender; | ||
use yew::{html, AttrValue, Component, Context, Html}; | ||
|
||
use crate::services::{emit_jokes, initialize_atomic_clocks, stream_time}; | ||
|
||
mod services; | ||
|
||
/// The AsyncComponent displays the current time and some silly jokes. Its main purpose is to | ||
/// demonstrate the use of async code in a yew component. It uses the following async features: | ||
/// - send_future | ||
/// - send_stream | ||
/// - spawn_local | ||
/// - mpsc::unbounded channels | ||
pub struct AsyncComponent { | ||
clock: Option<AttrValue>, | ||
joke: Option<AttrValue>, | ||
fun_score: Option<i16>, | ||
fun_score_channel: UnboundedSender<AttrValue>, | ||
} | ||
|
||
pub enum Msg { | ||
ClockInitialized(()), | ||
ClockTicked(DateTime<Local>), | ||
Joke(AttrValue), | ||
FunScore(i16), | ||
} | ||
|
||
impl Component for AsyncComponent { | ||
type Message = Msg; | ||
type Properties = (); | ||
|
||
fn create(ctx: &Context<Self>) -> Self { | ||
// Demonstrate how we can send a message to the component when a future completes. | ||
// This is the most straightforward way to use async code in a yew component. | ||
let is_initialized = initialize_atomic_clocks(); | ||
ctx.link() | ||
.send_future(is_initialized.map(Msg::ClockInitialized)); | ||
|
||
// The compute_fun_score launches a background task that is ready to compute the fun score | ||
// from jokes that are delivered on this channel. The outcome of the computation is | ||
// sent back to the component via the Msg::FunScore callback. | ||
let fun_score_cb = ctx.link().callback(Msg::FunScore); | ||
let fun_score_channel = compute_fun_score(fun_score_cb); | ||
|
||
Self { | ||
clock: None, | ||
joke: None, | ||
fun_score: None, | ||
fun_score_channel, | ||
} | ||
} | ||
|
||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool { | ||
match msg { | ||
Msg::ClockTicked(current_time) => { | ||
// Update the clock display | ||
self.clock = Some(AttrValue::from(current_time.to_rfc2822())); | ||
} | ||
Msg::ClockInitialized(_) => { | ||
// Now that the clock is initialized, we can start the time stream. | ||
self.clock = Some(AttrValue::from("Initialized")); | ||
|
||
// The stream_time method returns a stream of time updates. We use send_stream to | ||
// update the component with a Msg::ClockTicked message every time | ||
// the stream produces a new value. | ||
let time_steam = stream_time(); | ||
ctx.link().send_stream(time_steam.map(Msg::ClockTicked)); | ||
|
||
// In parallel we launch a background task that produces jokes to make the clock | ||
// more fun to watch. The jokes are emitted back to the component | ||
// throught the Msg::Joke callback. | ||
let joke_cb = ctx.link().callback(Msg::Joke); | ||
emit_jokes(joke_cb); | ||
} | ||
Msg::Joke(joke) => { | ||
// Update the joke | ||
self.joke = Some(joke.clone()); | ||
|
||
// Reset the fun score | ||
self.fun_score = None; | ||
|
||
// Send the joke to the background task that computes the fun score. | ||
self.fun_score_channel | ||
.send_now(joke) | ||
.expect("failed to send joke"); | ||
} | ||
Msg::FunScore(score) => { | ||
self.fun_score = Some(score); | ||
} | ||
} | ||
true | ||
} | ||
|
||
fn view(&self, _ctx: &Context<Self>) -> Html { | ||
let display = self.clock.as_deref().unwrap_or("Loading..."); | ||
let joke = self.joke.as_deref().unwrap_or("Loading..."); | ||
let fun_score = self | ||
.fun_score | ||
.map(|score| format!("Fun score: {}", score)) | ||
.unwrap_or_else(|| "Computing...".to_string()); | ||
|
||
html! { | ||
<div class="app"> | ||
<div class="clock"> | ||
<h2>{ "Asynchronous Examples" }</h2> | ||
<div class="time-display"> | ||
{ display } | ||
</div> | ||
<div class="joke-display"> | ||
{ joke } | ||
</div> | ||
<div class="fun-score-display"> | ||
{ fun_score } | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
} | ||
} | ||
|
||
fn main() { | ||
yew::Renderer::<AsyncComponent>::new().render(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use std::time::Duration; | ||
|
||
use chrono::{DateTime, Local}; | ||
use futures::{Stream, StreamExt}; | ||
use gloo_net::http::Request; | ||
use yew::platform::pinned::mpsc::UnboundedSender; | ||
use yew::platform::spawn_local; | ||
use yew::platform::time::{interval, sleep}; | ||
use yew::{AttrValue, Callback}; | ||
|
||
const ONE_SEC: Duration = Duration::from_secs(1); | ||
const TEN_SECS: Duration = Duration::from_secs(10); | ||
|
||
/// Demonstration code to show how to use async code in a yew component. | ||
pub async fn initialize_atomic_clocks() { | ||
// aligning with atomic clocks :-) | ||
sleep(ONE_SEC).await; | ||
} | ||
|
||
/// Returns a stream of time updates. | ||
pub fn stream_time() -> impl Stream<Item = DateTime<Local>> { | ||
interval(ONE_SEC).map(|_| Local::now()) | ||
} | ||
|
||
/// Emit entertaining jokes every 10 seconds. | ||
pub fn emit_jokes(joke_cb: Callback<AttrValue>) { | ||
// Spawn a background task that will fetch a joke and send it to the component. | ||
spawn_local(async move { | ||
loop { | ||
// Fetch the online joke | ||
let fun_fact = Request::get("https://v2.jokeapi.dev/joke/Programming?format=txt") | ||
.send() | ||
.await | ||
.unwrap() | ||
.text() | ||
.await | ||
.unwrap(); | ||
|
||
// Emit it to the component | ||
joke_cb.emit(AttrValue::from(fun_fact)); | ||
sleep(TEN_SECS).await; | ||
} | ||
}); | ||
} | ||
|
||
/// Background task that computes the fun score from jokes that are delivered on the channel. | ||
pub fn compute_fun_score(fun_score_cb: Callback<i16>) -> UnboundedSender<AttrValue> { | ||
let (tx, mut rx) = yew::platform::pinned::mpsc::unbounded::<AttrValue>(); | ||
|
||
// Read endlessly from the UnboundedReceiver and compute the fun score. | ||
spawn_local(async move { | ||
while let Some(joke) = rx.next().await { | ||
sleep(ONE_SEC).await; | ||
let score = joke.len() as i16; | ||
fun_score_cb.emit(score); | ||
} | ||
}); | ||
|
||
tx | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.