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 | 2 | mod data; |
3 | +mod error; | |
4 | +mod client; | |
2 | 5 | |
3 | -use artshop_common::types::MarkdownJson; | |
6 | +use api::markdown::Markdown; | |
4 | 7 | use data::icons::*; |
5 | -use js_sys::JsString; | |
6 | 8 | use log::Level; |
7 | 9 | use mogwai::prelude::*; |
8 | -use web_sys::{RequestInit, RequestMode, Request, Response}; | |
9 | 10 | use std::panic; |
10 | 11 | use wasm_bindgen::prelude::*; |
11 | 12 | |
... | ... | @@ -18,69 +19,16 @@ enum AppLogic { |
18 | 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 | 22 | async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
78 | 23 | , tx_view: broadcast::Sender<String> |
79 | 24 | , mut rx_dom: broadcast::Receiver<Dom> ) |
80 | 25 | { |
81 | 26 | let dom = rx_dom.next().await.unwrap(); |
82 | 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 | 33 | let container = match dom.inner_read() { |
86 | 34 | Either::Left(dom_js) => |
... | ... | @@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
117 | 65 | |
118 | 66 | let update = move || { |
119 | 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 | 78 | None => (), |
124 | 79 | } |
125 | 80 | }; |
126 | 81 | |
127 | - set_md(md.content.as_str()); | |
82 | + set_md(md.json.content.as_str()); | |
128 | 83 | update(); |
129 | 84 | |
130 | 85 | /* play with katex ==== */ |
... | ... | @@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
136 | 91 | |
137 | 92 | if let Either::Left(dom_js) = dom.inner_read() { |
138 | 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 | 101 | if let Either::Left(dom_js) = dom.inner_read() { |
147 | 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 | 111 | while let Some(msg) = rx_logic.next().await { |
157 | 112 | match msg { |
158 | 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 | 117 | AppLogic::Update => update(), |
163 | 118 | AppLogic::Toggle => { |
... | ... | @@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
170 | 125 | }; |
171 | 126 | }, |
172 | 127 | AppLogic::Discard => { |
173 | - set_md(md.content.as_str()); | |
128 | + set_md(md.json.content.as_str()); | |
174 | 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 | 134 | update(); |
180 | 135 | }, |
181 | 136 | } | ... | ... |
Please
register
or
login
to post a comment