}
#[derive(Serialize, Deserialize)]
-pub struct CreateCommunityResponse {
+pub struct CommunityResponse {
op: String,
community: CommunityView
}
#[derive(Serialize, Deserialize)]
pub struct EditCommunity {
edit_id: i32,
+ name: String,
title: String,
description: Option<String>,
category_id: i32,
let edit_post: EditPost = serde_json::from_str(&data.to_string()).unwrap();
edit_post.perform(self, msg.id)
},
+ UserOperation::EditCommunity => {
+ let edit_community: EditCommunity = serde_json::from_str(&data.to_string()).unwrap();
+ edit_community.perform(self, msg.id)
+ },
_ => {
let e = ErrorMessage {
op: "Unknown".to_string(),
let community_view = CommunityView::read(&conn, inserted_community.id).unwrap();
serde_json::to_string(
- &CreateCommunityResponse {
+ &CommunityResponse {
op: self.op_type().to_string(),
community: community_view
}
}
};
-
let moderators = match CommunityModeratorView::for_community(&conn, self.id) {
Ok(moderators) => moderators,
Err(_e) => {
post_out
}
}
+
+impl Perform for EditCommunity {
+ fn op_type(&self) -> UserOperation {
+ UserOperation::EditCommunity
+ }
+
+ 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 community_form = CommunityForm {
+ name: self.name.to_owned(),
+ title: self.title.to_owned(),
+ description: self.description.to_owned(),
+ category_id: self.category_id.to_owned(),
+ creator_id: user_id,
+ updated: Some(naive_now())
+ };
+
+ let _updated_community = match Community::update(&conn, self.edit_id, &community_form) {
+ Ok(community) => community,
+ Err(_e) => {
+ return self.error("Couldn't update Community");
+ }
+ };
+
+ let community_view = CommunityView::read(&conn, self.edit_id).unwrap();
+
+ // Do the subscriber stuff here
+ // let mut community_sent = post_view.clone();
+ // community_sent.my_vote = None;
+
+ let community_out = serde_json::to_string(
+ &CommunityResponse {
+ op: self.op_type().to_string(),
+ community: community_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, &community_out, addr);
+
+ community_out
+ }
+}
// impl Handler<Login> for ChatServer {
// type Result = MessageResult<Login>;
// MessageResult(
// Ok(
-// CreateCommunityResponse {
+// CommunityResponse {
// op: UserOperation::CreateCommunity.to_string(),
// community: community
// }
--- /dev/null
+import { Component, linkEvent } from 'inferno';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { CommunityForm as CommunityFormI, UserOperation, Category, ListCategoriesResponse, CommunityResponse } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp } from '../utils';
+
+import { Community } from '../interfaces';
+
+interface CommunityFormProps {
+ community?: Community; // If a community is given, that means this is an edit
+ onCancel?();
+ onCreate?(id: number);
+ onEdit?(community: Community);
+}
+
+interface CommunityFormState {
+ communityForm: CommunityFormI;
+ categories: Array<Category>;
+}
+
+export class CommunityForm extends Component<CommunityFormProps, CommunityFormState> {
+ private subscription: Subscription;
+
+ private emptyState: CommunityFormState = {
+ communityForm: {
+ name: null,
+ title: null,
+ category_id: null
+ },
+ categories: []
+ }
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = this.emptyState;
+
+ if (this.props.community) {
+ this.state.communityForm = {
+ name: this.props.community.name,
+ title: this.props.community.title,
+ category_id: this.props.community.category_id,
+ description: this.props.community.description,
+ edit_id: this.props.community.id,
+ 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.listCategories();
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+
+ render() {
+ return (
+ <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Name</label>
+ <div class="col-sm-10">
+ <input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} pattern="[a-z0-9_]+" title="lowercase, underscores, and no spaces."/>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Title / Headline</label>
+ <div class="col-sm-10">
+ <input type="text" value={this.state.communityForm.title} onInput={linkEvent(this, this.handleCommunityTitleChange)} class="form-control" required minLength={3} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Description / Sidebar</label>
+ <div class="col-sm-10">
+ <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={6} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Category</label>
+ <div class="col-sm-10">
+ <select class="form-control" value={this.state.communityForm.category_id} onInput={linkEvent(this, this.handleCommunityCategoryChange)}>
+ {this.state.categories.map(category =>
+ <option value={category.id}>{category.name}</option>
+ )}
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-secondary">{this.props.community ? 'Edit' : 'Create'} Community</button>
+ </div>
+ </div>
+ </form>
+ );
+ }
+
+ handleCreateCommunitySubmit(i: CommunityForm, event) {
+ event.preventDefault();
+ if (i.props.community) {
+ WebSocketService.Instance.editCommunity(i.state.communityForm);
+ } else {
+ WebSocketService.Instance.createCommunity(i.state.communityForm);
+ }
+ }
+
+ handleCommunityNameChange(i: CommunityForm, event) {
+ i.state.communityForm.name = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleCommunityTitleChange(i: CommunityForm, event) {
+ i.state.communityForm.title = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleCommunityDescriptionChange(i: CommunityForm, event) {
+ i.state.communityForm.description = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleCommunityCategoryChange(i: CommunityForm, event) {
+ i.state.communityForm.category_id = Number(event.target.value);
+ i.setState(i.state);
+ }
+
+ parseMessage(msg: any) {
+ let op: UserOperation = msgOp(msg);
+ console.log(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else if (op == UserOperation.ListCategories){
+ let res: ListCategoriesResponse = msg;
+ this.state.categories = res.categories;
+ this.state.communityForm.category_id = res.categories[0].id;
+ this.setState(this.state);
+ } else if (op == UserOperation.CreateCommunity) {
+ let res: CommunityResponse = msg;
+ this.props.onCreate(res.community.id);
+ } else if (op == UserOperation.EditCommunity) {
+ let res: CommunityResponse = msg;
+ this.props.onEdit(res.community);
+ }
+ }
+
+}
import { Link } from 'inferno-router';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community as CommunityI, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces';
+import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { MomentTime } from './moment-time';
import { PostListing } from './post-listing';
alert(msg.error);
return;
} else if (op == UserOperation.GetCommunity) {
- let res: CommunityResponse = msg;
+ let res: GetCommunityResponse = msg;
this.state.community = res.community;
this.state.moderators = res.moderators;
this.setState(this.state);
found.upvotes = res.post.upvotes;
found.downvotes = res.post.downvotes;
this.setState(this.state);
+ } else if (op == UserOperation.EditCommunity) {
+ let res: CommunityResponse = msg;
+ this.state.community = res.community;
+ this.setState(this.state);
}
}
}
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
-import { retryWhen, delay, take } from 'rxjs/operators';
-import { CommunityForm, UserOperation, Category, ListCategoriesResponse } from '../interfaces';
-import { WebSocketService, UserService } from '../services';
-import { msgOp } from '../utils';
+import { CommunityForm } from './community-form';
-import { Community } from '../interfaces';
-
-interface State {
- communityForm: CommunityForm;
- categories: Array<Category>;
-}
-
-export class CreateCommunity extends Component<any, State> {
- private subscription: Subscription;
-
- private emptyState: State = {
- communityForm: {
- name: null,
- title: null,
- category_id: null
- },
- categories: []
- }
+export class CreateCommunity 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.listCategories();
- }
-
- componentWillUnmount() {
- this.subscription.unsubscribe();
+ this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
}
render() {
<div class="container">
<div class="row">
<div class="col-12 col-lg-6 mb-4">
- {this.communityForm()}
+ <h3>Create Forum</h3>
+ <CommunityForm onCreate={this.handleCommunityCreate}/>
</div>
</div>
</div>
)
}
- communityForm() {
- return (
- <div>
- <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
- <h3>Create Forum</h3>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Name</label>
- <div class="col-sm-10">
- <input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} pattern="[a-z0-9_]+" title="lowercase, underscores, and no spaces."/>
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Title / Headline</label>
- <div class="col-sm-10">
- <input type="text" value={this.state.communityForm.title} onInput={linkEvent(this, this.handleCommunityTitleChange)} class="form-control" required minLength={3} />
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Description / Sidebar</label>
- <div class="col-sm-10">
- <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={6} />
- </div>
- </div>
- <div class="form-group row">
- <label class="col-sm-2 col-form-label">Category</label>
- <div class="col-sm-10">
- <select class="form-control" value={this.state.communityForm.category_id} onInput={linkEvent(this, this.handleCommunityCategoryChange)}>
- {this.state.categories.map(category =>
- <option value={category.id}>{category.name}</option>
- )}
- </select>
- </div>
- </div>
- <div class="form-group row">
- <div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">Create</button>
- </div>
- </div>
- </form>
- </div>
- );
- }
-
- handleCreateCommunitySubmit(i: CreateCommunity, event) {
- event.preventDefault();
- WebSocketService.Instance.createCommunity(i.state.communityForm);
- }
-
- handleCommunityNameChange(i: CreateCommunity, event) {
- i.state.communityForm.name = event.target.value;
- i.setState(i.state);
- }
-
- handleCommunityTitleChange(i: CreateCommunity, event) {
- i.state.communityForm.title = event.target.value;
- i.setState(i.state);
- }
-
- handleCommunityDescriptionChange(i: CreateCommunity, event) {
- i.state.communityForm.description = event.target.value;
- i.setState(i.state);
- }
-
- handleCommunityCategoryChange(i: CreateCommunity, event) {
- i.state.communityForm.category_id = Number(event.target.value);
- i.setState(i.state);
+ handleCommunityCreate(id: number) {
+ this.props.history.push(`/community/${id}`);
}
+}
- parseMessage(msg: any) {
- let op: UserOperation = msgOp(msg);
- console.log(msg);
- if (msg.error) {
- alert(msg.error);
- return;
- } else if (op == UserOperation.ListCategories){
- let res: ListCategoriesResponse = msg;
- this.state.categories = res.categories;
- this.state.communityForm.category_id = res.categories[0].id;
- this.setState(this.state);
- } else if (op == UserOperation.CreateCommunity) {
- let community: Community = msg.community;
- this.props.history.push(`/community/${community.id}`);
- }
- }
-}
handlePostSubmit(i: PostForm, event) {
event.preventDefault();
- console.log(i.state.postForm);
if (i.props.post) {
WebSocketService.Instance.editPost(i.state.postForm);
} else {
this.handleEditPost = this.handleEditPost.bind(this);
}
- render() {
- return (
- <div>
- {!this.state.showEdit
- ? this.listing()
- : <PostForm post={this.props.post} onEdit={this.handleEditPost} />
- }
- </div>
- )
+ render() {
+ return (
+ <div>
+ {!this.state.showEdit
+ ? this.listing()
+ : <PostForm post={this.props.post} onEdit={this.handleEditPost} />
+ }
+ </div>
+ )
}
listing() {
<Link to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
</li>
</ul>
- {this.myPost &&
+ {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>
i.setState(i.state);
}
+ // The actual editing is done in the recieve for post
handleEditPost(post: Post) {
this.state.showEdit = false;
this.setState(this.state);
import { Link } from 'inferno-router';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CommentSortType, CreatePostLikeResponse, CommunityUser } from '../interfaces';
+import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse } from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank,mdToHtml } from '../utils';
import { MomentTime } from './moment-time';
let res: PostResponse = msg;
this.state.post = res.post;
this.setState(this.state);
+ } else if (op == UserOperation.EditCommunity) {
+ let res: CommunityResponse = msg;
+ this.state.community = res.community;
+ this.state.post.community_id = res.community.id;
+ this.state.post.community_name = res.community.name;
+ this.setState(this.state);
}
}
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { Community, CommunityUser } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
import { mdToHtml } from '../utils';
+import { CommunityForm } from './community-form';
interface SidebarProps {
community: Community;
}
interface SidebarState {
+ showEdit: boolean;
}
export class Sidebar extends Component<SidebarProps, SidebarState> {
+ private emptyState: SidebarState = {
+ showEdit: false
+ }
+
constructor(props, context) {
super(props, context);
+ this.state = this.emptyState;
+ this.handleEditCommunity = this.handleEditCommunity.bind(this);
}
-
render() {
+ return (
+ <div>
+ {!this.state.showEdit
+ ? this.sidebar()
+ : <CommunityForm community={this.props.community} onEdit={this.handleEditCommunity} />
+ }
+ </div>
+ )
+ }
+
+ sidebar() {
let community = this.props.community;
return (
<div>
<h4>{community.title}</h4>
+ {this.amMod &&
+ <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>
+ {this.amCreator &&
+ <li className="list-inline-item">
+ {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
+ </li>
+ }
+ </ul>
+ }
<ul class="list-inline">
<li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
<li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
</div>
);
}
+
+ handleEditClick(i: Sidebar, event) {
+ i.state.showEdit = true;
+ i.setState(i.state);
+ }
+
+ handleEditCommunity(community: Community) {
+ this.state.showEdit = false;
+ this.setState(this.state);
+ }
+
+ // TODO no deleting communities yet
+ handleDeleteClick(i: Sidebar, event) {
+ }
+
+ private get amCreator(): boolean {
+ return UserService.Instance.loggedIn && this.props.community.creator_id == UserService.Instance.user.id;
+ }
+
+ private get amMod(): boolean {
+ console.log(this.props.moderators);
+ console.log(this.props);
+ return UserService.Instance.loggedIn &&
+ this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
+ }
}
title: string;
description?: string,
category_id: number,
+ edit_id?: number;
auth?: string;
}
-export interface CommunityResponse {
+export interface GetCommunityResponse {
op: string;
community: Community;
moderators: Array<CommunityUser>;
}
+
+export interface CommunityResponse {
+ op: string;
+ community: Community;
+}
+
export interface ListCommunitiesResponse {
op: string;
communities: Array<Community>;
import { wsUri } from '../env';
-import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetListingsForm, CreatePostLikeForm } from '../interfaces';
+import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm } from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
this.subject.next(this.wsSendWrapper(UserOperation.CreateCommunity, communityForm));
}
+ public editCommunity(communityForm: CommunityForm) {
+ this.setAuth(communityForm);
+ this.subject.next(this.wsSendWrapper(UserOperation.EditCommunity, communityForm));
+ }
+
public listCommunities() {
this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, undefined));
}
this.subject.next(this.wsSendWrapper(UserOperation.CreateCommentLike, form));
}
- public getPosts(form: GetListingsForm) {
+ public getPosts(form: GetPostsForm) {
this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.GetPosts, form));
}
{
"compilerOptions": {
"module": "commonjs",
- "target": "es2015",
+ "target": "es2016",
"sourceMap": true,
"inlineSources": true,
"jsx": "preserve",