list: Add load_more. (#532)

This commit is contained in:
Jason Lee 2025-01-08 15:05:40 +08:00 committed by GitHub
parent e345bc9afb
commit 3773337e04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 104 additions and 12 deletions

View file

@ -1,4 +1,5 @@
use core::time;
use std::time::Duration;
use fake::Fake;
use gpui::{
@ -135,6 +136,9 @@ struct CompanyListDelegate {
matched_companies: Vec<Company>,
selected_index: usize,
confirmed_index: Option<usize>,
query: String,
loading: bool,
is_eof: bool,
}
impl ListDelegate for CompanyListDelegate {
@ -149,6 +153,7 @@ impl ListDelegate for CompanyListDelegate {
}
fn perform_search(&mut self, query: &str, _: &mut ViewContext<List<Self>>) -> Task<()> {
self.query = query.to_string();
self.matched_companies = self
.companies
.iter()
@ -214,6 +219,38 @@ impl ListDelegate for CompanyListDelegate {
None
}
fn can_load_more(&self, _: &AppContext) -> bool {
return !self.loading && !self.is_eof;
}
fn load_more_threshold(&self) -> usize {
150
}
fn load_more(&mut self, cx: &mut ViewContext<List<Self>>) {
self.loading = true;
cx.spawn(|view, mut cx| async move {
dbg!("Load more data");
// Simulate network request, delay 1s to load data.
Timer::after(Duration::from_secs(1)).await;
cx.update(|cx| {
_ = view.update(cx, move |view, cx| {
let query = view.delegate().query.clone();
view.delegate_mut()
.companies
.extend((0..200).map(|_| random_company()));
view.delegate_mut().perform_search(&query, cx);
view.delegate_mut().loading = false;
dbg!("Loaded more data len: ", view.delegate().companies.len());
view.delegate_mut().is_eof = view.delegate().companies.len() >= 6000;
});
})
})
.detach();
}
}
impl CompanyListDelegate {
@ -259,6 +296,9 @@ impl ListStory {
companies,
selected_index: 0,
confirmed_index: None,
query: "".to_string(),
loading: false,
is_eof: false,
},
cx,
)

View file

@ -1,3 +1,4 @@
use std::ops::Range;
use std::time::Duration;
use std::{cell::Cell, rc::Rc};
@ -77,6 +78,29 @@ pub trait ListDelegate: Sized + 'static {
/// Cancel the selection, e.g.: Pressed ESC.
fn cancel(&mut self, cx: &mut ViewContext<List<Self>>) {}
/// Return true to enable load more data when scrolling to the bottom.
///
/// Default: true
fn can_load_more(&self, cx: &AppContext) -> bool {
true
}
/// Returns a threshold value (n rows), of course, when scrolling to the bottom,
/// the remaining number of rows triggers `load_more`.
///
/// Default: 50 rows
fn load_more_threshold(&self) -> usize {
50
}
/// Load more data when the table is scrolled to the bottom.
///
/// This will performed in a background task.
///
/// This is always called when the table is near the bottom,
/// so you must check if there is more data to load or lock the loading state.
fn load_more(&mut self, cx: &mut ViewContext<List<Self>>) {}
}
pub struct List<D: ListDelegate> {
@ -259,6 +283,31 @@ where
cx.notify();
}
/// Dispatch delegate's `load_more` method when the visible range is near the end.
fn load_more_if_need(&mut self, visible_range: Range<usize>, cx: &mut ViewContext<Self>) {
if !self.delegate.can_load_more(cx) {
return;
}
let items_count = self.delegate.items_count(cx);
let threshold = self.delegate.load_more_threshold();
if items_count < threshold {
return;
}
// Securely handle subtract logic to prevent attempt to subtract with overflow
if visible_range.end >= items_count - threshold {
cx.spawn(|view, mut cx| async move {
cx.update(|cx| {
view.update(cx, |view, cx| {
view.delegate.load_more(cx);
})
})
})
.detach()
}
}
fn on_action_cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
self.set_selected_index(None, cx);
self.delegate.cancel(cx);
@ -430,6 +479,8 @@ where
this.child(
uniform_list(view, "uniform-list", items_count, {
move |list, visible_range, cx| {
list.load_more_if_need(visible_range.clone(), cx);
visible_range
.map(|ix| list.render_list_item(ix, cx))
.collect::<Vec<_>>()

View file

@ -583,26 +583,27 @@ where
}
/// Dispatch delegate's `load_more` method when the visible range is near the end.
fn load_more(&mut self, visible_range: Range<usize>, cx: &mut ViewContext<Self>) {
fn load_more_if_need(&mut self, visible_range: Range<usize>, cx: &mut ViewContext<Self>) {
if !self.delegate.can_load_more(cx) {
return;
}
let row_count = self.delegate.rows_count(cx);
let load_more_count = self.delegate.load_more_threshold();
let threshold = self.delegate.load_more_threshold();
if row_count < threshold {
return;
}
// Securely handle subtract logic to prevent attempt to subtract with overflow
if row_count >= load_more_count {
if visible_range.end >= row_count - load_more_count {
cx.spawn(|view, mut cx| async move {
cx.update(|cx| {
view.update(cx, |view, cx| {
view.delegate.load_more(cx);
})
if visible_range.end >= row_count - threshold {
cx.spawn(|view, mut cx| async move {
cx.update(|cx| {
view.update(cx, |view, cx| {
view.delegate.load_more(cx);
})
})
.detach()
}
})
.detach()
}
}
@ -1202,7 +1203,7 @@ where
rows_count + extra_rows_needed,
{
move |table, visible_range, cx| {
table.load_more(visible_range.clone(), cx);
table.load_more_if_need(visible_range.clone(), cx);
if visible_range.end > rows_count {
table.scroll_to_row(