Skip to content

Commit

Permalink
add commenting feature
Browse files Browse the repository at this point in the history
  • Loading branch information
The-DevBlog committed Feb 12, 2024
1 parent e2224cc commit f38e842
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 29 deletions.
17 changes: 13 additions & 4 deletions devblog/devblog/Controllers/AccountsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ public async Task<IActionResult> SignIn(SignIn signIn)
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo,
username = user.UserName.Normalize()
username = user.UserName.Normalize(),
authenticated = true,
});
}
else
Expand All @@ -161,12 +162,19 @@ public async Task<IActionResult> SignIn(SignIn signIn)
/// Signs out the currently sign in user
/// </summary>
/// <returns>Task<IActionResult></returns>
[Authorize]
//[Authorize]
[HttpPost("signout")]
public async Task<IActionResult> LogOut()
{
await _signInMgr.SignOutAsync();
return Ok();

return Ok(new
{
token = "",
expiration = "",
username = "",
authenticated = false
});
}

/// <summary>
Expand Down Expand Up @@ -215,7 +223,8 @@ public async Task<IActionResult> SignUp(User user)
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo,
username = user.UserName
username = user.UserName,
authenticated = true,
});
}
else
Expand Down
16 changes: 10 additions & 6 deletions devblog/devblog/client/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ const URL: &str = "https://localhost:44482/api/";

#[derive(Clone, PartialEq)]
pub enum Api {
AddComment,
DeleteCurrentAccount,
DeleteAccount(String),
GetPage(u32),
GetPost(i32),
GetPostsCount,
GetPagesCount,
GetUsers,
GetCurrentUser,
ToggleSubscribe,
SignIn,
SignUp,
DeleteCurrentAccount,
DeleteAccount(String),
SignOut,
ToggleSubscribe,
}

impl Api {
Expand Down Expand Up @@ -51,17 +53,19 @@ impl Api {

fn uri(&self) -> String {
match self {
Api::AddComment => format!("{}comments", URL),
Api::DeleteCurrentAccount => format!("{}accounts", URL),
Api::DeleteAccount(username) => format!("{}accounts/adminDelete/{}", URL, username),
Api::GetPage(num) => format!("{}posts/?page={}", URL, num),
Api::GetPost(id) => format!("{}posts/{}", URL, id),
Api::GetPostsCount => format!("{}posts/countPosts", URL),
Api::GetPagesCount => format!("{}posts/countPages", URL),
Api::GetUsers => format!("{}accounts", URL),
Api::GetCurrentUser => format!("{}accounts/user", URL),
Api::ToggleSubscribe => format!("{}accounts/subscribe", URL),
Api::SignIn => format!("{}accounts/signin", URL),
Api::SignUp => format!("{}accounts/signup", URL),
Api::DeleteCurrentAccount => format!("{}accounts", URL),
Api::DeleteAccount(username) => format!("{}accounts/adminDelete/{}", URL, username),
Api::SignOut => format!("{}accounts/signout", URL),
Api::ToggleSubscribe => format!("{}accounts/subscribe", URL),
}
}
}
77 changes: 77 additions & 0 deletions devblog/devblog/client/src/components/add_comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::{helpers, store::Store, Api, CommentModel};
use gloo::console::log;
use gloo_net::http::{Headers, Method};
use std::ops::Deref;
use stylist::Style;
use web_sys::{Event, HtmlInputElement, HtmlTextAreaElement, SubmitEvent};
use yew::prelude::*;
use yewdux::prelude::*;

const STYLE: &str = include_str!("styles/addComment.css");

#[derive(Properties, PartialEq)]
pub struct Props {
pub post_id: u32,
}

#[function_component(AddComment)]
pub fn add_comment(props: &Props) -> Html {
let style = Style::new(STYLE).unwrap();
let comment = use_state(CommentModel::default);
let store = use_store_value::<Store>();

let onchange = {
let comment = comment.clone();
Callback::from(move |e: Event| {
let mut comment_clone = comment.deref().clone();
let input = e.target_dyn_into::<HtmlTextAreaElement>();
if let Some(value) = input {
comment_clone.content = value.value();
comment.set(comment_clone);
}
})
};

let onsubmit = {
let comment = comment.clone();
let post_id = props.post_id.clone();
Callback::from(move |e: SubmitEvent| {
e.prevent_default();
let auth = format!("Bearer {}", store.token);
let hdrs = Headers::new();
hdrs.append("Authorization", &auth);
hdrs.append("content-type", "application/json");

let new_comment = CommentModel::new(
0,
post_id,
comment.deref().content.clone(),
store.username.clone(),
);
comment.set(new_comment.clone());

let body = Some(helpers::to_jsvalue(new_comment.clone()));
log!("Response: ", body.clone().unwrap());

wasm_bindgen_futures::spawn_local(async move {
let res = Api::AddComment.fetch(Some(hdrs), body, Method::POST).await;
});
})
};

html! {
<div class={style}>
<div class="create-comment">
<form {onsubmit}>
<textarea placeholder="your comments here..."
type="text"
required={true}
value={comment.content.clone()}
onchange={onchange}>
</textarea>
<button>{"Add Comment"}</button>
</form>
</div>
</div>
}
}
4 changes: 2 additions & 2 deletions devblog/devblog/client/src/components/footer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub fn footer() -> Html {
<div class={style}>
<div class="footer">
// <DeleteAccount />
// {loggedIn &&
if store.authenticated {
<span>{"Welcome "}{store.username.clone()}</span>
// }
}
</div>
</div>
}
Expand Down
1 change: 1 addition & 0 deletions devblog/devblog/client/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod add_comment;
pub mod comment;
pub mod footer;
pub mod items;
Expand Down
12 changes: 10 additions & 2 deletions devblog/devblog/client/src/components/navbar.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::router::Route;
use crate::{pages::sign_out::SignOut, router::Route, store::Store};
use stylist::Style;
use yew::prelude::*;
use yew_router::prelude::*;
use yewdux::use_store_value;

const STYLE: &str = include_str!("styles/navbar.css");

#[function_component(Navbar)]
pub fn navbar() -> Html {
let style = Style::new(STYLE).unwrap();
let store = use_store_value::<Store>();

html! {
<div class={style}>
Expand All @@ -25,8 +27,14 @@ pub fn navbar() -> Html {
<Link<Route> to={Route::About}>{"About"}</Link<Route>>
<Link<Route> to={Route::Insights}>{"Insights"}</Link<Route>>
<Link<Route> to={Route::Account}>{"Account"}</Link<Route>>

// <Link<Route> to={Route::SignOut}>{"SignOut"}</Link<Route>>

if store.authenticated {
<SignOut />
}

<Link<Route> to={Route::SignIn}>{"SignIn"}</Link<Route>>
<Link<Route> to={Route::SignOut}>{"SignOut"}</Link<Route>>
<Link<Route> to={Route::SignUp}>{"SignUp"}</Link<Route>>
</div>
</nav>
Expand Down
3 changes: 2 additions & 1 deletion devblog/devblog/client/src/components/post.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
components::{comment::Comment, vote::Vote},
components::{add_comment::AddComment, comment::Comment, vote::Vote},
PostModel,
};
use chrono::{Local, TimeZone};
Expand Down Expand Up @@ -41,6 +41,7 @@ pub fn post(props: &Props) -> Html {
<div>{&props.post.description}</div>

// COMMENTS
<AddComment post_id={props.post.id}/>
<div>
{for props.post.comments.iter().map(|comment| {
html! {<Comment comment={comment.clone()} />}
Expand Down
21 changes: 21 additions & 0 deletions devblog/devblog/client/src/components/styles/addComment.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.create-comment {
margin-bottom: 20px;
}

.create-comment form {
display: inline-flex;
flex-direction: column;
width: 100%;
}

.create-comment form input {
width: 10em;
margin: .5em 0 .5em 0;
border: 0 0 1px 0;
}

.create-comment button {
margin-top: .5em;
width: fit-content;
padding: 8px;
}
15 changes: 12 additions & 3 deletions devblog/devblog/client/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::store::Store;
use crate::{helpers, Api};
use crate::{router::Route, User, UserField};
use gloo::console::log;
// use gloo::utils::format::JsValueSerdeExt;
use gloo_net::http::{Headers, Method, Response};
use serde::de::DeserializeOwned;
use serde::Serialize;
Expand All @@ -13,7 +14,7 @@ use yew::{Callback, TargetCast, UseStateHandle};
use yew_router::navigator::Navigator;
use yewdux::Dispatch;

pub fn onchange(user: &UseStateHandle<User>, field: UserField) -> Callback<Event> {
pub fn on_change(user: &UseStateHandle<User>, field: UserField) -> Callback<Event> {
let user = user.clone();
Callback::from(move |e: Event| {
let user_clone = user.deref().clone();
Expand Down Expand Up @@ -60,23 +61,31 @@ pub fn on_submit(
let api = Rc::clone(&api);
Callback::from(move |e: SubmitEvent| {
e.prevent_default();

// clone parameter fields to prevent ownership issues with callbacks
let api = Rc::clone(&api);
let dispatch_clone = dispatch.clone();
let nav = nav.clone();
let mut user = user.deref().clone();
user.subscribed = true;

// build headers
let hdrs = Headers::new();
hdrs.append("content-type", "application/json");
let api = Rc::clone(&api);

wasm_bindgen_futures::spawn_local(async move {
let body = Some(helpers::to_jsvalue(user));

// navigate home if the submission is successful
if let Some(res) = api.fetch(Some(hdrs), body, Method::POST).await {
if res.status() == 200 {
let obj: Store = serde_json::from_str(&res.text().await.unwrap()).unwrap();

// log!("Response: ", <JsValue as JsValueSerdeExt>::from_serde(&obj).unwrap());
dispatch_clone.reduce_mut(move |store| {
store.token = obj.token;
store.username = obj.username;
store.authenticated = obj.authenticated;
});
nav.push(&Route::Home);
}
Expand All @@ -85,7 +94,7 @@ pub fn on_submit(
})
}

fn to_jsvalue<T>(body: T) -> JsValue
pub fn to_jsvalue<T>(body: T) -> JsValue
where
T: Serialize,
{
Expand Down
14 changes: 13 additions & 1 deletion devblog/devblog/client/src/models.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use chrono::NaiveDateTime;
use chrono::{NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug, Default)]
Expand All @@ -25,6 +25,18 @@ pub struct CommentModel {
pub username: String,
}

impl CommentModel {
pub fn new(id: u32, post_id: u32, content: String, username: String) -> Self {
CommentModel {
id,
post_id,
content,
date: Utc::now().naive_utc(),
username,
}
}
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug, Default)]
pub struct ImgModel {
#[serde(rename = "postId")]
Expand Down
7 changes: 3 additions & 4 deletions devblog/devblog/client/src/pages/sign_in.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::rc::Rc;

use crate::{components::items::text_input::TextInput, helpers, Api, Store, User, UserField};
use std::rc::Rc;
use stylist::Style;
use yew::prelude::*;
use yew_router::hooks::use_navigator;
Expand All @@ -20,8 +19,8 @@ pub fn sign_in() -> Html {
<div class="sign-in-container">
// {error && <span>{error}</span>}
<form onsubmit={helpers::on_submit(&user, nav, Rc::new(Api::SignIn), dispatch)} class="sign-in">
<TextInput label="username" input_type="text" value={user.username.clone()} onchange={helpers::onchange(&user, UserField::Username)}/>
<TextInput label="password" input_type="password" value={user.password.clone()} onchange={helpers::onchange(&user, UserField::Password)}/>
<TextInput label="username" input_type="text" value={user.username.clone()} onchange={helpers::on_change(&user, UserField::Username)}/>
<TextInput label="password" input_type="password" value={user.password.clone()} onchange={helpers::on_change(&user, UserField::Password)}/>
<button>{"Login"}</button>
</form>
</div>
Expand Down
20 changes: 17 additions & 3 deletions devblog/devblog/client/src/pages/sign_out.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
use crate::{helpers, Api, Store, User};
use std::rc::Rc;
use stylist::Style;
use yew::prelude::*;
use yew_router::hooks::use_navigator;
use yewdux::prelude::*;

const STYLE: &str = include_str!("styles/signOut.css");

#[function_component(SignOut)]
pub fn sign_out() -> Html {
let style = Style::new(STYLE).unwrap();
let user = use_state(User::default);
let nav = use_navigator().unwrap();
let (_, dispatch) = use_store::<Store>();

html! {
<section>
<h1>{"SignOut Page"}</h1>
</section>
<span class={style}>
<form onsubmit={helpers::on_submit(&user, nav, Rc::new(Api::SignOut), dispatch)} class="logout-form">
<button class="logout">{"Logout"}</button>
</form>
</span>
}
}
6 changes: 3 additions & 3 deletions devblog/devblog/client/src/pages/sign_up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ pub fn sign_up() -> Html {
<div class={style}>
<div class="sign-up-container">
<form onsubmit={helpers::on_submit(&user, nav, Rc::new(Api::SignUp), dispatch)}>
<TextInput label="username" input_type="text" value={user.username.clone()} onchange={helpers::onchange(&user, UserField::Username)}/>
<TextInput label="email" input_type="text" value={user.email.clone()} onchange={helpers::onchange(&user, UserField::Email)}/>
<TextInput label="password" input_type="password" value={user.password_hash.clone()} onchange={helpers::onchange(&user, UserField::PasswordHash)}/>
<TextInput label="username" input_type="text" value={user.username.clone()} onchange={helpers::on_change(&user, UserField::Username)}/>
<TextInput label="email" input_type="text" value={user.email.clone()} onchange={helpers::on_change(&user, UserField::Email)}/>
<TextInput label="password" input_type="password" value={user.password_hash.clone()} onchange={helpers::on_change(&user, UserField::PasswordHash)}/>
// <TextInput label="confirmPassword" input_type="password" value={register.confirm_password.clone()} onchange={onchange(RegisterField::ConfirmPassword)}/>
<button>{"Sign Up"}</button>
</form>
Expand Down
Loading

0 comments on commit f38e842

Please sign in to comment.