This commit is contained in:
Pascal Engélibert 2023-05-02 18:03:21 +02:00 committed by Pascal Engélibert
commit 359d5909a8
46 changed files with 5400 additions and 815 deletions

98
webui/src/api.rs Normal file
View file

@ -0,0 +1,98 @@
use crate::types::*;
use webcomment_common::{api::*, types::*};
use gloo::{console, net::http};
use parking_lot::RwLock;
use std::collections::HashMap;
pub struct ApiInner {
pub admin_psw: Option<String>,
pub comments: HashMap<CommentId, StoredComment>,
/// Comments that are not yet attributed CommentId by the server
pub local_comments: HashMap<CommentId, StoredComment>,
pub url: String,
}
pub struct Api {
pub inner: RwLock<ApiInner>,
}
impl Api {
pub async fn get_comments_by_topic(&self, topic: String) {
match http::Request::post(&format!("{}/api/comments_by_topic", &self.inner.read().url))
.body(
serde_json::to_string(&queries::CommentsByTopic {
mutation_token: None,
topic,
})
.unwrap(),
)
.send()
.await
{
Ok(resp) => {
let Ok(Ok(resps::Response::CommentsByTopic(resp))) = resp.json::<resps::Result>().await else {
// TODO error
return;
};
let mut inner = self.inner.write();
for comment in resp.approved_comments {
let Ok(comment_id) = CommentId::from_base64(&comment.id) else {
continue
};
inner.comments.insert(
comment_id,
StoredComment {
author: comment.author,
email: comment.email,
last_edit_time: comment.last_edit_time,
post_time: comment.post_time,
text: comment.text,
},
);
}
}
Err(e) => console::log!("get_comments_by_topic: {e:?}"),
}
}
pub async fn new_comment(&self, new_comment: StoredComment, topic: String) {
let local_id = CommentId::new();
self.inner.write().local_comments.insert(local_id.clone(), new_comment.clone());
match http::Request::post(&format!("{}/api/query_new_comment", &self.inner.read().url))
.body(
serde_json::to_string(&queries::NewComment {
author: new_comment.author,
topic,
email: new_comment.email.unwrap_or_default(),
text: new_comment.text,
})
.unwrap(),
)
.send()
.await
{
Ok(resp) => {
let Ok(Ok(resps::Response::NewComment(resp))) = resp.json::<resps::Result>().await else {
// TODO error
return;
};
let mut inner = self.inner.write();
let Ok(comment_id) = CommentId::from_base64(&resp.id) else {
// TODO error
return;
};
let Some(comment) = inner.local_comments.remove(&local_id) else {
// TODO error
return;
};
inner.comments.insert(
comment_id,
comment,
);
}
Err(e) => console::log!("get_comments_by_topic: {e:?}"),
}
}
}

4
webui/src/components.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod comment;
pub mod new_comment_form;
pub use comment::*;

View file

@ -0,0 +1,47 @@
use crate::types::*;
use yew::{html, Component, Context, Html, Properties};
pub struct CommentComponent {}
#[derive(Properties, PartialEq)]
pub struct CommentProps {
pub root_id: String, // TODO maybe opti
pub comment: FullComment,
}
impl Component for CommentComponent {
type Message = ();
type Properties = CommentProps;
fn create(_ctx: &Context<Self>) -> Self {
Self {}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
false
}
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
let comment_id = props.comment.id.to_base64();
let elem_id = format!("{}-{}", props.root_id, comment_id);
html! {
<div class={ format!("comment comment-{}", comment_id) } id={ elem_id.clone() }>
<p class="comment-meta">
<a class="comment-post_time" aria-label="Permalink" title="Permalink" href={ format!("#{elem_id}") }>{ props.comment.post_time }</a>
<span class="comment-author"></span>
{
if let Some(email) = &props.comment.email {
html! { <span class="comment-email">{ email }</span> }
} else {
html! {}
}
}
<a class="comment-edition comment-edition-remove" href="#">{ "Remove" }</a>
</p>
<p class="comment-text">{ &props.comment.text }</p>
</div>
}
}
}

View file

@ -0,0 +1,47 @@
use crate::types::*;
use yew::{html, Component, Context, Html, Properties};
pub struct NewCommentFormComponent {}
#[derive(Properties, PartialEq)]
pub struct NewCommentFormProps {
pub root_id: String, // TODO maybe opti
pub topic: String,
}
impl Component for NewCommentFormComponent {
type Message = ();
type Properties = NewCommentFormProps;
fn create(_ctx: &Context<Self>) -> Self {
Self {}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
false
}
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
let comment_id = props.comment.id.to_base64();
let elem_id = format!("{}-{}", props.root_id, comment_id);
html! {
<div class={ format!("comment comment-{}", comment_id) } id={ elem_id.clone() }>
<p class="comment-meta">
<a class="comment-post_time" aria-label="Permalink" title="Permalink" href={ format!("#{elem_id}") }>{ props.comment.post_time }</a>
<span class="comment-author"></span>
{
if let Some(email) = &props.comment.email {
html! { <span class="comment-email">{ email }</span> }
} else {
html! {}
}
}
<a class="comment-edition comment-edition-remove" href="#">{ "Remove" }</a>
</p>
<p class="comment-text">{ &props.comment.text }</p>
</div>
}
}
}

89
webui/src/lib.rs Normal file
View file

@ -0,0 +1,89 @@
mod api;
mod components;
mod types;
use gloo::console;
use js_sys::Date;
use parking_lot::RwLock;
use wasm_bindgen::prelude::*;
use webcomment_common::types::*;
use yew::{html, Component, Context, Html, Properties};
use crate::{components::*, types::*};
pub enum Msg {
Increment,
Decrement,
}
#[derive(Properties, PartialEq)]
pub struct AppProps {
root_id: String,
}
impl Default for AppProps {
fn default() -> Self {
Self {
root_id: String::from("comments"),
}
}
}
pub struct App {
value: i64,
}
impl Component for App {
type Message = Msg;
type Properties = AppProps;
fn create(_ctx: &Context<Self>) -> Self {
Self { value: 0 }
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Increment => {
self.value += 1;
console::log!("plus one"); // Will output a string to the browser console
true // Return true to cause the displayed change to update
}
Msg::Decrement => {
self.value -= 1;
console::log!("minus one");
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
let comment_props = CommentProps {
root_id: String::from("comments"),
comment: FullComment {
author: String::from("Toto"),
email: Some(String::from("toto@fai.tld")),
id: CommentId::new(),
last_edit_time: Some(123),
post_time: 42,
text: String::from("Bonjour"),
},
};
html! {
<div id={ props.root_id.clone() }>
<CommentComponent ..comment_props />
</div>
}
}
}
#[wasm_bindgen(start)]
async fn main_js() {
/*let api = api::Api {
inner: RwLock::new(api::ApiInner { admin_psw: None, comments: Default::default(), url: "http://127.0.0.1:31720".into() })
};
api.get_comments_by_topic("test".into()).await;*/
yew::Renderer::<App>::new().render();
}
fn main() {}

22
webui/src/types.rs Normal file
View file

@ -0,0 +1,22 @@
use webcomment_common::types::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StoredComment {
pub author: String,
pub email: Option<String>,
pub last_edit_time: Option<u64>,
pub post_time: u64,
pub text: String,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FullComment {
pub author: String,
pub email: Option<String>,
pub id: CommentId,
pub last_edit_time: Option<u64>,
pub post_time: u64,
pub text: String,
}