Commit c179a4f809d04c0edab19d002f1fd13eb15f22b2

Authored by Georg Hopp
1 parent b9da2ed6

Add patch creation an apply on load logic

@@ -8,7 +8,7 @@ PROFILE = @@ -8,7 +8,7 @@ PROFILE =
8 endif 8 endif
9 9
10 start: 10 start:
11 - systemfd --no-pid -s http::3000 -- \ 11 + systemfd --no-pid -s 0.0.0.0:3000 -- \
12 cargo watch -i static/ -s "make run" 12 cargo watch -i static/ -s "make run"
13 13
14 wasm: 14 wasm:
No preview for this file type
@@ -6,71 +6,20 @@ mod models; @@ -6,71 +6,20 @@ mod models;
6 mod routes; 6 mod routes;
7 mod schema; 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 use crate::routes::other::*; 10 use crate::routes::other::*;
12 use crate::routes::user::*; 11 use crate::routes::user::*;
13 12
14 use actix_web::{guard, web, App, HttpResponse, HttpServer}; 13 use actix_web::{guard, web, App, HttpResponse, HttpServer};
15 use diesel::r2d2::{self, ConnectionManager}; 14 use diesel::r2d2::{self, ConnectionManager};
16 use diesel::SqliteConnection; 15 use diesel::SqliteConnection;
17 -use diffy::create_patch;  
18 -use flate2::Compression;  
19 -use flate2::write::{DeflateEncoder, DeflateDecoder};  
20 use listenfd::ListenFd; 16 use listenfd::ListenFd;
  17 +use routes::markdown::get_markdown;
21 18
22 pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; 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 #[actix_rt::main] 21 #[actix_rt::main]
54 async fn main() -> std::io::Result<()> { 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 let mut listenfd = ListenFd::from_env(); 23 let mut listenfd = ListenFd::from_env();
75 24
76 dotenv::dotenv().ok(); 25 dotenv::dotenv().ok();
@@ -87,6 +36,10 @@ async fn main() -> std::io::Result<()> { @@ -87,6 +36,10 @@ async fn main() -> std::io::Result<()> {
87 . service( web::resource("/markdowns") 36 . service( web::resource("/markdowns")
88 . route(web::get().to(get_markdowns)) 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 . service( web::resource("/users") 43 . service( web::resource("/users")
91 . route(web::get().to(get_users)) 44 . route(web::get().to(get_users))
92 . route(web::put().to(create_user)) 45 . route(web::put().to(create_user))
@@ -8,6 +8,10 @@ use diesel::{ @@ -8,6 +8,10 @@ use diesel::{
8 RunQueryDsl 8 RunQueryDsl
9 }; 9 };
10 use serde::{Deserialize, Serialize}; 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 #[derive(Debug, Serialize, Deserialize, Queryable, Identifiable)] 17 #[derive(Debug, Serialize, Deserialize, Queryable, Identifiable)]
@@ -20,6 +24,16 @@ pub struct Markdown { @@ -20,6 +24,16 @@ pub struct Markdown {
20 pub date_updated: String, 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 #[derive(Debug, Insertable)] 37 #[derive(Debug, Insertable)]
24 #[table_name = "markdowns"] 38 #[table_name = "markdowns"]
25 pub struct MarkdownNew<'a> { 39 pub struct MarkdownNew<'a> {
@@ -30,12 +44,23 @@ pub struct MarkdownNew<'a> { @@ -30,12 +44,23 @@ pub struct MarkdownNew<'a> {
30 pub date_updated: &'a str, 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 #[derive(Debug, Serialize, Deserialize, AsChangeset)] 56 #[derive(Debug, Serialize, Deserialize, AsChangeset)]
34 #[table_name="markdowns"] 57 #[table_name="markdowns"]
35 pub struct MarkdownJson { 58 pub struct MarkdownJson {
36 pub name: String, 59 pub name: String,
37 pub content: String, 60 pub content: String,
38 pub number_of_versions: i32, 61 pub number_of_versions: i32,
  62 + pub date_created: String,
  63 + pub date_updated: String,
39 } 64 }
40 65
41 pub(crate) enum Action { 66 pub(crate) enum Action {
@@ -82,11 +107,34 @@ pub(crate) fn get_markdowns(pool: Arc<Pool>) -> Result<Vec<Markdown>, Error> @@ -82,11 +107,34 @@ pub(crate) fn get_markdowns(pool: Arc<Pool>) -> Result<Vec<Markdown>, Error>
82 } 107 }
83 108
84 pub(crate) fn get_markdown( pool: Arc<Pool> 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 use crate::schema::markdowns::dsl::*; 113 use crate::schema::markdowns::dsl::*;
  114 + use crate::schema::markdown_diffs::dsl::*;
88 let db_connection = pool.get()?; 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 pub(crate) fn delete_markdown( pool: Arc<Pool> 140 pub(crate) fn delete_markdown( pool: Arc<Pool>
@@ -98,19 +146,54 @@ 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 pub(crate) fn update_markdown( pool: Arc<Pool> 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 use crate::schema::markdowns::dsl::*; 152 use crate::schema::markdowns::dsl::*;
  153 + use crate::schema::markdown_diffs::dsl::*;
105 let db_connection = pool.get()?; 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 let now = chrono::Local::now().naive_local(); 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 markdown.name = item.name; 193 markdown.name = item.name;
111 markdown.content = item.content; 194 markdown.content = item.content;
112 markdown.number_of_versions = item.number_of_versions; 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 Ok(markdown) 198 Ok(markdown)
116 } 199 }
@@ -3,12 +3,50 @@ use crate::Pool; @@ -3,12 +3,50 @@ use crate::Pool;
3 3
4 use actix_web::{Error, HttpResponse, web}; 4 use actix_web::{Error, HttpResponse, web};
5 use anyhow::Result; 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 pub async fn get_markdowns(pool: web::Data<Pool>) 13 pub async fn get_markdowns(pool: web::Data<Pool>)
8 -> Result<HttpResponse, Error> 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 . await 49 . await
12 - . map(|markdowns| HttpResponse::Ok().json(markdowns)) 50 + . map(|markdown| HttpResponse::Ok().json(markdown))
13 . map_err(|_| HttpResponse::InternalServerError())?) 51 . map_err(|_| HttpResponse::InternalServerError())?)
14 } 52 }
@@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"] @@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"]
12 default = ["console_error_panic_hook"] 12 default = ["console_error_panic_hook"]
13 13
14 [dependencies] 14 [dependencies]
  15 +katex = { version = "0.4", default-features = false, features = ["wasm-js"] }
15 pulldown-cmark = "0.9" 16 pulldown-cmark = "0.9"
16 console_log = "^0.1" 17 console_log = "^0.1"
17 log = "^0.4" 18 log = "^0.4"
@@ -39,6 +40,7 @@ features = [ @@ -39,6 +40,7 @@ features = [
39 "Headers", 40 "Headers",
40 "HtmlElement", 41 "HtmlElement",
41 "HtmlInputElement", 42 "HtmlInputElement",
  43 + "MouseEvent",
42 "Node", 44 "Node",
43 "Request", 45 "Request",
44 "RequestInit", 46 "RequestInit",
@@ -3,7 +3,7 @@ mod data; @@ -3,7 +3,7 @@ mod data;
3 use js_sys::JsString; 3 use js_sys::JsString;
4 use log::Level; 4 use log::Level;
5 use mogwai::prelude::*; 5 use mogwai::prelude::*;
6 -use web_sys::{RequestInit, RequestMode, Request, Response}; 6 +use web_sys::{RequestInit, RequestMode, Request, Response, MouseEvent};
7 use serde::{Deserialize, Serialize}; 7 use serde::{Deserialize, Serialize};
8 use std::panic; 8 use std::panic;
9 use wasm_bindgen::prelude::*; 9 use wasm_bindgen::prelude::*;
@@ -12,6 +12,7 @@ use wasm_bindgen::prelude::*; @@ -12,6 +12,7 @@ use wasm_bindgen::prelude::*;
12 enum AppLogic { 12 enum AppLogic {
13 Update, 13 Update,
14 Toggle, 14 Toggle,
  15 + Store,
15 } 16 }
16 17
17 fn md_to_html(source: &str) -> String { 18 fn md_to_html(source: &str) -> String {
@@ -24,21 +25,23 @@ fn md_to_html(source: &str) -> String { @@ -24,21 +25,23 @@ fn md_to_html(source: &str) -> String {
24 html_output 25 html_output
25 } 26 }
26 27
27 -#[derive(Debug, Serialize, Deserialize)] 28 +#[derive(Clone, Debug, Serialize, Deserialize)]
28 pub struct MarkdownJson { 29 pub struct MarkdownJson {
29 pub name: String, 30 pub name: String,
30 pub content: String, 31 pub content: String,
31 pub number_of_versions: i32, 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 let window = web_sys::window().unwrap(); 38 let window = web_sys::window().unwrap();
38 let mut opts = RequestInit::new(); 39 let mut opts = RequestInit::new();
39 opts.method("GET").mode(RequestMode::Cors); 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 request.headers().set("Accept", "application/json").unwrap(); 45 request.headers().set("Accept", "application/json").unwrap();
43 46
44 let response = JsFuture::from(window.fetch_with_request(&request)) 47 let response = JsFuture::from(window.fetch_with_request(&request))
@@ -46,15 +49,38 @@ async fn md_from_db() -> String { @@ -46,15 +49,38 @@ async fn md_from_db() -> String {
46 . dyn_into::<Response>().unwrap(); 49 . dyn_into::<Response>().unwrap();
47 let data = String::from( JsFuture::from(response.text().unwrap()) 50 let data = String::from( JsFuture::from(response.text().unwrap())
48 . await.unwrap() 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 async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> 80 async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
56 , tx_view: broadcast::Sender<String> 81 , tx_view: broadcast::Sender<String>
57 - , mut rx_dom: broadcast::Receiver<Dom> ) { 82 + , mut rx_dom: broadcast::Receiver<Dom>
  83 + , md :MarkdownJson ) {
58 let dom = rx_dom.next().await.unwrap(); 84 let dom = rx_dom.next().await.unwrap();
59 let mut show_edit = false; 85 let mut show_edit = false;
60 86
@@ -82,8 +108,39 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> @@ -82,8 +108,39 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
82 108
83 update(&dom); 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 while let Some(msg) = rx_logic.next().await { 137 while let Some(msg) = rx_logic.next().await {
86 match msg { 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 AppLogic::Update => update(&dom), 144 AppLogic::Update => update(&dom),
88 AppLogic::Toggle => { 145 AppLogic::Toggle => {
89 show_edit = ! show_edit; 146 show_edit = ! show_edit;
@@ -101,23 +158,42 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> @@ -101,23 +158,42 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
101 fn editor_view( tx_logic: broadcast::Sender<AppLogic> 158 fn editor_view( tx_logic: broadcast::Sender<AppLogic>
102 , rx_view: broadcast::Receiver<String> 159 , rx_view: broadcast::Receiver<String>
103 , tx_dom: broadcast::Sender<Dom> 160 , tx_dom: broadcast::Sender<Dom>
104 - , init_data: &str 161 + , md: &MarkdownJson
105 ) -> ViewBuilder<Dom> { 162 ) -> ViewBuilder<Dom> {
106 let ns = "http://www.w3.org/2000/svg"; 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 builder! { 185 builder! {
109 <div class="input" 186 <div class="input"
110 style:width="33%" 187 style:width="33%"
111 - on:input=tx_logic.sink().contra_map(|_| AppLogic::Update) 188 + on:input=input_filter
112 capture:view=tx_dom.sink()> 189 capture:view=tx_dom.sink()>
113 <div contenteditable="true" 190 <div contenteditable="true"
114 style:cursor="text" 191 style:cursor="text"
115 style:display=("none", rx_view)> 192 style:display=("none", rx_view)>
116 - <pre>{init_data}</pre> 193 + <pre on:click=store_filter>{md.content.clone()}</pre>
117 </div> 194 </div>
118 <div> 195 <div>
119 - <button on:click=tx_logic . sink()  
120 - . contra_map(|_| AppLogic::Toggle)> 196 + <button on:click=toggle_filter>
121 <svg version="1.1" id="Capa_1" xmlns=ns 197 <svg version="1.1" id="Capa_1" xmlns=ns
122 x="0px" y="0px" viewBox="0 0 220.001 220.001" 198 x="0px" y="0px" viewBox="0 0 220.001 220.001"
123 style:width="1.5em" style:height="1.5em"> 199 style:width="1.5em" style:height="1.5em">
@@ -131,6 +207,8 @@ fn editor_view( tx_logic: broadcast::Sender<AppLogic> @@ -131,6 +207,8 @@ fn editor_view( tx_logic: broadcast::Sender<AppLogic>
131 </svg> 207 </svg>
132 </button> 208 </button>
133 <div></div> 209 <div></div>
  210 + <div></div>
  211 + <div></div>
134 </div> 212 </div>
135 </div> 213 </div>
136 } 214 }
@@ -141,13 +219,13 @@ pub async fn main() -> Result<(), JsValue> { @@ -141,13 +219,13 @@ pub async fn main() -> Result<(), JsValue> {
141 panic::set_hook(Box::new(console_error_panic_hook::hook)); 219 panic::set_hook(Box::new(console_error_panic_hook::hook));
142 console_log::init_with_level(Level::Trace).unwrap(); 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 let (tx_dom, rx_dom) = broadcast::bounded(1); 224 let (tx_dom, rx_dom) = broadcast::bounded(1);
147 let (tx_logic, rx_logic) = broadcast::bounded(1); 225 let (tx_logic, rx_logic) = broadcast::bounded(1);
148 let (tx_view, rx_view) = broadcast::bounded(1); 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 let page = Component::from(builder! {{comp}}); 230 let page = Component::from(builder! {{comp}});
153 page.build()?.run() 231 page.build()?.run()
Please register or login to post a comment