Add wasm live rendering of the Qr-Code only svg
This commit is contained in:
		
							parent
							
								
									a5cfdeff54
								
							
						
					
					
						commit
						6b0daecd31
					
				
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -465,6 +465,7 @@ version = "0.3.1" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "enum-map", |  "enum-map", | ||||||
|  "fluent 0.15.0", |  "fluent 0.15.0", | ||||||
|  |  "qrcode", | ||||||
|  "seed", |  "seed", | ||||||
|  "serde", |  "serde", | ||||||
|  "shared", |  "shared", | ||||||
|  | |||||||
| @ -23,5 +23,6 @@ unic-langid = "0.9" | |||||||
| strum_macros = "0.20" | strum_macros = "0.20" | ||||||
| strum = "0.20" | strum = "0.20" | ||||||
| enum-map = "1" | enum-map = "1" | ||||||
|  | qrcode = "0.12" | ||||||
| 
 | 
 | ||||||
| shared = { path = "../shared" } | shared = { path = "../shared" } | ||||||
|  | |||||||
| @ -4,14 +4,9 @@ pub mod pages; | |||||||
| 
 | 
 | ||||||
| use pages::list_links; | use pages::list_links; | ||||||
| use pages::list_users; | use pages::list_users; | ||||||
| use seed::attrs; | use seed::{attrs, button, div, input, label, log, prelude::*, App, Url, C}; | ||||||
| use seed::button; |  | ||||||
| use seed::input; |  | ||||||
| use seed::label; |  | ||||||
| use seed::{div, log, prelude::*, App, Url, C}; |  | ||||||
| use shared::apirequests::users::LoginUser; | use shared::apirequests::users::LoginUser; | ||||||
| use shared::datatypes::Loadable; | use shared::datatypes::{Loadable, User}; | ||||||
| use shared::datatypes::User; |  | ||||||
| 
 | 
 | ||||||
| use crate::i18n::{I18n, Lang}; | use crate::i18n::{I18n, Lang}; | ||||||
| 
 | 
 | ||||||
| @ -23,13 +18,14 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model { | |||||||
|     orders.subscribe(Msg::UrlChanged); |     orders.subscribe(Msg::UrlChanged); | ||||||
|     orders.send_msg(Msg::GetLoggedUser); |     orders.send_msg(Msg::GetLoggedUser); | ||||||
| 
 | 
 | ||||||
|     log!(url); |     log!(&url); | ||||||
| 
 | 
 | ||||||
|     let lang = I18n::new(Lang::EnUS); |     let lang = I18n::new(Lang::EnUS); | ||||||
| 
 | 
 | ||||||
|     Model { |     Model { | ||||||
|         index: 0, |         index: 0, | ||||||
|         base_url: Url::new().add_path_part("app"), |         base_url: Url::new().add_path_part("app"), | ||||||
|  |         current_url: url.clone(), | ||||||
|         page: Page::init(url, orders, lang.clone()), |         page: Page::init(url, orders, lang.clone()), | ||||||
|         i18n: lang, |         i18n: lang, | ||||||
|         user: Loadable::Data(None), |         user: Loadable::Data(None), | ||||||
| @ -46,6 +42,7 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model { | |||||||
| struct Model { | struct Model { | ||||||
|     index: usize, |     index: usize, | ||||||
|     base_url: Url, |     base_url: Url, | ||||||
|  |     current_url: Url, | ||||||
|     page: Page, |     page: Page, | ||||||
|     i18n: i18n::I18n, |     i18n: i18n::I18n, | ||||||
|     user: Loadable<User>, |     user: Loadable<User>, | ||||||
| @ -100,6 +97,7 @@ pub enum Msg { | |||||||
|     UserReceived(User), |     UserReceived(User), | ||||||
|     NoMessage, |     NoMessage, | ||||||
|     NotAuthenticated, |     NotAuthenticated, | ||||||
|  |     Logout, | ||||||
|     Login, |     Login, | ||||||
|     UsernameChanged(String), |     UsernameChanged(String), | ||||||
|     PasswordChanged(String), |     PasswordChanged(String), | ||||||
| @ -122,31 +120,38 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { | |||||||
|         } |         } | ||||||
|         Msg::NoMessage => (), |         Msg::NoMessage => (), | ||||||
|         Msg::GetLoggedUser => { |         Msg::GetLoggedUser => { | ||||||
|             orders.skip(); // No need to rerender
 |             model.user = Loadable::Loading; | ||||||
|             orders.perform_cmd(async { |             orders.perform_cmd(async { | ||||||
|                 // create request
 |                 // create request
 | ||||||
|                 let request = unwrap_or_return!( |                 let request = unwrap_or_return!( | ||||||
|                     Request::new("/admin/json/get_logged_user/") |                     Request::new("/admin/json/get_logged_user/") | ||||||
|                         .method(Method::Post) |                         .method(Method::Post) | ||||||
|                         .json(&()), |                         .json(&()), | ||||||
|                     Msg::NotAuthenticated |                     Msg::Logout | ||||||
|                 ); |                 ); | ||||||
|                 // perform and get response
 |                 // perform and get response
 | ||||||
|                 let response = unwrap_or_return!(fetch(request).await, Msg::NotAuthenticated); |                 let response = unwrap_or_return!(fetch(request).await, Msg::Logout); | ||||||
|                 // validate response status
 |                 // validate response status
 | ||||||
|                 let response = unwrap_or_return!(response.check_status(), Msg::NotAuthenticated); |                 let response = unwrap_or_return!(response.check_status(), Msg::Logout); | ||||||
|                 let user: User = unwrap_or_return!(response.json().await, Msg::NotAuthenticated); |                 let user: User = unwrap_or_return!(response.json().await, Msg::Logout); | ||||||
| 
 | 
 | ||||||
|                 Msg::UserReceived(user) |                 Msg::UserReceived(user) | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|         Msg::UserReceived(user) => model.user = Loadable::Data(Some(user)), |         Msg::UserReceived(user) => { | ||||||
|  |             model.user = Loadable::Data(Some(user)); | ||||||
|  |             model.page = Page::init(model.current_url.clone(), orders, model.i18n.clone()); | ||||||
|  |         } | ||||||
|         Msg::NotAuthenticated => { |         Msg::NotAuthenticated => { | ||||||
|             if model.user.is_some() { |             if model.user.is_some() { | ||||||
|                 model.user = Loadable::Data(None); |                 model.user = Loadable::Data(None); | ||||||
|                 logout(orders) |                 logout(orders) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         Msg::Logout => { | ||||||
|  |             model.user = Loadable::Data(None); | ||||||
|  |             logout(orders) | ||||||
|  |         } | ||||||
|         Msg::Login => login_user(model, orders), |         Msg::Login => login_user(model, orders), | ||||||
|         Msg::UsernameChanged(s) => model.login_data.username = s, |         Msg::UsernameChanged(s) => model.login_data.username = s, | ||||||
|         Msg::PasswordChanged(s) => model.login_data.password = s, |         Msg::PasswordChanged(s) => model.login_data.password = s, | ||||||
| @ -162,7 +167,7 @@ fn logout(orders: &mut impl Orders<Msg>) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn login_user(model: &mut Model, orders: &mut impl Orders<Msg>) { | fn login_user(model: &mut Model, orders: &mut impl Orders<Msg>) { | ||||||
|     orders.skip(); // No need to rerender
 |     model.user = Loadable::Loading; | ||||||
|     let data = model.login_data.clone(); |     let data = model.login_data.clone(); | ||||||
| 
 | 
 | ||||||
|     orders.perform_cmd(async { |     orders.perform_cmd(async { | ||||||
| @ -245,13 +250,13 @@ impl<'a> Urls<'a> { | |||||||
| fn view(model: &Model) -> Node<Msg> { | fn view(model: &Model) -> Node<Msg> { | ||||||
|     div![ |     div![ | ||||||
|         C!["page"], |         C!["page"], | ||||||
|         if let Some(ref user) = *model.user { |         match model.user { | ||||||
|             div![ |             Loadable::Data(Some(ref user)) => div![ | ||||||
|                 navigation::navigation(&model.i18n, &model.base_url, user), |                 navigation::navigation(&model.i18n, &model.base_url, user), | ||||||
|                 view_content(&model.page, &model.base_url) |                 view_content(&model.page, &model.base_url) | ||||||
|             ] |             ], | ||||||
|         } else { |             Loadable::Data(None) => view_login(&model.i18n, model), | ||||||
|             view_login(&model.i18n, model) |             Loadable::Loading => div![C!("lds-ellipsis"), div!(), div!(), div!(), div!()], | ||||||
|         } |         } | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,9 +2,10 @@ use std::cell::RefCell; | |||||||
| 
 | 
 | ||||||
| use enum_map::EnumMap; | use enum_map::EnumMap; | ||||||
| use fluent::fluent_args; | use fluent::fluent_args; | ||||||
|  | use qrcode::{render::svg, QrCode}; | ||||||
| use seed::{ | use seed::{ | ||||||
|     a, attrs, button, div, h1, img, input, log, prelude::*, section, span, table, td, th, tr, Url, |     a, attrs, button, div, h1, img, input, log, prelude::*, raw, section, span, table, td, th, tr, | ||||||
|     C, |     Url, C, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use shared::{ | use shared::{ | ||||||
| @ -508,7 +509,7 @@ fn view_link_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> N | |||||||
|     ] |     ] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// display a single link
 | /// display a single table row containing one link
 | ||||||
| fn view_link(l: &FullLink) -> Node<Msg> { | fn view_link(l: &FullLink) -> Node<Msg> { | ||||||
|     // Ugly hack - this is needed to be able to move the l into the closures... l.clone() in place does not work.
 |     // Ugly hack - this is needed to be able to move the l into the closures... l.clone() in place does not work.
 | ||||||
|     let link = LinkDelta::from(l.clone()); |     let link = LinkDelta::from(l.clone()); | ||||||
| @ -598,6 +599,10 @@ fn edit_or_create_link<F: Fn(&str) -> String>(l: &RefCell<LinkDelta>, t: F) -> N | |||||||
|                     }, |                     }, | ||||||
|                     input_ev(Ev::Input, |s| { Msg::Edit(EditMsg::EditCodeChanged(s)) }), |                     input_ev(Ev::Input, |s| { Msg::Edit(EditMsg::EditCodeChanged(s)) }), | ||||||
|                 ],] |                 ],] | ||||||
|  |             ], | ||||||
|  |             tr![ | ||||||
|  |                 th![t("qr-code")], | ||||||
|  |                 td![raw!(&generate_qr_code(&format!("http://{}", &link.code)))] | ||||||
|             ] |             ] | ||||||
|         ], |         ], | ||||||
|         a![ |         a![ | ||||||
| @ -611,6 +616,20 @@ fn edit_or_create_link<F: Fn(&str) -> String>(l: &RefCell<LinkDelta>, t: F) -> N | |||||||
|     ] |     ] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn generate_qr_code(link: &str) -> String { | ||||||
|  |     if let Ok(qr) = QrCode::with_error_correction_level(&link, qrcode::EcLevel::L) { | ||||||
|  |         let svg = qr | ||||||
|  |             .render() | ||||||
|  |             .min_dimensions(100, 100) | ||||||
|  |             .dark_color(svg::Color("#000000")) | ||||||
|  |             .light_color(svg::Color("#ffffff")) | ||||||
|  |             .build(); | ||||||
|  |         svg | ||||||
|  |     } else { | ||||||
|  |         "".to_string() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// a close button for dialogs
 | /// a close button for dialogs
 | ||||||
| fn close_button() -> Node<Msg> { | fn close_button() -> Node<Msg> { | ||||||
|     div![ |     div![ | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user