use std::rc::Rc;
use crate::Decorated;
use day_core::{BuildCx, Flex, Piece, RNode};
use day_reactive::Signal;
use day_spec::Event;
pub use day_spec::controls::pullrefresh::*;
pub fn support() -> day_spec::Support {
let support = day_core::capability(day_spec::Cap::PullRefresh);
if support == day_spec::Support::Unsupported {
day_spec::Support::Emulated
} else {
support
}
}
pub struct PullRefresh<P: Piece> {
refreshing: Signal<bool>,
child: P,
on_refresh: Option<Rc<dyn Fn()>>,
}
pub fn pull_to_refresh<P: Piece>(refreshing: Signal<bool>, child: P) -> PullRefresh<P> {
PullRefresh {
refreshing,
child,
on_refresh: None,
}
}
#[crate::builder(PullRefreshBuilder)]
impl<P: Piece> PullRefresh<P> {
pub fn on_refresh(mut self, f: impl Fn() + 'static) -> Self {
self.on_refresh = Some(Rc::new(f));
self
}
}
fn begin(refreshing: Signal<bool>, on_refresh: &Option<Rc<dyn Fn()>>) {
if !refreshing.get_untracked() {
refreshing.set(true);
if let Some(f) = on_refresh {
f();
}
}
}
fn wire(cx: &mut BuildCx, node: RNode, refreshing: Signal<bool>, on_refresh: Option<Rc<dyn Fn()>>) {
cx.on(node, move |ev| match ev {
Event::Custom { .. } => begin(refreshing, &on_refresh),
Event::ToggleChanged(true) => begin(refreshing, &on_refresh),
Event::ToggleChanged(false) => refreshing.set(false),
_ => {}
});
}
impl<P: Piece> Piece for PullRefresh<P> {
fn build(self, cx: &mut BuildCx) -> RNode {
if support() == day_spec::Support::Native {
build_native(self, cx)
} else {
build_emulated(self, cx)
}
}
}
fn build_native<P: Piece>(piece: PullRefresh<P>, cx: &mut BuildCx) -> RNode {
use day_core::{FrameLayout, with_tree};
use day_reactive::watch;
let PullRefresh {
refreshing,
child,
on_refresh,
} = piece;
let node = cx.native(
KIND,
&RefreshProps {
refreshing: refreshing.get_untracked(),
},
Rc::new(FrameLayout {
width: None,
height: None,
}),
Flex {
grow_w: true,
grow_h: true,
..Default::default()
},
day_core::Boundary::Yes,
);
cx.under(node, |cx| {
let _ = child.build(cx);
});
wire(cx, node, refreshing, on_refresh.clone());
watch(
move || refreshing.get(),
move |now, _| {
let set = *now;
with_tree(|t| t.patch(node, Box::new(RefreshPatch::SetRefreshing(set)), false));
},
);
node
}
fn build_emulated<P: Piece>(piece: PullRefresh<P>, cx: &mut BuildCx) -> RNode {
use crate::prelude::{Color, Decorate, spinner, when};
use day_core::{Alignment, Boundary, OverlayLayout};
use day_spec::props::ContainerProps;
let PullRefresh {
refreshing,
child,
on_refresh,
} = piece;
let node = cx.native(
day_spec::kinds::CONTAINER,
&ContainerProps::default(),
Rc::new(OverlayLayout {
align: Alignment::Top,
size_to_first: false,
}),
Flex {
grow_w: true,
grow_h: true,
..Default::default()
},
Boundary::No,
);
cx.under(node, |cx| {
let host = day_core::rnode_to_id(node);
child
.tweak(move |child| {
day_core::with_tree(|tree| tree.attach_refresh(child, host));
})
.build(cx);
let _ = when(
move || refreshing.get(),
|| {
spinner()
.padding(10.0)
.background(Color::rgba(0.5, 0.5, 0.55, 0.18))
.corner_radius(21.0)
.padding(10.0)
},
)
.build(cx);
});
wire(cx, node, refreshing, on_refresh);
node
}
#[cfg(feature = "conformance")]
pub mod conformance {
use crate::*;
use day_core::conformance::{Case, Drive};
use day_reactive::Signal;
use super::{KIND, pull_to_refresh};
#[day_macros::test(day_core)]
fn pullrefresh_begins_and_ends() -> Case {
Case::new()
.proves(KIND)
.page(|| {
let refreshing = Signal::new(false);
let runs = Signal::new(0i64);
column((
label(move || format!("refreshing {} runs {}", refreshing.get(), runs.get()))
.id(day_core::id!(pullrefresh::state)),
button("Done")
.action(move || refreshing.set(false))
.id(day_core::id!(pullrefresh::done)),
pull_to_refresh(refreshing, scroll(column((label("Row 1"), label("Row 2")))))
.on_refresh(move || runs.update(|n| *n += 1))
.id(day_core::id!(pullrefresh::pull))
.height(200.0),
))
})
.drive(|d: Drive| async move {
d.toggle("pullrefresh::pull", true).await?;
d.assert_text("pullrefresh::state", "refreshing true runs 1")
.await?;
d.tap("pullrefresh::done").await?;
d.assert_text("pullrefresh::state", "refreshing false runs 1")
.await
})
}
}
pub fn gesture_support() -> day_spec::Support {
day_core::capability(day_spec::Cap::PullRefreshGesture)
}