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