Skip to content

Commit 994f1ee

Browse files
authored
feat: use_channel (#3)
* feat: use_channel * improvements * simplified * clean up * clean up * clean up
1 parent 88f49bb commit 994f1ee

File tree

9 files changed

+193
-1
lines changed

9 files changed

+193
-1
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2-
"rust-analyzer.cargo.features": ["geolocation"],
2+
"rust-analyzer.cargo.features": [
3+
"geolocation",
4+
"use_channel"
5+
],
36
//"rust-analyzer.check.allTargets": true,
47
}

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ clipboard = ["dep:clipboard"]
2121
notifications = ["dep:notify-rust"]
2222
geolocation = ["dep:dioxus", "dep:windows", "dep:futures-util", "dep:futures"]
2323

24+
use_channel = ["dep:async-broadcast", "dep:uuid", "dep:dioxus"]
2425
use_preferred_color_scheme = ["dep:dioxus", "dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"]
2526
use_rw = ["dep:dioxus"]
2627

@@ -30,6 +31,8 @@ dioxus = { version = "0.3", optional = true }
3031
cfg-if = "1.0.0"
3132
futures-util = {version = "0.3.28", optional = true }
3233

34+
uuid = { version = "1.3.2", features = ["v4"], optional = true }
35+
async-broadcast = { version = "0.5.1", optional = true }
3336
clipboard = { version = "0.5.0", optional = true }
3437
notify-rust = { version = "4.8.0", optional = true }
3538
futures = { version = "0.3.28", features = ["std"], optional = true }
@@ -42,6 +45,7 @@ web-sys = { version = "0.3.60", features = ["Window", "MediaQueryList", "Navigat
4245
wasm-bindgen = { version = "0.2.85", optional = true }
4346
wasm-bindgen-futures = { version = "0.4.35", optional = true}
4447
js-sys = "0.3.62"
48+
uuid = { version = "1.3.2", features = ["v4", "js"]}
4549

4650
[package.metadata.docs.rs]
4751
all-features = true

examples/channel/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "channel"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
dioxus-std = { path="../../", features = ["use_channel"] }
8+
dioxus = "0.3"
9+
dioxus-web = "0.3"
10+
11+
log = "0.4.6"
12+
13+
# WebAssembly Debug
14+
wasm-logger = "0.2.0"
15+
console_error_panic_hook = "0.1.7"

examples/channel/Dioxus.toml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[application]
2+
3+
# App (Project) Name
4+
name = "channel"
5+
6+
# Dioxus App Default Platform
7+
# desktop, web, mobile, ssr
8+
default_platform = "web"
9+
10+
# `build` & `serve` dist path
11+
out_dir = "dist"
12+
13+
# resource (public) file folder
14+
asset_dir = "public"
15+
16+
[web.app]
17+
18+
# HTML title tag content
19+
title = "dioxus | ⛺"
20+
21+
[web.watcher]
22+
23+
# when watcher trigger, regenerate the `index.html`
24+
reload_html = true
25+
26+
# which files or dirs will be watcher monitoring
27+
watch_path = ["src", "public"]
28+
29+
# include `assets` in web platform
30+
[web.resource]
31+
32+
# CSS style file
33+
style = []
34+
35+
# Javascript code file
36+
script = []
37+
38+
[web.resource.dev]
39+
40+
# Javascript code file
41+
# serve: [dev-server] only
42+
script = []

examples/channel/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# use_channel
2+
3+
Learn how to use `use_channel`.
4+
5+
Run:
6+
7+
```dioxus serve```
130 KB
Binary file not shown.

examples/channel/src/main.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use dioxus::prelude::*;
2+
use dioxus_std::hooks::{use_channel, use_listen_channel};
3+
4+
fn main() {
5+
// init debug tool for WebAssembly
6+
wasm_logger::init(wasm_logger::Config::default());
7+
console_error_panic_hook::set_once();
8+
9+
dioxus_web::launch(app);
10+
}
11+
12+
fn app(cx: Scope) -> Element {
13+
let channel = use_channel::<String>(cx, 5);
14+
15+
use_listen_channel(cx, &channel, move |message| async move {
16+
match message {
17+
Ok(value) => log::info!("Incoming message: {value}"),
18+
Err(err) => log::info!("Error: {err:?}"),
19+
}
20+
});
21+
22+
let send = move |_: MouseEvent| {
23+
to_owned![channel];
24+
async move {
25+
channel.send("Hello").await.ok();
26+
}
27+
};
28+
29+
render!(
30+
button {
31+
onclick: send,
32+
"Send hello"
33+
}
34+
)
35+
}

src/hooks/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,10 @@ cfg_if::cfg_if! {
1919
pub use use_rw::*;
2020
}
2121
}
22+
23+
cfg_if::cfg_if! {
24+
if #[cfg(feature = "use_channel")] {
25+
pub mod use_channel;
26+
pub use use_channel::*;
27+
}
28+
}

src/hooks/use_channel.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use std::future::Future;
2+
3+
use async_broadcast::{broadcast, InactiveReceiver, Receiver, RecvError, SendError, Sender};
4+
use dioxus::prelude::{to_owned, use_effect, ScopeState};
5+
use uuid::Uuid;
6+
7+
pub type UseListenChannelError = RecvError;
8+
9+
/// Send and listen for messages between multiple components.
10+
#[derive(Debug, Clone)]
11+
pub struct UseChannel<MessageType: Clone> {
12+
id: Uuid,
13+
sender: Sender<MessageType>,
14+
inactive_receiver: InactiveReceiver<MessageType>,
15+
}
16+
17+
impl<T: Clone> PartialEq for UseChannel<T> {
18+
fn eq(&self, other: &Self) -> bool {
19+
self.id == other.id
20+
}
21+
}
22+
23+
impl<MessageType: Clone> UseChannel<MessageType> {
24+
/// Sends a message to all listeners of the channel.
25+
pub async fn send(&self, msg: impl Into<MessageType>) -> Result<(), SendError<MessageType>> {
26+
self.sender.broadcast(msg.into()).await.map(|_| ())
27+
}
28+
29+
/// Create a receiver for the channel.
30+
/// You probably want to use [`use_listen_channel`].
31+
pub fn receiver(&mut self) -> Receiver<MessageType> {
32+
self.inactive_receiver.clone().activate()
33+
}
34+
}
35+
36+
/// Send and listen for messages between multiple components.
37+
pub fn use_channel<MessageType: Clone + 'static>(
38+
cx: &ScopeState,
39+
size: usize,
40+
) -> UseChannel<MessageType> {
41+
let id = cx.use_hook(Uuid::new_v4);
42+
let (sender, inactive_receiver) = cx.use_hook(|| {
43+
let (sender, receiver) = broadcast::<MessageType>(size);
44+
45+
(sender, receiver.deactivate())
46+
});
47+
48+
UseChannel {
49+
id: *id,
50+
sender: sender.clone(),
51+
inactive_receiver: inactive_receiver.clone(),
52+
}
53+
}
54+
55+
/// Create a messages listener for the given channel.
56+
pub fn use_listen_channel<MessageType: Clone + 'static, Handler>(
57+
cx: &ScopeState,
58+
channel: &UseChannel<MessageType>,
59+
action: impl Fn(Result<MessageType, UseListenChannelError>) -> Handler + 'static,
60+
) where
61+
Handler: Future<Output = ()> + 'static,
62+
{
63+
use_effect(cx, (channel,), move |(channel,)| {
64+
to_owned![channel];
65+
async move {
66+
let action = Box::new(action);
67+
let mut receiver = channel.receiver();
68+
69+
loop {
70+
let message = receiver.recv().await;
71+
let message_err = message.clone().err();
72+
action(message).await;
73+
if message_err == Some(UseListenChannelError::Closed) {
74+
break;
75+
}
76+
}
77+
}
78+
});
79+
}

0 commit comments

Comments
 (0)