Skip to content

Commit

Permalink
use_infinite_scroll on profile page (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
PenguinWithATie authored Jul 31, 2024
1 parent 84ecd6a commit ba932de
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 156 deletions.
27 changes: 6 additions & 21 deletions apis/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
use crate::pages::profile_view::ProfileGamesView;
use crate::{
components::layouts::base_layout::BaseLayout,
pages::{
account::Account,
admin::Admin,
analysis::Analysis,
challenge_view::ChallengeView,
config::Config,
display_games::DisplayGames,
donate::Donate,
faq::Faq,
home::Home,
login::Login,
play::Play,
profile_view::{ProfileGamesView, ProfileView},
puzzles::Puzzles,
register::Register,
resources::Resources,
rules::Rules,
strategy::Strategy,
top_players::TopPlayers,
tournament::Tournament,
tournament_create::TournamentCreate,
tournaments::Tournaments,
account::Account, admin::Admin, analysis::Analysis, challenge_view::ChallengeView,
config::Config, display_games::DisplayGames, donate::Donate, faq::Faq, home::Home,
login::Login, play::Play, profile_view::ProfileView, puzzles::Puzzles, register::Register,
resources::Resources, rules::Rules, strategy::Strategy, top_players::TopPlayers,
tournament::Tournament, tournament_create::TournamentCreate, tournaments::Tournaments,
tutorial::Tutorial,
},
providers::{
Expand Down
2 changes: 1 addition & 1 deletion apis/src/components/organisms/analysis/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl AnalysisTree {
hashes,
game_type: gs.state.game_type,
};
tree.update_node(gs.history_turn.unwrap_or(0)as i32);
tree.update_node(gs.history_turn.unwrap_or(0) as i32);
Some(tree)
}

Expand Down
99 changes: 81 additions & 18 deletions apis/src/pages/display_games.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,93 @@
use html::Div;
use leptos::*;
use crate::{
functions::users::get::get_finished_games_in_batches, pages::profile_view::ProfileGamesContext,
components::molecules::game_row::GameRow,
pages::profile_view::{AllUserGames, ProfileGamesView},
components::organisms::display_profile::DisplayProfile,
};
use leptos::*;
use super::profile_view::ProfileGamesView;
use leptos_router::A;
use leptos_use::{use_infinite_scroll_with_options, UseInfiniteScrollOptions};

#[component]
pub fn DisplayGames(tab_view: ProfileGamesView) -> impl IntoView {
let all_games = expect_context::<AllUserGames>();
let games = match tab_view {
ProfileGamesView::Finished => all_games.finished,
ProfileGamesView::Playing => all_games.playing,
ProfileGamesView::Unstarted => all_games.unstarted,
let ctx = expect_context::<ProfileGamesContext>();
let el = create_node_ref::<Div>();
let active = move |view: ProfileGamesView| {
let button_style = String::from("hover:bg-pillbug-teal transform transition-transform duration-300 active:scale-95 text-white font-bold py-2 px-4 m-1 rounded");
if tab_view == view {
button_style + " bg-pillbug-teal"
} else {
button_style + " bg-button-dawn dark:bg-button-twilight"
}
};
let is_active = expect_context::<RwSignal<ProfileGamesView>>();
let elem = create_node_ref::<html::Div>();
elem.on_load(move |_| is_active.update(|v| *v = tab_view));
let username = store_value(ctx.user.username.clone());
let _ = use_infinite_scroll_with_options(
el,
move |_| async move {
if tab_view == ProfileGamesView::Finished && ctx.more_finished.get() {
let games = get_finished_games_in_batches(
username(),
ctx.finished_last_timestamp.get(),
ctx.finished_last_id.get(),
5,
)
.await;
if let Ok((g, are_more)) = games {
ctx.finished_last_timestamp
.update(|v| *v = g.last().map(|gr| gr.updated_at));
ctx.finished_last_id
.update(|v| *v = g.last().map(|gr| gr.uuid));
ctx.more_finished.set(are_more);
ctx.finished.update(|v| v.extend(g.clone()));
}
}
},
UseInfiniteScrollOptions::default().distance(10.0),
);
view! {
<div ref=elem class="flex flex-col items-center w-full">
<For
each=games
<div class="flex flex-col w-full">
<DisplayProfile user=store_value(ctx.user.clone())/>
<div class="flex gap-1 ml-3">
<Show when=move || !ctx.unstarted.get().is_empty()>
<A
href=format!("/@/{}/unstarted", username())
class=move || active(ProfileGamesView::Unstarted)
>
"Unstarted Tournament Games"
</A>
</Show>
<Show when=move || !ctx.playing.get().is_empty()>
<A
href=format!("/@/{}/playing", username())
class=move || active(ProfileGamesView::Playing)
>
"Playing "
</A>
</Show>
<Show when=move || !ctx.finished.get().is_empty()>
<A
href=format!("/@/{}/finished", username())
class=move || active(ProfileGamesView::Finished)
>
"Finished Games "
</A>
</Show>
</div>
<div node_ref=el class="flex flex-col overflow-x-hidden items-center h-[72vh]">
<For
each=move || match tab_view {
ProfileGamesView::Finished => ctx.finished.get(),
ProfileGamesView::Playing => ctx.playing.get(),
ProfileGamesView::Unstarted => ctx.unstarted.get(),
}

key=|game| (game.uuid)
let:game
>
<GameRow game=store_value(game)/>
</For>
key=|game| (game.uuid)
let:game
>
<GameRow game=store_value(game)/>
</For>
</div>
</div>
}
}
157 changes: 41 additions & 116 deletions apis/src/pages/profile_view.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
use crate::{
components::organisms::display_profile::DisplayProfile,
functions::users::get::{
get_finished_games_in_batches, get_ongoing_games, get_user_by_username,
},
responses::GameResponse,
use crate::functions::users::get::{
get_finished_games_in_batches, get_ongoing_games, get_user_by_username,
};
use crate::responses::GameResponse;
use crate::responses::UserResponse;
use chrono::{DateTime, Utc};
use hive_lib::GameStatus;
use leptos::{ev::scroll, *};
use leptos::*;
use leptos_router::*;
use leptos_use::{use_document, use_event_listener, use_throttle_fn, use_window};
use shared_types::GameStart;
use uuid::Uuid;

#[derive(Params, PartialEq, Eq)]
struct UsernameParams {
username: String,
}

#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Copy)]
pub enum ProfileGamesView {
Unstarted,
Playing,
Finished,
}

#[derive(Debug, Clone)]
pub struct AllUserGames {
pub struct ProfileGamesContext {
pub unstarted: RwSignal<Vec<GameResponse>>,
pub playing: RwSignal<Vec<GameResponse>>,
pub finished: RwSignal<Vec<GameResponse>>,
pub more_finished: RwSignal<bool>,
pub finished_last_timestamp: RwSignal<Option<DateTime<Utc>>>,
pub finished_last_id: RwSignal<Option<Uuid>>,
pub user: UserResponse,
}

#[component]
pub fn ProfileView(children: ChildrenFn) -> impl IntoView {
let params = use_params::<UsernameParams>();
//TODO: @ion fix finished.clear()
let finished: RwSignal<Vec<GameResponse>> = RwSignal::new(Vec::new());
let username = move || {
params.with(|params| {
params
Expand All @@ -44,53 +45,23 @@ pub fn ProfileView(children: ChildrenFn) -> impl IntoView {
})
};
let user = Resource::new(username, move |_| get_user_by_username(username()));
let get_more = RwSignal::new(0);
let last_timestamp = RwSignal::new(None);
let last_id = RwSignal::new(None);
let finished_games = Resource::new(
move || (username(), get_more()),
move |_| {
get_finished_games_in_batches(
username(),
last_timestamp.get_untracked(),
last_id.get_untracked(),
3,
)
},
);
let ongoing_games = Resource::new(username, move |_| get_ongoing_games(username()));
let stored_children = store_value(children);
let tab_view = create_rw_signal(ProfileGamesView::Playing);
let active = move |view: ProfileGamesView| {
let button_style = String::from("hover:bg-pillbug-teal transform transition-transform duration-300 active:scale-95 text-white font-bold py-2 px-4 m-1 rounded");
if tab_view() == view {
button_style + " bg-pillbug-teal"
} else {
button_style + " bg-button-dawn dark:bg-button-twilight"
}
};
let still_more_games = RwSignal::from(true);
let throttled_more_games = use_throttle_fn(
move || {
if tab_view.get_untracked() == ProfileGamesView::Finished
&& is_end_of_page()
&& still_more_games.get_untracked()
{
get_more.update(|v| *v += 1)
}
},
500.0,
);
provide_context(tab_view);
_ = use_event_listener(use_window(), scroll, move |_| {
throttled_more_games();
let children = store_value(children);
let first_batch_finished = Resource::new(username, move |_| {
get_finished_games_in_batches(
username(),
last_timestamp.get_untracked(),
last_id.get_untracked(),
5,
)
});

let ongoing_games = Resource::new(username, move |_| get_ongoing_games(username()));
view! {
<div class="flex flex-col pt-12 bg-light dark:bg-gray-950">
<Transition>
<Suspense>
{move || {
let (current_finished_games, more_games) = finished_games()
let (first_batch, more_finished) = first_batch_finished()
.and_then(|games| games.ok())
.unwrap_or((Vec::new(), false));
let mut ongoing_games = ongoing_games()
Expand All @@ -108,78 +79,32 @@ pub fn ProfileView(children: ChildrenFn) -> impl IntoView {
true
}
});
finished.update(move |v| v.extend(current_finished_games));
still_more_games.set(more_games);
let playing = RwSignal::from(ongoing_games);
let unstarted = RwSignal::from(unstarted);
last_id.update(move |v| { *v = finished().last().map(|gr| gr.uuid) });
let first_batch = store_value(first_batch);
let playing = RwSignal::new(ongoing_games);
let unstarted = RwSignal::new(unstarted);
let finished = RwSignal::new(first_batch());
last_id.update(move |v| { *v = first_batch().last().map(|gr| gr.uuid) });
last_timestamp
.update(move |v| { *v = finished().last().map(|gr| gr.updated_at) });
provide_context(AllUserGames {
unstarted,
finished,
playing,
});
.update(move |v| { *v = first_batch().last().map(|gr| gr.updated_at) });
user()
.map(|data| match data {
Err(_) => view! { <pre>"Page not found"</pre> }.into_view(),
Ok(user) => {
view! {
<DisplayProfile user=store_value(user)/>
<div class="flex gap-1 ml-3">
<Show when=move || !unstarted().is_empty()>
<A
href="unstarted"
class=move || active(ProfileGamesView::Unstarted)
on:click=move |_| finished.update(|v| v.clear())
>
"Unstarted Tournament Games"
</A>
</Show>
<Show when=move || !playing().is_empty()>
<A
href="playing"
class=move || active(ProfileGamesView::Playing)
on:click=move |_| finished.update(|v| v.clear())
>
"Playing "
</A>
</Show>
<Show when=move || !finished().is_empty()>
<A
href="finished"
class=move || active(ProfileGamesView::Finished)
on:click=move |_| finished.update(|v| v.clear())
>
"Finished Games "
</A>
</Show>
</div>
{stored_children()()}
<Show when=finished_games.loading()>
<div class="place-self-center p-4 w-5 h-5 rounded-full border-t-2 border-b-2 border-blue-500 animate-spin"></div>
</Show>
}
.into_view()
provide_context(ProfileGamesContext {
unstarted,
finished,
playing,
finished_last_timestamp: last_timestamp,
finished_last_id: last_id,
more_finished: RwSignal::new(more_finished),
user,
});
children()().into_view()
}
})
}}

</Transition>
</Suspense>
</div>
}
}

fn is_end_of_page() -> bool {
let document = use_document();
const OFFSET_PX: f64 = 200.0;
let inner_height = window()
.inner_height()
.expect("window")
.as_f64()
.expect("Converted to f64");
let page_y_offset = window().page_y_offset().expect("window again");
let body_offset_height = document.body().expect("Body").offset_height() as f64;

inner_height + page_y_offset >= body_offset_height - OFFSET_PX
}

0 comments on commit ba932de

Please sign in to comment.