+
-
+
A Landing message
{UserService.Instance.loggedIn &&
-
-
Subscribed forums
-
- {this.state.subscribedCommunities.map(community =>
- - {community.community_name}
- )}
-
+ {this.state.loading ?
+
:
+
+
+
Subscribed forums
+
+ {this.state.subscribedCommunities.map(community =>
+ - {community.community_name}
+ )}
+
+
+ }
}
@@ -78,6 +83,7 @@ export class Main extends Component
{
} else if (op == UserOperation.GetFollowedCommunities) {
let res: GetFollowedCommunitiesResponse = msg;
this.state.subscribedCommunities = res.communities;
+ this.state.loading = false;
this.setState(this.state);
}
}
diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx
index e0886cb..b7402e7 100644
--- a/ui/src/components/moment-time.tsx
+++ b/ui/src/components/moment-time.tsx
@@ -1,4 +1,4 @@
-import { Component, linkEvent } from 'inferno';
+import { Component } from 'inferno';
import * as moment from 'moment';
interface MomentTimeProps {
@@ -10,18 +10,18 @@ interface MomentTimeProps {
export class MomentTime extends Component {
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
}
render() {
if (this.props.data.updated) {
return (
- modified {moment.utc(this.props.data.updated).fromNow()}
+ modified {moment.utc(this.props.data.updated).fromNow()}
)
} else {
return (
- {moment.utc(this.props.data.published).fromNow()}
+ {moment.utc(this.props.data.published).fromNow()}
)
}
}
diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx
index 2032d49..ca0c5a2 100644
--- a/ui/src/components/navbar.tsx
+++ b/ui/src/components/navbar.tsx
@@ -2,12 +2,26 @@ import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { repoUrl } from '../utils';
import { UserService } from '../services';
+import { version } from '../version';
-export class Navbar extends Component {
+interface NavbarState {
+ isLoggedIn: boolean;
+ expanded: boolean;
+ expandUserDropdown: boolean;
+}
- constructor(props, context) {
+export class Navbar extends Component {
+
+ emptyState: NavbarState = {
+ isLoggedIn: UserService.Instance.loggedIn,
+ expanded: false,
+ expandUserDropdown: false
+ }
+
+ constructor(props: any, context: any) {
super(props, context);
- this.state = {isLoggedIn: UserService.Instance.loggedIn, expanded: false};
+ this.state = this.emptyState;
+ this.handleOverviewClick = this.handleOverviewClick.bind(this);
// Subscribe to user changes
UserService.Instance.sub.subscribe(user => {
@@ -27,7 +41,7 @@ export class Navbar extends Component {
navbar() {
return (
);
}
- handleLogoutClick(i: Navbar, event) {
- UserService.Instance.logout();
- // i.props.history.push('/');
+ expandUserDropdown(i: Navbar) {
+ i.state.expandUserDropdown = !i.state.expandUserDropdown;
+ i.setState(i.state);
}
- expandNavbar(i: Navbar, event) {
+ handleLogoutClick(i: Navbar) {
+ i.state.expandUserDropdown = false;
+ UserService.Instance.logout();
+ }
+
+ handleOverviewClick(i: Navbar) {
+ i.state.expandUserDropdown = false;
+ i.setState(i.state);
+ let userPage = `/user/${UserService.Instance.user.id}`;
+ i.context.router.history.push(userPage);
+ }
+
+ expandNavbar(i: Navbar) {
i.state.expanded = !i.state.expanded;
i.setState(i.state);
}
}
+
diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx
index a8621ce..9845a1b 100644
--- a/ui/src/components/post-form.tsx
+++ b/ui/src/components/post-form.tsx
@@ -2,20 +2,21 @@ 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 { WebSocketService } from '../services';
import { msgOp } from '../utils';
-import { MomentTime } from './moment-time';
+import * as autosize from 'autosize';
interface PostFormProps {
post?: Post; // If a post is given, that means this is an edit
- onCancel?();
- onCreate?(id: number);
- onEdit?(post: Post);
+ onCancel?(): any;
+ onCreate?(id: number): any;
+ onEdit?(post: Post): any;
}
interface PostFormState {
postForm: PostFormI;
communities: Array
;
+ loading: boolean;
}
export class PostForm extends Component {
@@ -27,10 +28,11 @@ export class PostForm extends Component {
auth: null,
community_id: null
},
- communities: []
+ communities: [],
+ loading: false
}
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
@@ -57,6 +59,10 @@ export class PostForm extends Component {
WebSocketService.Instance.listCommunities();
}
+ componentDidMount() {
+ autosize(document.querySelectorAll('textarea'));
+ }
+
componentWillUnmount() {
this.subscription.unsubscribe();
}
@@ -74,13 +80,13 @@ export class PostForm extends Component {
@@ -104,42 +113,45 @@ export class PostForm extends Component {
);
}
- handlePostSubmit(i: PostForm, event) {
+ handlePostSubmit(i: PostForm, event: any) {
event.preventDefault();
if (i.props.post) {
WebSocketService.Instance.editPost(i.state.postForm);
} else {
WebSocketService.Instance.createPost(i.state.postForm);
}
+ i.state.loading = true;
+ i.setState(i.state);
}
- handlePostUrlChange(i: PostForm, event) {
+ handlePostUrlChange(i: PostForm, event: any) {
i.state.postForm.url = event.target.value;
i.setState(i.state);
}
- handlePostNameChange(i: PostForm, event) {
+ handlePostNameChange(i: PostForm, event: any) {
i.state.postForm.name = event.target.value;
i.setState(i.state);
}
- handlePostBodyChange(i: PostForm, event) {
+ handlePostBodyChange(i: PostForm, event: any) {
i.state.postForm.body = event.target.value;
i.setState(i.state);
}
- handlePostCommunityChange(i: PostForm, event) {
+ handlePostCommunityChange(i: PostForm, event: any) {
i.state.postForm.community_id = Number(event.target.value);
i.setState(i.state);
}
- handleCancel(i: PostForm, event) {
+ handleCancel(i: PostForm) {
i.props.onCancel();
}
parseMessage(msg: any) {
let op: UserOperation = msgOp(msg);
if (msg.error) {
+ this.state.loading = false;
return;
} else if (op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg;
@@ -151,9 +163,11 @@ export class PostForm extends Component {
}
this.setState(this.state);
} else if (op == UserOperation.CreatePost) {
+ this.state.loading = false;
let res: PostResponse = msg;
this.props.onCreate(res.post.id);
} else if (op == UserOperation.EditPost) {
+ this.state.loading = false;
let res: PostResponse = msg;
this.props.onEdit(res.post);
}
diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx
index c5052ef..d52dc93 100644
--- a/ui/src/components/post-listing.tsx
+++ b/ui/src/components/post-listing.tsx
@@ -1,9 +1,7 @@
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
-import { Subscription } from "rxjs";
-import { retryWhen, delay, take } from 'rxjs/operators';
import { WebSocketService, UserService } from '../services';
-import { Post, CreatePostLikeResponse, CreatePostLikeForm, PostForm as PostFormI } from '../interfaces';
+import { Post, CreatePostLikeForm, PostForm as PostFormI } from '../interfaces';
import { MomentTime } from './moment-time';
import { PostForm } from './post-form';
import { mdToHtml } from '../utils';
@@ -18,6 +16,7 @@ interface PostListingProps {
editable?: boolean;
showCommunity?: boolean;
showBody?: boolean;
+ viewOnly?: boolean;
}
export class PostListing extends Component {
@@ -27,7 +26,7 @@ export class PostListing extends Component {
iframeExpanded: false
}
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
@@ -52,7 +51,7 @@ export class PostListing extends Component {
let post = this.props.post;
return (
-
+
▲
{post.score}
▼
@@ -80,7 +79,7 @@ export class PostListing extends Component
{
-
by
- {post.creator_name}
+ {post.creator_name}
{this.props.showCommunity &&
to
@@ -100,7 +99,7 @@ export class PostListing extends Component {
-
- {post.number_of_comments} Comments
+ {post.number_of_comments} Comments
{this.myPost &&
@@ -123,7 +122,7 @@ export class PostListing extends Component {
return this.props.editable && UserService.Instance.loggedIn && this.props.post.creator_id == UserService.Instance.user.id;
}
- handlePostLike(i: PostListing, event) {
+ handlePostLike(i: PostListing) {
let form: CreatePostLikeForm = {
post_id: i.props.post.id,
@@ -132,7 +131,7 @@ export class PostListing extends Component {
WebSocketService.Instance.likePost(form);
}
- handlePostDisLike(i: PostListing, event) {
+ handlePostDisLike(i: PostListing) {
let form: CreatePostLikeForm = {
post_id: i.props.post.id,
score: (i.props.post.my_vote == -1) ? 0 : -1
@@ -140,7 +139,7 @@ export class PostListing extends Component {
WebSocketService.Instance.likePost(form);
}
- handleEditClick(i: PostListing, event) {
+ handleEditClick(i: PostListing) {
i.state.showEdit = true;
i.setState(i.state);
}
@@ -151,12 +150,12 @@ export class PostListing extends Component {
}
// The actual editing is done in the recieve for post
- handleEditPost(post: Post) {
+ handleEditPost() {
this.state.showEdit = false;
this.setState(this.state);
}
- handleDeleteClick(i: PostListing, event) {
+ handleDeleteClick(i: PostListing) {
let deleteForm: PostFormI = {
body: '',
community_id: i.props.post.community_id,
@@ -168,7 +167,7 @@ export class PostListing extends Component {
WebSocketService.Instance.editPost(deleteForm);
}
- handleIframeExpandClick(i: PostListing, event) {
+ handleIframeExpandClick(i: PostListing) {
i.state.iframeExpanded = !i.state.iframeExpanded;
i.setState(i.state);
}
diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx
index 98fe8f2..ce7833b 100644
--- a/ui/src/components/post-listings.tsx
+++ b/ui/src/components/post-listings.tsx
@@ -2,12 +2,10 @@ import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces';
+import { UserOperation, Community as CommunityI, Post, GetPostsForm, SortType, ListingType, GetPostsResponse, CreatePostLikeResponse, CommunityUser} from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { MomentTime } from './moment-time';
import { PostListing } from './post-listing';
-import { Sidebar } from './sidebar';
-import { msgOp, mdToHtml } from '../utils';
+import { msgOp } from '../utils';
interface PostListingsProps {
@@ -18,8 +16,9 @@ interface PostListingsState {
community: CommunityI;
moderators: Array;
posts: Array;
- sortType: ListingSortType;
+ sortType: SortType;
type_: ListingType;
+ loading: boolean;
}
export class PostListings extends Component {
@@ -41,15 +40,16 @@ export class PostListings extends Component
- {this.selects()}
- {this.state.posts.length > 0
- ? this.state.posts.map(post =>
- )
- : No Listings. Subscribe to some forums.
+ {this.state.loading ?
+ :
+
+
{this.selects()}
+ {this.state.posts.length > 0
+ ? this.state.posts.map(post =>
+
)
+ : No Listings. Subscribe to some forums.
+ }
+
}
)
@@ -94,22 +99,22 @@ export class PostListings extends Component
{!this.props.communityId &&
UserService.Instance.loggedIn &&
-
+
}
@@ -117,26 +122,26 @@ export class PostListings extends Component
;
-};
interface PostState {
post: PostI;
@@ -21,7 +17,9 @@ interface PostState {
commentSort: CommentSortType;
community: Community;
moderators: Array;
- scrolled: boolean;
+ scrolled?: boolean;
+ scrolled_comment_id?: number;
+ loading: boolean;
}
export class Post extends Component {
@@ -33,15 +31,19 @@ export class Post extends Component {
commentSort: CommentSortType.Hot,
community: null,
moderators: [],
- scrolled: false
+ scrolled: false,
+ loading: true
}
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
let postId = Number(this.props.match.params.id);
+ if (this.props.match.params.comment_id) {
+ this.state.scrolled_comment_id = this.props.match.params.comment_id;
+ }
this.subscription = WebSocketService.Instance.subject
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
@@ -62,11 +64,11 @@ export class Post extends Component {
autosize(document.querySelectorAll('textarea'));
}
- componentDidUpdate(lastProps: any, lastState: PostState, snapshot: any) {
- if (!this.state.scrolled && lastState.comments.length > 0 && window.location.href.includes('#comment-')) {
- let id = window.location.hash.split("#")[2];
- var elmnt = document.getElementById(`${id}`);
+ componentDidUpdate(_lastProps: any, lastState: PostState, _snapshot: any) {
+ if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) {
+ var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`);
elmnt.scrollIntoView();
+ elmnt.classList.add("mark");
this.state.scrolled = true;
}
}
@@ -74,8 +76,9 @@ export class Post extends Component {
render() {
return (
- {this.state.post &&
-
+ {this.state.loading ?
+
:
+
@@ -136,7 +139,7 @@ export class Post extends Component
{
);
}
- handleCommentSortChange(i: Post, event) {
+ handleCommentSortChange(i: Post, event: any) {
i.state.commentSort = Number(event.target.value);
i.setState(i.state);
}
@@ -202,6 +205,7 @@ export class Post extends Component {
this.state.comments = res.comments;
this.state.community = res.community;
this.state.moderators = res.moderators;
+ this.state.loading = false;
this.setState(this.state);
} else if (op == UserOperation.CreateComment) {
let res: CommentResponse = msg;
@@ -250,252 +254,5 @@ export class Post extends Component {
}
}
-interface CommentNodesState {
-}
-
-interface CommentNodesProps {
- nodes: Array;
- noIndent?: boolean;
-}
-
-export class CommentNodes extends Component {
-
- constructor(props, context) {
- super(props, context);
- }
-
- render() {
- return (
-
- {this.props.nodes.map(node =>
-
- )}
-
- )
- }
-}
-interface CommentNodeState {
- showReply: boolean;
- showEdit: boolean;
-}
-
-interface CommentNodeProps {
- node: CommentNodeI;
- noIndent?: boolean;
-}
-
-export class CommentNode extends Component {
-
- private emptyState: CommentNodeState = {
- showReply: false,
- showEdit: false
- }
-
- constructor(props, context) {
- super(props, context);
-
- this.state = this.emptyState;
- this.handleReplyCancel = this.handleReplyCancel.bind(this);
- this.handleCommentLike = this.handleCommentLike.bind(this);
- this.handleCommentDisLike = this.handleCommentDisLike.bind(this);
- }
-
- render() {
- let node = this.props.node;
- return (
-
- )
- }
-
- private get myComment(): boolean {
- return UserService.Instance.loggedIn && this.props.node.comment.creator_id == UserService.Instance.user.id;
- }
-
- handleReplyClick(i: CommentNode, event) {
- i.state.showReply = true;
- i.setState(i.state);
- }
-
- handleEditClick(i: CommentNode, event) {
- i.state.showEdit = true;
- i.setState(i.state);
- }
-
- handleDeleteClick(i: CommentNode, event) {
- let deleteForm: CommentFormI = {
- content: "*deleted*",
- edit_id: i.props.node.comment.id,
- post_id: i.props.node.comment.post_id,
- parent_id: i.props.node.comment.parent_id,
- auth: null
- };
- WebSocketService.Instance.editComment(deleteForm);
- }
-
- handleReplyCancel(): any {
- this.state.showReply = false;
- this.state.showEdit = false;
- this.setState(this.state);
- }
-
-
- handleCommentLike(i: CommentNodeI, event) {
-
- let form: CommentLikeForm = {
- comment_id: i.comment.id,
- post_id: i.comment.post_id,
- score: (i.comment.my_vote == 1) ? 0 : 1
- };
- WebSocketService.Instance.likeComment(form);
- }
-
- handleCommentDisLike(i: CommentNodeI, event) {
- let form: CommentLikeForm = {
- comment_id: i.comment.id,
- post_id: i.comment.post_id,
- score: (i.comment.my_vote == -1) ? 0 : -1
- };
- WebSocketService.Instance.likeComment(form);
- }
-}
-
-interface CommentFormProps {
- postId?: number;
- node?: CommentNodeI;
- onReplyCancel?();
- edit?: boolean;
-}
-
-interface CommentFormState {
- commentForm: CommentFormI;
- buttonTitle: string;
-}
-
-export class CommentForm extends Component {
-
- private emptyState: CommentFormState = {
- commentForm: {
- auth: null,
- content: null,
- post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId
- },
- buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply"
- }
-
- constructor(props, context) {
- super(props, context);
-
- this.state = this.emptyState;
-
- if (this.props.node) {
- if (this.props.edit) {
- this.state.commentForm.edit_id = this.props.node.comment.id;
- this.state.commentForm.parent_id = this.props.node.comment.parent_id;
- this.state.commentForm.content = this.props.node.comment.content;
- } else {
- // A reply gets a new parent id
- this.state.commentForm.parent_id = this.props.node.comment.id;
- }
- }
- }
-
- componentDidMount() {
- autosize(document.querySelectorAll('textarea'));
- }
-
- render() {
- return (
-
- );
- }
-
- handleCommentSubmit(i: CommentForm, event) {
- if (i.props.edit) {
- WebSocketService.Instance.editComment(i.state.commentForm);
- } else {
- WebSocketService.Instance.createComment(i.state.commentForm);
- }
-
- i.state.commentForm.content = undefined;
- i.setState(i.state);
- event.target.reset();
- if (i.props.node) {
- i.props.onReplyCancel();
- }
- }
-
- handleCommentContentChange(i: CommentForm, event) {
- i.state.commentForm.content = event.target.value;
- i.setState(i.state);
- }
-
- handleReplyCancel(i: CommentForm, event) {
- i.props.onReplyCancel();
- }
-}
diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx
index ad3eecc..6fd2bb5 100644
--- a/ui/src/components/sidebar.tsx
+++ b/ui/src/components/sidebar.tsx
@@ -20,7 +20,7 @@ export class Sidebar extends Component {
showEdit: false
}
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
this.handleEditCommunity = this.handleEditCommunity.bind(this);
@@ -42,7 +42,8 @@ export class Sidebar extends Component {
let community = this.props.community;
return (
-
{community.title}
+
{community.title}
+
/f/{community.name}
{this.amMod &&
-
@@ -82,12 +83,12 @@ export class Sidebar extends Component {
);
}
- handleEditClick(i: Sidebar, event) {
+ handleEditClick(i: Sidebar) {
i.state.showEdit = true;
i.setState(i.state);
}
- handleEditCommunity(community: Community) {
+ handleEditCommunity() {
this.state.showEdit = false;
this.setState(this.state);
}
@@ -98,8 +99,8 @@ export class Sidebar extends Component {
}
// TODO no deleting communities yet
- handleDeleteClick(i: Sidebar, event) {
- }
+ // handleDeleteClick(i: Sidebar, event) {
+ // }
handleUnsubscribe(communityId: number) {
let form: FollowCommunityForm = {
diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx
new file mode 100644
index 0000000..4f757db
--- /dev/null
+++ b/ui/src/components/symbols.tsx
@@ -0,0 +1,80 @@
+import { Component } from 'inferno';
+
+export class Symbols extends Component {
+
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx
new file mode 100644
index 0000000..5dd3ac6
--- /dev/null
+++ b/ui/src/components/user.tsx
@@ -0,0 +1,264 @@
+import { Component, linkEvent } from 'inferno';
+import { Link } from 'inferno-router';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView } from '../interfaces';
+import { WebSocketService } from '../services';
+import { msgOp } from '../utils';
+import { PostListing } from './post-listing';
+import { CommentNodes } from './comment-nodes';
+import { MomentTime } from './moment-time';
+
+enum View {
+ Overview, Comments, Posts, Saved
+}
+
+interface UserState {
+ user: UserView;
+ follows: Array;
+ moderates: Array;
+ comments: Array;
+ posts: Array;
+ saved?: Array;
+ view: View;
+ sort: SortType;
+}
+
+export class User extends Component {
+
+ private subscription: Subscription;
+ private emptyState: UserState = {
+ user: {
+ id: null,
+ name: null,
+ fedi_name: null,
+ published: null,
+ number_of_posts: null,
+ post_score: null,
+ number_of_comments: null,
+ comment_score: null,
+ },
+ follows: [],
+ moderates: [],
+ comments: [],
+ posts: [],
+ view: View.Overview,
+ sort: SortType.New
+ }
+
+ constructor(props: any, context: any) {
+ super(props, context);
+
+ this.state = this.emptyState;
+
+ let userId = Number(this.props.match.params.id);
+
+ 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')
+ );
+
+ let form: GetUserDetailsForm = {
+ user_id: userId,
+ sort: SortType[this.state.sort],
+ limit: 999
+ };
+ WebSocketService.Instance.getUserDetails(form);
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+ render() {
+ return (
+
+
+
+
/u/{this.state.user.name}
+ {this.selects()}
+ {this.state.view == View.Overview &&
+ this.overview()
+ }
+ {this.state.view == View.Comments &&
+ this.comments()
+ }
+ {this.state.view == View.Posts &&
+ this.posts()
+ }
+
+
+ {this.userInfo()}
+ {this.moderates()}
+ {this.follows()}
+
+
+
+ )
+ }
+
+ selects() {
+ return (
+
+
+
+
+ )
+
+ }
+
+ overview() {
+ let combined: Array = [];
+ combined.push(...this.state.comments);
+ combined.push(...this.state.posts);
+
+ // Sort it
+ if (this.state.sort == SortType.New) {
+ combined.sort((a, b) => b.published.localeCompare(a.published));
+ } else {
+ combined.sort((a, b) => b.score - a.score);
+ }
+
+ return (
+
+ {combined.map(i =>
+
+ {i.community_id
+ ?
+ :
+ }
+
+ )
+ }
+
+ )
+ }
+
+ comments() {
+ return (
+
+ {this.state.comments.map(comment =>
+
+ )}
+
+ );
+ }
+
+ posts() {
+ return (
+
+ {this.state.posts.map(post =>
+
+ )}
+
+ );
+ }
+
+ userInfo() {
+ let user = this.state.user;
+ return (
+
+
{user.name}
+
Joined
+
+
+ {user.post_score} points |
+ {user.number_of_posts} posts |
+
+
+ {user.comment_score} points |
+ {user.number_of_comments} comments |
+
+
+
+
+ )
+ }
+
+ moderates() {
+ return (
+
+ {this.state.moderates.length > 0 &&
+
+
Moderates
+
+ {this.state.moderates.map(community =>
+ - {community.community_name}
+ )}
+
+
+ }
+
+ )
+ }
+
+ follows() {
+ return (
+
+ {this.state.follows.length > 0 &&
+
+
+
Subscribed
+
+ {this.state.follows.map(community =>
+ - {community.community_name}
+ )}
+
+
+ }
+
+ )
+ }
+
+ handleSortChange(i: User, event: any) {
+ i.state.sort = Number(event.target.value);
+ i.setState(i.state);
+
+ let form: GetUserDetailsForm = {
+ user_id: i.state.user.id,
+ sort: SortType[i.state.sort],
+ limit: 999
+ };
+ WebSocketService.Instance.getUserDetails(form);
+ }
+
+ handleViewChange(i: User, event: any) {
+ i.state.view = 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.GetUserDetails) {
+ let res: UserDetailsResponse = msg;
+ this.state.user = res.user;
+ this.state.comments = res.comments;
+ this.state.follows = res.follows;
+ this.state.moderates = res.moderates;
+ this.state.posts = res.posts;
+ this.setState(this.state);
+ }
+ }
+}
+
diff --git a/ui/src/env.ts b/ui/src/env.ts
index 954d835..aebbc57 100644
--- a/ui/src/env.ts
+++ b/ui/src/env.ts
@@ -1,2 +1,4 @@
-export const endpoint = `${window.location.hostname}:8536`;
-export let wsUri = (window.location.protocol=='https:') ? 'wss://' : 'ws://' + endpoint + '/service/ws';
+let host = `${window.location.hostname}`;
+let port = `${window.location.port == "4444" ? '8536' : window.location.port}`;
+let endpoint = `${host}:${port}`;
+export let wsUri = `${(window.location.protocol=='https:') ? 'wss://' : 'ws://'}${endpoint}/service/ws`;
diff --git a/ui/src/index.tsx b/ui/src/index.tsx
index d374ec1..608e487 100644
--- a/ui/src/index.tsx
+++ b/ui/src/index.tsx
@@ -9,6 +9,8 @@ import { CreateCommunity } from './components/create-community';
import { Post } from './components/post';
import { Community } from './components/community';
import { Communities } from './components/communities';
+import { User } from './components/user';
+import { Symbols } from './components/symbols';
import './main.css';
@@ -18,7 +20,7 @@ const container = document.getElementById('app');
class Index extends Component {
- constructor(props, context) {
+ constructor(props: any, context: any) {
super(props, context);
WebSocketService.Instance;
UserService.Instance;
@@ -35,71 +37,18 @@ class Index extends Component {
+
+
+
- {this.symbols()}
+
);
}
- symbols() {
- return(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-
}
render(, container);
diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts
index 6d314c6..6608864 100644
--- a/ui/src/interfaces.ts
+++ b/ui/src/interfaces.ts
@@ -1,5 +1,5 @@
export enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails
}
export interface User {
@@ -8,6 +8,17 @@ export interface User {
username: string;
}
+export interface UserView {
+ id: number;
+ name: string;
+ fedi_name: string;
+ published: string;
+ number_of_posts: number;
+ post_score: number;
+ number_of_comments: number;
+ comment_score: number;
+}
+
export interface CommunityUser {
id: number;
user_id: number;
@@ -144,6 +155,11 @@ export interface CommentLikeForm {
auth?: string;
}
+export interface CommentNode {
+ comment: Comment;
+ children?: Array;
+}
+
export interface GetPostsForm {
type_: string;
sort: string;
@@ -184,6 +200,27 @@ export interface GetFollowedCommunitiesResponse {
communities: Array;
}
+export interface GetUserDetailsForm {
+ user_id: number;
+ sort: string; // TODO figure this one out
+ limit: number;
+ community_id?: number;
+ auth?: string;
+}
+
+
+
+export interface UserDetailsResponse {
+ op: string;
+ user: UserView;
+ follows: Array;
+ moderates: Array;
+ comments: Array;
+ posts: Array;
+ saved?: Array;
+}
+
+
export interface LoginForm {
username_or_email: string;
password: string;
@@ -210,7 +247,7 @@ export enum ListingType {
All, Subscribed, Community
}
-export enum ListingSortType {
+export enum SortType {
Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll
}
diff --git a/ui/src/main.css b/ui/src/main.css
index ac58c99..0b7da97 100644
--- a/ui/src/main.css
+++ b/ui/src/main.css
@@ -6,6 +6,11 @@ body {
cursor: pointer;
}
+.no-click {
+ pointer-events:none;
+ opacity: 0.65;
+}
+
.upvote:hover {
color: var(--info);
}
@@ -24,6 +29,10 @@ body {
background-color: var(--secondary);
}
+.mark {
+ background-color: #322a00;
+}
+
.md-div p {
margin-bottom: 0px;
}
@@ -45,5 +54,15 @@ body {
stroke: currentColor;
fill: currentColor;
vertical-align: middle;
- margin-bottom: 6px;
+ align-self: center;
+}
+
+
+.spin {
+ animation: spins 2s linear infinite;
+}
+
+@keyframes spins {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(359deg); }
}
diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts
index 79f6750..b5efd6a 100644
--- a/ui/src/services/WebSocketService.ts
+++ b/ui/src/services/WebSocketService.ts
@@ -1,5 +1,5 @@
import { wsUri } from '../env';
-import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm } from '../interfaces';
+import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm } from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
@@ -106,6 +106,11 @@ export class WebSocketService {
this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
}
+ public getUserDetails(form: GetUserDetailsForm) {
+ this.setAuth(form, false);
+ this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form));
+ }
+
private wsSendWrapper(op: UserOperation, data: any) {
let send = { op: UserOperation[op], data: data };
console.log(send);
@@ -122,7 +127,7 @@ export class WebSocketService {
}
-window.onbeforeunload = (e => {
+window.onbeforeunload = (() => {
WebSocketService.Instance.subject.unsubscribe();
WebSocketService.Instance.subject = null;
});
diff --git a/ui/src/utils.ts b/ui/src/utils.ts
index 21122d0..e01e4ce 100644
--- a/ui/src/utils.ts
+++ b/ui/src/utils.ts
@@ -2,7 +2,6 @@ import { UserOperation, Comment } from './interfaces';
import * as markdown_it from 'markdown-it';
export let repoUrl = 'https://github.com/dessalines/lemmy';
-export let wsUri = (window.location.protocol=='https:'&&'wss://'||'ws://')+window.location.host + '/service/ws/';
export function msgOp(msg: any): UserOperation {
let opStr: string = msg.op;
diff --git a/ui/src/version.ts b/ui/src/version.ts
new file mode 100644
index 0000000..3f71007
--- /dev/null
+++ b/ui/src/version.ts
@@ -0,0 +1 @@
+export let version: string = "v0.0.2-0-gdae6651";
\ No newline at end of file
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 4946978..5756a44 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -9,11 +9,47 @@
dependencies:
regenerator-runtime "^0.12.0"
+"@types/autosize@^3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/autosize/-/autosize-3.0.6.tgz#9022e6a783ec5a4d5e570013701dbc0bfe7667fa"
+ integrity sha512-gpfmXswGISLSWNOOdF2PDK96SfkaZdNtNixWJbYH10xn3Hqdt4VyS1GmoutuwOshWyCLuJw2jGhF0zkK7PUhrg==
+ dependencies:
+ "@types/jquery" "*"
+
+"@types/jquery@*":
+ version "3.3.29"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd"
+ integrity sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ==
+ dependencies:
+ "@types/sizzle" "*"
+
"@types/js-cookie@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.1.tgz#aa6f6d5e5aaf7d97959e9fa938ac2501cf1a76f4"
integrity sha512-VIVurImEhQ95jxtjs8baVU5qCzVfwYfuMrpXwdRykJ5MCI5iY7/jB4cDSgwBVeYqeXrhT7GfJUwoDOmN0OMVCA==
+"@types/jwt-decode@^2.2.1":
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/@types/jwt-decode/-/jwt-decode-2.2.1.tgz#afdf5c527fcfccbd4009b5fd02d1e18241f2d2f2"
+ integrity sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A==
+
+"@types/linkify-it@*":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
+ integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
+
+"@types/markdown-it@^0.0.7":
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.7.tgz#75070485a3d8ad11e7deb8287f4430be15bf4d39"
+ integrity sha512-WyL6pa76ollQFQNEaLVa41ZUUvDvPY+qAUmlsphnrpL6I9p1m868b26FyeoOmo7X3/Ta/S9WKXcEYXUSHnxoVQ==
+ dependencies:
+ "@types/linkify-it" "*"
+
+"@types/sizzle@*":
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
+ integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"