Commit c179a4f809d04c0edab19d002f1fd13eb15f22b2
1 parent
b9da2ed6
Add patch creation an apply on load logic
Showing
7 changed files
with
235 additions
and
81 deletions
No preview for this file type
... | ... | @@ -6,71 +6,20 @@ mod models; |
6 | 6 | mod routes; |
7 | 7 | mod schema; |
8 | 8 | |
9 | -use std::io::Write; | |
10 | -use crate::routes::markdown::get_markdowns; | |
9 | +use crate::routes::markdown::{get_markdowns, update_markdown}; | |
11 | 10 | use crate::routes::other::*; |
12 | 11 | use crate::routes::user::*; |
13 | 12 | |
14 | 13 | use actix_web::{guard, web, App, HttpResponse, HttpServer}; |
15 | 14 | use diesel::r2d2::{self, ConnectionManager}; |
16 | 15 | use diesel::SqliteConnection; |
17 | -use diffy::create_patch; | |
18 | -use flate2::Compression; | |
19 | -use flate2::write::{DeflateEncoder, DeflateDecoder}; | |
20 | 16 | use listenfd::ListenFd; |
17 | +use routes::markdown::get_markdown; | |
21 | 18 | |
22 | 19 | pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; |
23 | 20 | |
24 | -const ORIGINAL :&str = r"This is a long Multiline text to | |
25 | -see if we can create a small patch set from | |
26 | -a longer text. | |
27 | - | |
28 | -This paragraph will be kept without modification. The reason I | |
29 | -try this is to see if the diff becomes smaller than storing a | |
30 | -compressed new version of this text... | |
31 | - | |
32 | -So this whole paragraph is obsolete. It will be removed in the | |
33 | -modified text. | |
34 | - | |
35 | -There is probably a threshold up to what amount of text is needed | |
36 | -to make the diff smaller. | |
37 | -"; | |
38 | - | |
39 | -const MODIFIED :&str = r"This is a long multiline text to | |
40 | -see if we can create a small patch set from | |
41 | -a longer text. | |
42 | - | |
43 | -This paragraph will be kept without modification. The reason I | |
44 | -try this is to see if the diff becomes smaller than storing a | |
45 | -compressed new version of this text... | |
46 | - | |
47 | -This is the replacement for the previous paragraph. | |
48 | - | |
49 | -There is probably a threshold up to what amount of text is needed | |
50 | -to make the diff smaller. | |
51 | -"; | |
52 | - | |
53 | 21 | #[actix_rt::main] |
54 | 22 | async fn main() -> std::io::Result<()> { |
55 | - /* just some playing with diffy */ | |
56 | - let patch = format!("{}", create_patch(ORIGINAL, MODIFIED)); | |
57 | - println!("{} - {}", patch.len(), patch); | |
58 | - | |
59 | - let mut encoder = DeflateEncoder::new(Vec::new(), Compression::best()); | |
60 | - encoder.write_all(patch.as_bytes()).unwrap(); | |
61 | - let compressed = encoder.finish().unwrap(); | |
62 | - println!("{} - {:?}", compressed.len(), compressed); | |
63 | - | |
64 | - let mut decoder = DeflateDecoder::new(Vec::new()); | |
65 | - decoder.write_all(compressed.as_ref()).unwrap(); | |
66 | - let decompressed = decoder.finish().unwrap(); | |
67 | - let decompressed = match std::str::from_utf8(decompressed.as_ref()) { | |
68 | - Ok(v) => v, | |
69 | - Err(e) => panic!("Invalid UTF-8 sequence: {}", e), | |
70 | - }; | |
71 | - println!("{} - {}", decompressed.len(), decompressed); | |
72 | - /* ======= */ | |
73 | - | |
74 | 23 | let mut listenfd = ListenFd::from_env(); |
75 | 24 | |
76 | 25 | dotenv::dotenv().ok(); |
... | ... | @@ -87,6 +36,10 @@ async fn main() -> std::io::Result<()> { |
87 | 36 | . service( web::resource("/markdowns") |
88 | 37 | . route(web::get().to(get_markdowns)) |
89 | 38 | ) |
39 | + . service( web::resource("/markdowns/{id}") | |
40 | + . route(web::get().to(get_markdown)) | |
41 | + . route(web::put().to(update_markdown)) | |
42 | + ) | |
90 | 43 | . service( web::resource("/users") |
91 | 44 | . route(web::get().to(get_users)) |
92 | 45 | . route(web::put().to(create_user)) | ... | ... |
... | ... | @@ -8,6 +8,10 @@ use diesel::{ |
8 | 8 | RunQueryDsl |
9 | 9 | }; |
10 | 10 | use serde::{Deserialize, Serialize}; |
11 | +use std::io::Write; | |
12 | +use diffy::{apply, create_patch, Patch}; | |
13 | +use flate2::Compression; | |
14 | +use flate2::write::{DeflateEncoder, DeflateDecoder}; | |
11 | 15 | |
12 | 16 | |
13 | 17 | #[derive(Debug, Serialize, Deserialize, Queryable, Identifiable)] |
... | ... | @@ -20,6 +24,16 @@ pub struct Markdown { |
20 | 24 | pub date_updated: String, |
21 | 25 | } |
22 | 26 | |
27 | +#[derive(Debug, Serialize, Deserialize, Queryable, Identifiable)] | |
28 | +#[table_name = "markdown_diffs"] | |
29 | +#[primary_key(markdown_id, diff_id)] | |
30 | +pub struct MarkdownDiff { | |
31 | + pub markdown_id: i32, | |
32 | + pub diff_id: i32, | |
33 | + pub diff: Vec<u8>, | |
34 | + pub date_created: String, | |
35 | +} | |
36 | + | |
23 | 37 | #[derive(Debug, Insertable)] |
24 | 38 | #[table_name = "markdowns"] |
25 | 39 | pub struct MarkdownNew<'a> { |
... | ... | @@ -30,12 +44,23 @@ pub struct MarkdownNew<'a> { |
30 | 44 | pub date_updated: &'a str, |
31 | 45 | } |
32 | 46 | |
47 | +#[derive(Debug, Insertable)] | |
48 | +#[table_name = "markdown_diffs"] | |
49 | +pub struct MarkdownDiffNew<'a> { | |
50 | + pub markdown_id: i32, | |
51 | + pub diff_id: i32, | |
52 | + pub diff: &'a [u8], | |
53 | + pub date_created: &'a str, | |
54 | +} | |
55 | + | |
33 | 56 | #[derive(Debug, Serialize, Deserialize, AsChangeset)] |
34 | 57 | #[table_name="markdowns"] |
35 | 58 | pub struct MarkdownJson { |
36 | 59 | pub name: String, |
37 | 60 | pub content: String, |
38 | 61 | pub number_of_versions: i32, |
62 | + pub date_created: String, | |
63 | + pub date_updated: String, | |
39 | 64 | } |
40 | 65 | |
41 | 66 | pub(crate) enum Action { |
... | ... | @@ -82,11 +107,34 @@ pub(crate) fn get_markdowns(pool: Arc<Pool>) -> Result<Vec<Markdown>, Error> |
82 | 107 | } |
83 | 108 | |
84 | 109 | pub(crate) fn get_markdown( pool: Arc<Pool> |
85 | - , ident: i32 ) -> Result<Markdown, Error> | |
110 | + , ident: &str | |
111 | + , patch: Option<i32> ) -> Result<Markdown, Error> | |
86 | 112 | { |
87 | 113 | use crate::schema::markdowns::dsl::*; |
114 | + use crate::schema::markdown_diffs::dsl::*; | |
88 | 115 | let db_connection = pool.get()?; |
89 | - Ok(markdowns.find(ident).first::<Markdown>(&db_connection)?) | |
116 | + | |
117 | + let mut markdown = markdowns | |
118 | + . filter(name.eq(ident)) | |
119 | + . first::<Markdown>(&db_connection)?; | |
120 | + | |
121 | + if let Some(patch) = patch { | |
122 | + let result = markdown_diffs . filter(markdown_id.eq(markdown.id)) | |
123 | + . filter(diff_id.ge(patch)) | |
124 | + . order(diff_id.desc()) | |
125 | + . load::<MarkdownDiff>(&db_connection)?; | |
126 | + | |
127 | + let mut decoder = DeflateDecoder::new(Vec::new()); | |
128 | + for patch in result { | |
129 | + decoder.write_all(patch.diff.as_ref()).unwrap(); | |
130 | + let decomp = decoder.reset(Vec::new()).unwrap(); | |
131 | + let decomp = Patch::from_str( | |
132 | + std::str::from_utf8(decomp.as_ref()).unwrap()).unwrap(); | |
133 | + markdown.content = apply(&mut markdown.content, &decomp).unwrap(); | |
134 | + } | |
135 | + }; | |
136 | + | |
137 | + Ok(markdown) | |
90 | 138 | } |
91 | 139 | |
92 | 140 | pub(crate) fn delete_markdown( pool: Arc<Pool> |
... | ... | @@ -98,19 +146,54 @@ pub(crate) fn delete_markdown( pool: Arc<Pool> |
98 | 146 | } |
99 | 147 | |
100 | 148 | pub(crate) fn update_markdown( pool: Arc<Pool> |
101 | - , ident: i32 | |
102 | - , item: MarkdownJson ) -> Result<Markdown, Error> | |
149 | + , ident: String | |
150 | + , mut item: MarkdownJson ) -> Result<Markdown, Error> | |
103 | 151 | { |
104 | 152 | use crate::schema::markdowns::dsl::*; |
153 | + use crate::schema::markdown_diffs::dsl::*; | |
105 | 154 | let db_connection = pool.get()?; |
106 | - let mut markdown = markdowns.find(ident).first::<Markdown>(&db_connection)?; | |
155 | + let mut markdown = markdowns | |
156 | + . filter(name.eq(ident)) | |
157 | + . first::<Markdown>(&db_connection)?; | |
158 | + | |
159 | + let patch = format!( "{}", create_patch( item.content.as_str() | |
160 | + , markdown.content.as_str() )); | |
161 | + let mut encoder = DeflateEncoder::new(Vec::new(), Compression::best()); | |
162 | + encoder.write_all(patch.as_bytes()).unwrap(); | |
163 | + let compressed = encoder.finish().unwrap(); | |
164 | + | |
165 | + let last_diff = match markdown_diffs . filter(markdown_id.eq(markdown.id)) | |
166 | + . order(diff_id.desc()) | |
167 | + . first::<MarkdownDiff>(&db_connection) | |
168 | + { | |
169 | + Ok(result) => (result.diff_id, Some(result)), | |
170 | + Err(_) => (0, None), | |
171 | + }; | |
172 | + | |
107 | 173 | let now = chrono::Local::now().naive_local(); |
174 | + let new_markdown_diff = MarkdownDiffNew { | |
175 | + markdown_id: markdown.id, | |
176 | + diff_id: last_diff.0 + 1, | |
177 | + diff: compressed.as_ref(), | |
178 | + date_created: markdown.date_updated.as_str(), | |
179 | + }; | |
180 | + | |
181 | + item.date_updated = format!("{}", now); | |
182 | + item.number_of_versions = item.number_of_versions + 1; | |
183 | + | |
184 | + db_connection.transaction::<_, Error, _>(|| { | |
185 | + insert_into(markdown_diffs) . values(&new_markdown_diff) | |
186 | + . execute(&db_connection)?; | |
187 | + | |
188 | + update(&markdown).set(&item).execute(&db_connection)?; | |
189 | + | |
190 | + Ok(()) | |
191 | + }).unwrap(); | |
108 | 192 | |
109 | - update(markdowns.find(ident)).set(&item).execute(&db_connection)?; | |
110 | 193 | markdown.name = item.name; |
111 | 194 | markdown.content = item.content; |
112 | 195 | markdown.number_of_versions = item.number_of_versions; |
113 | - markdown.date_updated = format!("{}", now); | |
196 | + markdown.date_updated = item.date_updated; | |
114 | 197 | |
115 | 198 | Ok(markdown) |
116 | 199 | } | ... | ... |
... | ... | @@ -3,12 +3,50 @@ use crate::Pool; |
3 | 3 | |
4 | 4 | use actix_web::{Error, HttpResponse, web}; |
5 | 5 | use anyhow::Result; |
6 | +use serde::Deserialize; | |
7 | + | |
8 | +#[derive(Debug, Deserialize)] | |
9 | +pub struct Patchset { | |
10 | + patch: Option<i32>, | |
11 | +} | |
6 | 12 | |
7 | 13 | pub async fn get_markdowns(pool: web::Data<Pool>) |
8 | 14 | -> Result<HttpResponse, Error> |
9 | 15 | { |
10 | - Ok(web::block(move || markdown::get_markdowns(pool.into_inner())) | |
16 | + Ok( web::block(move || markdown::get_markdowns(pool.into_inner())) | |
17 | + . await | |
18 | + . map(|markdowns| HttpResponse::Ok().json(markdowns)) | |
19 | + . map_err(|_| HttpResponse::InternalServerError())? | |
20 | + ) | |
21 | +} | |
22 | + | |
23 | +pub async fn get_markdown( pool: web::Data<Pool> | |
24 | + , name: web::Path<String> | |
25 | + , patch: web::Query<Patchset> | |
26 | + ) -> Result<HttpResponse, Error> | |
27 | +{ | |
28 | + let pool = pool.into_inner(); | |
29 | + let name = name.into_inner(); | |
30 | + let patch = patch.into_inner(); | |
31 | + | |
32 | + Ok( web::block(move || markdown::get_markdown(pool, name.as_str(), patch.patch)) | |
33 | + . await | |
34 | + . map(|markdowns| HttpResponse::Ok().json(markdowns)) | |
35 | + . map_err(|_| HttpResponse::InternalServerError())? | |
36 | + ) | |
37 | +} | |
38 | + | |
39 | +pub async fn update_markdown( pool: web::Data<Pool> | |
40 | + , name: web::Path<String> | |
41 | + , item: web::Json<markdown::MarkdownJson> ) | |
42 | + -> Result<HttpResponse, Error> | |
43 | +{ | |
44 | + let pool = pool.into_inner(); | |
45 | + let name = name.into_inner(); | |
46 | + let item = item.into_inner(); | |
47 | + | |
48 | + Ok(web::block(move || markdown::update_markdown(pool, name, item)) | |
11 | 49 | . await |
12 | - . map(|markdowns| HttpResponse::Ok().json(markdowns)) | |
50 | + . map(|markdown| HttpResponse::Ok().json(markdown)) | |
13 | 51 | . map_err(|_| HttpResponse::InternalServerError())?) |
14 | 52 | } | ... | ... |
... | ... | @@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"] |
12 | 12 | default = ["console_error_panic_hook"] |
13 | 13 | |
14 | 14 | [dependencies] |
15 | +katex = { version = "0.4", default-features = false, features = ["wasm-js"] } | |
15 | 16 | pulldown-cmark = "0.9" |
16 | 17 | console_log = "^0.1" |
17 | 18 | log = "^0.4" |
... | ... | @@ -39,6 +40,7 @@ features = [ |
39 | 40 | "Headers", |
40 | 41 | "HtmlElement", |
41 | 42 | "HtmlInputElement", |
43 | + "MouseEvent", | |
42 | 44 | "Node", |
43 | 45 | "Request", |
44 | 46 | "RequestInit", | ... | ... |
... | ... | @@ -3,7 +3,7 @@ mod data; |
3 | 3 | use js_sys::JsString; |
4 | 4 | use log::Level; |
5 | 5 | use mogwai::prelude::*; |
6 | -use web_sys::{RequestInit, RequestMode, Request, Response}; | |
6 | +use web_sys::{RequestInit, RequestMode, Request, Response, MouseEvent}; | |
7 | 7 | use serde::{Deserialize, Serialize}; |
8 | 8 | use std::panic; |
9 | 9 | use wasm_bindgen::prelude::*; |
... | ... | @@ -12,6 +12,7 @@ use wasm_bindgen::prelude::*; |
12 | 12 | enum AppLogic { |
13 | 13 | Update, |
14 | 14 | Toggle, |
15 | + Store, | |
15 | 16 | } |
16 | 17 | |
17 | 18 | fn md_to_html(source: &str) -> String { |
... | ... | @@ -24,21 +25,23 @@ fn md_to_html(source: &str) -> String { |
24 | 25 | html_output |
25 | 26 | } |
26 | 27 | |
27 | -#[derive(Debug, Serialize, Deserialize)] | |
28 | +#[derive(Clone, Debug, Serialize, Deserialize)] | |
28 | 29 | pub struct MarkdownJson { |
29 | 30 | pub name: String, |
30 | 31 | pub content: String, |
31 | 32 | pub number_of_versions: i32, |
33 | + pub date_created: String, | |
34 | + pub date_updated: String, | |
32 | 35 | } |
33 | 36 | |
34 | -pub type MarkdownsJson = Vec<MarkdownJson>; | |
35 | - | |
36 | -async fn md_from_db() -> String { | |
37 | +async fn md_from_db() -> MarkdownJson { | |
37 | 38 | let window = web_sys::window().unwrap(); |
38 | 39 | let mut opts = RequestInit::new(); |
39 | 40 | opts.method("GET").mode(RequestMode::Cors); |
40 | 41 | |
41 | - let request = Request::new_with_str_and_init("/api/v0/markdowns", &opts).unwrap(); | |
42 | + let request = Request::new_with_str_and_init( | |
43 | + "/api/v0/markdowns/md-example?patch=3" | |
44 | + , &opts ).unwrap(); | |
42 | 45 | request.headers().set("Accept", "application/json").unwrap(); |
43 | 46 | |
44 | 47 | let response = JsFuture::from(window.fetch_with_request(&request)) |
... | ... | @@ -46,15 +49,38 @@ async fn md_from_db() -> String { |
46 | 49 | . dyn_into::<Response>().unwrap(); |
47 | 50 | let data = String::from( JsFuture::from(response.text().unwrap()) |
48 | 51 | . await.unwrap() |
49 | - . dyn_into::<JsString>().unwrap() ); | |
50 | - let data :MarkdownsJson = serde_json::from_str(data.as_str()).unwrap(); | |
52 | + . dyn_into::<JsString>().unwrap() | |
53 | + ); | |
54 | + let data :MarkdownJson = serde_json::from_str(data.as_str()).unwrap(); | |
55 | + | |
56 | + data.clone() | |
57 | +} | |
58 | + | |
59 | +async fn md_to_db(md :MarkdownJson) { | |
60 | + let encoded = serde_json::to_string(&md).unwrap(); | |
61 | + | |
62 | + let window = web_sys::window().unwrap(); | |
63 | + let mut opts = RequestInit::new(); | |
64 | + opts . method("PUT") | |
65 | + . mode(RequestMode::Cors) | |
66 | + . body(Some(&encoded.into())); | |
51 | 67 | |
52 | - String::from(&data[0].content) | |
68 | + let url_str = format!("/api/v0/markdowns/{}", md.name); | |
69 | + let request = Request::new_with_str_and_init(url_str.as_str(), &opts) | |
70 | + . unwrap(); | |
71 | + request.headers().set("Content-Type", "application/json").unwrap(); | |
72 | + | |
73 | + let _response = JsFuture::from(window.fetch_with_request(&request)) | |
74 | + . await.unwrap() | |
75 | + . dyn_into::<Response>().unwrap(); | |
76 | + | |
77 | + /* do something with the response here.... */ | |
53 | 78 | } |
54 | 79 | |
55 | 80 | async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
56 | 81 | , tx_view: broadcast::Sender<String> |
57 | - , mut rx_dom: broadcast::Receiver<Dom> ) { | |
82 | + , mut rx_dom: broadcast::Receiver<Dom> | |
83 | + , md :MarkdownJson ) { | |
58 | 84 | let dom = rx_dom.next().await.unwrap(); |
59 | 85 | let mut show_edit = false; |
60 | 86 | |
... | ... | @@ -82,8 +108,39 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
82 | 108 | |
83 | 109 | update(&dom); |
84 | 110 | |
111 | + /* play with katex ==== */ | |
112 | + let opts = katex::Opts::builder() | |
113 | + . output_type(katex::opts::OutputType::Mathml) | |
114 | + . build().unwrap(); | |
115 | + let formula1 = katex::render_with_opts("E = mc^2", &opts).unwrap(); | |
116 | + let formula2 = katex::render_with_opts("e^{i*\\pi} +1 = 0", &opts).unwrap(); | |
117 | + | |
118 | + if let Either::Left(dom_js) = dom.inner_read() { | |
119 | + dom_js . to_owned() | |
120 | + . dyn_into::<Node>().unwrap() | |
121 | + . child_nodes().get(1).unwrap() | |
122 | + . child_nodes().get(2).unwrap() | |
123 | + . dyn_into::<HtmlElement>().unwrap() | |
124 | + . set_inner_html(formula1.as_str()) | |
125 | + }; | |
126 | + | |
127 | + if let Either::Left(dom_js) = dom.inner_read() { | |
128 | + dom_js . to_owned() | |
129 | + . dyn_into::<Node>().unwrap() | |
130 | + . child_nodes().get(1).unwrap() | |
131 | + . child_nodes().get(3).unwrap() | |
132 | + . dyn_into::<HtmlElement>().unwrap() | |
133 | + . set_inner_html(formula2.as_str()) | |
134 | + }; | |
135 | + /* =========== */ | |
136 | + | |
85 | 137 | while let Some(msg) = rx_logic.next().await { |
86 | 138 | match msg { |
139 | + AppLogic::Store => { | |
140 | + let mut new_md = md.clone(); | |
141 | + new_md.content = get_md(&dom); | |
142 | + md_to_db(new_md).await; | |
143 | + }, | |
87 | 144 | AppLogic::Update => update(&dom), |
88 | 145 | AppLogic::Toggle => { |
89 | 146 | show_edit = ! show_edit; |
... | ... | @@ -101,23 +158,42 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> |
101 | 158 | fn editor_view( tx_logic: broadcast::Sender<AppLogic> |
102 | 159 | , rx_view: broadcast::Receiver<String> |
103 | 160 | , tx_dom: broadcast::Sender<Dom> |
104 | - , init_data: &str | |
161 | + , md: &MarkdownJson | |
105 | 162 | ) -> ViewBuilder<Dom> { |
106 | 163 | let ns = "http://www.w3.org/2000/svg"; |
107 | 164 | |
165 | + let input_filter = tx_logic | |
166 | + . sink() | |
167 | + . contra_map(|_| AppLogic::Update); | |
168 | + let store_filter = tx_logic | |
169 | + . sink() | |
170 | + . contra_filter_map(|e :DomEvent| { | |
171 | + if let Either::Left(e) = e.clone_inner() { | |
172 | + let e = e.dyn_into::<MouseEvent>().unwrap(); | |
173 | + match e.alt_key() { | |
174 | + true => Some(AppLogic::Store), | |
175 | + false => None | |
176 | + } | |
177 | + } else { | |
178 | + None | |
179 | + } | |
180 | + }); | |
181 | + let toggle_filter = tx_logic | |
182 | + . sink() | |
183 | + . contra_map(|_| AppLogic::Toggle); | |
184 | + | |
108 | 185 | builder! { |
109 | 186 | <div class="input" |
110 | 187 | style:width="33%" |
111 | - on:input=tx_logic.sink().contra_map(|_| AppLogic::Update) | |
188 | + on:input=input_filter | |
112 | 189 | capture:view=tx_dom.sink()> |
113 | 190 | <div contenteditable="true" |
114 | 191 | style:cursor="text" |
115 | 192 | style:display=("none", rx_view)> |
116 | - <pre>{init_data}</pre> | |
193 | + <pre on:click=store_filter>{md.content.clone()}</pre> | |
117 | 194 | </div> |
118 | 195 | <div> |
119 | - <button on:click=tx_logic . sink() | |
120 | - . contra_map(|_| AppLogic::Toggle)> | |
196 | + <button on:click=toggle_filter> | |
121 | 197 | <svg version="1.1" id="Capa_1" xmlns=ns |
122 | 198 | x="0px" y="0px" viewBox="0 0 220.001 220.001" |
123 | 199 | style:width="1.5em" style:height="1.5em"> |
... | ... | @@ -131,6 +207,8 @@ fn editor_view( tx_logic: broadcast::Sender<AppLogic> |
131 | 207 | </svg> |
132 | 208 | </button> |
133 | 209 | <div></div> |
210 | + <div></div> | |
211 | + <div></div> | |
134 | 212 | </div> |
135 | 213 | </div> |
136 | 214 | } |
... | ... | @@ -141,13 +219,13 @@ pub async fn main() -> Result<(), JsValue> { |
141 | 219 | panic::set_hook(Box::new(console_error_panic_hook::hook)); |
142 | 220 | console_log::init_with_level(Level::Trace).unwrap(); |
143 | 221 | |
144 | - let data = md_from_db().await; | |
222 | + let md = md_from_db().await; | |
145 | 223 | |
146 | 224 | let (tx_dom, rx_dom) = broadcast::bounded(1); |
147 | 225 | let (tx_logic, rx_logic) = broadcast::bounded(1); |
148 | 226 | let (tx_view, rx_view) = broadcast::bounded(1); |
149 | - let comp = Component::from( editor_view(tx_logic, rx_view, tx_dom, data.as_str()) ) | |
150 | - . with_logic( editor_logic(rx_logic, tx_view, rx_dom) ); | |
227 | + let comp = Component::from( editor_view(tx_logic, rx_view, tx_dom, &md) ) | |
228 | + . with_logic( editor_logic(rx_logic, tx_view, rx_dom, md) ); | |
151 | 229 | |
152 | 230 | let page = Component::from(builder! {{comp}}); |
153 | 231 | page.build()?.run() | ... | ... |
Please
register
or
login
to post a comment