Skip to content

Commit f38ec9d

Browse files
feat(crypto-helper): table: implement table view for jwt header and payload
1 parent 8e0ff02 commit f38ec9d

File tree

6 files changed

+128
-6
lines changed

6 files changed

+128
-6
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
dist/
22
target/
33
test_keys/
4+
.idea/

public/styles/style.scss

+23-1
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,26 @@ article {
175175

176176
.action-button:focus {
177177
box-shadow: 0 0 0 2px #314157;
178-
}
178+
}
179+
180+
.table-container {
181+
background: #e0e0e0;
182+
width: 100%;
183+
display: grid;
184+
grid-template-columns: 30% auto;
185+
gap: 0.2em;
186+
}
187+
188+
.table-cell {
189+
padding: 0.2em;
190+
background: #dbcfbf;
191+
cursor: pointer;
192+
color: #50437f;
193+
border-radius: 0.2em;
194+
border: 2px solid transparent;
195+
}
196+
197+
.table-cell:hover {
198+
transition: all 0.3s;
199+
border: 2px solid #50437f;
200+
}

src/common/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ mod bytes_viewer;
33
mod checkbox;
44
mod simple_output;
55
mod switch;
6+
mod table;
67

78
pub use byte_input::{build_byte_input, ByteInput, ByteInputProps};
89
pub use bytes_viewer::{BytesViewer, BytesViewerProps};
910
pub use checkbox::{Checkbox, CheckboxProps};
1011
pub use simple_output::build_simple_output;
1112
pub use switch::{Switch, SwitchProps};
13+
pub use table::{TableView, TableViewProps};
1214
use web_sys::MouseEvent;
1315
use yew::{classes, Callback, Classes, UseStateSetter};
1416

src/common/table.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use serde_json::Value;
2+
use yew::virtual_dom::VNode;
3+
use yew::{function_component, html, Callback, Html, Properties};
4+
use yew_hooks::use_clipboard;
5+
use yew_notifications::{use_notification, Notification, NotificationType};
6+
7+
#[derive(Properties, PartialEq, Debug, Clone)]
8+
pub struct TableViewProps {
9+
pub value: Value,
10+
}
11+
12+
#[function_component(TableView)]
13+
pub fn table_view(props: &TableViewProps) -> Html {
14+
let clipboard = use_clipboard();
15+
let copy_text = Callback::from(move |text: String| clipboard.write_text(text.clone()));
16+
let notifications = use_notification::<Notification>();
17+
let spawn_notification = Callback::from(move |notification| notifications.spawn(notification));
18+
19+
html! {
20+
<div class="table-container">
21+
{build_cells(&props.value, copy_text, spawn_notification)}
22+
</div>
23+
}
24+
}
25+
26+
fn format_json_value(value: &Value, copy_text: Callback<String>, spawn_notification: Callback<Notification>) -> VNode {
27+
let data = match value {
28+
Value::Null => String::from("Null"),
29+
Value::Bool(bool) => format!("{}", bool),
30+
Value::Number(number) => format!("{}", number),
31+
Value::String(string) => string.clone(),
32+
Value::Array(array) => serde_json::to_string(array).unwrap(),
33+
Value::Object(obj) => serde_json::to_string(obj).unwrap(),
34+
};
35+
let data_to_copy = data.clone();
36+
let onclick = Callback::from(move |_| {
37+
copy_text.emit(data_to_copy.clone());
38+
spawn_notification.emit(Notification::from_description_and_type(
39+
NotificationType::Info,
40+
"Copied!",
41+
));
42+
});
43+
html! {
44+
<span class="table-cell" {onclick}>{data}</span>
45+
}
46+
}
47+
48+
fn build_cells(value: &Value, copy_text: Callback<String>, spawn_notification: Callback<Notification>) -> Vec<VNode> {
49+
if let Some(obj) = value.as_object() {
50+
obj.iter()
51+
.flat_map(|(key, value)| {
52+
let key_copy_text = copy_text.clone();
53+
let key_notification = spawn_notification.clone();
54+
let key_value = key.to_owned();
55+
let on_key_click = Callback::from(move |_| {
56+
key_copy_text.emit(key_value.clone());
57+
key_notification.emit(Notification::from_description_and_type(
58+
NotificationType::Info,
59+
"Copied!",
60+
));
61+
});
62+
vec![
63+
html! { <span class="table-cell" onclick={on_key_click}>{key}</span> },
64+
format_json_value(value, copy_text.clone(), spawn_notification.clone()),
65+
]
66+
})
67+
.collect()
68+
} else {
69+
vec![format_json_value(value, copy_text, spawn_notification)]
70+
}
71+
}

src/jwt/jwt/editor.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::fmt::Debug;
22

33
use serde_json::{to_string_pretty, Value};
44
use web_sys::{HtmlInputElement, MouseEvent};
5-
use yew::{function_component, html, Callback, Html, Properties, TargetCast};
5+
use yew::{function_component, html, use_state, Callback, Html, Properties, TargetCast};
66
use yew_hooks::use_clipboard;
77
use yew_notifications::{use_notification, Notification, NotificationType};
88

99
use super::Jwt;
10-
use crate::common::{build_simple_output, BytesFormat};
10+
use crate::common::{build_simple_output, BytesFormat, Switch, TableView};
1111
use crate::utils::copy_to_clipboard_with_notification;
1212

1313
#[derive(PartialEq, Properties)]
@@ -142,6 +142,14 @@ pub fn jwt_editor(props: &JwtEditorProps) -> Html {
142142
set_jwt.emit(jwt);
143143
});
144144

145+
// false - raw view
146+
// true - table view
147+
let header_view = use_state(|| true);
148+
let payload_view = use_state(|| true);
149+
150+
let header_view_setter = header_view.setter();
151+
let payload_view_setter = payload_view.setter();
152+
145153
let notifications = use_notification::<Notification>();
146154
let clipboard = use_clipboard();
147155

@@ -152,16 +160,34 @@ pub fn jwt_editor(props: &JwtEditorProps) -> Html {
152160
<span class="jwt-header" onclick={copy_to_clipboard_with_notification(header.clone(), clipboard.clone(), "Header", notifications.clone())}>{"Header"}</span>
153161
<button onclick={header_on_pretty} class="jwt-util-button">{"Prettify"}</button>
154162
<button onclick={header_on_minify} class="jwt-util-button">{"Minify"}</button>
163+
<div class="horizontal">
164+
<span class="total">{"raw"}</span>
165+
<Switch id={String::from("jwt-header-view")} state={*header_view} setter={Callback::from(move |view| header_view_setter.set(view))} />
166+
<span class="total">{"table"}</span>
167+
</div>
155168
</div>
156-
<textarea rows="4" class="base-input" value={header} oninput={on_header_input} />
169+
{if !*header_view {html! {
170+
<textarea rows="4" class="base-input" value={header} oninput={on_header_input} />
171+
}} else {html! {
172+
<TableView value={serde_json::from_str::<Value>(&props.jwt.parsed_header).unwrap()} />
173+
}}}
157174
</div>
158175
<div class="vertical">
159176
<div class="horizontal">
160177
<span class="jwt-payload" onclick={copy_to_clipboard_with_notification(payload.clone(), clipboard.clone(), "Payload", notifications.clone())}>{"Payload"}</span>
161178
<button onclick={payload_on_pretty} class="jwt-util-button">{"Prettify"}</button>
162179
<button onclick={payload_on_minify} class="jwt-util-button">{"Minify"}</button>
180+
<div class="horizontal">
181+
<span class="total">{"raw"}</span>
182+
<Switch id={String::from("jwt-payload-view")} state={*payload_view} setter={Callback::from(move |view| payload_view_setter.set(view))} />
183+
<span class="total">{"table"}</span>
184+
</div>
163185
</div>
164-
<textarea rows="6" class="base-input" value={payload} oninput={on_payload_input} />
186+
{if !*payload_view {html! {
187+
<textarea rows="6" class="base-input" value={payload} oninput={on_payload_input} />
188+
}} else {html! {
189+
<TableView value={serde_json::from_str::<Value>(&props.jwt.parsed_payload).unwrap()} />
190+
}}}
165191
</div>
166192
<div class="vertical">
167193
<span class="jwt-signature" onclick={copy_to_clipboard_with_notification(signature.clone(), clipboard, "Signature", notifications.clone())}>{"Signature"}</span>

src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fn switch(routes: Route) -> Html {
4545

4646
#[function_component(App)]
4747
pub fn app() -> Html {
48-
let component_creator = NotificationFactory::default();
48+
let component_creator = NotificationFactory;
4949

5050
html! {
5151
<BrowserRouter>

0 commit comments

Comments
 (0)