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 pub update_count: u32,
35 pub session_props: SessionProps,
38}
39
40#[function_component]
44pub fn StatusIndicator(props: &StatusIndicatorProps) -> Html {
45 let has_table_cells = props
46 .session_props
47 .stats
48 .as_ref()
49 .and_then(|s| s.num_table_cells)
50 .is_some();
51
52 let state = if let Some(err) = &props.session_props.error {
53 StatusIconState::Errored(
54 err.message(),
55 err.stacktrace(),
56 err.kind(),
57 err.is_reconnect(),
58 )
59 } else if !has_table_cells
60 && matches!(props.session_props.has_table, Some(TableLoadState::Loading))
61 {
62 StatusIconState::Loading
63 } else if props.update_count > 0 {
64 StatusIconState::Updating
65 } else if has_table_cells {
66 StatusIconState::Normal
67 } else {
68 StatusIconState::Uninitialized
69 };
70
71 let class_name = match &state {
72 StatusIconState::Errored(_, _, _, true) => "errored",
73 StatusIconState::Errored(_, _, _, false) => "errored disabled",
74 StatusIconState::Normal => "connected",
75 StatusIconState::Updating => "updating",
76 StatusIconState::Loading => "loading",
77 StatusIconState::Uninitialized => "uninitialized",
78 };
79
80 let onclick = use_async_callback(
81 (props.session.clone(), props.renderer.clone(), state.clone()),
82 async move |_: MouseEvent, (session, renderer, state)| {
83 match &state {
84 StatusIconState::Errored(..) => {
85 session.reconnect().await?;
86 apply_and_render(session, renderer, ViewConfigUpdate::default())?.await?;
87 },
88 StatusIconState::Normal => {
89 session.status_indicator_clicked.emit(());
90 },
91 _ => {},
92 };
93
94 Ok::<_, ApiError>(())
95 },
96 );
97
98 html! {
99 <>
100 <div class="section">
101 <div id="status_reconnect" class={class_name} {onclick}>
102 <span id="status" class={class_name} />
103 <span id="status_updating" class={class_name} />
104 </div>
105 if let StatusIconState::Errored(err, stack, kind, _) = &state {
106 <div class="error-dialog">
107 <div class="error-dialog-message">{ format!("{} {}", kind, err) }</div>
108 <div class="error-dialog-stack">{ stack }</div>
109 </div>
110 }
111 </div>
112 </>
113 }
114}
115
116#[derive(Clone, Debug, PartialEq)]
117enum StatusIconState {
118 Loading,
119 Updating,
120 Errored(String, String, &'static str, bool),
121 Normal,
122 Uninitialized,
123}