chore: make decling and accepting TOS better
This commit is contained in:
parent
bd8dc0424c
commit
37001e7a7c
2 changed files with 85 additions and 54 deletions
|
|
@ -8,7 +8,7 @@ use oneclient_db::models::ClusterId;
|
|||
pub struct ActiveClusterState(pub State<Option<ClusterId>>);
|
||||
|
||||
pub fn use_provide_active_cluster(active: ActiveClusterState) {
|
||||
use_provide_root_context(move || active.clone());
|
||||
use_hook(move || provide_root_context(active));
|
||||
}
|
||||
|
||||
pub fn use_active_cluster_id() -> State<Option<ClusterId>> {
|
||||
|
|
@ -48,7 +48,7 @@ pub fn use_start_maximized() -> bool {
|
|||
pub struct BrowserCompatState(pub State<bool>);
|
||||
|
||||
pub fn use_provide_browser_compat(state: BrowserCompatState) {
|
||||
use_provide_root_context(move || state.clone());
|
||||
use_hook(move || provide_root_context(state));
|
||||
}
|
||||
|
||||
pub fn use_browser_compat() -> State<bool> {
|
||||
|
|
@ -89,7 +89,7 @@ impl Default for BrowserUiState {
|
|||
pub struct BrowserStateStore(pub State<HashMap<String, BrowserUiState>>);
|
||||
|
||||
pub fn use_provide_browser_state(store: BrowserStateStore) {
|
||||
use_provide_root_context(move || store.clone());
|
||||
use_hook(move || provide_root_context(store));
|
||||
}
|
||||
|
||||
pub fn use_browser_state_store() -> State<HashMap<String, BrowserUiState>> {
|
||||
|
|
@ -114,9 +114,63 @@ pub struct OnboardingSelectionState {
|
|||
}
|
||||
|
||||
pub fn use_provide_onboarding_selection(state: OnboardingSelectionState) {
|
||||
use_provide_root_context(move || state.clone());
|
||||
use_hook(move || provide_root_context(state));
|
||||
}
|
||||
|
||||
pub fn use_onboarding_selection() -> OnboardingSelectionState {
|
||||
consume_root_context::<OnboardingSelectionState>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use freya_testing::TestingRunner;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Stands in for a route layout: it owns the state and publishes it as a root
|
||||
/// context, so unmounting it drops the value the context still points at
|
||||
#[derive(PartialEq)]
|
||||
struct Shell;
|
||||
|
||||
impl Component for Shell {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let active = use_state(|| None::<ClusterId>);
|
||||
use_provide_active_cluster(ActiveClusterState(active));
|
||||
rect().child(Reader)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Reader;
|
||||
|
||||
impl Component for Reader {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let active = *use_active_cluster_id().read();
|
||||
label().text(format!("{active:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn app() -> impl IntoElement {
|
||||
let mounted = use_consume::<State<bool>>();
|
||||
rect().maybe_child(mounted().then(|| Shell.into_element()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_remounted_shell_replaces_the_dropped_root_state() {
|
||||
let (mut test, mut mounted) = TestingRunner::new(
|
||||
app,
|
||||
Size2D::new(300., 300.),
|
||||
|runner| runner.provide_root_context(|| State::create(true)),
|
||||
1.,
|
||||
);
|
||||
test.sync_and_update();
|
||||
|
||||
// Leaving the shell drops the state the root context holds
|
||||
test.run_in(|| mounted.set(false));
|
||||
test.sync_and_update();
|
||||
|
||||
// Coming back must not hand the reader that dropped state
|
||||
test.run_in(|| mounted.set(true));
|
||||
test.sync_and_update();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use freya::router::RouterContext;
|
|||
|
||||
use crate::components::{
|
||||
Button, Icon, IconType, Markdown, MarkdownStyle, OverlayPopup, ScrollArea, Segment,
|
||||
SegmentedControl, toggle,
|
||||
SegmentedControl,
|
||||
};
|
||||
use crate::hooks::{
|
||||
TermsQuery, has_migration_data, terms_document, terms_error, terms_is_loading, use_dispatch,
|
||||
|
|
@ -38,7 +38,6 @@ impl Component for OnboardingTerms {
|
|||
let error = terms_error(&query);
|
||||
let loading = terms_is_loading(&query);
|
||||
|
||||
let accepted = use_state(|| false);
|
||||
let tab = use_state(|| LegalTab::Terms);
|
||||
let confirming_decline = use_state(|| false);
|
||||
|
||||
|
|
@ -100,19 +99,21 @@ impl Component for OnboardingTerms {
|
|||
.maybe_child(tabs)
|
||||
.child(body)
|
||||
.child(link_row(terms_url, privacy_url))
|
||||
.child(accept_row(accepted))
|
||||
.into_element();
|
||||
|
||||
let accept_dispatch = dispatch.clone();
|
||||
let nav = terms_nav(back, !loading, move || {
|
||||
if *accepted.read() {
|
||||
let nav = terms_nav(
|
||||
back,
|
||||
!loading,
|
||||
move || {
|
||||
accept_dispatch.accept_tos(terms_version, privacy_version);
|
||||
let _ = RouterContext::get().replace(next.clone());
|
||||
} else {
|
||||
},
|
||||
move || {
|
||||
let mut confirming = confirming_decline;
|
||||
confirming.set(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let modal = confirming_decline.read().then(|| {
|
||||
decline_modal(confirming_decline, move || {
|
||||
|
|
@ -266,46 +267,14 @@ fn external_link_button(text: &'static str, url: String) -> impl IntoElement {
|
|||
.child(Icon::new(IconType::LinkExternal01).size(14.))
|
||||
}
|
||||
|
||||
fn accept_row(accepted: State<bool>) -> Element {
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(16.)
|
||||
.content(Content::Flex)
|
||||
.padding(Gaps::new_symmetric(12., 16.))
|
||||
.corner_radius(CornerRadius::new_all(12.))
|
||||
.background(colors::page_elevated())
|
||||
.border(border_all_color(1., colors::component_border()))
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::flex(1.0))
|
||||
.spacing(3.)
|
||||
.child(
|
||||
label()
|
||||
.text("I accept the Terms of Service and Privacy Policy")
|
||||
.font_size(14.)
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text("Required for world hosting, socials, nametag indicator, and more.")
|
||||
.font_size(11.)
|
||||
.color(colors::fg_secondary()),
|
||||
),
|
||||
)
|
||||
.child(toggle(accepted))
|
||||
.into_element()
|
||||
}
|
||||
|
||||
fn terms_nav(
|
||||
back: Option<Route>,
|
||||
next_enabled: bool,
|
||||
on_next: impl FnMut() + 'static,
|
||||
accept_enabled: bool,
|
||||
on_accept: impl FnMut() + 'static,
|
||||
on_decline: impl FnMut() + 'static,
|
||||
) -> Element {
|
||||
let mut on_next = on_next;
|
||||
let mut on_accept = on_accept;
|
||||
let mut on_decline = on_decline;
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
|
|
@ -323,14 +292,22 @@ fn terms_nav(
|
|||
.text("Back")
|
||||
.into_element()
|
||||
}))
|
||||
.child(
|
||||
Button::new()
|
||||
.secondary()
|
||||
.width(Size::px(140.))
|
||||
.on_press(move |_| on_decline())
|
||||
.text("Decline")
|
||||
.child(Icon::new(IconType::X).size(16.)),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
.width(Size::px(140.))
|
||||
.enabled(next_enabled)
|
||||
.on_press(move |_| on_next())
|
||||
.text("Next")
|
||||
.child(Icon::new(IconType::ArrowRight).size(16.)),
|
||||
.enabled(accept_enabled)
|
||||
.on_press(move |_| on_accept())
|
||||
.text("Accept")
|
||||
.child(Icon::new(IconType::Check).size(16.)),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
|
|
@ -407,7 +384,7 @@ fn decline_modal(confirming: State<bool>, on_confirm: impl FnMut() + 'static) ->
|
|||
Button::new()
|
||||
.secondary()
|
||||
.on_press(move |_| confirming.set(false))
|
||||
.text("Close"),
|
||||
.text("Cancel"),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue