- [Rust JWT](https://github.com/Keats/jsonwebtoken)
- [Hierarchical tree building javascript](https://stackoverflow.com/a/40732240/1655478)
- [Hot sorting discussion](https://meta.stackexchange.com/questions/11602/what-formula-should-be-used-to-determine-hot-questions) [2](https://medium.com/hacking-and-gonzo/how-reddit-ranking-algorithms-work-ef111e33d0d9)
+- [Classification types.](https://www.reddit.com/r/ModeratorDuck/wiki/subreddit_classification)
## TODOs
- Endpoints
drop table community_moderator;
drop table community_follower;
drop table community;
+drop table category;
+create table category (
+ id serial primary key,
+ name varchar(100) not null unique
+);
+
+insert into category (name) values
+('Discussion'),
+('Humor/Memes'),
+('Gaming'),
+('Movies'),
+('TV'),
+('Music'),
+('Literature'),
+('Comics'),
+('Photography'),
+('Art'),
+('Learning'),
+('DIY'),
+('Lifestyle'),
+('News'),
+('Politics'),
+('Society'),
+('Gender/Identity/Sexuality'),
+('Race/Colonisation'),
+('Religion'),
+('Science/Technology'),
+('Programming/Software'),
+('Health/Sports/Fitness'),
+('Porn'),
+('Places'),
+('Meta'),
+('Other');
+
+
+
create table community (
id serial primary key,
name varchar(20) not null unique,
+ title varchar(100) not null,
+ description text,
+ category_id int references category on update cascade on delete cascade not null,
creator_id int references user_ on update cascade on delete cascade not null,
published timestamp not null default now(),
updated timestamp
published timestamp not null default now()
);
-insert into community (name, creator_id) values ('main', 1);
+insert into community (name, title, category_id, creator_id) values ('main', 'The default Community', 1, 1);
with all_post as
(
select
- p.id as id,
- p.name as name,
- p.url,
- p.body,
- p.creator_id,
+ p.*,
(select name from user_ where p.creator_id = user_.id) creator_name,
- p.community_id,
(select name from community where p.community_id = community.id) as community_name,
(select count(*) from comment where comment.post_id = p.id) as number_of_comments,
coalesce(sum(pl.score), 0) as score,
count (case when pl.score = 1 then 1 else null end) as upvotes,
count (case when pl.score = -1 then 1 else null end) as downvotes,
- hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank,
- p.published,
- p.updated
+ hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
from post p
left join post_like pl on p.id = pl.post_id
group by p.id
)
select
+ap.*,
u.id as user_id,
-coalesce(pl.score, 0) as my_vote,
-ap.*
+coalesce(pl.score, 0) as my_vote
from user_ u
cross join all_post ap
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
union all
select
- null as user_id,
- null as my_vote,
- ap.*
+ap.*,
+null as user_id,
+null as my_vote
from all_post ap
;
--- /dev/null
+drop view community_view;
+drop view community_moderator_view;
+drop view community_follower_view;
--- /dev/null
+create view community_view as
+select *,
+(select name from user_ u where c.creator_id = u.id) as creator_name,
+(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
+(select count(*) from post p where p.community_id = c.id) as number_of_posts
+from community c;
+
+create view community_moderator_view as
+select *,
+(select name from user_ u where cm.user_id = u.id) as user_name
+from community_moderator cm;
+
+create view community_follower_view as
+select *,
+(select name from user_ u where cf.user_id = u.id) as user_name
+from community_follower cf;
--- /dev/null
+drop view comment_view;
--- /dev/null
+create view comment_view as
+with all_comment as
+(
+ select
+ c.*,
+ (select name from user_ where c.creator_id = user_.id) creator_name,
+ coalesce(sum(cl.score), 0) as score,
+ count (case when cl.score = 1 then 1 else null end) as upvotes,
+ count (case when cl.score = -1 then 1 else null end) as downvotes
+ from comment c
+ left join comment_like cl on c.id = cl.comment_id
+ group by c.id
+)
+
+select
+ac.*,
+u.id as user_id,
+coalesce(cl.score, 0) as my_vote
+from user_ u
+cross join all_comment ac
+left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
+
+union all
+
+select
+ ac.*,
+ null as user_id,
+ null as my_vote
+from all_comment ac
+;
}
}
-
-
-impl Comment {
- fn from_post(conn: &PgConnection, post_id_from: i32) -> Result<Vec<Self>, Error> {
- use schema::comment::dsl::*;
- comment
- .filter(post_id.eq(post_id_from))
- .order_by(published.desc())
- .load::<Self>(conn)
- }
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct CommentView {
- pub id: i32,
- pub creator_id: i32,
- pub content: String,
- pub post_id: i32,
- pub parent_id: Option<i32>,
- pub published: chrono::NaiveDateTime,
- pub updated: Option<chrono::NaiveDateTime>,
- pub score: i32,
- pub upvotes: i32,
- pub downvotes: i32,
- pub my_vote: Option<i16>
-}
-
-impl CommentView {
- pub fn from_comment(comment: &Comment, likes: &Vec<CommentLike>, user_id: Option<i32>) -> Self {
- let mut upvotes: i32 = 0;
- let mut downvotes: i32 = 0;
- let mut my_vote: Option<i16> = Some(0);
-
- for like in likes.iter() {
- if like.score == 1 {
- upvotes += 1;
- } else if like.score == -1 {
- downvotes += 1;
- }
-
- if let Some(user) = user_id {
- if like.user_id == user {
- my_vote = Some(like.score);
- }
- }
-
- }
-
- let score: i32 = upvotes - downvotes;
-
- CommentView {
- id: comment.id,
- content: comment.content.to_owned(),
- parent_id: comment.parent_id,
- post_id: comment.post_id,
- creator_id: comment.creator_id,
- published: comment.published,
- updated: comment.updated,
- upvotes: upvotes,
- score: score,
- downvotes: downvotes,
- my_vote: my_vote
- }
- }
-
- pub fn read(conn: &PgConnection, comment_id: i32, user_id: Option<i32>) -> Self {
- let comment = Comment::read(&conn, comment_id).unwrap();
- let likes = CommentLike::read(&conn, comment_id).unwrap();
- Self::from_comment(&comment, &likes, user_id)
- }
-
- pub fn from_post(conn: &PgConnection, post_id: i32, user_id: Option<i32>) -> Vec<Self> {
- let comments = Comment::from_post(&conn, post_id).unwrap();
- let post_comment_likes = CommentLike::from_post(&conn, post_id).unwrap();
-
- let mut views = Vec::new();
- for comment in comments.iter() {
- let comment_likes: Vec<CommentLike> = post_comment_likes
- .iter()
- .filter(|like| comment.id == like.comment_id)
- .cloned()
- .collect();
- let comment_view = CommentView::from_comment(&comment, &comment_likes, user_id);
- views.push(comment_view);
- };
-
- views
- }
-}
-
-
#[cfg(test)]
mod tests {
use establish_connection;
let new_community = CommunityForm {
name: "test community".to_string(),
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
creator_id: inserted_user.id,
updated: None
};
--- /dev/null
+extern crate diesel;
+use diesel::*;
+use diesel::result::Error;
+use serde::{Deserialize, Serialize};
+
+// The faked schema since diesel doesn't do views
+table! {
+ comment_view (id) {
+ id -> Int4,
+ creator_id -> Int4,
+ post_id -> Int4,
+ parent_id -> Nullable<Int4>,
+ content -> Text,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ creator_name -> Varchar,
+ score -> BigInt,
+ upvotes -> BigInt,
+ downvotes -> BigInt,
+ user_id -> Nullable<Int4>,
+ my_vote -> Nullable<Int4>,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="comment_view"]
+pub struct CommentView {
+ pub id: i32,
+ pub creator_id: i32,
+ pub post_id: i32,
+ pub parent_id: Option<i32>,
+ pub content: String,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub creator_name: String,
+ pub score: i64,
+ pub upvotes: i64,
+ pub downvotes: i64,
+ pub user_id: Option<i32>,
+ pub my_vote: Option<i32>,
+}
+
+impl CommentView {
+
+ pub fn list(conn: &PgConnection, from_post_id: i32, from_user_id: Option<i32>) -> Result<Vec<Self>, Error> {
+ use actions::comment_view::comment_view::dsl::*;
+ use diesel::prelude::*;
+
+ let mut query = comment_view.into_boxed();
+
+ // The view lets you pass a null user_id, if you're not logged in
+ if let Some(from_user_id) = from_user_id {
+ query = query.filter(user_id.eq(from_user_id));
+ } else {
+ query = query.filter(user_id.is_null());
+ }
+
+ query = query.filter(post_id.eq(from_post_id)).order_by(published.desc());
+
+ query.load::<Self>(conn)
+ }
+
+ pub fn read(conn: &PgConnection, from_comment_id: i32, from_user_id: Option<i32>) -> Result<Self, Error> {
+ use actions::comment_view::comment_view::dsl::*;
+ use diesel::prelude::*;
+
+ let mut query = comment_view.into_boxed();
+
+ // The view lets you pass a null user_id, if you're not logged in
+ if let Some(from_user_id) = from_user_id {
+ query = query.filter(user_id.eq(from_user_id));
+ } else {
+ query = query.filter(user_id.is_null());
+ }
+
+ query = query.filter(id.eq(from_comment_id)).order_by(published.desc());
+
+ query.first::<Self>(conn)
+ }
+
+}
+
+
+#[cfg(test)]
+mod tests {
+ use establish_connection;
+ use super::*;
+ use actions::post::*;
+ use actions::community::*;
+ use actions::user::*;
+ use actions::comment::*;
+ use {Crud,Likeable};
+ #[test]
+ fn test_crud() {
+ let conn = establish_connection();
+
+ let new_user = UserForm {
+ name: "timmy".into(),
+ fedi_name: "rrf".into(),
+ preferred_username: None,
+ password_encrypted: "nope".into(),
+ email: None,
+ updated: None
+ };
+
+ let inserted_user = User_::create(&conn, &new_user).unwrap();
+
+ let new_community = CommunityForm {
+ name: "test community 5".to_string(),
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
+ creator_id: inserted_user.id,
+ updated: None
+ };
+
+ let inserted_community = Community::create(&conn, &new_community).unwrap();
+
+ let new_post = PostForm {
+ name: "A test post 2".into(),
+ creator_id: inserted_user.id,
+ url: None,
+ body: None,
+ community_id: inserted_community.id,
+ updated: None
+ };
+
+ let inserted_post = Post::create(&conn, &new_post).unwrap();
+
+ let comment_form = CommentForm {
+ content: "A test comment 32".into(),
+ creator_id: inserted_user.id,
+ post_id: inserted_post.id,
+ parent_id: None,
+ updated: None
+ };
+
+ let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
+
+ let comment_like_form = CommentLikeForm {
+ comment_id: inserted_comment.id,
+ post_id: inserted_post.id,
+ user_id: inserted_user.id,
+ score: 1
+ };
+
+ let _inserted_comment_like = CommentLike::like(&conn, &comment_like_form).unwrap();
+
+ let expected_comment_view_no_user = CommentView {
+ id: inserted_comment.id,
+ content: "A test comment 32".into(),
+ creator_id: inserted_user.id,
+ post_id: inserted_post.id,
+ parent_id: None,
+ published: inserted_comment.published,
+ updated: None,
+ creator_name: inserted_user.name.to_owned(),
+ score: 1,
+ downvotes: 0,
+ upvotes: 1,
+ user_id: None,
+ my_vote: None
+ };
+
+ let expected_comment_view_with_user = CommentView {
+ id: inserted_comment.id,
+ content: "A test comment 32".into(),
+ creator_id: inserted_user.id,
+ post_id: inserted_post.id,
+ parent_id: None,
+ published: inserted_comment.published,
+ updated: None,
+ creator_name: inserted_user.name.to_owned(),
+ score: 1,
+ downvotes: 0,
+ upvotes: 1,
+ user_id: Some(inserted_user.id),
+ my_vote: Some(1),
+ };
+
+ let read_comment_views_no_user = CommentView::list(&conn, inserted_post.id, None).unwrap();
+ let read_comment_views_with_user = CommentView::list(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
+ let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
+ let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
+ Post::delete(&conn, inserted_post.id).unwrap();
+ Community::delete(&conn, inserted_community.id).unwrap();
+ User_::delete(&conn, inserted_user.id).unwrap();
+
+ assert_eq!(expected_comment_view_no_user, read_comment_views_no_user[0]);
+ assert_eq!(expected_comment_view_with_user, read_comment_views_with_user[0]);
+ assert_eq!(1, num_deleted);
+ assert_eq!(1, like_removed);
+ }
+}
+
pub struct Community {
pub id: i32,
pub name: String,
+ pub title: String,
+ pub description: Option<String>,
+ pub category_id: i32,
pub creator_id: i32,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
#[table_name="community"]
pub struct CommunityForm {
pub name: String,
+ pub title: String,
+ pub description: Option<String>,
+ pub category_id: i32,
pub creator_id: i32,
pub updated: Option<chrono::NaiveDateTime>
}
let new_community = CommunityForm {
name: "TIL".into(),
+ creator_id: inserted_user.id,
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
updated: None,
- creator_id: inserted_user.id
};
let inserted_community = Community::create(&conn, &new_community).unwrap();
id: inserted_community.id,
creator_id: inserted_user.id,
name: "TIL".into(),
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
published: inserted_community.published,
updated: None
};
let updated_community = Community::update(&conn, inserted_community.id, &new_community).unwrap();
let ignored_community = CommunityFollower::ignore(&conn, &community_follower_form).unwrap();
let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap();
- let loaded_count = Community::list_all(&conn).unwrap().len();
let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
User_::delete(&conn, inserted_user.id).unwrap();
pub mod post;
pub mod comment;
pub mod post_view;
+pub mod comment_view;
let new_community = CommunityForm {
name: "test community_2".to_string(),
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
creator_id: inserted_user.id,
updated: None
};
// The faked schema since diesel doesn't do views
table! {
post_view (id) {
- user_id -> Nullable<Int4>,
- my_vote -> Nullable<Int4>,
id -> Int4,
name -> Varchar,
url -> Nullable<Text>,
body -> Nullable<Text>,
creator_id -> Int4,
- creator_name -> Varchar,
community_id -> Int4,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ creator_name -> Varchar,
community_name -> Varchar,
number_of_comments -> BigInt,
score -> BigInt,
upvotes -> BigInt,
downvotes -> BigInt,
hot_rank -> Int4,
- published -> Timestamp,
- updated -> Nullable<Timestamp>,
+ user_id -> Nullable<Int4>,
+ my_vote -> Nullable<Int4>,
}
}
-#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName)]
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
#[table_name="post_view"]
pub struct PostView {
- pub user_id: Option<i32>,
- pub my_vote: Option<i32>,
pub id: i32,
pub name: String,
pub url: Option<String>,
pub body: Option<String>,
pub creator_id: i32,
- pub creator_name: String,
pub community_id: i32,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub creator_name: String,
pub community_name: String,
pub number_of_comments: i64,
pub score: i64,
pub upvotes: i64,
pub downvotes: i64,
pub hot_rank: i32,
- pub published: chrono::NaiveDateTime,
- pub updated: Option<chrono::NaiveDateTime>
+ pub user_id: Option<i32>,
+ pub my_vote: Option<i32>,
}
impl PostView {
pub fn get(conn: &PgConnection, from_post_id: i32, from_user_id: Option<i32>) -> Result<Self, Error> {
use actions::post_view::post_view::dsl::*;
- use diesel::dsl::*;
use diesel::prelude::*;
let mut query = post_view.into_boxed();
if let Some(from_user_id) = from_user_id {
query = query.filter(user_id.eq(from_user_id));
} else {
- // This fills in nulls for the user_id and user vote
- query = query
- .select((
- sql("null"),
- sql("null"),
- id,
- name,
- url,
- body,
- creator_id,
- creator_name,
- community_id,
- community_name,
- number_of_comments,
- score,
- upvotes,
- downvotes,
- hot_rank,
- published,
- updated
- ))
- .group_by((
- id,
- name,
- url,
- body,
- creator_id,
- creator_name,
- community_id,
- community_name,
- number_of_comments,
- score,
- upvotes,
- downvotes,
- hot_rank,
- published,
- updated
- ));
- };
+ query = query.filter(user_id.is_null());
+ }
query.first::<Self>(conn)
}
let new_community = CommunityForm {
name: community_name.to_owned(),
+ title: "nada".to_owned(),
+ description: None,
creator_id: inserted_user.id,
+ category_id: 1,
updated: None
};
#[cfg(test)]
mod tests {
- use super::activitypub::{context, actor::Person};
use super::User_;
use naive_now;
+table! {
+ category (id) {
+ id -> Int4,
+ name -> Varchar,
+ }
+}
+
table! {
comment (id) {
id -> Int4,
community (id) {
id -> Int4,
name -> Varchar,
+ title -> Varchar,
+ description -> Nullable<Text>,
+ category_id -> Int4,
creator_id -> Int4,
published -> Timestamp,
updated -> Nullable<Timestamp>,
joinable!(comment_like -> comment (comment_id));
joinable!(comment_like -> post (post_id));
joinable!(comment_like -> user_ (user_id));
+joinable!(community -> category (category_id));
joinable!(community -> user_ (creator_id));
joinable!(community_follower -> community (community_id));
joinable!(community_follower -> user_ (user_id));
joinable!(post_like -> user_ (user_id));
allow_tables_to_appear_in_same_query!(
+ category,
comment,
comment_like,
community,
use actions::post::*;
use actions::comment::*;
use actions::post_view::*;
+use actions::comment_view::*;
#[derive(EnumString,ToString,Debug)]
pub enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct CreateCommunity {
name: String,
+ title: String,
+ description: Option<String>,
+ category_id: i32 ,
auth: String
}
}
#[derive(Serialize, Deserialize)]
-pub struct CreatePostResponse {
+pub struct PostResponse {
op: String,
- post: Post
+ post: PostView
}
auth: String
}
-#[derive(Serialize, Deserialize)]
-pub struct CreateCommentResponse {
- op: String,
- comment: CommentView
-}
-
-
#[derive(Serialize, Deserialize)]
pub struct EditComment {
content: String,
}
#[derive(Serialize, Deserialize)]
-pub struct EditCommentResponse {
+pub struct CommentResponse {
op: String,
comment: CommentView
}
auth: String
}
-#[derive(Serialize, Deserialize)]
-pub struct CreateCommentLikeResponse {
- op: String,
- comment: CommentView
-}
-
#[derive(Serialize, Deserialize)]
pub struct CreatePostLike {
post: PostView
}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditPost {
+ edit_id: i32,
+ community_id: i32,
+ name: String,
+ url: Option<String>,
+ body: Option<String>,
+ auth: String
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditCommunity {
+ edit_id: i32,
+ title: String,
+ description: Option<String>,
+ category_id: i32,
+ auth: String
+}
+
/// `ChatServer` manages chat rooms and responsible for coordinating chat
/// session. implementation is super primitive
pub struct ChatServer {
}
}
- /// Send message only to self
- fn send(&self, message: &str, id: &usize) {
- // println!("{:?}", self.sessions);
- if let Some(addr) = self.sessions.get(id) {
- println!("msg: {}", message);
- // println!("{:?}", addr.connected());
- let _ = addr.do_send(WSMessage(message.to_owned()));
- }
- }
+ // /// Send message only to self
+ // fn send(&self, message: &str, id: &usize) {
+ // // println!("{:?}", self.sessions);
+ // if let Some(addr) = self.sessions.get(id) {
+ // println!("msg: {}", message);
+ // // println!("{:?}", addr.connected());
+ // let _ = addr.do_send(WSMessage(message.to_owned()));
+ // }
+ // }
}
/// Make actor from `ChatServer`
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
println!("Someone disconnected");
- let mut rooms: Vec<i32> = Vec::new();
+ // let mut rooms: Vec<i32> = Vec::new();
// remove address
if self.sessions.remove(&msg.id).is_some() {
// remove session from all rooms
- for (id, sessions) in &mut self.rooms {
+ for (_id, sessions) in &mut self.rooms {
if sessions.remove(&msg.id) {
// rooms.push(*id);
}
let create_post_like: CreatePostLike = serde_json::from_str(&data.to_string()).unwrap();
create_post_like.perform(self, msg.id)
},
+ UserOperation::EditPost => {
+ let edit_post: EditPost = serde_json::from_str(&data.to_string()).unwrap();
+ edit_post.perform(self, msg.id)
+ },
_ => {
let e = ErrorMessage {
op: "Unknown".to_string(),
pub trait Perform {
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String;
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String;
fn op_type(&self) -> UserOperation;
fn error(&self, error_msg: &str) -> String {
serde_json::to_string(
fn op_type(&self) -> UserOperation {
UserOperation::Login
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
// Fetch that username / email
let user: User_ = match User_::find_by_email_or_username(&conn, &self.username_or_email) {
Ok(user) => user,
- Err(e) => return self.error("Couldn't find that username or email")
+ Err(_e) => return self.error("Couldn't find that username or email")
};
// Verify the password
fn op_type(&self) -> UserOperation {
UserOperation::Register
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
// Create the user
let inserted_user = match User_::create(&conn, &user_form) {
Ok(user) => user,
- Err(e) => {
+ Err(_e) => {
return self.error("User already exists.");
}
};
UserOperation::CreateCommunity
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
let user_id = claims.id;
- let username = claims.username;
- let iss = claims.iss;
// When you create a community, make sure the user becomes a moderator and a follower
let community_form = CommunityForm {
name: self.name.to_owned(),
+ title: self.title.to_owned(),
+ description: self.description.to_owned(),
+ category_id: self.category_id,
creator_id: user_id,
updated: None
};
let inserted_community = match Community::create(&conn, &community_form) {
Ok(community) => community,
- Err(e) => {
+ Err(_e) => {
return self.error("Community already exists.");
}
};
user_id: user_id
};
- let inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
+ let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
Ok(user) => user,
- Err(e) => {
+ Err(_e) => {
return self.error("Community moderator already exists.");
}
};
user_id: user_id
};
- let inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) {
+ let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user,
- Err(e) => {
+ Err(_e) => {
return self.error("Community follower already exists.");
}
};
UserOperation::ListCommunities
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
UserOperation::CreatePost
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
let user_id = claims.id;
- let username = claims.username;
- let iss = claims.iss;
let post_form = PostForm {
name: self.name.to_owned(),
let inserted_post = match Post::create(&conn, &post_form) {
Ok(post) => post,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't create Post");
}
};
};
// Only add the like if the score isnt 0
- let inserted_like = match PostLike::like(&conn, &like_form) {
+ let _inserted_like = match PostLike::like(&conn, &like_form) {
Ok(like) => like,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't like post.");
}
};
+
+ // Refetch the view
+ let post_view = match PostView::get(&conn, inserted_post.id, Some(user_id)) {
+ Ok(post) => post,
+ Err(_e) => {
+ return self.error("Couldn't find Post");
+ }
+ };
serde_json::to_string(
- &CreatePostResponse {
+ &PostResponse {
op: self.op_type().to_string(),
- post: inserted_post
+ post: post_view
}
)
.unwrap()
match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
- let username = claims.claims.username;
- let iss = claims.claims.iss;
Some(user_id)
}
- Err(e) => None
+ Err(_e) => None
}
}
None => None
let post_view = match PostView::get(&conn, self.id, user_id) {
Ok(post) => post,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't find Post");
}
};
// remove session from all rooms
- for (n, sessions) in &mut chat.rooms {
+ for (_n, sessions) in &mut chat.rooms {
sessions.remove(&addr);
}
chat.rooms.get_mut(&self.id).unwrap().insert(addr);
- let comments = CommentView::from_post(&conn, self.id, user_id);
-
- // println!("{:?}", chat.rooms.keys());
- // println!("{:?}", chat.rooms.get(&5i32).unwrap());
+ let comments = CommentView::list(&conn, self.id, user_id).unwrap();
// Return the jwt
serde_json::to_string(
UserOperation::GetCommunity
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
let community = match Community::read(&conn, self.id) {
Ok(community) => community,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't find Community");
}
};
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
let user_id = claims.id;
- let username = claims.username;
- let iss = claims.iss;
- let fedi_user_id = format!("{}/{}", iss, username);
let comment_form = CommentForm {
content: self.content.to_owned(),
let inserted_comment = match Comment::create(&conn, &comment_form) {
Ok(comment) => comment,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't create Comment");
}
};
score: 1
};
- let inserted_like = match CommentLike::like(&conn, &like_form) {
+ let _inserted_like = match CommentLike::like(&conn, &like_form) {
Ok(like) => like,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't like comment.");
}
};
- let likes: Vec<CommentLike> = vec![inserted_like];
-
- let comment_view = CommentView::from_comment(&inserted_comment, &likes, Some(user_id));
+ let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id)).unwrap();
let mut comment_sent = comment_view.clone();
comment_sent.my_vote = None;
+ comment_sent.user_id = None;
let comment_out = serde_json::to_string(
- &CreateCommentResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: comment_view
}
.unwrap();
let comment_sent_out = serde_json::to_string(
- &CreateCommentLikeResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: comment_sent
}
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
let user_id = claims.id;
- let username = claims.username;
- let iss = claims.iss;
- let fedi_user_id = format!("{}/{}", iss, username);
let comment_form = CommentForm {
content: self.content.to_owned(),
updated: Some(naive_now())
};
- let updated_comment = match Comment::update(&conn, self.edit_id, &comment_form) {
+ let _updated_comment = match Comment::update(&conn, self.edit_id, &comment_form) {
Ok(comment) => comment,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't update Comment");
}
};
- let likes = match CommentLike::read(&conn, self.edit_id) {
- Ok(likes) => likes,
- Err(e) => {
- return self.error("Couldn't get likes");
- }
- };
- let comment_view = CommentView::from_comment(&updated_comment, &likes, Some(user_id));
+ let comment_view = CommentView::read(&conn, self.edit_id, Some(user_id)).unwrap();
let mut comment_sent = comment_view.clone();
comment_sent.my_vote = None;
+ comment_sent.user_id = None;
let comment_out = serde_json::to_string(
- &CreateCommentResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: comment_view
}
.unwrap();
let comment_sent_out = serde_json::to_string(
- &CreateCommentLikeResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: comment_sent
}
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
let user_id = claims.id;
- let username = claims.username;
- let iss = claims.iss;
- let fedi_user_id = format!("{}/{}", iss, username);
let like_form = CommentLikeForm {
comment_id: self.comment_id,
// Only add the like if the score isnt 0
if &like_form.score != &0 {
- let inserted_like = match CommentLike::like(&conn, &like_form) {
+ let _inserted_like = match CommentLike::like(&conn, &like_form) {
Ok(like) => like,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't like comment.");
}
};
}
// Have to refetch the comment to get the current state
- // thread::sleep(time::Duration::from_secs(1));
- let liked_comment = CommentView::read(&conn, self.comment_id, Some(user_id));
+ let liked_comment = CommentView::read(&conn, self.comment_id, Some(user_id)).unwrap();
let mut liked_comment_sent = liked_comment.clone();
liked_comment_sent.my_vote = None;
+ liked_comment_sent.user_id = None;
let like_out = serde_json::to_string(
- &CreateCommentLikeResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: liked_comment
}
.unwrap();
let like_sent_out = serde_json::to_string(
- &CreateCommentLikeResponse {
+ &CommentResponse {
op: self.op_type().to_string(),
comment: liked_comment_sent
}
UserOperation::GetPosts
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
let user_id = claims.claims.id;
Some(user_id)
}
- Err(e) => None
+ Err(_e) => None
}
}
None => None
let posts = match PostView::list(&conn, type_, sort, self.community_id, user_id, self.limit) {
Ok(posts) => posts,
- Err(e) => {
- eprintln!("{}", e);
+ Err(_e) => {
+ eprintln!("{}", _e);
return self.error("Couldn't get posts");
}
};
UserOperation::CreatePostLike
}
- fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
let conn = establish_connection();
let claims = match Claims::decode(&self.auth) {
Ok(claims) => claims.claims,
- Err(e) => {
+ Err(_e) => {
return self.error("Not logged in.");
}
};
// Only add the like if the score isnt 0
if &like_form.score != &0 {
- let inserted_like = match PostLike::like(&conn, &like_form) {
+ let _inserted_like = match PostLike::like(&conn, &like_form) {
Ok(like) => like,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't like post.");
}
};
let post_view = match PostView::get(&conn, self.post_id, Some(user_id)) {
Ok(post) => post,
- Err(e) => {
+ Err(_e) => {
return self.error("Couldn't find Post");
}
};
}
}
+impl Perform for EditPost {
+ fn op_type(&self) -> UserOperation {
+ UserOperation::EditPost
+ }
+
+ fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+
+ let conn = establish_connection();
+
+ let claims = match Claims::decode(&self.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => {
+ return self.error("Not logged in.");
+ }
+ };
+
+ let user_id = claims.id;
+
+ let post_form = PostForm {
+ name: self.name.to_owned(),
+ url: self.url.to_owned(),
+ body: self.body.to_owned(),
+ creator_id: user_id,
+ community_id: self.community_id,
+ updated: Some(naive_now())
+ };
+
+ let _updated_post = match Post::update(&conn, self.edit_id, &post_form) {
+ Ok(post) => post,
+ Err(_e) => {
+ return self.error("Couldn't update Post");
+ }
+ };
+
+ let post_view = PostView::get(&conn, self.edit_id, Some(user_id)).unwrap();
+
+ let mut post_sent = post_view.clone();
+ post_sent.my_vote = None;
+
+ let post_out = serde_json::to_string(
+ &PostResponse {
+ op: self.op_type().to_string(),
+ post: post_view
+ }
+ )
+ .unwrap();
+
+ let post_sent_out = serde_json::to_string(
+ &PostResponse {
+ op: self.op_type().to_string(),
+ post: post_sent
+ }
+ )
+ .unwrap();
+
+ chat.send_room_message(self.edit_id, &post_sent_out, addr);
+
+ post_out
+ }
+}
// impl Handler<Login> for ChatServer {
// type Result = MessageResult<Login>;
// // Fetch that username / email
// let user: User_ = match User_::find_by_email_or_username(&conn, &msg.username_or_email) {
// Ok(user) => user,
-// Err(e) => return MessageResult(
+// Err(_e) => return MessageResult(
// Err(
// ErrorMessage {
// op: UserOperation::Login.to_string(),
// // Create the user
// let inserted_user = match User_::create(&conn, &user_form) {
// Ok(user) => user,
-// Err(e) => return MessageResult(
+// Err(_e) => return MessageResult(
// Err(
// ErrorMessage {
// op: UserOperation::Register.to_string(),
// let community = match Community::create(&conn, &community_form) {
// Ok(community) => community,
-// Err(e) => return MessageResult(
+// Err(_e) => return MessageResult(
// Err(
// ErrorMessage {
// op: UserOperation::CreateCommunity.to_string(),
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
-import { retryWhen, delay, take } from 'rxjs/operators';
-import { PostForm, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse } from '../interfaces';
-import { WebSocketService, UserService } from '../services';
-import { msgOp } from '../utils';
-import { MomentTime } from './moment-time';
+import { PostForm } from './post-form';
-interface State {
- postForm: PostForm;
- communities: Array<Community>;
-}
-
-
-export class CreatePost extends Component<any, State> {
-
- private subscription: Subscription;
- private emptyState: State = {
- postForm: {
- name: null,
- auth: null,
- community_id: null
- },
- communities: []
- }
+export class CreatePost extends Component<any, any> {
constructor(props, context) {
super(props, context);
-
- this.state = this.emptyState;
-
- this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
- () => console.log('complete')
- );
-
- WebSocketService.Instance.listCommunities();
- }
-
- componentWillUnmount() {
- this.subscription.unsubscribe();
+ this.handlePostCreate = this.handlePostCreate.bind(this);
}
render() {
<div class="container">
<div class="row">
<div class="col-12 col-lg-6 mb-4">
- {this.postForm()}
+ <h3>Create a Post</h3>
+ <PostForm onCreate={this.handlePostCreate}/>
</div>
</div>
</div>
)
}
- postForm() {
- return (
- <div>
- <form onSubmit={linkEvent(this, this.handlePostSubmit)}>
- <h3>Create a Post</h3>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">URL</label>
- <div class="col-sm-10">
- <input type="url" class="form-control" value={this.state.postForm.url} onInput={linkEvent(this, this.handlePostUrlChange)} />
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Title</label>
- <div class="col-sm-10">
- <textarea value={this.state.postForm.name} onInput={linkEvent(this, this.handlePostNameChange)} class="form-control" required rows={3} />
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Body</label>
- <div class="col-sm-10">
- <textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} class="form-control" rows={6} />
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Forum</label>
- <div class="col-sm-10">
- <select class="form-control" value={this.state.postForm.community_id} onInput={linkEvent(this, this.handlePostCommunityChange)}>
- {this.state.communities.map(community =>
- <option value={community.id}>{community.name}</option>
- )}
- </select>
- </div>
- </div>
- <div class="form-group row">
- <div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">Create Post</button>
- </div>
- </div>
- </form>
- </div>
- );
- }
-
- handlePostSubmit(i: CreatePost, event) {
- event.preventDefault();
- WebSocketService.Instance.createPost(i.state.postForm);
- }
-
- handlePostUrlChange(i: CreatePost, event) {
- i.state.postForm.url = event.target.value;
- }
-
- handlePostNameChange(i: CreatePost, event) {
- i.state.postForm.name = event.target.value;
- }
-
- handlePostBodyChange(i: CreatePost, event) {
- i.state.postForm.body = event.target.value;
- }
-
- handlePostCommunityChange(i: CreatePost, event) {
- i.state.postForm.community_id = Number(event.target.value);
+ handlePostCreate(id: number) {
+ this.props.history.push(`/post/${id}`);
}
+}
- parseMessage(msg: any) {
- console.log(msg);
- let op: UserOperation = msgOp(msg);
- if (msg.error) {
- alert(msg.error);
- return;
- } else if (op == UserOperation.ListCommunities) {
- let res: ListCommunitiesResponse = msg;
- this.state.communities = res.communities;
- this.state.postForm.community_id = res.communities[0].id; // TODO set it to the default community
- this.setState(this.state);
- } else if (op == UserOperation.CreatePost) {
- let res: PostResponse = msg;
- this.props.history.push(`/post/${res.post.id}`);
- }
- }
-}
--- /dev/null
+import { Component, linkEvent } from 'inferno';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp } from '../utils';
+import { MomentTime } from './moment-time';
+
+interface PostFormProps {
+ post?: Post; // If a post is given, that means this is an edit
+ onCancel?();
+ onCreate?(id: number);
+ onEdit?(post: Post);
+}
+
+interface PostFormState {
+ postForm: PostFormI;
+ communities: Array<Community>;
+}
+
+export class PostForm extends Component<PostFormProps, PostFormState> {
+
+ private subscription: Subscription;
+ private emptyState: PostFormState = {
+ postForm: {
+ name: null,
+ auth: null,
+ community_id: null
+ },
+ communities: []
+ }
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = this.emptyState;
+
+ if (this.props.post) {
+ this.state.postForm = {
+ body: this.props.post.body,
+ name: this.props.post.name,
+ community_id: this.props.post.community_id,
+ edit_id: this.props.post.id,
+ url: this.props.post.url,
+ auth: null
+ }
+ }
+
+ this.subscription = WebSocketService.Instance.subject
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
+ (err) => console.error(err),
+ () => console.log('complete')
+ );
+
+ WebSocketService.Instance.listCommunities();
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+ render() {
+ return (
+ <div>
+ <form onSubmit={linkEvent(this, this.handlePostSubmit)}>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">URL</label>
+ <div class="col-sm-10">
+ <input type="url" class="form-control" value={this.state.postForm.url} onInput={linkEvent(this, this.handlePostUrlChange)} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Title</label>
+ <div class="col-sm-10">
+ <textarea value={this.state.postForm.name} onInput={linkEvent(this, this.handlePostNameChange)} class="form-control" required rows={3} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Body</label>
+ <div class="col-sm-10">
+ <textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} class="form-control" rows={6} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Forum</label>
+ <div class="col-sm-10">
+ <select class="form-control" value={this.state.postForm.community_id} onInput={linkEvent(this, this.handlePostCommunityChange)}>
+ {this.state.communities.map(community =>
+ <option value={community.id}>{community.name}</option>
+ )}
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-secondary">{this.props.post ? 'Edit' : 'Create'} Post</button>
+ </div>
+ </div>
+ </form>
+ </div>
+ );
+ }
+
+ handlePostSubmit(i: PostForm, event) {
+ event.preventDefault();
+ console.log(i.state.postForm);
+ if (i.props.post) {
+ WebSocketService.Instance.editPost(i.state.postForm);
+ } else {
+ WebSocketService.Instance.createPost(i.state.postForm);
+ }
+ }
+
+ handlePostUrlChange(i: PostForm, event) {
+ i.state.postForm.url = event.target.value;
+ i.setState(i.state);
+ }
+
+ handlePostNameChange(i: PostForm, event) {
+ i.state.postForm.name = event.target.value;
+ i.setState(i.state);
+ }
+
+ handlePostBodyChange(i: PostForm, event) {
+ i.state.postForm.body = event.target.value;
+ i.setState(i.state);
+ }
+
+ handlePostCommunityChange(i: PostForm, event) {
+ i.state.postForm.community_id = Number(event.target.value);
+ i.setState(i.state);
+ }
+
+ parseMessage(msg: any) {
+ console.log(msg);
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else if (op == UserOperation.ListCommunities) {
+ let res: ListCommunitiesResponse = msg;
+ this.state.communities = res.communities;
+ if (this.props.post) {
+ this.state.postForm.community_id = this.props.post.community_id;
+ } else {
+ this.state.postForm.community_id = res.communities[0].id;
+ }
+ this.setState(this.state);
+ } else if (op == UserOperation.CreatePost) {
+ let res: PostResponse = msg;
+ this.props.onCreate(res.post.id);
+ } else if (op == UserOperation.EditPost) {
+ let res: PostResponse = msg;
+ this.props.onEdit(res.post);
+ }
+ }
+
+}
+
+
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
import { WebSocketService, UserService } from '../services';
-import { Post, CreatePostLikeResponse, CreatePostLikeForm } from '../interfaces';
+import { Post, CreatePostLikeResponse, CreatePostLikeForm, PostForm as PostFormI } from '../interfaces';
import { MomentTime } from './moment-time';
+import { PostForm } from './post-form';
import { mdToHtml } from '../utils';
interface PostListingState {
+ showEdit: boolean;
}
interface PostListingProps {
post: Post;
+ editable?: boolean;
showCommunity?: boolean;
showBody?: boolean;
}
export class PostListing extends Component<PostListingProps, PostListingState> {
private emptyState: PostListingState = {
+ showEdit: false
}
constructor(props, context) {
this.state = this.emptyState;
this.handlePostLike = this.handlePostLike.bind(this);
this.handlePostDisLike = this.handlePostDisLike.bind(this);
+ this.handleEditPost = this.handleEditPost.bind(this);
}
- render() {
+ render() {
+ return (
+ <div>
+ {!this.state.showEdit
+ ? this.listing()
+ : <PostForm post={this.props.post} onEdit={this.handleEditPost} />
+ }
+ </div>
+ )
+ }
+
+ listing() {
let post = this.props.post;
return (
<div class="listing">
<Link to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
</li>
</ul>
+ {this.myPost &&
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
+ </li>
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
+ </li>
+ </ul>
+ }
{this.props.showBody && this.props.post.body && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />}
</div>
</div>
)
}
- // private get myPost(): boolean {
- // return this.props.node.comment.attributed_to == UserService.Instance.fediUserId;
- // }
+ private get myPost(): boolean {
+ return this.props.editable && UserService.Instance.loggedIn && this.props.post.creator_id == UserService.Instance.user.id;
+ }
handlePostLike(i: PostListing, event) {
};
WebSocketService.Instance.likePost(form);
}
+
+ handleEditClick(i: PostListing, event) {
+ i.state.showEdit = true;
+ i.setState(i.state);
+ }
+
+ handleEditPost(post: Post) {
+ this.state.showEdit = false;
+ this.setState(this.state);
+ }
+
+ handleDeleteClick(i: PostListing, event) {
+ let deleteForm: PostFormI = {
+ body: '',
+ community_id: i.props.post.community_id,
+ name: "deleted",
+ url: '',
+ edit_id: i.props.post.id,
+ auth: null
+ };
+ WebSocketService.Instance.editPost(deleteForm);
+ }
}
import { Component, linkEvent } from 'inferno';
+import { Link } from 'inferno-router';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community, Post as PostI, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CreateCommentLikeResponse, CommentSortType, CreatePostLikeResponse } from '../interfaces';
+import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CreateCommentLikeResponse, CommentSortType, CreatePostLikeResponse } from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank,mdToHtml } from '../utils';
import { MomentTime } from './moment-time';
{this.state.post &&
<div class="row">
<div class="col-12 col-sm-8 col-lg-7 mb-3">
- <PostListing post={this.state.post} showBody showCommunity />
+ <PostListing post={this.state.post} showBody showCommunity editable />
<div className="mb-2" />
<CommentForm postId={this.state.post.id} />
{this.sortRadios()}
alert(msg.error);
return;
} else if (op == UserOperation.GetPost) {
- let res: PostResponse = msg;
+ let res: GetPostResponse = msg;
this.state.post = res.post;
this.state.comments = res.comments;
this.setState(this.state);
this.state.post.upvotes = res.post.upvotes;
this.state.post.downvotes = res.post.downvotes;
this.setState(this.state);
+ } else if (op == UserOperation.EditPost) {
+ let res: PostResponse = msg;
+ this.state.post = res.post;
+ this.setState(this.state);
}
}
<div className="details ml-4">
<ul class="list-inline mb-0 text-muted small">
<li className="list-inline-item">
- <a href={node.comment.attributed_to}>{node.comment.attributed_to}</a>
+ <Link to={`/user/${node.comment.creator_id}`}>{node.comment.creator_name}</Link>
</li>
<li className="list-inline-item">
<span>(
}
private get myComment(): boolean {
- return this.props.node.comment.attributed_to == UserService.Instance.fediUserId;
+ return UserService.Instance.loggedIn && this.props.node.comment.creator_id == UserService.Instance.user.id;
}
handleReplyClick(i: CommentNode, event) {
export enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity
}
export interface User {
body?: string;
community_id: number;
updated?: number;
+ edit_id?: number;
auth: string;
}
-export interface PostResponse {
+export interface GetPostResponse {
op: string;
post: Post;
comments: Array<Comment>;
}
+export interface PostResponse {
+ op: string;
+ post: Post;
+}
+
export interface Comment {
id: number;
content: string;
creator_id: number;
+ creator_name: string;
post_id: number,
parent_id?: number;
published: string;
auth?: string;
}
-export interface CreateCommentLikeResponse {
- op: string;
- comment: Comment;
-}
-
export interface GetPostsForm {
type_: string;
sort: string;
export class UserService {
private static _instance: UserService;
- private user: User;
+ public user: User;
public sub: Subject<User> = new Subject<User>();
private constructor() {
console.log(this.user);
}
- public get fediUserId(): string {
- return `${this.user.iss}/${this.user.username}`;
- }
-
public static get Instance(){
return this._instance || (this._instance = new this());
}
this.subject.next(this.wsSendWrapper(UserOperation.CreatePostLike, form));
}
+ public editPost(postForm: PostForm) {
+ this.setAuth(postForm);
+ this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
+ }
+
private wsSendWrapper(op: UserOperation, data: any) {
let send = { op: UserOperation[op], data: data };
console.log(send);