Skip to content

Commit 757e9ff

Browse files
committed
refactor: Fix Query throttle strategy and replace refetch_with with set_refetch_strategy
1 parent 8a9f4eb commit 757e9ff

3 files changed

Lines changed: 31 additions & 51 deletions

File tree

src/dialogs/create_distrobox_dialog.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,11 @@ impl CreateDistroboxDialog {
397397
}
398398
));
399399

400+
prefill_query.set_refetch_strategy(Query::debounce(Duration::from_millis(300)));
400401
*imp.prefill_query.borrow_mut() = Some(prefill_query.clone());
401402

402403
imp.name_row.connect_changed(move |_| {
403-
prefill_query.refetch_with(Query::debounce(Duration::from_millis(300)));
404+
prefill_query.refetch();
404405
});
405406

406407
page
@@ -570,6 +571,7 @@ impl CreateDistroboxDialog {
570571
}
571572
));
572573

574+
ini_content_query.set_refetch_strategy(Query::debounce(Duration::from_millis(500)));
573575
*self.imp().ini_content_query.borrow_mut() = Some(ini_content_query.clone());
574576

575577
url_row.connect_changed(clone!(
@@ -589,7 +591,7 @@ impl CreateDistroboxDialog {
589591
text_view.buffer().set_text("");
590592

591593
// Debounced download (validation happens implicitly)
592-
ini_content_query.refetch_with(Query::debounce(Duration::from_millis(500)));
594+
ini_content_query.refetch();
593595
}
594596
));
595597

src/models/root_store.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ impl RootStore {
254254
Ok(containers)
255255
}
256256
});
257+
this.containers_query().set_refetch_strategy(Query::throttle(Duration::from_secs(1), true));
257258

258259
let this_clone = this.clone();
259260
this.containers_query().connect_success(move |containers| {
@@ -341,8 +342,7 @@ impl RootStore {
341342
}
342343

343344
pub fn load_containers(&self) {
344-
self.containers_query()
345-
.refetch_with(Query::throttle(Duration::from_secs(1), true));
345+
self.containers_query().refetch();
346346
}
347347

348348
pub fn download_distrobox(&self) -> DistroboxTask {

src/query/mod.rs

Lines changed: 25 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ pub struct QueryInner<T> {
99
key: String,
1010
/// The current data (if any successful fetch has occurred)
1111
pub data: Option<T>,
12-
/// Whether a fetch is currently in progress
13-
pub is_loading: bool,
14-
/// The last error (if any)
15-
pub error: Option<Rc<anyhow::Error>>,
1612
/// Timestamp of the last successful fetch
1713
pub last_fetched_at: Option<SystemTime>,
14+
/// The last error (if any) - stored as Rc for signal emission
15+
pub error: Option<Rc<anyhow::Error>>,
1816
query_fn: Option<
1917
Box<dyn Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<T>>>>>,
2018
>,
@@ -27,6 +25,8 @@ pub struct QueryInner<T> {
2725

2826
retry_strategy: Option<Box<dyn Fn(u32) -> Option<Duration>>>,
2927
retry_count: u32,
28+
29+
refetch_strategy: Option<Rc<dyn Fn(&Query<T>) + 'static>>,
3030
}
3131

3232
impl<T> QueryInner<T> {
@@ -42,7 +42,6 @@ impl<T> QueryInner<T> {
4242
Self {
4343
key,
4444
data: None,
45-
is_loading: false,
4645
error: None,
4746
last_fetched_at: None,
4847
query_fn,
@@ -52,6 +51,7 @@ impl<T> QueryInner<T> {
5251
timeout,
5352
retry_strategy: None,
5453
retry_count: 0,
54+
refetch_strategy: None,
5555
}
5656
}
5757

@@ -213,7 +213,7 @@ where
213213
}
214214

215215
/// Strategy: Execute fetch immediately
216-
pub fn immediate() -> impl FnOnce(&Query<T>) {
216+
pub fn immediate() -> impl Fn(&Query<T>) {
217217
|query: &Query<T>| {
218218
query.fetch();
219219
}
@@ -222,9 +222,7 @@ where
222222
/// Strategy: Debounce fetch calls
223223
/// Waits for `duration` after the last call before executing.
224224
/// If another call arrives before the timer fires, the timer resets.
225-
///
226-
/// State is managed internally by the strategy and not stored in Query.
227-
pub fn debounce(duration: Duration) -> impl FnOnce(&Query<T>) {
225+
pub fn debounce(duration: Duration) -> impl Fn(&Query<T>) {
228226
// Strategy state: managed by the closure itself
229227
let debounce_state: Rc<RefCell<Option<glib::SourceId>>> = Rc::new(RefCell::new(None));
230228

@@ -259,9 +257,7 @@ where
259257
/// Executes at most once per `interval`.
260258
/// If `trailing` is true, a trailing fetch will be scheduled after the interval
261259
/// if calls arrived during the throttle period.
262-
///
263-
/// State is managed internally by the strategy and not stored in Query.
264-
pub fn throttle(interval: Duration, trailing: bool) -> impl FnOnce(&Query<T>) {
260+
pub fn throttle(interval: Duration, trailing: bool) -> impl Fn(&Query<T>) {
265261
// Strategy state: managed by the closure itself
266262
let throttle_state: Rc<RefCell<(Option<Instant>, Option<glib::SourceId>)>> =
267263
Rc::new(RefCell::new((None, None)));
@@ -353,8 +349,8 @@ where
353349
if let Some(interval) = options.refetch_interval {
354350
let weak = Rc::downgrade(&inner);
355351
let source_id = glib::timeout_add_seconds_local(interval, move || {
356-
if let Some(query) = Self::from_weak(&weak) {
357-
query.fetch();
352+
if let Some(inner) = weak.upgrade() {
353+
Self { inner }.fetch();
358354
}
359355
glib::ControlFlow::Continue
360356
});
@@ -363,15 +359,6 @@ where
363359
query
364360
}
365361

366-
fn from_strong(strong: &Rc<RefCell<QueryInner<T>>>) -> Self {
367-
Self {
368-
inner: strong.clone(),
369-
}
370-
}
371-
fn from_weak(weak: &std::rc::Weak<RefCell<QueryInner<T>>>) -> Option<Self> {
372-
weak.upgrade().map(|inner| Self { inner })
373-
}
374-
375362
/// Execute a fetch operation and handle the result
376363
async fn execute_fetch(inner: &Rc<RefCell<QueryInner<T>>>) {
377364
let key = { inner.borrow().key.clone() };
@@ -407,7 +394,6 @@ where
407394
match result {
408395
Ok(_data) => {
409396
inner.borrow_mut().data = Some(_data.clone());
410-
inner.borrow_mut().is_loading = false;
411397
inner.borrow_mut().error = None;
412398
query_obj.set_is_loading(false);
413399
query_obj.set_is_success(true);
@@ -421,13 +407,12 @@ where
421407
}
422408
Err(error) => {
423409
if inner.borrow().retry_strategy.is_some() {
424-
Self::from_strong(inner).retry();
410+
Self { inner: inner.clone() }.retry();
425411
return;
426412
}
427413
let rc_error = Rc::new(error);
428414
let error_msg = rc_error.to_string();
429415
// Keep the previous data, just mark as error
430-
inner.borrow_mut().is_loading = false;
431416
inner.borrow_mut().error = Some(rc_error);
432417
query_obj.set_is_loading(false);
433418
query_obj.set_is_error(true);
@@ -451,11 +436,10 @@ where
451436
handle.abort();
452437
}
453438

454-
// Set loading inner, but preserve any previous data
439+
// Set loading state, but preserve any previous data
455440
query_obj.set_is_loading(true);
456441
query_obj.set_is_error(false);
457442
query_obj.set_is_success(false);
458-
self.inner.borrow_mut().is_loading = true;
459443
self.inner.borrow_mut().last_fetched_at = Some(SystemTime::now());
460444

461445
let inner = self.inner.clone();
@@ -468,21 +452,21 @@ where
468452
self.inner.borrow_mut().fetch_task_handle = Some(handle);
469453
}
470454

471-
/// Refetch with immediate strategy (for backward compatibility)
472-
pub fn refetch(&self) {
473-
self.refetch_with(Self::immediate());
455+
/// Set the refetch strategy for this query.
456+
/// The strategy is a closure that determines when and how to execute the fetch.
457+
/// Common strategies are `Query::immediate`, `Query::debounce`, and `Query::throttle`.
458+
pub fn set_refetch_strategy(&self, strategy: impl Fn(&Query<T>) + 'static) {
459+
self.inner.borrow_mut().refetch_strategy = Some(Rc::new(strategy));
474460
}
475461

476-
/// Refetch with a specific strategy
477-
///
478-
/// # Example
479-
/// ```
480-
/// query.refetch_with(Query::immediate());
481-
/// query.refetch_with(Query::debounce(Duration::from_millis(300)));
482-
/// query.refetch_with(Query::throttle(Duration::from_secs(1), true));
483-
/// ```
484-
pub fn refetch_with(&self, strategy: impl FnOnce(&Query<T>)) {
485-
strategy(self);
462+
/// Refetch using the configured strategy (or immediate if none set)
463+
pub fn refetch(&self) {
464+
let strategy = self.inner.borrow().refetch_strategy.clone();
465+
if let Some(strategy) = strategy {
466+
strategy(self);
467+
} else {
468+
self.fetch();
469+
}
486470
}
487471

488472
pub fn retry(&self) {
@@ -547,12 +531,6 @@ where
547531
})
548532
}
549533

550-
/// Bind a widget property to the query inner
551-
pub fn bind_to_widget<W: IsA<gtk::Widget>>(&self, widget: &W, property: &str) {
552-
let query_obj = { self.inner.borrow().query_obj.clone() };
553-
query_obj.bind_property(property, widget, property).build();
554-
}
555-
556534
pub fn data(&self) -> Option<T> {
557535
self.inner.borrow().data.clone()
558536
}

0 commit comments

Comments
 (0)