2020-05-12 00:35:07 +00:00
use horrorshow ::helper ::doctype ;
use horrorshow ::prelude ::* ;
use horrorshow ::{ box_html , owned_html } ;
use crate ::index ;
2020-05-12 04:00:44 +00:00
use crate ::page ::{ Parsed , Tag } ;
#[ derive(Clone, Debug) ]
pub struct PageState {
// currently rendered path
pub path : String ,
pub edit : bool ,
pub page : Option < Parsed > ,
2020-05-15 01:29:40 +00:00
pub original_page_id : Option < crate ::page ::Id > ,
2020-05-13 06:53:33 +00:00
pub subtags : Vec < ( String , usize ) > ,
2020-05-12 04:00:44 +00:00
}
2020-05-12 00:35:07 +00:00
pub fn html_page ( body : impl RenderOnce ) -> impl RenderOnce {
owned_html! {
: doctype ::HTML ;
head {
link ( rel = " stylesheet " , href = " https://unpkg.com/purecss@2.0.1/build/pure-min.css " , crossorigin = " anonymous " ) ;
link ( rel = " stylesheet " , href = " https://unpkg.com/purecss@2.0.1/build/grids-responsive-min.css " ) ;
meta ( name = " viewport " , content = " width=device-width, initial-scale=1 " ) ;
link ( rel = " stylesheet " , media = " all " , href = " /_style.css " ) ;
}
body {
2020-05-12 07:15:27 +00:00
: body ;
script ( src = " /_script.js " ) ;
2020-05-12 00:35:07 +00:00
}
}
}
2020-05-12 04:00:44 +00:00
pub fn page ( page_state : PageState ) -> Box < dyn RenderBox > {
2020-05-13 06:53:33 +00:00
if page_state . edit . clone ( ) {
Box ::new ( page_editing_view ( page_state . clone ( ) ) ) as Box < dyn RenderBox >
2020-05-12 00:35:07 +00:00
} else {
2020-05-13 06:53:33 +00:00
let page_state_clone = page_state . clone ( ) ;
let sub_pages = owned_html! {
@ if ! page_state_clone . subtags . is_empty ( ) {
2020-05-15 01:29:40 +00:00
h1 { : " Subtags " }
2020-05-13 06:53:33 +00:00
ul {
@ for tag in & page_state_clone . subtags {
li {
a ( href = format! ( " ./ {} / " , tag . 0 ) ) : format ! ( " {} ({}) " , tag . 0 , tag . 1 )
}
}
}
}
} ;
Box ::new ( page_view ( page_state . clone ( ) , sub_pages ) ) as Box < dyn RenderBox >
2020-05-12 00:35:07 +00:00
}
}
2020-05-12 04:00:44 +00:00
pub fn page_editing_view ( page_state : PageState ) -> impl RenderOnce {
if let Some ( page ) = page_state . page . as_ref ( ) {
2020-05-12 00:35:07 +00:00
let body = page . source_body . clone ( ) ;
2020-05-12 04:00:44 +00:00
menu (
page_state . clone ( ) ,
Some (
( box_html! {
2020-05-13 06:53:33 +00:00
textarea ( name = " body " , id = " source-editor " , class = " append " , autofocus ) {
2020-05-12 04:00:44 +00:00
: body
}
} ) as Box < dyn RenderBox > ,
) ,
)
2020-05-12 00:35:07 +00:00
} else {
2020-05-13 06:53:33 +00:00
let starting_tags = page_state
. path
. split ( " / " )
. filter ( | t | ! t . trim ( ) . is_empty ( ) )
. map ( | t | format! ( " # {} " , t ) )
. collect ::< Vec < _ > > ( )
. join ( " " ) ;
let starting_text = " \n \n \n " . to_string ( ) + & starting_tags ;
2020-05-12 04:00:44 +00:00
menu (
page_state . clone ( ) ,
Some (
( box_html! {
2020-05-13 06:53:33 +00:00
textarea ( name = " body " , id = " source-editor " , class = " prepend " , autofocus ) {
: starting_text
}
2020-05-12 04:00:44 +00:00
} ) as Box < dyn RenderBox > ,
) ,
)
2020-05-12 00:35:07 +00:00
}
}
2020-05-12 04:00:44 +00:00
pub fn menu ( page_state : PageState , subform : Option < Box < dyn RenderBox > > ) -> impl RenderOnce {
let id = page_state . page . map ( | p | p . id ( ) . to_owned ( ) ) ;
let edit = page_state . edit ;
2020-05-15 01:29:40 +00:00
let original_page_id = page_state . original_page_id ;
2020-05-13 06:53:33 +00:00
let path_tags : String = page_state
. path
. split ( " / " )
. filter ( | f | ! f . trim ( ) . is_empty ( ) )
. collect ::< Vec < & str > > ( )
. join ( " " ) ;
2020-05-12 04:00:44 +00:00
2020-05-13 06:53:33 +00:00
// # The sucky menu mega-form
// I really want one top-bar with all the buttons, and because I want
// everything to work even without JS enabled, all stuff here is quirky
// * GET queries are just links to avoid conflicting with other inputs
// * other buttons use `_method=METHOD` and `formaction` and `formmethod` + server-side redirect
// * query text uses a server-side redirect on `q`
// If more stuff is cramed in here, this will all eventually fall appart. :)
2020-05-12 00:35:07 +00:00
owned_html! {
2020-05-13 06:53:33 +00:00
form ( class = " pure-form " ) {
2020-05-12 04:00:44 +00:00
div ( class = " pure-menu pure-menu-horizontal " ) {
@ if let Some ( id ) = id . as_deref ( ) {
input ( type = " hidden " , name = " id " , value = id ) ;
2020-05-12 00:35:07 +00:00
}
2020-05-12 04:00:44 +00:00
@ if edit {
@ if let Some ( id ) = id . as_deref ( ) {
2020-05-12 07:15:27 +00:00
a ( href = format! ( " ?id= {} " , id ) , class = " pure-button " , id = " cancel-button " ) { : " Cancel " }
2020-05-12 04:00:44 +00:00
: " " ;
} else {
2020-05-12 07:15:27 +00:00
a ( href = " javascript:history.back() " , class = " pure-button " , id = " cancel-button " ) { : " Cancel " }
2020-05-12 04:00:44 +00:00
: " " ;
}
} else {
2020-05-15 01:29:40 +00:00
@ if let Some ( _id ) = original_page_id {
a ( href = " . " , class = " pure-button " , id = " up-button " ) { : " Up " }
} else {
a ( href = " .. " , class = " pure-button " , id = " up-button " ) { : " Up " }
}
2020-05-12 04:00:44 +00:00
: " " ;
2020-05-12 00:35:07 +00:00
}
2020-05-12 04:00:44 +00:00
@ if edit {
@ if let Some ( _id ) = id . as_deref ( ) {
2020-05-12 07:15:27 +00:00
button ( type = " submit " , id = " save-button " , class = " pure-button pure-button-primary " , formaction = " . " , formmethod = " post " ) {
: Raw ( " <u>S</u>ave " )
2020-05-12 04:00:44 +00:00
}
} else {
2020-05-12 07:15:27 +00:00
button ( type = " submit " , id = " save-button " , class = " pure-button pure-button-primary " , formaction = " . " , formmethod = " post " , name = " _method " , value = " put " ) {
: Raw ( " <u>S</u>ave " )
2020-05-12 04:00:44 +00:00
}
}
: " " ;
} else {
2020-05-13 06:53:33 +00:00
a ( href = " ?edit=true " , id = " new-button " , class = " pure-button button-green " ) { : Raw ( " <u>N</u>ew " ) }
2020-05-12 04:00:44 +00:00
: " " ;
2020-05-12 00:35:07 +00:00
}
2020-05-12 07:15:27 +00:00
@ if ! edit & & id . is_some ( ) {
2020-05-13 06:53:33 +00:00
a ( type = " submit " , href = format! ( " ?id= {} &edit=true " , id . as_ref ( ) . unwrap ( ) ) , id = " edit-button " , class = " pure-button pure-button-primary " ) {
2020-05-12 07:15:27 +00:00
: Raw ( " <u>E</u>dit " )
2020-05-12 04:00:44 +00:00
}
: " " ;
2020-05-12 07:15:27 +00:00
button ( type = " submit " , id = " delete-button " , class = " pure-button button-warning " , formaction = " . " , formmethod = " post " , name = " _method " , value = " delete " , onclick = " return confirm('Are you sure?'); " ) {
: Raw ( " <u>D</u>elete " )
2020-05-12 04:00:44 +00:00
}
2020-05-12 00:35:07 +00:00
}
2020-05-13 06:53:33 +00:00
: " " ;
button ( type = " submit " , id = " query-button " , class = " pure-button float-right " , formaction = " /_query " , formmethod = " get " ) {
: " Search "
}
input ( type = " text " , class = " float-right " , id = " query-text " , name = " q " , placeholder = " tag1 tag2... " , value = path_tags ) ;
2020-05-12 00:35:07 +00:00
}
2020-05-12 04:00:44 +00:00
: subform
2020-05-12 00:35:07 +00:00
}
2020-05-12 04:00:44 +00:00
}
}
2020-05-13 06:53:33 +00:00
pub fn page_view ( page_state : PageState , sub_pages : impl RenderOnce ) -> impl RenderOnce {
2020-05-12 04:00:44 +00:00
let menu = menu ( page_state . clone ( ) , None ) ;
let page = page_state . page . expect ( " always some " ) ;
let page_html = page . html . clone ( ) ;
owned_html! {
: menu ;
2020-05-13 06:53:33 +00:00
article ( id = " page-content " ) {
: Raw ( page_html ) ;
: sub_pages ;
}
2020-05-12 00:35:07 +00:00
}
}
pub fn post_list (
2020-05-12 07:15:27 +00:00
page_state : PageState ,
2020-05-12 00:35:07 +00:00
unmatched_tags : impl Iterator < Item = ( Tag , usize ) > ,
posts : impl Iterator < Item = index ::PageInfo > + 'static ,
) -> impl RenderOnce {
2020-05-12 07:15:27 +00:00
let menu = menu ( page_state . clone ( ) , None ) ;
2020-05-12 00:35:07 +00:00
owned_html! {
2020-05-12 07:15:27 +00:00
: menu ;
2020-05-13 06:53:33 +00:00
div ( id = " page-content " ) {
2020-05-15 01:29:40 +00:00
h1 { : " Pages " }
2020-05-13 06:53:33 +00:00
ul ( id = " index " ) {
@ for post in posts {
li {
a ( href = format! ( " ./?id= {} " , post . id ) ) : post . title
}
2020-05-12 00:35:07 +00:00
}
2020-05-15 01:29:40 +00:00
}
h1 { : " Subtags " }
ul ( id = " index " ) {
2020-05-13 06:53:33 +00:00
@ for tag in unmatched_tags {
li {
a ( href = format! ( " ./ {} / " , tag . 0 ) ) : format ! ( " {} ({}) " , tag . 0 , tag . 1 )
}
2020-05-12 00:35:07 +00:00
}
}
}
}
}