Commit accd8aba2a684cdb34e55f996ba0262525f75a4f
1 parent
55351af6
Client code cleanup and refactoring
Showing
5 changed files
with
229 additions
and
80 deletions
ui/src/api/markdown.rs
0 → 100644
1 | +use std::fmt::Display; | ||
2 | + | ||
3 | +use artshop_common::types::MarkdownJson; | ||
4 | + | ||
5 | +use super::super::error::*; | ||
6 | +use super::super::client::Client; | ||
7 | + | ||
8 | +#[derive(Debug, Clone)] | ||
9 | +pub struct Markdown { | ||
10 | + client: Client, | ||
11 | + pub json: MarkdownJson, | ||
12 | +} | ||
13 | + | ||
14 | +impl Markdown { | ||
15 | + pub(crate) async fn new(name :&str) -> Result<Markdown> { | ||
16 | + let client = Client::new()?; | ||
17 | + let api_uri = format!("/api/v0/markdowns/{}", name); | ||
18 | + let (response, data) = client.get(&api_uri).await?; | ||
19 | + | ||
20 | + match response.status() { | ||
21 | + 200 => Ok(Self { client | ||
22 | + , json: serde_json::from_str(data.as_str())? }), | ||
23 | + status => Err(Self::status_error(status)), | ||
24 | + } | ||
25 | + } | ||
26 | + | ||
27 | + pub(crate) async fn read( &mut self | ||
28 | + , patch :Option<i32> | ||
29 | + ) -> Result<&Markdown> | ||
30 | + { | ||
31 | + let api_uri = match patch { | ||
32 | + Some(i) => format!( "/api/v0/markdowns/{}?patch={}" | ||
33 | + , self.json.name, i ), | ||
34 | + None => format!("/api/v0/markdowns/{}", self.json.name), | ||
35 | + }; | ||
36 | + let (response, data) = self.client.get(&api_uri).await?; | ||
37 | + | ||
38 | + match response.status() { | ||
39 | + 200 => { | ||
40 | + self.json = serde_json::from_str(data.as_str())?; | ||
41 | + Ok(self) | ||
42 | + }, | ||
43 | + status => Err(Self::status_error(status)), | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + pub(crate) async fn save(&self) -> Result<&Markdown> { | ||
48 | + let url = format!("/api/v0/markdowns/{}", self.json.name); | ||
49 | + let data = serde_json::to_string(&self.json)?; | ||
50 | + | ||
51 | + let response = self.client.put(url.as_str(), data.as_str()).await?; | ||
52 | + | ||
53 | + match response.status() { | ||
54 | + 200 => Ok(self), | ||
55 | + status => Err(Self::status_error(status)), | ||
56 | + } | ||
57 | + } | ||
58 | + | ||
59 | + pub(crate) fn _to_html_string(&self) -> String { | ||
60 | + use pulldown_cmark::{Parser, Options, html}; | ||
61 | + | ||
62 | + let mut html_out = String::new(); | ||
63 | + let parser = Parser::new_ext(&self.json.content, Options::all()); | ||
64 | + | ||
65 | + html::push_html(&mut html_out, parser); | ||
66 | + html_out | ||
67 | + } | ||
68 | + | ||
69 | + | ||
70 | + fn status_error<I: Display>(status :I) -> Error { | ||
71 | + let err_str = format!("Invalid response status: {}", status); | ||
72 | + Error::from(err_str.as_str()) | ||
73 | + } | ||
74 | +} |
ui/src/api/mod.rs
0 → 100644
1 | +pub(crate) mod markdown; |
ui/src/client.rs
0 → 100644
1 | +use js_sys::JsString; | ||
2 | +use mogwai::prelude::*; | ||
3 | +use wasm_bindgen::prelude::*; | ||
4 | +use web_sys::{Window, window, Response, Request, RequestInit, RequestMode}; | ||
5 | +use super::error::*; | ||
6 | + | ||
7 | +use std::result::Result as StdResult; | ||
8 | + | ||
9 | +#[derive(Debug, Clone)] | ||
10 | +pub(crate) struct Client { | ||
11 | + window :Window, | ||
12 | +} | ||
13 | + | ||
14 | +type ReqGetter = fn(&str, &RequestInit) -> StdResult<Request, JsValue>; | ||
15 | + | ||
16 | +const REQUEST :ReqGetter = Request::new_with_str_and_init; | ||
17 | + | ||
18 | +impl Client { | ||
19 | + pub fn new() -> Result<Self> { | ||
20 | + const WINDOW_ERROR :&str = "Unable to get window instance"; | ||
21 | + | ||
22 | + Ok(Self { window: window() | ||
23 | + . ok_or(Error::from(WINDOW_ERROR))? }) | ||
24 | + } | ||
25 | + | ||
26 | + pub async fn get(&self, url :&str) -> Result<(Response, String)> { | ||
27 | + let mut init = RequestInit::new(); | ||
28 | + let request = REQUEST( &url | ||
29 | + , init . method("GET") | ||
30 | + . mode(RequestMode::Cors) )?; | ||
31 | + | ||
32 | + request . headers() | ||
33 | + . set("Accept", "application/json")?; | ||
34 | + | ||
35 | + let response = JsFuture::from( self.window | ||
36 | + . fetch_with_request(&request) ) | ||
37 | + . await? | ||
38 | + . dyn_into::<Response>()?; | ||
39 | + | ||
40 | + let data = JsFuture::from(response.text()?) | ||
41 | + . await? | ||
42 | + . dyn_into::<JsString>()?; | ||
43 | + | ||
44 | + Ok((response, String::from(data))) | ||
45 | + } | ||
46 | + | ||
47 | + pub async fn put(&self, url :&str, data :&str) -> Result<Response> { | ||
48 | + let mut init = RequestInit::new(); | ||
49 | + let request = REQUEST( &url | ||
50 | + , init . method("PUT") | ||
51 | + . mode(RequestMode::Cors) | ||
52 | + . body(Some(&data.into())) )?; | ||
53 | + | ||
54 | + request . headers() | ||
55 | + . set("Content-Type", "application/json")?; | ||
56 | + | ||
57 | + let response = JsFuture::from( self.window | ||
58 | + . fetch_with_request(&request)) | ||
59 | + . await? | ||
60 | + . dyn_into::<Response>()?; | ||
61 | + | ||
62 | + Ok(response) | ||
63 | + } | ||
64 | +} |
ui/src/error.rs
0 → 100644
1 | +use std::fmt::Display; | ||
2 | +use wasm_bindgen::prelude::*; | ||
3 | + | ||
4 | +type ParentError = Option<Box<dyn std::error::Error>>; | ||
5 | + | ||
6 | +#[derive(Debug)] | ||
7 | +pub(crate) struct Error { | ||
8 | + source: ParentError, | ||
9 | + message: String, | ||
10 | +} | ||
11 | + | ||
12 | +pub(crate) type Result<T> = std::result::Result<T, Error>; | ||
13 | + | ||
14 | +impl std::error::Error for Error { | ||
15 | + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
16 | + self.source.as_deref() | ||
17 | + } | ||
18 | +} | ||
19 | + | ||
20 | +impl Display for Error { | ||
21 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
22 | + match self { | ||
23 | + Error { source: Some(source), message } => | ||
24 | + write!(f, "{}: {}", message, source), | ||
25 | + Error { source: None, message } => write!(f, "{}", message), | ||
26 | + } | ||
27 | + } | ||
28 | +} | ||
29 | + | ||
30 | +impl From<&str> for Error { | ||
31 | + fn from(message: &str) -> Self { | ||
32 | + Self { source: None, message: String::from(message) } | ||
33 | + } | ||
34 | +} | ||
35 | + | ||
36 | +impl From<JsValue> for Error { | ||
37 | + fn from(source: JsValue) -> Self { | ||
38 | + let source = js_sys::Error::from(source); | ||
39 | + | ||
40 | + let message = format!("[{}] {}", source.name(), source.message()); | ||
41 | + let source = Error::from(message.as_str()); | ||
42 | + | ||
43 | + Self { source: Some(Box::new(source)) | ||
44 | + , message: String::from("WebSys Error") | ||
45 | + } | ||
46 | + } | ||
47 | +} | ||
48 | + | ||
49 | +impl From<serde_json::Error> for Error { | ||
50 | + fn from(source: serde_json::Error) -> Self { | ||
51 | + Self { source: Some(Box::new(source)) | ||
52 | + , message: String::from("Serde Error") | ||
53 | + } | ||
54 | + } | ||
55 | +} |
1 | +mod api; | ||
1 | mod data; | 2 | mod data; |
3 | +mod error; | ||
4 | +mod client; | ||
2 | 5 | ||
3 | -use artshop_common::types::MarkdownJson; | 6 | +use api::markdown::Markdown; |
4 | use data::icons::*; | 7 | use data::icons::*; |
5 | -use js_sys::JsString; | ||
6 | use log::Level; | 8 | use log::Level; |
7 | use mogwai::prelude::*; | 9 | use mogwai::prelude::*; |
8 | -use web_sys::{RequestInit, RequestMode, Request, Response}; | ||
9 | use std::panic; | 10 | use std::panic; |
10 | use wasm_bindgen::prelude::*; | 11 | use wasm_bindgen::prelude::*; |
11 | 12 | ||
@@ -18,69 +19,16 @@ enum AppLogic { | @@ -18,69 +19,16 @@ enum AppLogic { | ||
18 | Discard, | 19 | Discard, |
19 | } | 20 | } |
20 | 21 | ||
21 | -fn md_to_html(source: &str) -> String { | ||
22 | - use pulldown_cmark::{Parser, Options, html}; | ||
23 | - | ||
24 | - let parser = Parser::new_ext(source, Options::all()); | ||
25 | - let mut html_output = String::new(); | ||
26 | - html::push_html(&mut html_output, parser); | ||
27 | - | ||
28 | - html_output | ||
29 | -} | ||
30 | - | ||
31 | -async fn md_from_db(patch :Option<i32>) -> MarkdownJson { | ||
32 | - let window = web_sys::window().unwrap(); | ||
33 | - let mut opts = RequestInit::new(); | ||
34 | - opts.method("GET").mode(RequestMode::Cors); | ||
35 | - | ||
36 | - let api_uri = match patch { | ||
37 | - Some(i) => format!("/api/v0/markdowns/md-example?patch={}", i), | ||
38 | - None => String::from("/api/v0/markdowns/md-example"), | ||
39 | - }; | ||
40 | - | ||
41 | - let request = Request::new_with_str_and_init(&api_uri, &opts).unwrap(); | ||
42 | - request.headers().set("Accept", "application/json").unwrap(); | ||
43 | - | ||
44 | - let response = JsFuture::from(window.fetch_with_request(&request)) | ||
45 | - . await.unwrap() | ||
46 | - . dyn_into::<Response>().unwrap(); | ||
47 | - let data = String::from( JsFuture::from(response.text().unwrap()) | ||
48 | - . await.unwrap() | ||
49 | - . dyn_into::<JsString>().unwrap() | ||
50 | - ); | ||
51 | - let data :MarkdownJson = serde_json::from_str(data.as_str()).unwrap(); | ||
52 | - | ||
53 | - data | ||
54 | -} | ||
55 | - | ||
56 | -async fn md_to_db(md :&MarkdownJson) { | ||
57 | - let encoded = serde_json::to_string(&md).unwrap(); | ||
58 | - | ||
59 | - let window = web_sys::window().unwrap(); | ||
60 | - let mut opts = RequestInit::new(); | ||
61 | - opts . method("PUT") | ||
62 | - . mode(RequestMode::Cors) | ||
63 | - . body(Some(&encoded.into())); | ||
64 | - | ||
65 | - let url_str = format!("/api/v0/markdowns/{}", md.name); | ||
66 | - let request = Request::new_with_str_and_init(url_str.as_str(), &opts) | ||
67 | - . unwrap(); | ||
68 | - request.headers().set("Content-Type", "application/json").unwrap(); | ||
69 | - | ||
70 | - let _response = JsFuture::from(window.fetch_with_request(&request)) | ||
71 | - . await.unwrap() | ||
72 | - . dyn_into::<Response>().unwrap(); | ||
73 | - | ||
74 | - /* do something with the response here.... */ | ||
75 | -} | ||
76 | - | ||
77 | async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | 22 | async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
78 | , tx_view: broadcast::Sender<String> | 23 | , tx_view: broadcast::Sender<String> |
79 | , mut rx_dom: broadcast::Receiver<Dom> ) | 24 | , mut rx_dom: broadcast::Receiver<Dom> ) |
80 | { | 25 | { |
81 | let dom = rx_dom.next().await.unwrap(); | 26 | let dom = rx_dom.next().await.unwrap(); |
82 | let mut show_edit = false; | 27 | let mut show_edit = false; |
83 | - let mut md = md_from_db(Some(2)).await; | 28 | + let mut md = Markdown::new("md-example").await.unwrap(); |
29 | + | ||
30 | + // TODO Only for experiments... remove when ready | ||
31 | + md.read(Some(2)).await.unwrap(); | ||
84 | 32 | ||
85 | let container = match dom.inner_read() { | 33 | let container = match dom.inner_read() { |
86 | Either::Left(dom_js) => | 34 | Either::Left(dom_js) => |
@@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | @@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | ||
117 | 65 | ||
118 | let update = move || { | 66 | let update = move || { |
119 | match cont_ref { | 67 | match cont_ref { |
120 | - Some((md_cont, view_cont)) => | ||
121 | - view_cont.set_inner_html( | ||
122 | - md_to_html(md_cont.inner_text().as_str()).as_str() ), | 68 | + Some((_, view_cont)) => { |
69 | + use pulldown_cmark::{Parser, Options, html}; | ||
70 | + | ||
71 | + let mut html_out = String::new(); | ||
72 | + let md = get_md(); | ||
73 | + let parser = Parser::new_ext(&md, Options::all()); | ||
74 | + | ||
75 | + html::push_html(&mut html_out, parser); | ||
76 | + view_cont.set_inner_html(&html_out) | ||
77 | + }, | ||
123 | None => (), | 78 | None => (), |
124 | } | 79 | } |
125 | }; | 80 | }; |
126 | 81 | ||
127 | - set_md(md.content.as_str()); | 82 | + set_md(md.json.content.as_str()); |
128 | update(); | 83 | update(); |
129 | 84 | ||
130 | /* play with katex ==== */ | 85 | /* play with katex ==== */ |
@@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | @@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | ||
136 | 91 | ||
137 | if let Either::Left(dom_js) = dom.inner_read() { | 92 | if let Either::Left(dom_js) = dom.inner_read() { |
138 | dom_js . to_owned() | 93 | dom_js . to_owned() |
139 | - . dyn_into::<Node>().unwrap() | ||
140 | - . child_nodes().get(1).unwrap() | ||
141 | - . child_nodes().get(2).unwrap() | ||
142 | - . dyn_into::<HtmlElement>().unwrap() | ||
143 | - . set_inner_html(formula1.as_str()) | 94 | + . dyn_into::<Node>().unwrap() |
95 | + . child_nodes().get(1).unwrap() | ||
96 | + . child_nodes().get(2).unwrap() | ||
97 | + . dyn_into::<HtmlElement>().unwrap() | ||
98 | + . set_inner_html(formula1.as_str()) | ||
144 | }; | 99 | }; |
145 | 100 | ||
146 | if let Either::Left(dom_js) = dom.inner_read() { | 101 | if let Either::Left(dom_js) = dom.inner_read() { |
147 | dom_js . to_owned() | 102 | dom_js . to_owned() |
148 | - . dyn_into::<Node>().unwrap() | ||
149 | - . child_nodes().get(1).unwrap() | ||
150 | - . child_nodes().get(3).unwrap() | ||
151 | - . dyn_into::<HtmlElement>().unwrap() | ||
152 | - . set_inner_html(formula2.as_str()) | 103 | + . dyn_into::<Node>().unwrap() |
104 | + . child_nodes().get(1).unwrap() | ||
105 | + . child_nodes().get(3).unwrap() | ||
106 | + . dyn_into::<HtmlElement>().unwrap() | ||
107 | + . set_inner_html(formula2.as_str()) | ||
153 | }; | 108 | }; |
154 | /* =========== */ | 109 | /* =========== */ |
155 | 110 | ||
156 | while let Some(msg) = rx_logic.next().await { | 111 | while let Some(msg) = rx_logic.next().await { |
157 | match msg { | 112 | match msg { |
158 | AppLogic::Store => { | 113 | AppLogic::Store => { |
159 | - md.content = get_md(); | ||
160 | - md_to_db(&md).await; | 114 | + md.json.content = get_md(); |
115 | + md.save().await.unwrap(); | ||
161 | }, | 116 | }, |
162 | AppLogic::Update => update(), | 117 | AppLogic::Update => update(), |
163 | AppLogic::Toggle => { | 118 | AppLogic::Toggle => { |
@@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | @@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> | ||
170 | }; | 125 | }; |
171 | }, | 126 | }, |
172 | AppLogic::Discard => { | 127 | AppLogic::Discard => { |
173 | - set_md(md.content.as_str()); | 128 | + set_md(md.json.content.as_str()); |
174 | update(); | 129 | update(); |
175 | }, | 130 | }, |
176 | - AppLogic::Select(id) => { | ||
177 | - md = md_from_db(id).await; | ||
178 | - set_md(md.content.as_str()); | 131 | + AppLogic::Select(_id) => { |
132 | + md.read(None).await.unwrap(); | ||
133 | + set_md(md.json.content.as_str()); | ||
179 | update(); | 134 | update(); |
180 | }, | 135 | }, |
181 | } | 136 | } |
Please
register
or
login
to post a comment