Skip to content

Commit

Permalink
feat: show loading icon when retrieving list of nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
bochaco committed Dec 15, 2024
1 parent cf41ce4 commit 717fadb
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 31 deletions.
13 changes: 7 additions & 6 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl ImmutableNodeStatus {
#[derive(Clone, Copy, Debug)]
pub struct ClientGlobalState {
// List of nodes instances and their info/state
pub nodes: RwSignal<HashMap<String, RwSignal<NodeInstanceInfo>>>,
pub nodes: RwSignal<(bool, HashMap<String, RwSignal<NodeInstanceInfo>>)>,
// Flag to enable/disable nodes' logs stream
pub logs_stream_on_for: RwSignal<Option<ContainerId>>,
// Flag to enable/disable nodes' metrics charts update
Expand Down Expand Up @@ -209,7 +209,7 @@ pub fn App() -> impl IntoView {

// Provide context to manage all client side states that need to be used globally
provide_context(ClientGlobalState {
nodes: RwSignal::new(HashMap::default()),
nodes: RwSignal::new((false, HashMap::default())),
logs_stream_on_for: RwSignal::new(None),
metrics_update_on_for: RwSignal::new(None),
latest_bin_version: RwSignal::new(None),
Expand Down Expand Up @@ -282,14 +282,15 @@ fn spawn_nodes_list_polling() {
.update(|b| *b = info.batch_in_progress);

// first let's get rid of those removed remotely
context.nodes.update(|cx_nodes| {
context.nodes.update(|(loaded, cx_nodes)| {
*loaded = true;
cx_nodes.retain(|id, node_info| {
node_info.read_untracked().status.is_creating()
|| info.nodes.contains_key(id)
})
});
// let's now update those with new values
context.nodes.with_untracked(|cx_nodes| {
context.nodes.with_untracked(|(_, cx_nodes)| {
for (id, cn) in cx_nodes {
if let Some(updated) = info.nodes.get(id) {
if cn.read_untracked() != *updated {
Expand All @@ -301,9 +302,9 @@ fn spawn_nodes_list_polling() {
// we can add any new node created remotely, perhaps by another instance of the app
info.nodes
.into_iter()
.filter(|(id, _)| !context.nodes.read_untracked().contains_key(id))
.filter(|(id, _)| !context.nodes.read_untracked().1.contains_key(id))
.for_each(|(id, new_node)| {
context.nodes.update(|nodes| {
context.nodes.update(|(_, nodes)| {
let _ = nodes.insert(id.clone(), RwSignal::new(new_node));
})
});
Expand Down
14 changes: 9 additions & 5 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ pub async fn add_node_instances(
..Default::default()
};
context.nodes.update(|items| {
items.insert(tmp_container_id.clone(), RwSignal::new(tmp_container));
items
.1
.insert(tmp_container_id.clone(), RwSignal::new(tmp_container));
});

if count > 1 {
Expand All @@ -65,7 +67,7 @@ pub async fn add_node_instances(
)
.await?;
context.nodes.update(|items| {
items.remove(&tmp_container_id);
items.1.remove(&tmp_container_id);
});
context.batch_in_progress.update(|info| {
if let Some(b) = info {
Expand All @@ -82,8 +84,10 @@ pub async fn add_node_instances(
} else {
let node_info = create_node_instance(port, metrics_port, rewards_addr, auto_start).await?;
context.nodes.update(|items| {
items.remove(&tmp_container_id);
items.insert(node_info.container_id.clone(), RwSignal::new(node_info));
items.1.remove(&tmp_container_id);
items
.1
.insert(node_info.container_id.clone(), RwSignal::new(node_info));
});
};

Expand All @@ -97,7 +101,7 @@ pub async fn remove_node_instance(container_id: ContainerId) -> Result<(), Serve
delete_node_instance(container_id.clone()).await?;

context.nodes.update(|nodes| {
nodes.remove(&container_id);
nodes.1.remove(&container_id);
});

Ok(())
Expand Down
6 changes: 4 additions & 2 deletions src/node_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ pub fn NodesActionsView() -> impl IntoView {
context
.nodes
.read()
.1
.keys()
.for_each(|id| {
selected.insert(id.clone());
Expand All @@ -175,7 +176,7 @@ pub fn NodesActionsView() -> impl IntoView {
class=move || {
if is_selecting_nodes() {
"hidden"
} else if context.nodes.read().is_empty() {
} else if context.nodes.read().1.is_empty() {
"btn-disabled btn-manage-nodes-action"
} else {
"btn-manage-nodes-action"
Expand Down Expand Up @@ -204,7 +205,7 @@ pub fn NodesActionsView() -> impl IntoView {
class=move || {
if is_selecting_nodes() {
"hidden"
} else if context.nodes.read().is_empty() {
} else if context.nodes.read().1.is_empty() {
"btn-disabled btn-manage-nodes-action"
} else {
"btn-manage-nodes-action"
Expand Down Expand Up @@ -620,6 +621,7 @@ fn ActionsOnSelected(show_actions_menu: RwSignal<bool>) -> impl IntoView {
let nodes = context
.nodes
.read_untracked()
.1
.values()
.filter(|n| selected.contains(&n.read_untracked().container_id))
.cloned()
Expand Down
45 changes: 28 additions & 17 deletions src/nodes_list_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,48 @@ use leptos::{logging, prelude::*, task::spawn_local};

#[component]
pub fn NodesListView() -> impl IntoView {
// we use the context to switch on/off the streaming of logs
let context = expect_context::<ClientGlobalState>();
// this signal keeps the reactive list of log entries
let (logs, set_logs) = signal(Vec::new());
let (chart_data, set_chart_data) = signal((vec![], vec![]));
let (is_render_chart, set_render_chart) = signal(false);

// we display the instances sorted by creation time, newest to oldest
// we display the instances sorted with the currently selected strategy
let sorted_nodes = Memo::new(move |_| {
let mut sorted = context.nodes.get().into_iter().collect::<Vec<_>>();
let mut sorted = context.nodes.get().1.into_iter().collect::<Vec<_>>();
context.nodes_sort_strategy.read().sort_items(&mut sorted);
sorted
});

view! {
<div class="flex flex-wrap">
<BatchInProgressView batch_info=context.batch_in_progress />
<Show
when=move || context.nodes.read().0
fallback=move || {
view! {
<div class="text-center mt-12">
<span class="loading loading-bars loading-lg">Loading...</span>
</div>
}
}
>

<For
each=move || sorted_nodes.get()
key=|(container_id, _)| container_id.clone()
let:child
>
<Show
when=move || !child.1.read().status.is_creating()
fallback=move || { view! { <CreatingNodeInstanceView /> }.into_view() }
<div class="flex flex-wrap">
<BatchInProgressView batch_info=context.batch_in_progress />

<For
each=move || sorted_nodes.get()
key=|(container_id, _)| container_id.clone()
let:child
>
<NodeInstanceView info=child.1 set_logs set_render_chart set_chart_data />
</Show>
</For>
</div>
<Show
when=move || !child.1.read().status.is_creating()
fallback=move || { view! { <CreatingNodeInstanceView /> }.into_view() }
>
<NodeInstanceView info=child.1 set_logs set_render_chart set_chart_data />
</Show>
</For>
</div>
</Show>

<input type="checkbox" id="logs_stream_modal" class="modal-toggle" />
<div class="modal" role="dialog">
Expand Down
12 changes: 11 additions & 1 deletion src/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ use std::collections::HashMap;
pub fn AggregatedStatsView() -> impl IntoView {
let context = expect_context::<ClientGlobalState>();

let total_nodes = move || context.nodes.read().len();
let total_nodes = move || context.nodes.read().1.len();
let active_nodes = move || {
context
.nodes
.read()
.1
.values()
.filter(|n| n.read().status.is_active())
.count()
Expand All @@ -21,6 +22,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.filter(|n| n.read().status.is_inactive())
.count()
Expand All @@ -30,6 +32,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
let seen = context
.nodes
.read()
.1
.values()
.filter_map(|n| {
n.read()
Expand All @@ -51,6 +54,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.map(|n| n.read().connected_peers.unwrap_or_default())
.sum::<usize>()
Expand All @@ -59,6 +63,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.map(|n| n.read().shunned_count.unwrap_or_default())
.sum::<usize>()
Expand All @@ -67,6 +72,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
let weighted_estimations = context
.nodes
.read()
.1
.values()
.filter(|n| n.read().status.is_active())
.map(|n| {
Expand All @@ -76,6 +82,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
let weights = context
.nodes
.read()
.1
.values()
.filter(|n| n.read().status.is_active())
.map(|n| n.read().connected_peers.unwrap_or_default())
Expand All @@ -91,6 +98,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.map(|n| {
if n.read().status.is_active() {
Expand All @@ -105,6 +113,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.map(|n| {
if n.read().status.is_inactive() {
Expand All @@ -119,6 +128,7 @@ pub fn AggregatedStatsView() -> impl IntoView {
context
.nodes
.read()
.1
.values()
.map(|n| {
if n.read().status.is_active() {
Expand Down

0 comments on commit 717fadb

Please sign in to comment.