Skip to content

Commit b002082

Browse files
committed
docs: Orders comments and changelog update
1 parent 441b472 commit b002082

File tree

13 files changed

+189
-21
lines changed

13 files changed

+189
-21
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
- Added support for all `Iterator`s, `Option`, `u32`, `i32`, `usize`, `f64` and references in element creation macros (#365, #128).
1010
- [BREAKING] `String` implements `UpdateEl<T>`. (References are now required for `String` properties, e.g. `div![&model.title]`.)
1111
- Fixed `href` detection to ignore `use` elements (#384).
12+
- Added methods `subscribe`, `subscribe_with_handle`, `perform_cmd_with_handle`, `stream`, `stream_with_handle` and `notify` into `Orders` (#130).
13+
- Added `cmds::timeout`, `stream::interval`, `stream::window_event`, `subs::UrlChanged` and `subs::UrlRequested` (#131).
14+
- Added example `subscribe`.
15+
- Updated example `todomvc` to use subscription `UrlChanged` instead of `routes`.
16+
- [BREAKING] Futures in `perform_cmd` and `perform_g_cmd` are executed immediately.
17+
- Added `App` methods `notify` and `notify_with_notification`.
18+
- [BREAKING] `App` method `process_cmd_and_msg_queue` renamed to `process_effect_queue`.
19+
- [BREAKING] Url change listeners are always active (even if `routes` is not defined).
20+
- Added `cmds`, `streams`, `subs`, `CmdHandle`, `SubHandle` and `StreamHandle` into the Seed's prelude.
21+
- [BREAKING] Removed module `next_tick`.
1222

1323
## v0.6.0
1424
- Implemented `UpdateEl` for `Filter` and `FilterMap`.

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ How to perform commands and send messages from `update` function.
3939
And how to use [gloo](https://github.com/rustwasm/gloo) timers.
4040

4141
### [Subscribe](subscribe)
42-
How to create and use subscriptions.
42+
How to create and use subscriptions, streams, notifications and commands.
4343

4444
### [TEA component](tea_component)
4545
How to write a component in The Elm architecture.

examples/orders/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use gloo_timers::future::TimeoutFuture;
44
use seed::{prelude::*, *};
55

6+
// NOTE: See example `subscribe` to learn more about other `Orders` methods.
7+
68
// ------ ------
79
// Model
810
// ------ ------

examples/subscribe/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Subscribe example
22

3-
How to create and use subscriptions.
3+
How to create and use subscriptions, streams, notifications and commands.
44

55
---
66

src/app/cmd_manager.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
use futures::future::{abortable, AbortHandle, Future, FutureExt};
22
use wasm_bindgen_futures::spawn_local;
33

4+
// @TODO: Tighten up access - e.g. replace `pub` with `pub(crate)`. Applicable to the entire code base.
5+
46
// ------ CmdManager ------
57

68
pub struct CmdManager;
79

810
impl CmdManager {
911
pub fn perform_cmd(cmd: impl Future<Output = ()> + 'static) {
12+
// The future is "leaked" into the JS world as a promise.
13+
// It's always executed on the next JS tick to prevent stack overflow.
1014
spawn_local(cmd);
1115
}
1216

1317
pub fn perform_cmd_with_handle(cmd: impl Future<Output = ()> + 'static) -> CmdHandle {
1418
let (cmd, handle) = abortable(cmd);
19+
// Ignore the error when the future is aborted. I.e. just stop the future execution.
1520
spawn_local(cmd.map(move |_| ()));
1621
CmdHandle(handle)
1722
}

src/app/cmds.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
use futures::future::{Future, FutureExt};
22
use gloo_timers::future::TimeoutFuture;
33

4+
// @TODO add fetch cmd?
5+
46
// ------ Timeout cmd ------
57

8+
/// Set timeout in milliseconds.
9+
///
10+
/// # Example
11+
///
12+
/// ```rust,no_run
13+
///orders.perform_cmd_with_handle(cmds::timeout(2000, || Msg::OnTimeout));
14+
/// ```
615
pub fn timeout<Ms>(
716
ms: u32,
817
handler: impl FnOnce() -> Ms + Clone + 'static,

src/app/orders.rs

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use crate::virtual_dom::View;
33
use futures::stream::Stream;
44
use std::{any::Any, future::Future};
55

6+
// @TODO: Add links to doc comment once https://github.com/rust-lang/rust/issues/43466 is resolved
7+
// or use nightly rustdoc. Applicable to the entire code base.
8+
69
pub mod container;
710
pub mod proxy;
811

@@ -37,29 +40,55 @@ pub trait Orders<Ms: 'static, GMs = UndefinedGMsg> {
3740
/// Don't rerender web page after model update.
3841
fn skip(&mut self) -> &mut Self;
3942

43+
/// Notify all subscription handlers which listen for messages with the `message`'s type.
44+
///
45+
/// # Example
46+
///
47+
/// ```rust,no_run
48+
///orders.notify(counter::DoReset);
49+
///orders.notify("Hello!");
50+
/// ...
51+
///orders.subscribe(Msg::Reset); // `Msg::Reset(counter::DoReset)`
52+
///orders.subscribe(|greeting: &'static str| { log!(greeting); Msg::NoOp });
53+
/// ```
54+
///
55+
/// _Note:_: All notifications are pushed to the queue - i.e. `update` function is NOT called immediately.
4056
fn notify(&mut self, message: impl Any + Clone) -> &mut Self;
4157

42-
/// Call function `update` with the given `msg` after model update.
43-
/// - You can call this function multiple times - messages will be sent in the same order.
58+
/// Invoke function `update` with the given `msg`.
59+
///
60+
/// # Example
61+
///
62+
/// ```rust,no_run
63+
///orders.msg(Msg::Increment);
64+
/// ```
65+
///
66+
/// _Note:_: All `msg`s are pushed to the queue - i.e. `update` function is NOT called immediately.
4467
fn send_msg(&mut self, msg: Ms) -> &mut Self;
4568

46-
/// @TODO
47-
/// Schedule given future `cmd` to be executed after model update.
48-
/// - Result is send to function `update`.
49-
/// - You can call this function multiple times - futures will be scheduled in the same order.
69+
/// Execute given future `cmd` and send its output (`Msg`) to `update` function.
5070
///
5171
/// # Example
5272
///
5373
/// ```rust,no_run
54-
///async fn write_emoticon_after_delay() -> Msg {
55-
/// TimeoutFuture::new(2_000).await;
56-
/// Msg::WriteEmoticon
57-
///}
58-
///orders.perform_cmd(write_emoticon_after_delay());
74+
///orders.perform_cmd(cmds::timeout(2000, || Msg::OnTimeout)));
75+
///orders.perform_cmd(async { log!("Hello!"); Msg::NoOp });
5976
/// ```
77+
///
78+
/// _Note:_: Use the alternative `perform_cmd_with_handle` to control `cmd`'s lifetime.
6079
fn perform_cmd(&mut self, cmd: impl Future<Output = Ms> + 'static) -> &mut Self;
6180

62-
#[must_use]
81+
/// Execute given future `cmd` and send its output (`Msg`) to `update` function.
82+
/// - Returns `CmdHandle` that you should save to your `Model`.
83+
/// The `cmd` is aborted on the handle drop.
84+
///
85+
/// # Example
86+
///
87+
/// ```rust,no_run
88+
///let timeout_handle = orders.perform_cmd_with_handle(cmds::timeout(2000, || Msg::OnTimeout)));
89+
///let cmd_handle = orders.perform_cmd_with_handle(async { log!("Hello!"); Msg::NoOp });
90+
/// ```
91+
#[must_use = "cmd is aborted on its handle drop"]
6392
fn perform_cmd_with_handle(&mut self, cmd: impl Future<Output = Ms> + 'static) -> CmdHandle;
6493

6594
/// Similar to `send_msg`, but calls function `sink` with the given global message.
@@ -69,6 +98,9 @@ pub trait Orders<Ms: 'static, GMs = UndefinedGMsg> {
6998
fn perform_g_cmd(&mut self, g_cmd: impl Future<Output = GMs> + 'static) -> &mut Self;
7099

71100
/// Similar to `perform_g_cmd`, but result is send to function `sink`.
101+
/// - Returns `CmdHandle` that you should save to your `Model`.
102+
/// `cmd` is aborted on the handle drop.
103+
#[must_use = "cmd is aborted on its handle drop"]
72104
fn perform_g_cmd_with_handle(
73105
&mut self,
74106
g_cmd: impl Future<Output = GMs> + 'static,
@@ -103,19 +135,67 @@ pub trait Orders<Ms: 'static, GMs = UndefinedGMsg> {
103135
callback: impl FnOnce(Option<RenderTimestampDelta>) -> Ms + 'static,
104136
) -> &mut Self;
105137

138+
/// Subscribe for messages with the `handler`s input type.
139+
///
140+
/// # Example
141+
///
142+
/// ```rust,no_run
143+
///orders.subscribe(Msg::Reset); // `Msg::Reset(counter::DoReset)`
144+
///orders.subscribe(|greeting: &'static str| { log!(greeting); Msg::NoOp });
145+
///orders.subscribe(Msg::UrlChanged) // `update(... Msg::UrlChanged(subs::UrlChanged(url)) =>`
146+
/// ...
147+
///orders.notify(counter::DoReset);
148+
///orders.notify("Hello!");
149+
/// ```
150+
///
151+
/// _Note:_: Use the alternative `subscribe_with_handle` to control `sub`'s lifetime.
106152
fn subscribe<SubMs: 'static + Clone>(
107153
&mut self,
108154
handler: impl FnOnce(SubMs) -> Ms + Clone + 'static,
109155
) -> &mut Self;
110156

111-
#[must_use]
157+
/// Subscribe for messages with the `handler`s input type.
158+
/// - Returns `SubHandle` that you should save to your `Model`.
159+
/// The `sub` is cancelled on the handle drop.
160+
///
161+
/// # Example
162+
///
163+
/// ```rust,no_run
164+
///let sub_handle = orders.subscribe_with_handle(Msg::Reset); // `Msg::Reset(counter::DoReset)`
165+
///orders.subscribe_with_handle(|greeting: &'static str| { log!(greeting); Msg::NoOp });
166+
///let url_changed_handle = orders.subscribe_with_handle(Msg::UrlChanged) // `update(... Msg::UrlChanged(subs::UrlChanged(url)) =>`
167+
/// ...
168+
///orders.notify(counter::DoReset);
169+
///orders.notify("Hello!");
170+
/// ```
171+
#[must_use = "subscription is cancelled on its handle drop"]
112172
fn subscribe_with_handle<SubMs: 'static + Clone>(
113173
&mut self,
114174
handler: impl FnOnce(SubMs) -> Ms + Clone + 'static,
115175
) -> SubHandle;
116176

177+
/// Stream `Msg`s.
178+
///
179+
/// # Example
180+
///
181+
/// ```rust,no_run
182+
///orders.stream(streams::interval(1000, || Msg::OnTick)));
183+
///orders.stream(streams::window_event(Ev::Resize, |_| Msg::OnResize));
184+
/// ```
185+
///
186+
/// _Note:_: Use the alternative `stream_with_handle` to control `stream`'s lifetime.
117187
fn stream(&mut self, stream: impl Stream<Item = Ms> + 'static) -> &mut Self;
118188

119-
#[must_use]
189+
/// Stream `Msg`s.
190+
/// - Returns `StreamHandle` that you should save to your `Model`.
191+
/// The `stream` is cancelled on the handle drop.
192+
///
193+
/// # Example
194+
///
195+
/// ```rust,no_run
196+
///let timer_handler = orders.stream_with_handle(streams::interval(1000, || Msg::OnTick)));
197+
///let stream_handler = orders.stream_with_handle(streams::window_event(Ev::Resize, |_| Msg::OnResize));
198+
/// ```
199+
#[must_use = "stream is stopped on its handle drop"]
120200
fn stream_with_handle(&mut self, stream: impl Stream<Item = Ms> + 'static) -> StreamHandle;
121201
}

src/app/stream_manager.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ pub struct StreamManager;
88

99
impl StreamManager {
1010
pub fn stream(stream: impl Stream<Item = ()> + 'static) {
11+
// Convert `Stream` to `Future` and execute it. The stream is "leaked" into the JS world.
1112
spawn_local(stream.for_each(|_| ready(())));
1213
}
1314

1415
pub fn stream_with_handle(stream: impl Stream<Item = ()> + 'static) -> StreamHandle {
16+
// Convert `Stream` to `Future`.
1517
let stream = stream.for_each(|_| ready(()));
18+
// Create `AbortHandle`.
1619
let (stream, handle) = abortable(stream);
20+
// Ignore the error when the future is aborted. I.e. just stop the stream.
1721
spawn_local(stream.map(move |_| ()));
1822
StreamHandle(handle)
1923
}

src/app/streams.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ use gloo_timers::future::IntervalStream;
33

44
// ------ Interval stream ------
55

6+
/// Stream no values on predefined time interval in milliseconds.
7+
///
8+
/// # Example
9+
///
10+
/// ```rust,no_run
11+
///orders.stream_with_handle(streams::interval(1000, || Msg::OnTick));
12+
/// ```
613
pub fn interval<Ms>(
714
ms: u32,
815
handler: impl FnOnce() -> Ms + Clone + 'static,
@@ -12,5 +19,5 @@ pub fn interval<Ms>(
1219

1320
// ------ Window Event stream ------
1421

15-
pub mod window_event;
22+
mod window_event;
1623
pub use window_event::window_event;

src/app/streams/window_event.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,29 @@ use wasm_bindgen::closure::Closure;
88
use wasm_bindgen::{JsCast, JsValue};
99
use web_sys::{Event, EventTarget};
1010

11+
// ------ Window Event stream ------
12+
13+
/// Stream `Window` `web_sys::Event`s.
14+
///
15+
/// # Example
16+
///
17+
/// ```rust,no_run
18+
///orders.stream(streams::window_event(Ev::Resize, |_| Msg::OnResize));
19+
/// ```
1120
pub fn window_event<Ms>(
1221
trigger: impl Into<Ev>,
1322
handler: impl FnOnce(Event) -> Ms + Clone + 'static,
1423
) -> impl Stream<Item = Ms> {
1524
EventStream::new(&window(), trigger.into()).map(move |event| handler.clone()(event))
1625
}
1726

27+
// ------ EventStream ------
28+
29+
// @TODO Replace `mpsc` with `crossbeam`? (And integrate it into the other Seed parts (e.g. `Listener`, `SubManager`)).
30+
31+
// @TODO Update it to support different `web_sys` events
32+
// during implementation of https://github.com/seed-rs/seed/issues/331
33+
1834
pub struct EventStream<E> {
1935
node: EventTarget,
2036
trigger: Ev,
@@ -31,6 +47,7 @@ where
3147

3248
let (sender, receiver) = unbounded();
3349

50+
// @TODO replace with `Closure::new` once stable (or use the Seed's temporary one).
3451
let callback = Closure::wrap(Box::new(move |event: JsValue| {
3552
sender.unbounded_send(event.dyn_into().unwrap()).unwrap();
3653
}) as Box<dyn Fn(JsValue)>);

src/app/subs.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,15 @@ pub use url_requested::UrlRequested;
77

88
// ------ UrlChanged sub ------
99

10+
/// Subscribe to url changes.
11+
/// - Url change is fired also on application start by default.
12+
///
13+
/// # Example
14+
///
15+
/// ```rust,no_run
16+
///orders.subscribe(Msg::UrlChanged);
17+
///...
18+
///update(... Msg::UrlChanged(subs::UrlChanged(url)) =>
19+
/// ```
1020
#[derive(Clone)]
1121
pub struct UrlChanged(pub Url);

src/app/subs/url_requested.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,31 @@ use std::{cell::Cell, rc::Rc};
33

44
pub type PreventDefault = bool;
55

6+
// ------ UrlRequested sub ------
7+
8+
/// Subscribe to url requests. Requests are fired on a link click.
9+
///
10+
/// # Example
11+
///
12+
/// ```rust,no_run
13+
///orders.subscribe(Msg::UrlRequested);
14+
///...
15+
///update(... Msg::UrlRequested(subs::UrlRequested(url, url_request))) =>
16+
/// ```
17+
/// See `UrlRequest` for more info.
18+
#[derive(Clone)]
19+
pub struct UrlRequested(pub Url, pub UrlRequest);
20+
21+
// --- UrlRequestStatus ---
22+
623
#[derive(Copy, Clone)]
724
pub enum UrlRequestStatus {
825
Unhandled,
926
Handled(PreventDefault),
1027
}
1128

29+
// --- UrlRequest ---
30+
1231
#[derive(Clone)]
1332
pub struct UrlRequest(Rc<Cell<UrlRequestStatus>>);
1433

@@ -19,22 +38,26 @@ impl Default for UrlRequest {
1938
}
2039

2140
impl UrlRequest {
41+
/// Flag the url request as unhandled.
42+
/// - Seed prevents page refresh, pushes the route and fires `UrlChanged` notification.
43+
/// - It's the default behaviour.
2244
pub fn unhandled(self) {
2345
self.0.set(UrlRequestStatus::Unhandled);
2446
}
2547

48+
/// Flag the url request as handled.
49+
/// - Seed doesn't intercept or modify the click event and doesn't fire `UrlChanged` notification.
2650
pub fn handled(self) {
2751
self.0.set(UrlRequestStatus::Handled(false));
2852
}
2953

30-
pub fn handled_and_prevent_default(self) {
54+
/// Flag the url request as handled and prevent page refresh.
55+
/// - It's almost the same like `handled()` method, but Seed calls `prevent_default` on the click event.
56+
pub fn handled_and_prevent_refresh(self) {
3157
self.0.set(UrlRequestStatus::Handled(true));
3258
}
3359

3460
pub fn status(self) -> UrlRequestStatus {
3561
self.0.get()
3662
}
3763
}
38-
39-
#[derive(Clone)]
40-
pub struct UrlRequested(pub Url, pub UrlRequest);

0 commit comments

Comments
 (0)