Skip to content

Commit

Permalink
Merge pull request #48 from TheBestTvarynka/jwt-table-view
Browse files Browse the repository at this point in the history
JWT table view for header and payload
  • Loading branch information
TheBestTvarynka authored Dec 8, 2023
2 parents be43326 + 6d66c87 commit dad5154
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
target/
test_keys/
.idea/
24 changes: 23 additions & 1 deletion public/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,26 @@ article {

.action-button:focus {
box-shadow: 0 0 0 2px #314157;
}
}

.table-container {
background: #e0e0e0;
width: 100%;
display: grid;
grid-template-columns: 30% auto;
gap: 0.2em;
}

.table-cell {
padding: 0.2em;
background: #dbcfbf;
cursor: pointer;
color: #50437f;
border-radius: 0.2em;
border: 2px solid transparent;
}

.table-cell:hover {
transition: all 0.3s;
border: 2px solid #50437f;
}
2 changes: 2 additions & 0 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ mod bytes_viewer;
mod checkbox;
mod simple_output;
mod switch;
mod table;

pub use byte_input::{build_byte_input, ByteInput, ByteInputProps};
pub use bytes_viewer::{BytesViewer, BytesViewerProps};
pub use checkbox::{Checkbox, CheckboxProps};
pub use simple_output::build_simple_output;
pub use switch::{Switch, SwitchProps};
pub use table::{TableView, TableViewProps};
use web_sys::MouseEvent;
use yew::{classes, Callback, Classes, UseStateSetter};

Expand Down
71 changes: 71 additions & 0 deletions src/common/table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use serde_json::Value;
use yew::virtual_dom::VNode;
use yew::{function_component, html, Callback, Html, Properties};
use yew_hooks::use_clipboard;
use yew_notifications::{use_notification, Notification, NotificationType};

#[derive(Properties, PartialEq, Debug, Clone)]
pub struct TableViewProps {
pub value: Value,
}

#[function_component(TableView)]
pub fn table_view(props: &TableViewProps) -> Html {
let clipboard = use_clipboard();
let copy_text = Callback::from(move |text: String| clipboard.write_text(text.clone()));
let notifications = use_notification::<Notification>();
let spawn_notification = Callback::from(move |notification| notifications.spawn(notification));

html! {
<div class="table-container">
{build_cells(&props.value, copy_text, spawn_notification)}
</div>
}
}

fn format_json_value(value: &Value, copy_text: Callback<String>, spawn_notification: Callback<Notification>) -> VNode {
let data = match value {
Value::Null => String::from("Null"),
Value::Bool(bool) => format!("{}", bool),
Value::Number(number) => format!("{}", number),
Value::String(string) => string.clone(),
Value::Array(array) => serde_json::to_string(array).unwrap(),
Value::Object(obj) => serde_json::to_string(obj).unwrap(),
};
let data_to_copy = data.clone();
let onclick = Callback::from(move |_| {
copy_text.emit(data_to_copy.clone());
spawn_notification.emit(Notification::from_description_and_type(
NotificationType::Info,
"Copied!",
));
});
html! {
<span class="table-cell" {onclick}>{data}</span>
}
}

fn build_cells(value: &Value, copy_text: Callback<String>, spawn_notification: Callback<Notification>) -> Vec<VNode> {
if let Some(obj) = value.as_object() {
obj.iter()
.flat_map(|(key, value)| {
let key_copy_text = copy_text.clone();
let key_notification = spawn_notification.clone();
let key_value = key.to_owned();
let on_key_click = Callback::from(move |_| {
key_copy_text.emit(key_value.clone());
key_notification.emit(Notification::from_description_and_type(
NotificationType::Info,
"Copied!",
));
});
vec![
html! { <span class="table-cell" onclick={on_key_click}>{key}</span> },
format_json_value(value, copy_text.clone(), spawn_notification.clone()),
]
})
.collect()
} else {
vec![format_json_value(value, copy_text, spawn_notification)]
}
}
58 changes: 54 additions & 4 deletions src/jwt/jwt/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use std::fmt::Debug;

use serde_json::{to_string_pretty, Value};
use web_sys::{HtmlInputElement, MouseEvent};
use yew::{function_component, html, Callback, Html, Properties, TargetCast};
use yew::{function_component, html, use_state, Callback, Html, Properties, TargetCast};
use yew_hooks::use_clipboard;
use yew_notifications::{use_notification, Notification, NotificationType};

use super::Jwt;
use crate::common::{build_simple_output, BytesFormat};
use crate::common::{build_simple_output, BytesFormat, Switch, TableView};
use crate::utils::copy_to_clipboard_with_notification;

#[derive(PartialEq, Properties)]
Expand Down Expand Up @@ -55,6 +55,32 @@ fn format_json<E: Debug>(
})
}

#[derive(Clone, Copy, Debug, Default)]
enum JsonView {
Raw,
#[default]
Table,
}

impl From<bool> for JsonView {
fn from(value: bool) -> Self {
if value {
JsonView::Table
} else {
JsonView::Raw
}
}
}

impl From<JsonView> for bool {
fn from(value: JsonView) -> Self {
match value {
JsonView::Raw => false,
JsonView::Table => true,
}
}
}

#[function_component(JwtEditor)]
pub fn jwt_editor(props: &JwtEditorProps) -> Html {
let header = props.jwt.parsed_header.clone();
Expand Down Expand Up @@ -142,6 +168,12 @@ pub fn jwt_editor(props: &JwtEditorProps) -> Html {
set_jwt.emit(jwt);
});

let header_view = use_state(JsonView::default);
let payload_view = use_state(JsonView::default);

let header_view_setter = header_view.setter();
let payload_view_setter = payload_view.setter();

let notifications = use_notification::<Notification>();
let clipboard = use_clipboard();

Expand All @@ -152,16 +184,34 @@ pub fn jwt_editor(props: &JwtEditorProps) -> Html {
<span class="jwt-header" onclick={copy_to_clipboard_with_notification(header.clone(), clipboard.clone(), "Header", notifications.clone())}>{"Header"}</span>
<button onclick={header_on_pretty} class="jwt-util-button">{"Prettify"}</button>
<button onclick={header_on_minify} class="jwt-util-button">{"Minify"}</button>
<div class="horizontal">
<span class="total">{"raw"}</span>
<Switch id={String::from("jwt-header-view")} state={bool::from(*header_view)} setter={Callback::from(move |view: bool| header_view_setter.set(view.into()))} />
<span class="total">{"table"}</span>
</div>
</div>
<textarea rows="4" class="base-input" value={header} oninput={on_header_input} />
{if !bool::from(*header_view) {html! {
<textarea rows="4" class="base-input" value={header} oninput={on_header_input} />
}} else {html! {
<TableView value={serde_json::from_str::<Value>(&props.jwt.parsed_header).unwrap()} />
}}}
</div>
<div class="vertical">
<div class="horizontal">
<span class="jwt-payload" onclick={copy_to_clipboard_with_notification(payload.clone(), clipboard.clone(), "Payload", notifications.clone())}>{"Payload"}</span>
<button onclick={payload_on_pretty} class="jwt-util-button">{"Prettify"}</button>
<button onclick={payload_on_minify} class="jwt-util-button">{"Minify"}</button>
<div class="horizontal">
<span class="total">{"raw"}</span>
<Switch id={String::from("jwt-payload-view")} state={bool::from(*payload_view)} setter={Callback::from(move |view: bool| payload_view_setter.set(view.into()))} />
<span class="total">{"table"}</span>
</div>
</div>
<textarea rows="6" class="base-input" value={payload} oninput={on_payload_input} />
{if !bool::from(*payload_view) {html! {
<textarea rows="6" class="base-input" value={payload} oninput={on_payload_input} />
}} else {html! {
<TableView value={serde_json::from_str::<Value>(&props.jwt.parsed_payload).unwrap()} />
}}}
</div>
<div class="vertical">
<span class="jwt-signature" onclick={copy_to_clipboard_with_notification(signature.clone(), clipboard, "Signature", notifications.clone())}>{"Signature"}</span>
Expand Down

0 comments on commit dad5154

Please sign in to comment.