mirror of
https://github.com/Nutomic/ibis.git
synced 2024-12-23 20:01:23 +00:00
basic search input and results page
This commit is contained in:
parent
52121fae66
commit
baf1fb7505
12 changed files with 89 additions and 20 deletions
|
@ -6,12 +6,12 @@ use crate::backend::error::MyResult;
|
|||
use crate::backend::federation::activities::create_article::CreateArticle;
|
||||
use crate::backend::federation::activities::submit_article_update;
|
||||
use crate::backend::utils::generate_article_version;
|
||||
use crate::common::DbInstance;
|
||||
use crate::common::GetArticleData;
|
||||
use crate::common::LocalUserView;
|
||||
use crate::common::{ApiConflict, ResolveObject};
|
||||
use crate::common::{ArticleView, DbArticle, DbEdit};
|
||||
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
||||
use crate::common::{DbInstance, SearchArticleData};
|
||||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use anyhow::anyhow;
|
||||
|
@ -203,3 +203,13 @@ pub(super) async fn resolve_article(
|
|||
latest_version,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Search articles for matching title or body text.
|
||||
#[debug_handler]
|
||||
pub(super) async fn search_article(
|
||||
Query(query): Query<SearchArticleData>,
|
||||
data: Data<MyDataHandle>,
|
||||
) -> MyResult<Json<Vec<DbArticle>>> {
|
||||
let article = DbArticle::search(&query.query, &data.db_connection)?;
|
||||
Ok(Json(article))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::backend::api::article::{create_article, resolve_article};
|
||||
use crate::backend::api::article::{create_article, resolve_article, search_article};
|
||||
use crate::backend::api::article::{edit_article, fork_article, get_article};
|
||||
use crate::backend::api::instance::get_local_instance;
|
||||
use crate::backend::api::instance::{follow_instance, resolve_instance};
|
||||
|
@ -9,10 +9,9 @@ use crate::backend::api::user::{my_profile, AUTH_COOKIE};
|
|||
use crate::backend::database::conflict::DbConflict;
|
||||
use crate::backend::database::MyDataHandle;
|
||||
use crate::backend::error::MyResult;
|
||||
use crate::common::ApiConflict;
|
||||
use crate::common::LocalUserView;
|
||||
use crate::common::{ApiConflict, DbArticle, SearchArticleData};
|
||||
use activitypub_federation::config::Data;
|
||||
use axum::extract::Query;
|
||||
use axum::routing::{get, post};
|
||||
use axum::{
|
||||
http::Request,
|
||||
|
@ -85,13 +84,3 @@ async fn edit_conflicts(
|
|||
.collect();
|
||||
Ok(Json(conflicts))
|
||||
}
|
||||
|
||||
/// Search articles for matching title or body text.
|
||||
#[debug_handler]
|
||||
async fn search_article(
|
||||
Query(query): Query<SearchArticleData>,
|
||||
data: Data<MyDataHandle>,
|
||||
) -> MyResult<Json<Vec<DbArticle>>> {
|
||||
let article = DbArticle::search(&query.query, &data.db_connection)?;
|
||||
Ok(Json(article))
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ impl DbArticle {
|
|||
.replace('%', "\\%")
|
||||
.replace('_', "\\_")
|
||||
.replace(' ', "%");
|
||||
let replaced = format!("%{replaced}%");
|
||||
Ok(article::table
|
||||
.filter(
|
||||
article::dsl::title
|
||||
|
|
|
@ -44,6 +44,12 @@ pub struct DbArticle {
|
|||
pub local: bool,
|
||||
}
|
||||
|
||||
impl DbArticle {
|
||||
pub fn title(&self) -> String {
|
||||
self.title.replace('_', " ")
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single change to the article.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable))]
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::frontend::pages::article::read::ReadArticle;
|
|||
use crate::frontend::pages::diff::EditDiff;
|
||||
use crate::frontend::pages::login::Login;
|
||||
use crate::frontend::pages::register::Register;
|
||||
use crate::frontend::pages::search::Search;
|
||||
use crate::frontend::pages::Page;
|
||||
use leptos::{
|
||||
component, create_local_resource, create_rw_signal, expect_context, provide_context,
|
||||
|
@ -74,6 +75,7 @@ pub fn App() -> impl IntoView {
|
|||
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
||||
<Route path={Page::Login.path()} view=Login/>
|
||||
<Route path={Page::Register.path()} view=Register/>
|
||||
<Route path="/search" view=Search/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
|
|
|
@ -7,7 +7,7 @@ use leptos_router::*;
|
|||
pub fn ArticleNav(article: Resource<String, ArticleView>) -> impl IntoView {
|
||||
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
||||
view! {
|
||||
<Suspense fallback=|| view! { "Loading..." }>
|
||||
<Suspense>
|
||||
{move || article.get().map(|article| {
|
||||
let title = article.article.title;
|
||||
view!{
|
||||
|
|
|
@ -12,11 +12,28 @@ pub fn Nav() -> impl IntoView {
|
|||
.get_untracked()
|
||||
.update_my_profile();
|
||||
});
|
||||
let (search_query, set_search_query) = create_signal(String::new());
|
||||
view! {
|
||||
<nav class="inner">
|
||||
<li>
|
||||
<A href="/">"Main Page"</A>
|
||||
</li>
|
||||
<li>
|
||||
<form on:submit=move |ev| {
|
||||
ev.prevent_default();
|
||||
let navigate = leptos_router::use_navigate();
|
||||
let query = search_query.get();
|
||||
navigate(&format!("/search?query={query}"), Default::default());
|
||||
}>
|
||||
<input type="text" placeholder="Search"
|
||||
prop:value=search_query
|
||||
on:keyup=move |ev: ev::KeyboardEvent| {
|
||||
let val = event_target_value(&ev);
|
||||
set_search_query.update(|v| *v = val);
|
||||
} />
|
||||
<button>Go</button>
|
||||
</form>
|
||||
</li>
|
||||
<Show
|
||||
when=move || global_state.with(|state| state.my_profile.is_none())
|
||||
fallback=move || {
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn EditArticle() -> impl IntoView {
|
|||
set_text.set(article.article.text.clone());
|
||||
view! {
|
||||
<div class="item-view">
|
||||
<h1>{article.article.title.replace('_', " ")}</h1>
|
||||
<h1>{article.article.title()}</h1>
|
||||
<textarea on:keyup=move |ev| {
|
||||
let val = event_target_value(&ev);
|
||||
set_text.update(|p| *p = val);
|
||||
|
|
|
@ -13,13 +13,12 @@ pub fn ArticleHistory() -> impl IntoView {
|
|||
<ArticleNav article=article/>
|
||||
<Suspense fallback=|| view! { "Loading..." }> {
|
||||
move || article.get().map(|article| {
|
||||
let title = article.article.title;
|
||||
view! {
|
||||
<div class="item-view">
|
||||
<h1>{title.replace('_', " ")}</h1>
|
||||
<h1>{article.article.title()}</h1>
|
||||
{
|
||||
article.edits.into_iter().rev().map(|edit| {
|
||||
let path = format!("/article/{title}/diff/{}", edit.hash.0);
|
||||
let path = format!("/article/{}/diff/{}", article.article.title, edit.hash.0);
|
||||
// TODO: need to return username from backend and show it
|
||||
let label = format!("{} ({})", edit.summary, edit.created.to_rfc2822());
|
||||
view! {<li><a href={path}>{label}</a></li> }
|
||||
|
|
|
@ -21,7 +21,7 @@ pub fn ReadArticle() -> impl IntoView {
|
|||
move || article.get().map(|article|
|
||||
view! {
|
||||
<div class="item-view">
|
||||
<h1>{article.article.title.replace('_', " ")}</h1>
|
||||
<h1>{article.article.title()}</h1>
|
||||
<div inner_html={parser.parse(&article.article.text).render()}/>
|
||||
</div>
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@ pub(crate) mod article;
|
|||
pub(crate) mod diff;
|
||||
pub mod login;
|
||||
pub mod register;
|
||||
pub(crate) mod search;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum Page {
|
||||
|
|
44
src/frontend/pages/search.rs
Normal file
44
src/frontend/pages/search.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use crate::common::SearchArticleData;
|
||||
use crate::frontend::app::GlobalState;
|
||||
use leptos::*;
|
||||
use leptos_router::use_query_map;
|
||||
|
||||
#[component]
|
||||
pub fn Search() -> impl IntoView {
|
||||
let params = use_query_map();
|
||||
let query = params.get_untracked().get("query").cloned().unwrap();
|
||||
let query_ = query.clone();
|
||||
let search_results = create_resource(
|
||||
move || query_.clone(),
|
||||
move |query| async move {
|
||||
GlobalState::api_client()
|
||||
.search(&SearchArticleData { query })
|
||||
.await
|
||||
.unwrap()
|
||||
},
|
||||
);
|
||||
|
||||
view! {
|
||||
<h1>"Search results for "{query}</h1>
|
||||
<Suspense fallback=|| view! { "Loading..." }> {
|
||||
move || search_results.get().map(|search_results| {
|
||||
let is_empty = search_results.is_empty();
|
||||
view! {
|
||||
<Show when=move || !is_empty
|
||||
fallback=|| view! { <p>No results found</p> }>
|
||||
<ul>
|
||||
{
|
||||
search_results
|
||||
.iter()
|
||||
.map(|a| view! { <li>
|
||||
<a href={format!("/article/{}", a.title)}>{a.title()}</a>
|
||||
</li>})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
</ul>
|
||||
</Show>
|
||||
}})
|
||||
}
|
||||
</Suspense>
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue