perspective_viewer/components/
status_indicator.rs1use perspective_client::config::ViewConfigUpdate;
14use perspective_js::utils::ApiError;
15use web_sys::*;
16use yew::prelude::*;
17
18use crate::renderer::Renderer;
19use crate::session::{Session, SessionProps, TableLoadState};
20use crate::tasks::apply_and_render;
21use crate::utils::*;
22
23#[derive(PartialEq, Properties)]
28pub struct StatusIndicatorProps {
29 pub renderer: Renderer,
30 pub session: Session,
31
32 pub update_count: u32,
34 pub session_props: SessionProps,
35}
36
37#[function_component]
41pub fn StatusIndicator(props: &StatusIndicatorProps) -> Html {
42 let has_table_cells = props.session_props.has_table_cells;
43 let state = if let Some(err) = &props.session_props.error {
44 StatusIconState::Errored(
45 err.message(),
46 err.stacktrace(),
47 err.kind(),
48 err.is_reconnect(),
49 )
50 } else if !has_table_cells
51 && matches!(props.session_props.has_table, Some(TableLoadState::Loading))
52 {
53 StatusIconState::Loading
54 } else if matches!(props.session_props.has_table, Some(TableLoadState::Pending)) {
55 StatusIconState::Pending
56 } else if props.update_count > 0 {
57 StatusIconState::Updating
58 } else if has_table_cells {
59 StatusIconState::Normal
60 } else {
61 StatusIconState::Uninitialized
62 };
63
64 let class_name = match &state {
65 StatusIconState::Errored(_, _, _, true) => "errored",
66 StatusIconState::Errored(_, _, _, false) => "errored disabled",
67 StatusIconState::Normal => "connected",
68 StatusIconState::Updating => "updating",
69 StatusIconState::Loading => "loading",
70 StatusIconState::Pending => "pending",
71 StatusIconState::Uninitialized => "uninitialized",
72 };
73
74 let onclick = use_async_callback(
75 (props.session.clone(), props.renderer.clone(), state.clone()),
76 async move |_: MouseEvent, (session, renderer, state)| {
77 match &state {
78 StatusIconState::Errored(..) => {
79 session.reconnect().await?;
80 apply_and_render(session, renderer, ViewConfigUpdate::default())?.await?;
81 },
82 StatusIconState::Normal => {
83 session.status_indicator_clicked.emit(());
84 },
85 _ => {},
86 };
87
88 Ok::<_, ApiError>(())
89 },
90 );
91
92 html! {
93 <>
94 <div class="section">
95 <div id="status_reconnect" class={class_name} {onclick}>
96 <span id="status" class={class_name} />
97 <span id="status_updating" class={class_name} />
98 </div>
99 if let StatusIconState::Errored(err, stack, kind, _) = &state {
100 <div class="error-dialog">
101 <div class="error-dialog-message">{ format!("{} {}", kind, err) }</div>
102 <div class="error-dialog-stack">{ stack }</div>
103 </div>
104 }
105 </div>
106 </>
107 }
108}
109
110#[derive(Clone, Debug, PartialEq)]
111enum StatusIconState {
112 Loading,
113 Pending,
114 Updating,
115 Errored(String, String, &'static str, bool),
116 Normal,
117 Uninitialized,
118}