leptos_use/
watch_pausable.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::{watch_with_options, WatchOptions};
use leptos::prelude::*;

/// Pausable [`watch`].
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/watch_pausable)
///
/// ## Usage
///
/// ```
/// # use leptos::prelude::*;
/// # use leptos::logging::log;
/// # use leptos_use::{watch_pausable, WatchPausableReturn};
/// #
/// # pub fn Demo() -> impl IntoView {
/// let (source, set_source) = signal("foo".to_string());
///
/// let WatchPausableReturn {
///     stop,
///     pause,
///     resume,
///     ..
/// } = watch_pausable(
///     move || source.get(),
///     |v, _, _| {
///         log!("Changed to {}", v);
///     },
/// );
///
/// set_source.set("bar".to_string()); // > "Changed to bar"
///
/// pause();
///
/// set_source.set("foobar".to_string()); // (nothing happens)
///
/// resume();
///
/// set_source.set("hello".to_string()); // > "Changed to hello"
/// #    view! { }
/// # }
/// ```
///
/// There's also [`watch_pausable_with_options`] which takes the same options as [`watch`].
///
/// ## Server-Side Rendering
///
/// On the server this works just fine except if you throttle or debounce in which case the callback
/// will never be called except if you set `immediate` to `true` in which case the callback will be
/// called exactly once.
///
/// ## See also
///
/// * `leptos::watch`
pub fn watch_pausable<W, T, DFn, CFn>(
    deps: DFn,
    callback: CFn,
) -> WatchPausableReturn<
    impl Fn() + Clone + Send + Sync,
    impl Fn() + Clone + Send + Sync,
    impl Fn() + Clone + Send + Sync,
>
where
    DFn: Fn() -> W + 'static,
    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
    W: Clone + 'static,
    T: Clone + 'static,
{
    watch_pausable_with_options(deps, callback, WatchOptions::default())
}

/// Version of `watch_pausable` that accepts `WatchOptions`. See [`watch_pausable`] for how to use.
pub fn watch_pausable_with_options<W, T, DFn, CFn>(
    deps: DFn,
    callback: CFn,
    options: WatchOptions,
) -> WatchPausableReturn<
    impl Fn() + Clone + Send + Sync,
    impl Fn() + Clone + Send + Sync,
    impl Fn() + Clone + Send + Sync,
>
where
    DFn: Fn() -> W + 'static,
    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
    W: Clone + 'static,
    T: Clone + 'static,
{
    let (is_active, set_active) = signal(true);

    let pausable_callback = move |val: &W, prev_val: Option<&W>, prev_ret: Option<Option<T>>| {
        if is_active.get_untracked() {
            Some(callback(val, prev_val, prev_ret.unwrap_or(None)))
        } else {
            None
        }
    };

    let stop = watch_with_options(deps, pausable_callback, options);

    let pause = move || {
        set_active.set(false);
    };

    let resume = move || {
        set_active.set(true);
    };

    WatchPausableReturn {
        stop,
        pause,
        resume,
        is_active: is_active.into(),
    }
}

/// Return type of [`watch_pausable`]
pub struct WatchPausableReturn<StopFn, PauseFn, ResumeFn>
where
    StopFn: Fn() + Clone + Send + Sync,
    PauseFn: Fn() + Clone + Send + Sync,
    ResumeFn: Fn() + Clone + Send + Sync,
{
    /// Stops the watcher
    pub stop: StopFn,

    /// Pauses the watcher
    pub pause: PauseFn,

    /// Resumes the watcher
    pub resume: ResumeFn,

    /// Whether the watcher is active (not paused). This doesn't reflect if the watcher has been stopped
    pub is_active: Signal<bool>,
}