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