]> Untitled Git - lemmy.git/blobdiff - ui/src/components/post.tsx
routes.api: fix get_captcha endpoint (#1135)
[lemmy.git] / ui / src / components / post.tsx
index f51ba6ff91c62b6ef24578c22ded673360b28637..e9427a5eb32f17243ebc05743db7866c616ebd1a 100644 (file)
@@ -1,4 +1,5 @@
 import { Component, linkEvent } from 'inferno';
+import { Helmet } from 'inferno-helmet';
 import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
 import {
@@ -8,9 +9,8 @@ import {
   GetPostResponse,
   PostResponse,
   Comment,
-  CommentForm as CommentFormI,
+  MarkCommentAsReadForm,
   CommentResponse,
-  CommentSortType,
   CommunityUser,
   CommunityResponse,
   CommentNode as CommentNodeI,
@@ -18,7 +18,6 @@ import {
   BanUserResponse,
   AddModToCommunityResponse,
   AddAdminResponse,
-  UserView,
   SearchType,
   SortType,
   SearchForm,
@@ -27,7 +26,8 @@ import {
   GetSiteResponse,
   GetCommunityResponse,
   WebSocketJsonResponse,
-} from '../interfaces';
+} from 'lemmy-js-client';
+import { CommentSortType, CommentViewType } from '../interfaces';
 import { WebSocketService, UserService } from '../services';
 import {
   wsJsonToRes,
@@ -38,9 +38,9 @@ import {
   createPostLikeRes,
   commentsToFlatNodes,
   setupTippy,
+  favIconUrl,
 } from '../utils';
 import { PostListing } from './post-listing';
-import { PostListings } from './post-listings';
 import { Sidebar } from './sidebar';
 import { CommentForm } from './comment-form';
 import { CommentNodes } from './comment-nodes';
@@ -51,14 +51,15 @@ interface PostState {
   post: PostI;
   comments: Array<Comment>;
   commentSort: CommentSortType;
+  commentViewType: CommentViewType;
   community: Community;
   moderators: Array<CommunityUser>;
-  admins: Array<UserView>;
   online: number;
   scrolled?: boolean;
   scrolled_comment_id?: number;
   loading: boolean;
   crossPosts: Array<PostI>;
+  siteRes: GetSiteResponse;
 }
 
 export class Post extends Component<any, PostState> {
@@ -67,13 +68,36 @@ export class Post extends Component<any, PostState> {
     post: null,
     comments: [],
     commentSort: CommentSortType.Hot,
+    commentViewType: CommentViewType.Tree,
     community: null,
     moderators: [],
-    admins: [],
     online: null,
     scrolled: false,
     loading: true,
     crossPosts: [],
+    siteRes: {
+      admins: [],
+      banned: [],
+      site: {
+        id: undefined,
+        name: undefined,
+        creator_id: undefined,
+        published: undefined,
+        creator_name: undefined,
+        number_of_users: undefined,
+        number_of_posts: undefined,
+        number_of_comments: undefined,
+        number_of_communities: undefined,
+        enable_downvotes: undefined,
+        open_registration: undefined,
+        enable_nsfw: undefined,
+        icon: undefined,
+        banner: undefined,
+      },
+      online: null,
+      version: null,
+      federated_instances: undefined,
+    },
   };
 
   constructor(props: any, context: any) {
@@ -98,6 +122,7 @@ export class Post extends Component<any, PostState> {
       id: postId,
     };
     WebSocketService.Instance.getPost(form);
+    WebSocketService.Instance.getSite();
   }
 
   componentWillUnmount() {
@@ -147,26 +172,43 @@ export class Post extends Component<any, PostState> {
       UserService.Instance.user &&
       UserService.Instance.user.id == parent_user_id
     ) {
-      let form: CommentFormI = {
-        content: found.content,
+      let form: MarkCommentAsReadForm = {
         edit_id: found.id,
-        creator_id: found.creator_id,
-        post_id: found.post_id,
-        parent_id: found.parent_id,
         read: true,
         auth: null,
       };
-      WebSocketService.Instance.editComment(form);
-      UserService.Instance.user.unreadCount--;
-      UserService.Instance.sub.next({
-        user: UserService.Instance.user,
-      });
+      WebSocketService.Instance.markCommentAsRead(form);
+      UserService.Instance.unreadCountSub.next(
+        UserService.Instance.unreadCountSub.value - 1
+      );
+    }
+  }
+
+  get documentTitle(): string {
+    if (this.state.post) {
+      return `${this.state.post.name} - ${this.state.siteRes.site.name}`;
+    } else {
+      return 'Lemmy';
     }
   }
 
+  get favIcon(): string {
+    return this.state.siteRes.site.icon
+      ? this.state.siteRes.site.icon
+      : favIconUrl;
+  }
+
   render() {
     return (
       <div class="container">
+        <Helmet title={this.documentTitle}>
+          <link
+            id="favicon"
+            rel="icon"
+            type="image/x-icon"
+            href={this.favIcon}
+          />
+        </Helmet>
         {this.state.loading ? (
           <h5>
             <svg class="icon icon-spinner spin">
@@ -181,28 +223,22 @@ export class Post extends Component<any, PostState> {
                 showBody
                 showCommunity
                 moderators={this.state.moderators}
-                admins={this.state.admins}
+                admins={this.state.siteRes.admins}
+                enableDownvotes={this.state.siteRes.site.enable_downvotes}
+                enableNsfw={this.state.siteRes.site.enable_nsfw}
               />
-              {this.state.crossPosts.length > 0 && (
-                <>
-                  <div class="my-1 text-muted small font-weight-bold">
-                    {i18n.t('cross_posts')}
-                  </div>
-                  <PostListings showCommunity posts={this.state.crossPosts} />
-                </>
-              )}
               <div className="mb-2" />
               <CommentForm
                 postId={this.state.post.id}
                 disabled={this.state.post.locked}
               />
               {this.state.comments.length > 0 && this.sortRadios()}
-              {this.commentsTree()}
-            </div>
-            <div class="col-12 col-sm-12 col-md-4">
-              {this.state.comments.length > 0 && this.newComments()}
-              {this.sidebar()}
+              {this.state.commentViewType == CommentViewType.Tree &&
+                this.commentsTree()}
+              {this.state.commentViewType == CommentViewType.Chat &&
+                this.commentsFlat()}
             </div>
+            <div class="col-12 col-sm-12 col-md-4">{this.sidebar()}</div>
           </div>
         )}
       </div>
@@ -211,74 +247,94 @@ export class Post extends Component<any, PostState> {
 
   sortRadios() {
     return (
-      <div class="btn-group btn-group-toggle mb-2">
-        <label
-          className={`btn btn-sm btn-secondary pointer ${this.state
-            .commentSort === CommentSortType.Hot && 'active'}`}
-        >
-          {i18n.t('hot')}
-          <input
-            type="radio"
-            value={CommentSortType.Hot}
-            checked={this.state.commentSort === CommentSortType.Hot}
-            onChange={linkEvent(this, this.handleCommentSortChange)}
-          />
-        </label>
-        <label
-          className={`btn btn-sm btn-secondary pointer ${this.state
-            .commentSort === CommentSortType.Top && 'active'}`}
-        >
-          {i18n.t('top')}
-          <input
-            type="radio"
-            value={CommentSortType.Top}
-            checked={this.state.commentSort === CommentSortType.Top}
-            onChange={linkEvent(this, this.handleCommentSortChange)}
-          />
-        </label>
-        <label
-          className={`btn btn-sm btn-secondary pointer ${this.state
-            .commentSort === CommentSortType.New && 'active'}`}
-        >
-          {i18n.t('new')}
-          <input
-            type="radio"
-            value={CommentSortType.New}
-            checked={this.state.commentSort === CommentSortType.New}
-            onChange={linkEvent(this, this.handleCommentSortChange)}
-          />
-        </label>
-        <label
-          className={`btn btn-sm btn-secondary pointer ${this.state
-            .commentSort === CommentSortType.Old && 'active'}`}
-        >
-          {i18n.t('old')}
-          <input
-            type="radio"
-            value={CommentSortType.Old}
-            checked={this.state.commentSort === CommentSortType.Old}
-            onChange={linkEvent(this, this.handleCommentSortChange)}
-          />
-        </label>
-      </div>
+      <>
+        <div class="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
+          <label
+            className={`btn btn-outline-secondary pointer ${
+              this.state.commentSort === CommentSortType.Hot && 'active'
+            }`}
+          >
+            {i18n.t('hot')}
+            <input
+              type="radio"
+              value={CommentSortType.Hot}
+              checked={this.state.commentSort === CommentSortType.Hot}
+              onChange={linkEvent(this, this.handleCommentSortChange)}
+            />
+          </label>
+          <label
+            className={`btn btn-outline-secondary pointer ${
+              this.state.commentSort === CommentSortType.Top && 'active'
+            }`}
+          >
+            {i18n.t('top')}
+            <input
+              type="radio"
+              value={CommentSortType.Top}
+              checked={this.state.commentSort === CommentSortType.Top}
+              onChange={linkEvent(this, this.handleCommentSortChange)}
+            />
+          </label>
+          <label
+            className={`btn btn-outline-secondary pointer ${
+              this.state.commentSort === CommentSortType.New && 'active'
+            }`}
+          >
+            {i18n.t('new')}
+            <input
+              type="radio"
+              value={CommentSortType.New}
+              checked={this.state.commentSort === CommentSortType.New}
+              onChange={linkEvent(this, this.handleCommentSortChange)}
+            />
+          </label>
+          <label
+            className={`btn btn-outline-secondary pointer ${
+              this.state.commentSort === CommentSortType.Old && 'active'
+            }`}
+          >
+            {i18n.t('old')}
+            <input
+              type="radio"
+              value={CommentSortType.Old}
+              checked={this.state.commentSort === CommentSortType.Old}
+              onChange={linkEvent(this, this.handleCommentSortChange)}
+            />
+          </label>
+        </div>
+        <div class="btn-group btn-group-toggle flex-wrap mb-2">
+          <label
+            className={`btn btn-outline-secondary pointer ${
+              this.state.commentViewType === CommentViewType.Chat && 'active'
+            }`}
+          >
+            {i18n.t('chat')}
+            <input
+              type="radio"
+              value={CommentViewType.Chat}
+              checked={this.state.commentViewType === CommentViewType.Chat}
+              onChange={linkEvent(this, this.handleCommentViewTypeChange)}
+            />
+          </label>
+        </div>
+      </>
     );
   }
 
-  newComments() {
+  commentsFlat() {
     return (
-      <div class="d-none d-md-block new-comments mb-3 card border-secondary">
-        <div class="card-body small">
-          <h6>{i18n.t('recent_comments')}</h6>
-          <CommentNodes
-            nodes={commentsToFlatNodes(this.state.comments)}
-            noIndent
-            locked={this.state.post.locked}
-            moderators={this.state.moderators}
-            admins={this.state.admins}
-            postCreatorId={this.state.post.creator_id}
-            showContext
-          />
-        </div>
+      <div>
+        <CommentNodes
+          nodes={commentsToFlatNodes(this.state.comments)}
+          noIndent
+          locked={this.state.post.locked}
+          moderators={this.state.moderators}
+          admins={this.state.siteRes.admins}
+          postCreatorId={this.state.post.creator_id}
+          showContext
+          enableDownvotes={this.state.siteRes.site.enable_downvotes}
+          sort={this.state.commentSort}
+        />
       </div>
     );
   }
@@ -289,8 +345,10 @@ export class Post extends Component<any, PostState> {
         <Sidebar
           community={this.state.community}
           moderators={this.state.moderators}
-          admins={this.state.admins}
+          admins={this.state.siteRes.admins}
           online={this.state.online}
+          enableNsfw={this.state.siteRes.site.enable_nsfw}
+          showIcon
         />
       </div>
     );
@@ -298,6 +356,13 @@ export class Post extends Component<any, PostState> {
 
   handleCommentSortChange(i: Post, event: any) {
     i.state.commentSort = Number(event.target.value);
+    i.state.commentViewType = CommentViewType.Tree;
+    i.setState(i.state);
+  }
+
+  handleCommentViewTypeChange(i: Post, event: any) {
+    i.state.commentViewType = Number(event.target.value);
+    i.state.commentSort = CommentSortType.New;
     i.setState(i.state);
   }
 
@@ -341,9 +406,10 @@ export class Post extends Component<any, PostState> {
           nodes={nodes}
           locked={this.state.post.locked}
           moderators={this.state.moderators}
-          admins={this.state.admins}
+          admins={this.state.siteRes.admins}
           postCreatorId={this.state.post.creator_id}
           sort={this.state.commentSort}
+          enableDownvotes={this.state.siteRes.site.enable_downvotes}
         />
       </div>
     );
@@ -365,17 +431,15 @@ export class Post extends Component<any, PostState> {
       this.state.comments = data.comments;
       this.state.community = data.community;
       this.state.moderators = data.moderators;
-      this.state.admins = data.admins;
       this.state.online = data.online;
       this.state.loading = false;
-      document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`;
 
       // Get cross-posts
       if (this.state.post.url) {
         let form: SearchForm = {
           q: this.state.post.url,
-          type_: SearchType[SearchType.Url],
-          sort: SortType[SortType.TopAll],
+          type_: SearchType.Url,
+          sort: SortType.TopAll,
           page: 1,
           limit: 6,
         };
@@ -392,7 +456,11 @@ export class Post extends Component<any, PostState> {
         this.state.comments.unshift(data.comment);
         this.setState(this.state);
       }
-    } else if (res.op == UserOperation.EditComment) {
+    } else if (
+      res.op == UserOperation.EditComment ||
+      res.op == UserOperation.DeleteComment ||
+      res.op == UserOperation.RemoveComment
+    ) {
       let data = res.data as CommentResponse;
       editCommentRes(data, this.state.comments);
       this.setState(this.state);
@@ -409,7 +477,13 @@ export class Post extends Component<any, PostState> {
       let data = res.data as PostResponse;
       createPostLikeRes(data, this.state.post);
       this.setState(this.state);
-    } else if (res.op == UserOperation.EditPost) {
+    } else if (
+      res.op == UserOperation.EditPost ||
+      res.op == UserOperation.DeletePost ||
+      res.op == UserOperation.RemovePost ||
+      res.op == UserOperation.LockPost ||
+      res.op == UserOperation.StickyPost
+    ) {
       let data = res.data as PostResponse;
       this.state.post = data.post;
       this.setState(this.state);
@@ -419,7 +493,11 @@ export class Post extends Component<any, PostState> {
       this.state.post = data.post;
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.EditCommunity) {
+    } else if (
+      res.op == UserOperation.EditCommunity ||
+      res.op == UserOperation.DeleteCommunity ||
+      res.op == UserOperation.RemoveCommunity
+    ) {
       let data = res.data as CommunityResponse;
       this.state.community = data.community;
       this.state.post.community_id = data.community.id;
@@ -455,23 +533,28 @@ export class Post extends Component<any, PostState> {
       this.setState(this.state);
     } else if (res.op == UserOperation.AddAdmin) {
       let data = res.data as AddAdminResponse;
-      this.state.admins = data.admins;
+      this.state.siteRes.admins = data.admins;
       this.setState(this.state);
     } else if (res.op == UserOperation.Search) {
       let data = res.data as SearchResponse;
       this.state.crossPosts = data.posts.filter(
-        p => p.id != this.state.post.id
+        p => p.id != Number(this.props.match.params.id)
       );
+      if (this.state.crossPosts.length) {
+        this.state.post.duplicates = this.state.crossPosts;
+      }
       this.setState(this.state);
-    } else if (res.op == UserOperation.TransferSite) {
+    } else if (
+      res.op == UserOperation.TransferSite ||
+      res.op == UserOperation.GetSite
+    ) {
       let data = res.data as GetSiteResponse;
-      this.state.admins = data.admins;
+      this.state.siteRes = data;
       this.setState(this.state);
     } else if (res.op == UserOperation.TransferCommunity) {
       let data = res.data as GetCommunityResponse;
       this.state.community = data.community;
       this.state.moderators = data.moderators;
-      this.state.admins = data.admins;
       this.setState(this.state);
     }
   }