use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};
use db::Db;
use dioxus::prelude::*;
use server::sync::{SyncError, SyncReason, reconcile_favorites};
use crate::db_reactivity::{Table, use_generations};
static NUDGE: OnceLock<tokio::sync::Notify> = OnceLock::new();
static MUTATION_NUDGE: AtomicBool = AtomicBool::new(false);
fn nudge_handle() -> &'static tokio::sync::Notify {
NUDGE.get_or_init(tokio::sync::Notify::new)
}
pub fn nudge() {
MUTATION_NUDGE.store(true, Ordering::Relaxed);
nudge_handle().notify_one();
}
pub fn nudge_activate() {
nudge_handle().notify_one();
}
pub fn use_sync_task(config: Signal<config::AppConfig>, db: Db) {
let gens = use_generations();
use_future(move || {
let db = db.clone();
async move {
#[cfg(target_arch = "wasm32")]
{
let _ = (&db, &gens);
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut consecutive_failures: u32 = 0;
loop {
let interval = {
let base: u64 = match config.peek().active_service() {
Some(config::MusicService::YtMusic) => 5 * 60,
Some(_) => 10 * 60,
None => 10 * 60,
};
let backoff = base.saturating_mul(1 << consecutive_failures.min(3));
std::time::Duration::from_secs(backoff.min(30 * 60))
};
let nudged = tokio::select! {
_ = nudge_handle().notified() => true,
_ = utils::sleep(interval) => false,
};
if nudged {
utils::sleep(std::time::Duration::from_secs(2)).await;
}
let source = {
let cfg = config.peek();
let Some(source) = server::source::configured_server(db.clone(), &cfg)
else {
continue;
};
source
};
let server_id = source.source().as_str().to_string();
let reason = if nudged {
if MUTATION_NUDGE.swap(false, Ordering::Relaxed) {
SyncReason::AfterMutation
} else {
SyncReason::Activate
}
} else {
SyncReason::Interval
};
match reconcile_favorites(source.as_ref(), reason).await {
Ok(report) => {
consecutive_failures = 0;
if report.pushed_likes + report.pushed_unlikes > 0 || report.did_pull {
gens.bump(Table::Favorites);
}
}
Err(SyncError::Expired) => {
consecutive_failures = consecutive_failures.saturating_add(1);
tracing::warn!(
server = %server_id,
"favorites sync: credentials expired — sign in again from settings"
);
}
Err(SyncError::Unreachable(e)) => {
consecutive_failures = consecutive_failures.saturating_add(1);
tracing::debug!(error = %e, "favorites sync: server unreachable, backing off");
}
}
}
}
}
});
}