Skip to content

Commit

Permalink
Added pagination to player profile tournament list, and player list o…
Browse files Browse the repository at this point in the history
…n search (#76)

* added pagination

Signed-off-by: danbugs <[email protected]>

* fix && fmt

Signed-off-by: danbugs <[email protected]>

---------

Signed-off-by: danbugs <[email protected]>
  • Loading branch information
danbugs authored Jan 4, 2024
1 parent cb54398 commit 46daa9c
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 7 deletions.
94 changes: 89 additions & 5 deletions frontend/src/components/player_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use gloo_net::http::Request;
use yew::{function_component, html, use_effect_with, use_state, Callback, Html, Properties};
use yew_router::hooks::use_navigator;

use crate::{components::loading_spinner::LoadingSpinner, models::Player};
use crate::{
components::loading_spinner::LoadingSpinner, models::Player, utils::create_page_numbers,
};

const PAGE_SIZE: usize = 10; // Number of items per page

#[derive(Properties, PartialEq)]
pub struct Props {
Expand All @@ -19,9 +23,19 @@ pub fn player_list(props: &Props) -> Html {
let loading = use_state(|| false);
let search_results = use_state(|| Vec::<Player>::new());

let current_page = use_state(|| 1);
let total_pages = use_state(|| 0);
let start_index = use_state(|| 0);
let end_index = use_state(|| 0);

let paginated_results = use_state(|| Vec::<Player>::new());

{
let search_results = search_results.clone();
let loading = loading.clone();
let total_pages = total_pages.clone();
let end_index = end_index.clone();
let paginated_results = paginated_results.clone();

use_effect_with(gamer_tag.clone(), move |_| {
wasm_bindgen_futures::spawn_local(async move {
Expand All @@ -38,13 +52,46 @@ pub fn player_list(props: &Props) -> Html {

fetched_players.sort_by_key(|e| e.player_id);

search_results.set(fetched_players);
total_pages.set((fetched_players.len() as f32 / PAGE_SIZE as f32).ceil() as usize);
search_results.set(fetched_players.clone());

let end = usize::min(PAGE_SIZE, fetched_players.len());

end_index.set(end);

paginated_results.set(fetched_players[0..end].to_vec());

loading.set(false);
});
});
}

{
let start_index = start_index.clone();
let end_index = end_index.clone();
let paginated_results = paginated_results.clone();
let current_page = current_page.clone();
let search_results = search_results.clone();

use_effect_with(current_page.clone(), move |_| {
let start = (*current_page - 1) * PAGE_SIZE;
let end = usize::min(start + PAGE_SIZE, (*search_results).len());

start_index.set(start);
end_index.set(end);

let search_results = search_results.clone();
let paginated_results = paginated_results.clone();

paginated_results.set((*search_results)[start..end].to_vec());
});
}

let curr_page = (*current_page).clone();
let tot_pages = (*total_pages).clone();

let pagination_numbers = create_page_numbers(curr_page, tot_pages);

html! {
<>
if *loading {
Expand All @@ -54,7 +101,7 @@ pub fn player_list(props: &Props) -> Html {
<LoadingSpinner/>
</div>
} else {
if (*search_results).is_empty() {
if (*paginated_results).is_empty() {
<div class="text-center" style="color:#C6263E">
<br/>
<br/>
Expand All @@ -66,7 +113,7 @@ pub fn player_list(props: &Props) -> Html {
<br/>
<ul class="list-group list-group-hover list-group-striped">
{
(*search_results).iter().map(|p| {
(*paginated_results).iter().map(|p| {
let player_id = p.player_id;
let navigator = navigator.clone();
let onclick = Callback::from(move |_| {
Expand Down Expand Up @@ -110,7 +157,44 @@ pub fn player_list(props: &Props) -> Html {
}
}).collect::<Html>()
}
</ul>
</ul>
<br/>
<nav>
<ul class="pagination justify-content-center">
<li class={if *current_page == 1 { "page-item disabled" } else { "page-item" }}>
<a class="page-link" href="#" onclick={
let current_page = current_page.clone();
Callback::from(move |_| current_page.set(usize::max(1, *current_page - 1)))
}>{"Previous"}</a>
</li>
{
for pagination_numbers.iter().map(|&num| {
if num == 0 {
html! { <li class="page-item disabled"><span class="page-link">{"..."}</span></li> }
} else {
let is_active = num == *current_page;
html! {
<li class={if is_active { "page-item active" } else { "page-item" }}>
<a class="page-link" href="#"
onclick={
let current_page = current_page.clone();
Callback::from(move |_| current_page.set(num))}>
{ num.to_string() }
</a>
</li>
}
}
})
}
<li class={if *current_page == *total_pages { "page-item disabled" } else { "page-item" }}>
<a class="page-link" href="#" onclick={
let current_page = current_page.clone();
Callback::from(move |_| current_page.set(usize::min(*total_pages, *current_page + 1)))
}>{"Next"}</a>
</li>
</ul>
</nav>

<br/>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use yew::{function_component, html, use_effect_with, use_state, Callback, Html,
use crate::models::{CaptchaRequest, ImageData, Set, Tournament, UploadResponse};

use crate::components::loading_spinner::LoadingSpinner;
use crate::utils::calculate_spr_or_uf;
use crate::utils::{calculate_spr_or_uf, create_page_numbers};
use wasm_bindgen::prelude::*;
use yew_recaptcha_v3::recaptcha::use_recaptcha;

const RECAPTCHA_SITE_KEY: &str = std::env!("RECAPTCHA_SITE_KEY");
const TOURNAMENT_PAGE_SIZE: usize = 5;

#[derive(Properties, PartialEq)]
pub struct Props {
Expand All @@ -27,6 +28,69 @@ extern "C" {

#[function_component(PlayerProfileTournamentList)]
pub fn player_profile_tournament_list(props: &Props) -> Html {
let tournament_list_current_page = use_state(|| 1);
let tournament_list_total_pages = use_state(|| 0);
let tournament_list_start_index = use_state(|| 0);
let tournament_list_end_index = use_state(|| 0);

let paginated_tournaments = use_state(Vec::<Tournament>::new);

// Use effect that updates pagination state when selected_player_tournaments changes
{
let selected_player_tournaments = props.selected_player_tournaments.clone();
let tournament_list_total_pages = tournament_list_total_pages.clone();
let tournament_list_end_index = tournament_list_end_index.clone();
let paginated_tournaments = paginated_tournaments.clone();

use_effect_with(props.selected_player_tournaments.clone(), move |_| {
// Calculate total pages
let total_pages = selected_player_tournaments
.as_ref()
.map_or(0, |tournaments| {
(tournaments.len() as f32 / TOURNAMENT_PAGE_SIZE as f32).ceil() as usize
});

// Update total pages state
tournament_list_total_pages.set(total_pages);

// Calculate paginated tournaments
if let Some(tournaments) = selected_player_tournaments.as_ref() {
let end = usize::min(TOURNAMENT_PAGE_SIZE, tournaments.len());
tournament_list_end_index.set(end);
paginated_tournaments.set(tournaments[0..end].to_vec());
}
});
}

{
let tournament_list_start_index = tournament_list_start_index.clone();
let tournament_list_end_index = tournament_list_end_index.clone();
let paginated_tournaments = paginated_tournaments.clone();
let tournament_list_current_page = tournament_list_current_page.clone();
let selected_player_tournaments = props.selected_player_tournaments.clone();

use_effect_with(tournament_list_current_page.clone(), move |_| {
if let Some(spt) = selected_player_tournaments {
let start = (*tournament_list_current_page - 1) * TOURNAMENT_PAGE_SIZE;
let end = usize::min(start + TOURNAMENT_PAGE_SIZE, spt.len());

tournament_list_start_index.set(start);
tournament_list_end_index.set(end);

let spt = spt.clone();
let paginated_tournaments = paginated_tournaments.clone();

paginated_tournaments.set(spt[start..end].to_vec());
}
});
}

let tournament_list_curr_page = (*tournament_list_current_page).clone();
let tournament_list_tot_pages = (*tournament_list_total_pages).clone();

let tournament_list_pagination_numbers =
create_page_numbers(tournament_list_curr_page, tournament_list_tot_pages);

let is_screenshotting = use_state(|| None::<i32>);

let last_token = use_state(|| None);
Expand Down Expand Up @@ -148,7 +212,7 @@ pub fn player_profile_tournament_list(props: &Props) -> Html {
<div>
<div class="accordion" id="accordion">
{
props.selected_player_tournaments.as_ref().unwrap().iter().map(|t| {
paginated_tournaments.iter().map(|t| {
let onclick = {
let last_token = last_token.clone();
let on_execute = on_execute.clone();
Expand Down Expand Up @@ -311,6 +375,46 @@ pub fn player_profile_tournament_list(props: &Props) -> Html {
</div>
<br/>
<br/>
<nav>
<ul class="pagination justify-content-center">
<li class={if *tournament_list_current_page == 1 { "page-item disabled" } else { "page-item" }}>
<a class="page-link" href="#"
onclick={
let tournament_list_current_page = tournament_list_current_page.clone();
Callback::from(move |_| tournament_list_current_page.set(usize::max(1, *tournament_list_current_page - 1)))
}>{"Previous"}</a>
</li>
{
for tournament_list_pagination_numbers.iter().map(|&num| {
if num == 0 {
html! { <li class="page-item disabled"><span class="page-link">{"..."}</span></li> }
} else {
let is_active = num == *tournament_list_current_page;
html! {
<li class={if is_active { "page-item active" } else { "page-item" }}>
<a class="page-link" href="#"
onclick={
let tournament_list_current_page = tournament_list_current_page.clone();
Callback::from(move |_| tournament_list_current_page.set(num))}>
{ num.to_string() }
</a>
</li>
}
}
})
}
<li class={if *tournament_list_current_page == *tournament_list_total_pages { "page-item disabled" } else { "page-item" }}>
<a class="page-link" href="#"
onclick={
let tournament_list_current_page = tournament_list_current_page.clone();
Callback::from(move |_| tournament_list_current_page.set(usize::min(*tournament_list_total_pages, *tournament_list_current_page + 1)))
}
>{"Next"}</a>
</li>
</ul>
</nav>
<br/>
<br/>
</div>
}
}
Expand Down
48 changes: 48 additions & 0 deletions frontend/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,51 @@ pub fn parse_text_vector(text: &str) -> Vec<String> {
result.push(current);
result
}

pub fn create_page_numbers(curr_page: usize, tot_pages: usize) -> Vec<usize> {
let mut numbers = Vec::new();

// Check the viewport width using JavaScript
let window_size = if is_mobile_viewport() { 3 } else { 5 };
let half_window = window_size / 2;

// Calculate the start and end of the pagination window
let start = if curr_page > half_window {
curr_page.saturating_sub(half_window)
} else {
1
};
let end = usize::min(start + window_size - 1, tot_pages);

// Push your pagination numbers, handling the ellipsis as needed
if start > 1 {
numbers.push(1);
if start > 2 {
numbers.push(0); // 0 for ellipsis
}
}

numbers.extend(start..=end);

if end < tot_pages {
if end < tot_pages - 1 {
numbers.push(0); // 0 for ellipsis
}
numbers.push(tot_pages);
}

numbers
}

// Helper function to determine if the viewport is mobile-sized
fn is_mobile_viewport() -> bool {
// Using web_sys to check the inner width of the window
if let Some(window) = web_sys::window() {
if let Ok(width) = window.inner_width() {
if let Some(width) = width.as_f64() {
return width <= 768.0; // Use a mobile viewport width threshold, e.g., 768px
}
}
}
false
}
12 changes: 12 additions & 0 deletions frontend/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,15 @@ ul.list-group.list-group-hover button:hover{
.twitter-footer {
font-size: 15px; /* Slightly reduced font size */
}

.grecaptcha-badge {
z-index: 1000; /* Set z-index to a high value */
position: relative; /* Make sure it's positioned relative or absolute */
}

footer {
z-index: 10; /* Lower z-index than the reCAPTCHA */
position: fixed; /* Or use 'absolute' if 'fixed' is not necessary */
bottom: 0;
width: 100%;
}

0 comments on commit 46daa9c

Please sign in to comment.