WIP: Simple frontend and backend to search notmuch mail.
This commit is contained in:
32
web/Cargo.toml
Normal file
32
web/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
version = "0.1.0"
|
||||
name = "letterbox"
|
||||
repository = "https://github.com/seed-rs/seed-quickstart"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
description = "App Description"
|
||||
categories = ["category"]
|
||||
license = "MIT"
|
||||
readme = "./README.md"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.18"
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
log = "0.4.14"
|
||||
seed = "0.8.0"
|
||||
console_log = {git = "http://git.z.xinu.tv/wathiede/console_log"}
|
||||
serde = "1.0.126"
|
||||
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
codegen-units = 1
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ['-Os']
|
||||
71
web/Makefile.toml
Normal file
71
web/Makefile.toml
Normal file
@@ -0,0 +1,71 @@
|
||||
[env]
|
||||
PORT = "8000"
|
||||
|
||||
[config]
|
||||
skip_core_tasks = true
|
||||
|
||||
# ---- BASIC ----
|
||||
|
||||
[tasks.watch]
|
||||
description = "Watch files and recompile the project on change"
|
||||
run_task = [
|
||||
{ name = "build" },
|
||||
]
|
||||
watch = true
|
||||
|
||||
[tasks.serve]
|
||||
description = "Start server"
|
||||
install_crate = { crate_name = "microserver", binary = "microserver", test_arg = "-h" }
|
||||
command = "microserver"
|
||||
args = ["--port", "${PORT}"]
|
||||
|
||||
[tasks.verify]
|
||||
description = "Format, lint with Clippy and run tests"
|
||||
dependencies = ["fmt", "clippy", "test_h_firefox"]
|
||||
|
||||
# ---- BUILD ----
|
||||
|
||||
[tasks.build]
|
||||
description = "Build with wasm-pack"
|
||||
install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" }
|
||||
command = "wasm-pack"
|
||||
args = ["build", "--target", "web", "--out-name", "package", "--dev"]
|
||||
|
||||
[tasks.build_release]
|
||||
description = "Build with wasm-pack in release mode"
|
||||
install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" }
|
||||
command = "wasm-pack"
|
||||
args = ["build", "--target", "web", "--out-name", "package"]
|
||||
|
||||
# ---- LINT ----
|
||||
|
||||
[tasks.clippy]
|
||||
description = "Lint with Clippy"
|
||||
install_crate = { rustup_component_name = "clippy", binary = "cargo-clippy", test_arg = "--help" }
|
||||
command = "cargo"
|
||||
args = ["clippy", "--all-features", "--", "--deny", "warnings", "--deny", "clippy::pedantic", "--deny", "clippy::nursery"]
|
||||
|
||||
[tasks.fmt]
|
||||
description = "Format with rustfmt"
|
||||
install_crate = { rustup_component_name = "rustfmt", binary = "rustfmt", test_arg = "-V" }
|
||||
command = "cargo"
|
||||
args = ["fmt"]
|
||||
|
||||
|
||||
# ---- TEST ----
|
||||
|
||||
[tasks.test_h]
|
||||
description = "Run headless tests. Ex: 'cargo make test_h firefox'. Test envs: [chrome, firefox, safari]"
|
||||
extend = "test"
|
||||
args = ["test", "--headless", "--${@}"]
|
||||
|
||||
[tasks.test_h_firefox]
|
||||
description = "Run headless tests with Firefox."
|
||||
extend = "test"
|
||||
args = ["test", "--headless", "--firefox"]
|
||||
|
||||
[tasks.test]
|
||||
description = "Run tests. Ex: 'cargo make test firefox'. Test envs: [chrome, firefox, safari]"
|
||||
install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" }
|
||||
command = "wasm-pack"
|
||||
args = ["test", "--${@}"]
|
||||
76
web/README.md
Normal file
76
web/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Seed Quickstart
|
||||
|
||||
> Basic Rust-only template for your new Seed app.
|
||||
|
||||
## 1. Create a new project
|
||||
|
||||
1. You can use [cargo generate](https://github.com/ashleygwilliams/cargo-generate) to use this template.
|
||||
|
||||
```bash
|
||||
$ cargo generate --git https://github.com/seed-rs/seed-quickstart.git --name my-project
|
||||
$ cd my-project
|
||||
```
|
||||
|
||||
1. Alternatively, simply click on the green button **Use this template** on the GitHub [profile](https://github.com/seed-rs/seed-quickstart) of this quickstart.
|
||||
|
||||
1. Make sure Git doesn't automatically convert your newlines to CRLF because linters don't like it.
|
||||
- Run `$ git config --global core.autocrlf` in your terminal and it should return `input` or `false`. See [Git docs](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) for more info.
|
||||
|
||||
1. Clone your new repository to your local machine. I use [GitKraken](https://www.gitkraken.com/), but you are probably a better developer than me - use your favorite terminal.
|
||||
|
||||
## 2. Install / check required tools
|
||||
|
||||
1. Make sure you have basic tools installed:
|
||||
|
||||
- [Rust](https://www.rust-lang.org)
|
||||
- Check: `$ rustc -V` => `rustc 1.43.1 (8d69840ab 2020-05-04)`
|
||||
- Install: https://www.rust-lang.org/tools/install
|
||||
- [cargo-make](https://sagiegurari.github.io/cargo-make/)
|
||||
- Check: `$ cargo make -V` => `cargo-make 0.30.7`
|
||||
- Install: `$ cargo install cargo-make`
|
||||
|
||||
1. Platform-specific tools like `ssl` and `pkg-config`:
|
||||
- Follow recommendations in build errors (during the next chapter).
|
||||
- _Note_: Don't hesitate to write notes or a tutorial for your platform and create a PR .
|
||||
|
||||
## 3. Prepare your project for work
|
||||
|
||||
1. Open the project in your favorite IDE (I recommend [VS Code](https://code.visualstudio.com/) + [Rust Analyzer](https://rust-analyzer.github.io/)).
|
||||
1. Open a new terminal tab / window and run: `cargo make serve`
|
||||
1. Open a second terminal tab and run: `cargo make watch`
|
||||
1. If you see errors, try to fix them or write on our [chat](https://discord.gg/JHHcHp5) or [forum](https://seed.discourse.group/).
|
||||
1. Modify files like `README.md` and `Cargo.toml` as you wish.
|
||||
|
||||
## 4. Write your website
|
||||
|
||||
1. Open [localhost:8000](http://localhost:8000) in a browser (I recommend Firefox and Chrome).
|
||||
1. Modify source files (e.g. `/src/lib.rs` or `/index.html`).
|
||||
1. Watch compilation in the terminal tab where you run `cargo make watch`.
|
||||
1. You can watch dev-server responses in the tab where you run `cargo make serve`.
|
||||
1. Refresh your browser and see changes.
|
||||
1. Go to step 2.
|
||||
|
||||
## 5. Prepare your project for deploy
|
||||
|
||||
1. Run `cargo make verify` in your terminal to format and lint the code.
|
||||
1. Run `cargo make build_release`.
|
||||
1. Upload `index.html` and `pkg` into your server's public folder.
|
||||
- Don't forget to upload also configuration files for your hosting, see the [Netlify](https://www.netlify.com/) one below.
|
||||
|
||||
```toml
|
||||
# netlify.toml
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
```
|
||||
|
||||
## Other Seed quickstarts and projects
|
||||
|
||||
- [seed-rs/awesome-seed-rs](https://github.com/seed-rs/awesome-seed-rs)
|
||||
|
||||
---
|
||||
|
||||
**!!! New Rust-only quickstart in development! => [Seeder](https://github.com/MartinKavik/seeder) !!!**
|
||||
|
||||
---
|
||||
19
web/index.html
Normal file
19
web/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="modulepreload" href="/pkg/package.js" as="script" type="text/javascript">
|
||||
<link rel="preload" href="/pkg/package_bg.wasm" as="fetch" type="application/wasm" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section id="app"></section>
|
||||
<script type="module">
|
||||
import init from '/pkg/package.js';
|
||||
init('/pkg/package_bg.wasm');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
161
web/src/lib.rs
Normal file
161
web/src/lib.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
// (Lines like the one below ignore selected Clippy rules
|
||||
// - it's useful when you want to check your code with `cargo make verify`
|
||||
// but some rules are too "annoying" or are not applicable for your case.)
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
|
||||
use log::{error, info, Level};
|
||||
use seed::{prelude::*, *};
|
||||
use serde::Deserialize;
|
||||
use web_sys::HtmlInputElement;
|
||||
|
||||
// ------ ------
|
||||
// Init
|
||||
// ------ ------
|
||||
|
||||
// `init` describes what should happen when your app started.
|
||||
fn init(_: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
orders
|
||||
.skip()
|
||||
.perform_cmd(async { Msg::SearchResult(search_request("is:unread").await) });
|
||||
Model {
|
||||
search_results: None,
|
||||
}
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// Model
|
||||
// ------ ------
|
||||
|
||||
// `Model` describes our app state.
|
||||
struct Model {
|
||||
search_results: Option<Vec<SearchResult>>,
|
||||
}
|
||||
|
||||
/*
|
||||
[
|
||||
{
|
||||
"thread": "0000000000022fa5",
|
||||
"timestamp": 1634947104,
|
||||
"date_relative": "Yest. 16:58",
|
||||
"matched": 1,
|
||||
"total": 1,
|
||||
"authors": "Blue Cross Blue Shield Claims Administrator",
|
||||
"subject": "BCBS Settlement Claim Received",
|
||||
"query": [
|
||||
"id:20211022235824.232afe38e3e07950@bcbssettlement.com",
|
||||
null
|
||||
],
|
||||
"tags": [
|
||||
"inbox"
|
||||
]
|
||||
}
|
||||
]
|
||||
*/
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct SearchResult {
|
||||
thread: String,
|
||||
timestamp: u64,
|
||||
date_relative: String,
|
||||
matched: isize,
|
||||
total: isize,
|
||||
authors: String,
|
||||
subject: String,
|
||||
// TODO(wathiede): what are these?
|
||||
//query:Vec<_>,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// Update
|
||||
// ------ ------
|
||||
|
||||
// (Remove the line below once any of your `Msg` variants doesn't implement `Copy`.)
|
||||
// `Msg` describes the different events you can modify state with.
|
||||
enum Msg {
|
||||
SearchRequest(String),
|
||||
// TODO(wathiede): replace String with serde json decoded struct
|
||||
SearchResult(fetch::Result<Vec<SearchResult>>),
|
||||
}
|
||||
|
||||
// `update` describes how to handle each `Msg`.
|
||||
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::SearchRequest(query) => {
|
||||
orders
|
||||
.skip()
|
||||
.perform_cmd(async move { Msg::SearchResult(search_request(&query).await) });
|
||||
}
|
||||
|
||||
Msg::SearchResult(Ok(response_data)) => {
|
||||
info!("fetch ok {:?}", response_data);
|
||||
model.search_results = Some(response_data);
|
||||
}
|
||||
|
||||
Msg::SearchResult(Err(fetch_error)) => {
|
||||
error!("fetch failed {:?}", fetch_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn search_request(query: &str) -> fetch::Result<Vec<SearchResult>> {
|
||||
Request::new(get_search_request_url(query))
|
||||
.method(Method::Get)
|
||||
.fetch()
|
||||
.await?
|
||||
.check_status()?
|
||||
.json()
|
||||
.await
|
||||
}
|
||||
|
||||
fn get_search_request_url(query: &str) -> String {
|
||||
format!("http://nixos-07:9345/search/{}", query)
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// View
|
||||
// ------ ------
|
||||
|
||||
// `view` describes what to display.
|
||||
fn view(model: &Model) -> Node<Msg> {
|
||||
let results = if let Some(res) = &model.search_results {
|
||||
div![res.iter().map(|r| p![
|
||||
h3![&r.subject, " ", small![&r.date_relative]],
|
||||
div![span![&r.authors]]
|
||||
])]
|
||||
} else {
|
||||
div![]
|
||||
};
|
||||
div![
|
||||
"Do Something: ",
|
||||
button![
|
||||
"Unread",
|
||||
ev(Ev::Click, |_| Msg::SearchRequest("is:unread".to_string())),
|
||||
],
|
||||
input![
|
||||
attrs! {
|
||||
At::Placeholder => "Search";
|
||||
At::AutoFocus => true.as_at_value();
|
||||
},
|
||||
input_ev(Ev::Input, Msg::SearchRequest),
|
||||
],
|
||||
results,
|
||||
]
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// Start
|
||||
// ------ ------
|
||||
|
||||
// (This function is invoked by `init` function in `index.html`.)
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
// This provides better error messages in debug mode.
|
||||
// It's disabled in release mode so it doesn't bloat up the file size.
|
||||
#[cfg(debug_assertions)]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let lvl = Level::Info;
|
||||
console_log::init_with_level(lvl).expect("failed to initialize console logging");
|
||||
// Mount the `app` to the element with the `id` "app".
|
||||
App::start("app", init, update, view);
|
||||
}
|
||||
Reference in New Issue
Block a user