feat: Implement search result view & enable search

Implements a very simple and currently kinda broken-looking search
result view.
This commit is contained in:
Vincent Ambo 2018-04-14 22:06:30 +02:00 committed by Vincent Ambo
parent dae97fdaf5
commit 4132869277
6 changed files with 90 additions and 4 deletions

View file

@ -176,6 +176,22 @@ pub fn reply_thread(state: State<AppState>,
.responder() .responder()
} }
/// This handler executes a full-text search on the forum database and
/// displays the results to the user.
pub fn search_forum(state: State<AppState>,
query: Form<SearchPosts>) -> ConverseResponse {
let query_string = query.0.query.clone();
state.db.send(query.0)
.flatten()
.and_then(move |results| state.renderer.send(SearchResultPage {
results,
query: query_string,
}).from_err())
.flatten()
.map(|res| HttpResponse::Ok().content_type(HTML).body(res))
.responder()
}
/// This handler initiates an OIDC login. /// This handler initiates an OIDC login.
pub fn login(state: State<AppState>) -> ConverseResponse { pub fn login(state: State<AppState>) -> ConverseResponse {
state.oidc.send(GetLoginUrl) state.oidc.send(GetLoginUrl)

View file

@ -153,6 +153,7 @@ fn main() {
.resource("/thread/submit", |r| r.method(Method::POST).with3(submit_thread)) .resource("/thread/submit", |r| r.method(Method::POST).with3(submit_thread))
.resource("/thread/reply", |r| r.method(Method::POST).with3(reply_thread)) .resource("/thread/reply", |r| r.method(Method::POST).with3(reply_thread))
.resource("/thread/{id}", |r| r.method(Method::GET).with2(forum_thread)) .resource("/thread/{id}", |r| r.method(Method::GET).with2(forum_thread))
.resource("/search", |r| r.method(Method::POST).with2(search_forum))
.resource("/oidc/login", |r| r.method(Method::GET).with(login)) .resource("/oidc/login", |r| r.method(Method::GET).with(login))
.resource("/oidc/callback", |r| r.method(Method::POST).with3(callback)); .resource("/oidc/callback", |r| r.method(Method::POST).with3(callback));

View file

@ -74,7 +74,7 @@ pub struct NewPost {
/// This struct models the response of a full-text search query. It /// This struct models the response of a full-text search query. It
/// does not use a table/schema definition struct like the other /// does not use a table/schema definition struct like the other
/// tables, as no table of this type actually exists. /// tables, as no table of this type actually exists.
#[derive(QueryableByName, Debug)] #[derive(QueryableByName, Debug, Serialize)]
pub struct SearchResult { pub struct SearchResult {
#[sql_type = "Integer"] #[sql_type = "Integer"]
pub post_id: i32, pub post_id: i32,

View file

@ -175,3 +175,24 @@ impl Handler<NewThreadPage> for Renderer {
Ok(self.tera.render("new-thread.html", &ctx)?) Ok(self.tera.render("new-thread.html", &ctx)?)
} }
} }
/// Message used to render search results
pub struct SearchResultPage {
pub query: String,
pub results: Vec<SearchResult>,
}
impl Message for SearchResultPage {
type Result = Result<String>;
}
impl Handler<SearchResultPage> for Renderer {
type Result = Result<String>;
fn handle(&mut self, msg: SearchResultPage, _: &mut Self::Context) -> Self::Result {
let mut ctx = Context::new();
ctx.add("query", &msg.query);
ctx.add("results", &msg.results);
Ok(self.tera.render("search.html", &ctx)?)
}
}

View file

@ -14,9 +14,9 @@
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<h2>Converse</h2> <h2>Converse</h2>
</a> </a>
<form class="form-inline"> <form class="form-inline" method="post" action="/search">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" disabled> <input class="form-control mr-sm-2" type="search" placeholder="Search" name="query" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0 mr-1" type="submit" disabled>Search</button> <button class="btn btn-outline-success my-2 my-sm-0 mr-1" type="submit">Search</button>
<a class="btn btn-outline-secondary my-2" href="/thread/new">New thread</a> <a class="btn btn-outline-secondary my-2" href="/thread/new">New thread</a>
</form> </form>
</nav> </nav>

48
templates/search.html Normal file
View file

@ -0,0 +1,48 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<title>Converse Index</title>
</head>
<body>
<header>
<nav class="navbar navbar-light bg-light justify-content-between mb-3">
<a class="navbar-brand" href="/">
<h2>Converse</h2>
</a>
<form class="form-inline" method="post" action="/search">
<input class="form-control mr-sm-2" type="search" placeholder="Search" name="query" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0 mr-1" type="submit">Search</button>
<a class="btn btn-outline-secondary my-2" href="/thread/new">New thread</a>
<a class="btn btn-outline-secondary my-2" href="/">Back to index</a>
</form>
</nav>
</header>
<div class="container">
<div class="row">
<div class="col-4">
<h2>Search results for '{{ query }}':</h2>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="list-group">
{% for result in results -%}
<a href="/thread/{{ result.thread_id }}#post-{{ result.post_id }}" class="list-group-item flex-column list-group-item-action align-items-start">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">In thread '{{ result.title }}':</h5>
<div>{{ result.headline }}</div>
<div>(Posted by <i>{{ result.author }})</i></div>
</div>
</a>
{%- endfor %}
</div>
</div>
</div>
</div>
</body>
</html>