feat: added funfacts changing on background change (#718)

Co-authored-by: rozwader <maciek.rozwadowski2006@gmail.com>
This commit is contained in:
LynithDev 2026-08-19 16:05:42 +02:00 • committed by GitHub
parent 578ec187cd
commit efed361417
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 76 additions and 11 deletions

View file

@ -232,6 +232,17 @@ impl Actions {
}
}
pub fn reset_onboarding(&self) {
if let Some(updated) = self.mutate_settings(|settings| {
settings.seen_onboarding = false;
settings.accepted_tos_version = 0;
settings.accepted_privacy_version = 0;
settings.seen_versions.clear();
}) {
self.persist(updated);
}
}
pub fn accept_tos(&self, terms_version: u32, privacy_version: u32) {
if let Some(updated) = self.mutate_settings(|settings| {
settings.accepted_tos_version = terms_version;

View file

@ -6,6 +6,7 @@ use oneclient_auth::preview_samples;
use oneclient_core::simulate::Damage;
use oneclient_net::status::{self, ServiceStatus};
use crate::Actions;
use crate::components::{Button, Dropdown, Icon, IconType, TextInput, login_dialog, toggle};
use crate::hooks::use_dispatch;
use crate::notifications::{
@ -22,6 +23,7 @@ pub struct Debug;
impl Component for Debug {
fn render(&self) -> impl IntoElement {
let dispatch = use_dispatch();
let log_debug_info = use_state(|| false);
let show_dev_stuff = use_state(|| false);
let seen_onboarding = use_state(|| true);
@ -97,7 +99,7 @@ impl Component for Debug {
.child(divider())
.child(section(
"Other",
vec![action_row(vec![
vec![action_row(&dispatch, vec![
("Open Dev Tools", IconType::CodeSnippet02),
("Open Onboarding", IconType::Rocket02),
("Open Launcher Data", IconType::Folder),
@ -1142,7 +1144,7 @@ fn toggle_row(title: &'static str, on: State<bool>) -> Element {
.into_element()
}
fn action_row(buttons: Vec<(&'static str, IconType)>) -> Element {
fn action_row(dispatch: &Actions, buttons: Vec<(&'static str, IconType)>) -> Element {
let mut row = rect().horizontal().width(Size::fill()).spacing(12.);
for (text, icon) in buttons {
@ -1152,6 +1154,7 @@ fn action_row(buttons: Vec<(&'static str, IconType)>) -> Element {
.text(text);
if text == "Open Onboarding" {
dispatch.reset_onboarding();
button = button.on_press(|_| {
let _ = RouterContext::get().replace(Route::OnboardingWelcome {});
});

View file

@ -2,7 +2,7 @@ use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use oneclient_core::clusters::Cluster;
@ -11,7 +11,24 @@ use crate::hooks::use_settings_snapshot;
use crate::layout::gradient_overlay_radial;
use crate::theme::colors;
const BACKDROP_INTERVAL_SECS: u64 = 12;
pub(super) const BACKDROP_INTERVAL_SECS: u64 = 12;
const ROTATION_PERIOD: Duration = Duration::from_secs(BACKDROP_INTERVAL_SECS);
fn since_epoch() -> Duration {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
}
pub(super) fn rotation_tick() -> usize {
(since_epoch().as_secs() / BACKDROP_INTERVAL_SECS) as usize
}
pub(super) fn next_rotation_delay() -> Duration {
let into_period = since_epoch().as_nanos() % ROTATION_PERIOD.as_nanos();
ROTATION_PERIOD - Duration::from_nanos(into_period as u64)
}
const PARALLAX_SCALE: f32 = 1.10;
const PARALLAX_STRENGTH: f32 = 0.008;
@ -28,7 +45,10 @@ impl Component for LoadingBackdrop {
fn render(&self) -> impl IntoElement {
let clusters = &self.clusters;
let count = clusters.len().max(1);
let mut index = use_state(|| 0usize);
// Offset against the shared tick so the first art shown is still the
// first cluster
let start = use_hook(rotation_tick);
let mut tick = use_state(rotation_tick);
let stop = use_hook(|| Arc::new(AtomicBool::new(false)));
@ -46,20 +66,19 @@ impl Component for LoadingBackdrop {
spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(BACKDROP_INTERVAL_SECS)).await;
tokio::time::sleep(next_rotation_delay()).await;
if stop.load(Ordering::Relaxed) {
break;
}
let next = (*index.peek() + 1) % count;
index.set(next);
tick.set(rotation_tick());
}
});
}
});
let current = *index.read() % count;
let current = (*tick.read()).wrapping_sub(start) % count;
let parallax_enabled = use_settings_snapshot().settings.dynamic_background_enabled;

View file

@ -1,5 +1,9 @@
use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use super::backdrop::{next_rotation_delay, rotation_tick};
use crate::theme::colors;
const ONBOARDING_TIPS_BACKUP: &[&str] = &[
@ -35,7 +39,7 @@ fn parse_funfacts(text: &str) -> Vec<String> {
.collect()
}
/// One stable random tip for the lifetime of the component
/// A random tip that rotates in step with the backdrop art
pub(super) fn use_onboarding_tip() -> String {
let mut tips = use_state(|| {
ONBOARDING_TIPS_BACKUP
@ -78,18 +82,46 @@ pub(super) fn use_onboarding_tip() -> String {
});
});
// Random offset against the shared tick so the run does not always open on
// the same fact
let seed = use_hook(|| {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.subsec_nanos() as usize)
.unwrap_or(0)
});
let mut tick = use_state(rotation_tick);
let stop = use_hook(|| Arc::new(AtomicBool::new(false)));
use_drop({
let stop = stop.clone();
move || stop.store(true, Ordering::Relaxed)
});
use_hook({
let stop = stop.clone();
move || {
spawn(async move {
loop {
tokio::time::sleep(next_rotation_delay()).await;
if stop.load(Ordering::Relaxed) {
break;
}
tick.set(rotation_tick());
}
});
}
});
// Wrapped at read time the list is swapped out once the remote facts land
let list = tips.read();
if list.is_empty() {
String::new()
} else {
list[seed % list.len()].clone()
list[seed.wrapping_add(*tick.read()) % list.len()].clone()
}
}