tinki/
lib.rs

1use log::*;
2use sauron::{prelude::*, Cmd, Component, Node, Program};
3use std::collections::BTreeMap;
4use url_path::UrlPath;
5use wasm_bindgen::prelude::*;
6
7// Use `wee_alloc` as the global allocator.
8#[global_allocator]
9static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
10
11#[derive(Clone)]
12pub enum Msg {
13    UrlChanged(String),
14    FileReady(Result<String, JsValue>),
15}
16
17pub struct App {
18    raw: String,
19    content: String,
20    title: Option<String>,
21    current_file: UrlPath,
22}
23
24impl App {
25    fn new() -> Self {
26        let hash = Browser::get_hash();
27        let current_file = UrlPath::new(Self::clean_link(&hash));
28        App {
29            raw: String::new(),
30            content: String::new(),
31            title: None,
32            current_file,
33        }
34    }
35
36    fn clean_link(url: &str) -> &str {
37        let url = url.trim_start_matches("#/");
38        let url = url.trim_start_matches("#");
39		if url.trim().is_empty(){
40			"index.md"
41		}else{
42			url
43		}
44    }
45
46    fn fetch_current_file(&self) -> Cmd<App, Msg> {
47        let url = self.current_file.normalize();
48        trace!("fetching {}", url);
49        Http::fetch_with_text_response_decoder(&url, |v| v, Msg::FileReady)
50    }
51
52    fn markdown_to_html(&mut self) {
53        let embed_files = BTreeMap::new();
54        if let Ok(html) = spongedown::parse_with_base_dir(
55            &self.raw,
56            &self.current_file.parent().unwrap_or("/".to_string()),
57            &Some(embed_files),
58        ) {
59            self.title = html.title;
60            self.content = html.content;
61            if let Some(title) = &self.title{
62                Window::set_title(&title);
63            }
64        }
65    }
66}
67
68impl Component<Msg> for App {
69    fn init(&self) -> Cmd<App, Msg> {
70        Cmd::batch(vec![
71            Browser::onhashchange(Msg::UrlChanged),
72            self.fetch_current_file(),
73        ])
74    }
75    fn update(&mut self, msg: Msg) -> Cmd<App, Msg> {
76        match msg {
77            Msg::UrlChanged(url) => {
78                let url = Self::clean_link(&url);
79                trace!("url changed: {}", url);
80                self.current_file = UrlPath::new(url);
81                self.fetch_current_file()
82            }
83            Msg::FileReady(file) => {
84                Browser::scroll_to_top();
85                match file {
86                    Ok(raw) => {
87                        trace!("ok got file");
88                        self.raw = raw;
89                        self.markdown_to_html();
90                    }
91                    Err(_e) => {
92						//TODO: deal with errors here
93						// 404 error, http request error
94						error!("display 404 here")
95					}
96                }
97                Cmd::none()
98            }
99        }
100    }
101
102    fn view(&self) -> Node<Msg> {
103        div(
104            vec![],
105            vec![
106                nav(vec![], vec![a(vec![href("/")], vec![text("Home")])]),
107                article(vec![inner_html(&self.content)], vec![]),
108            ],
109        )
110    }
111}
112
113#[wasm_bindgen(start)]
114pub fn main() {
115    console_error_panic_hook::set_once();
116    console_log::init_with_level(log::Level::Trace).unwrap();
117    Program::mount_to_body(App::new());
118}