pub fn screen_items(viewport_height: u32, minimum_row_height: u32) -> usize {
let items = (viewport_height as f64 / minimum_row_height as f64).ceil() as usize;
items.max(1) }
pub fn live_window_size(screen_items: usize, threshold_screens: f64) -> usize {
((2.0 * threshold_screens + 1.0) * screen_items as f64).ceil() as usize
}
pub fn full_window_size(screen_items: usize, threshold_screens: f64) -> usize {
((4.0 * threshold_screens + 1.0) * screen_items as f64).ceil() as usize
}
pub fn trigger_threshold_px(viewport_height: u32, threshold_screens: f64) -> u32 {
(threshold_screens * viewport_height as f64).ceil() as u32
}
pub fn continuation_offset(screen_items: usize, threshold_screens: f64) -> usize {
(threshold_screens * screen_items as f64).ceil() as usize
}
pub fn min_buffer(screen_items: usize, threshold_screens: f64) -> usize {
(threshold_screens * screen_items as f64).ceil() as usize
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Backward,
Forward,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TriggerCheck {
None,
Trigger(Direction),
}
pub fn check_trigger(
trigger_threshold_px: u32,
top_gap_px: u32,
bottom_gap_px: u32,
scrolling_up: bool,
at_earliest: bool,
at_latest: bool,
) -> TriggerCheck {
if scrolling_up && top_gap_px < trigger_threshold_px && !at_earliest {
return TriggerCheck::Trigger(Direction::Backward);
}
if !scrolling_up && bottom_gap_px < trigger_threshold_px && !at_latest {
return TriggerCheck::Trigger(Direction::Forward);
}
TriggerCheck::None
}
pub fn continuation_index(
continuation_offset: usize,
current_len: usize,
direction: Direction,
) -> usize {
match direction {
Direction::Backward => {
current_len.saturating_sub(continuation_offset)
}
Direction::Forward => {
continuation_offset.saturating_sub(1).min(current_len.saturating_sub(1))
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WindowingParams {
pub screen_items: usize,
pub live_window_size: usize,
pub full_window_size: usize,
pub min_buffer: usize,
pub trigger_threshold_px: u32,
pub continuation_offset: usize,
}
impl WindowingParams {
pub fn compute(
viewport_height: u32,
minimum_row_height: u32,
threshold_screens: f64,
) -> Self {
let screen_items = screen_items(viewport_height, minimum_row_height);
Self {
screen_items,
live_window_size: live_window_size(screen_items, threshold_screens),
full_window_size: full_window_size(screen_items, threshold_screens),
min_buffer: min_buffer(screen_items, threshold_screens),
trigger_threshold_px: trigger_threshold_px(viewport_height, threshold_screens),
continuation_offset: continuation_offset(screen_items, threshold_screens),
}
}
}
pub fn simulate_pagination(
full_window_size: usize,
_current_start_id: i64,
_current_end_id: i64,
continuation_id: i64,
direction: Direction,
total_items_in_db: i64,
) -> (i64, i64) {
let window_size = full_window_size as i64;
match direction {
Direction::Backward => {
let new_end = continuation_id;
let new_start = (continuation_id - window_size + 1).max(0);
(new_start, new_end)
}
Direction::Forward => {
let new_start = continuation_id;
let new_end = (continuation_id + window_size - 1).min(total_items_in_db - 1);
(new_start, new_end)
}
}
}
pub fn find_intersection_range(
old_start: i64,
old_end: i64,
new_start: i64,
new_end: i64,
) -> Option<(i64, i64)> {
let inter_start = old_start.max(new_start);
let inter_end = old_end.min(new_end);
if inter_start <= inter_end {
Some((inter_start, inter_end))
} else {
None
}
}
pub fn select_anchor_id(
intersection_start: i64,
intersection_end: i64,
direction: Direction,
) -> i64 {
match direction {
Direction::Backward => intersection_start,
Direction::Forward => intersection_end,
}
}
pub fn visible_items_preserved(
visible_start_id: i64,
visible_end_id: i64,
new_start_id: i64,
new_end_id: i64,
) -> bool {
visible_start_id >= new_start_id && visible_end_id <= new_end_id
}
pub fn calculate_buffers(
visible_start_id: i64,
visible_end_id: i64,
new_start_id: i64,
new_end_id: i64,
) -> (i64, i64) {
let above = visible_start_id - new_start_id;
let below = new_end_id - visible_end_id;
(above.max(0), below.max(0))
}