]> Untitled Git - lemmy.git/commitdiff
Adding ability to do mod action on post creator
authorDessalines <tyhou13@gmx.com>
Fri, 6 Sep 2019 00:18:48 +0000 (17:18 -0700)
committerDessalines <tyhou13@gmx.com>
Fri, 6 Sep 2019 00:18:48 +0000 (17:18 -0700)
- Fixes #248
- Show banned on user comment and user overview. Fixes #264

server/migrations/2019-09-05-230317_add_mod_ban_views/down.sql [new file with mode: 0644]
server/migrations/2019-09-05-230317_add_mod_ban_views/up.sql [new file with mode: 0644]
server/src/db/post_view.rs
ui/src/components/comment-node.tsx
ui/src/components/post-listing.tsx
ui/src/components/post.tsx
ui/src/components/user.tsx
ui/src/interfaces.ts
ui/src/translations/en.ts

diff --git a/server/migrations/2019-09-05-230317_add_mod_ban_views/down.sql b/server/migrations/2019-09-05-230317_add_mod_ban_views/down.sql
new file mode 100644 (file)
index 0000000..c60b672
--- /dev/null
@@ -0,0 +1,44 @@
+-- Post view
+drop view post_view;
+create view post_view as
+with all_post as
+(
+  select        
+  p.*,
+  (select name from user_ where p.creator_id = user_.id) as creator_name,
+  (select name from community where p.community_id = community.id) as community_name,
+  (select removed from community c where p.community_id = c.id) as community_removed,
+  (select deleted from community c where p.community_id = c.id) as community_deleted,
+  (select nsfw from community c where p.community_id = c.id) as community_nsfw,
+  (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
+  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,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+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 
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
diff --git a/server/migrations/2019-09-05-230317_add_mod_ban_views/up.sql b/server/migrations/2019-09-05-230317_add_mod_ban_views/up.sql
new file mode 100644 (file)
index 0000000..d73b372
--- /dev/null
@@ -0,0 +1,47 @@
+-- Create post view, adding banned_from_community 
+
+drop view post_view;
+create view post_view as
+with all_post as
+(
+  select        
+  p.*,
+  (select u.banned from user_ u where p.creator_id = u.id) as banned,
+  (select cb.id::bool from community_user_ban cb where p.creator_id = cb.user_id and p.community_id = cb.community_id) as banned_from_community,
+  (select name from user_ where p.creator_id = user_.id) as creator_name,
+  (select name from community where p.community_id = community.id) as community_name,
+  (select removed from community c where p.community_id = c.id) as community_removed,
+  (select deleted from community c where p.community_id = c.id) as community_deleted,
+  (select nsfw from community c where p.community_id = c.id) as community_nsfw,
+  (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
+  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,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+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 
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
index c9d8cff7b47fb1405b80cb68e56fd4fa8b82c58f..a3d327dc758d8d4bcd73de7e6c9184acf829000e 100644 (file)
@@ -20,6 +20,8 @@ table! {
     updated -> Nullable<Timestamp>,
     deleted -> Bool,
     nsfw -> Bool,
+    banned -> Bool,
+    banned_from_community -> Bool,
     creator_name -> Varchar,
     community_name -> Varchar,
     community_removed -> Bool,
@@ -54,6 +56,8 @@ pub struct PostView {
   pub updated: Option<chrono::NaiveDateTime>,
   pub deleted: bool,
   pub nsfw: bool,
+  pub banned: bool,
+  pub banned_from_community: bool,
   pub creator_name: String,
   pub community_name: String,
   pub community_removed: bool,
@@ -279,6 +283,8 @@ mod tests {
       body: None,
       creator_id: inserted_user.id,
       creator_name: user_name.to_owned(),
+      banned: false,
+      banned_from_community: false,
       community_id: inserted_community.id,
       removed: false,
       deleted: false,
@@ -312,6 +318,8 @@ mod tests {
       locked: false,
       creator_id: inserted_user.id,
       creator_name: user_name.to_owned(),
+      banned: false,
+      banned_from_community: false,
       community_id: inserted_community.id,
       community_name: community_name.to_owned(),
       community_removed: false,
index 3eff8c793529609e54a1f5e2c4289da76ccf87ea..7dbaafdc28a0f37d0a3e7314ac9e4861aac76c9c 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
-import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, SaveCommentForm, BanFromCommunityForm, BanUserForm, CommunityUser, UserView, AddModToCommunityForm, AddAdminForm, TransferCommunityForm, TransferSiteForm } from '../interfaces';
+import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, SaveCommentForm, BanFromCommunityForm, BanUserForm, CommunityUser, UserView, AddModToCommunityForm, AddAdminForm, TransferCommunityForm, TransferSiteForm, BanType } from '../interfaces';
 import { WebSocketService, UserService } from '../services';
 import { mdToHtml, getUnixTime, canMod, isMod } from '../utils';
 import * as moment from 'moment';
@@ -10,8 +10,6 @@ import { CommentNodes } from './comment-nodes';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
 
-enum BanType {Community, Site};
-
 interface CommentNodeState {
   showReply: boolean;
   showEdit: boolean;
@@ -21,9 +19,9 @@ interface CommentNodeState {
   banReason: string;
   banExpires: string;
   banType: BanType;
-  collapsed: boolean;
   showConfirmTransferSite: boolean;
   showConfirmTransferCommunity: boolean;
+  collapsed: boolean;
 }
 
 interface CommentNodeProps {
@@ -87,6 +85,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {this.isAdmin && 
               <li className="list-inline-item badge badge-light"><T i18nKey="admin">#</T></li>
             }
+            {(node.comment.banned_from_community || node.comment.banned) &&  
+              <li className="list-inline-item badge badge-danger"><T i18nKey="banned">#</T></li>
+            }
             <li className="list-inline-item">
               <span>(
                 <span className="text-info">+{node.comment.upvotes}</span>
@@ -122,7 +123,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                         </li>
                         <li className="list-inline-item">
                           <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
-                            {!this.props.node.comment.deleted ? i18n.t('delete') : i18n.t('restore')}
+                            {!node.comment.deleted ? i18n.t('delete') : i18n.t('restore')}
                           </span>
                         </li>
                       </>
@@ -130,7 +131,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                     {/* Admins and mods can remove comments */}
                     {this.canMod && 
                       <li className="list-inline-item">
-                        {!this.props.node.comment.removed ? 
+                        {!node.comment.removed ? 
                         <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
                         <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
                         }
@@ -141,13 +142,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       <>
                         {!this.isMod && 
                           <li className="list-inline-item">
-                            {!this.props.node.comment.banned_from_community ? 
+                            {!node.comment.banned_from_community ? 
                             <span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunityShow)}><T i18nKey="ban">#</T></span> :
                             <span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunitySubmit)}><T i18nKey="unban">#</T></span>
                             }
                           </li>
                         }
-                        {!this.props.node.comment.banned_from_community &&
+                        {!node.comment.banned_from_community &&
                           <li className="list-inline-item">
                             <span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{this.isMod ? i18n.t('remove_as_mod') : i18n.t('appoint_as_mod')}</span>
                           </li>
@@ -172,13 +173,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       <>
                         {!this.isAdmin && 
                           <li className="list-inline-item">
-                            {!this.props.node.comment.banned ? 
+                            {!node.comment.banned ? 
                             <span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}><T i18nKey="ban_from_site">#</T></span> :
                             <span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}><T i18nKey="unban_from_site">#</T></span>
                             }
                           </li>
                         }
-                        {!this.props.node.comment.banned &&
+                        {!node.comment.banned &&
                           <li className="list-inline-item">
                             <span class="pointer" onClick={linkEvent(this, this.handleAddAdmin)}>{this.isAdmin ? i18n.t('remove_as_admin') : i18n.t('appoint_as_admin')}</span>
                           </li>
@@ -230,7 +231,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
             {/* </div> */}
             <div class="form-group row">
-              <button type="submit" class="btn btn-secondary">{i18n.t('ban')} {this.props.node.comment.creator_name}</button>
+              <button type="submit" class="btn btn-secondary">{i18n.t('ban')} {node.comment.creator_name}</button>
             </div>
           </form>
         }
@@ -241,9 +242,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             disabled={this.props.locked} 
           />
         }
-        {this.props.node.children && !this.state.collapsed &&
+        {node.children && !this.state.collapsed &&
           <CommentNodes 
-            nodes={this.props.node.children} 
+            nodes={node.children} 
             locked={this.props.locked} 
             moderators={this.props.moderators}
             admins={this.props.admins}
index 4a3b744a2734cb02af3b468fd943370d9508fa44..ccd3ce45f0f2b0e8ed0307c43e10febc1e1ace11 100644 (file)
@@ -1,10 +1,10 @@
 import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
 import { WebSocketService, UserService } from '../services';
-import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm, CommunityUser, UserView } from '../interfaces';
+import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm, CommunityUser, UserView, BanType, BanFromCommunityForm, BanUserForm, AddModToCommunityForm, AddAdminForm, TransferSiteForm, TransferCommunityForm } from '../interfaces';
 import { MomentTime } from './moment-time';
 import { PostForm } from './post-form';
-import { mdToHtml, canMod, isMod, isImage } from '../utils';
+import { mdToHtml, canMod, isMod, isImage, getUnixTime } from '../utils';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
 
@@ -12,6 +12,12 @@ interface PostListingState {
   showEdit: boolean;
   showRemoveDialog: boolean;
   removeReason: string;
+  showBanDialog: boolean;
+  banReason: string;
+  banExpires: string;
+  banType: BanType;
+  showConfirmTransferSite: boolean;
+  showConfirmTransferCommunity: boolean;
   imageExpanded: boolean;
 }
 
@@ -31,7 +37,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     showEdit: false,
     showRemoveDialog: false,
     removeReason: null,
-    imageExpanded: false
+    showBanDialog: false,
+    banReason: null,
+    banExpires: null,
+    banType: BanType.Community,
+    showConfirmTransferSite: false,
+    showConfirmTransferCommunity: false,
+    imageExpanded: false,
   }
 
   constructor(props: any, context: any) {
@@ -126,6 +138,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               {this.isAdmin && 
                 <span className="mx-1 badge badge-light"><T i18nKey="admin">#</T></span>
               }
+              {(post.banned_from_community || post.banned) &&  
+                <span className="mx-1 badge badge-danger"><T i18nKey="banned">#</T></span>
+              }
               {this.props.showCommunity && 
                 <span>
                   <span> {i18n.t('to')} </span>
@@ -169,17 +184,79 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 </>
               }
               {this.canMod &&
-                <span>
+                <>
                   <li className="list-inline-item">
-                    {!this.props.post.removed ? 
+                    {!post.removed ? 
                     <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
                     <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
                     }
                   </li>
                   <li className="list-inline-item">
-                    <span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{this.props.post.locked ? i18n.t('unlock') : i18n.t('lock')}</span>
+                    <span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{post.locked ? i18n.t('unlock') : i18n.t('lock')}</span>
                   </li>
-                </span>
+                </>
+              }
+              {/* Mods can ban from community, and appoint as mods to community */}
+              {this.canMod &&
+                <>
+                  {!this.isMod && 
+                    <li className="list-inline-item">
+                      {!post.banned_from_community ? 
+                      <span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunityShow)}><T i18nKey="ban">#</T></span> :
+                      <span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunitySubmit)}><T i18nKey="unban">#</T></span>
+                      }
+                    </li>
+                  }
+                  {!post.banned_from_community &&
+                    <li className="list-inline-item">
+                      <span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{this.isMod ? i18n.t('remove_as_mod') : i18n.t('appoint_as_mod')}</span>
+                    </li>
+                  }
+                </>
+              }
+              {/* Community creators and admins can transfer community to another mod */}
+              {(this.amCommunityCreator || this.canAdmin) && this.isMod &&
+                <li className="list-inline-item">
+                  {!this.state.showConfirmTransferCommunity ?
+                  <span class="pointer" onClick={linkEvent(this, this.handleShowConfirmTransferCommunity)}><T i18nKey="transfer_community">#</T>
+                </span> : <>
+                  <span class="d-inline-block mr-1"><T i18nKey="are_you_sure">#</T></span>
+                  <span class="pointer d-inline-block mr-1" onClick={linkEvent(this, this.handleTransferCommunity)}><T i18nKey="yes">#</T></span>
+                  <span class="pointer d-inline-block" onClick={linkEvent(this, this.handleCancelShowConfirmTransferCommunity)}><T i18nKey="no">#</T></span>
+                </>
+                  }
+                </li>
+              }
+              {/* Admins can ban from all, and appoint other admins */}
+              {this.canAdmin &&
+                <>
+                  {!this.isAdmin && 
+                    <li className="list-inline-item">
+                      {!post.banned ? 
+                      <span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}><T i18nKey="ban_from_site">#</T></span> :
+                      <span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}><T i18nKey="unban_from_site">#</T></span>
+                      }
+                    </li>
+                  }
+                  {!post.banned &&
+                    <li className="list-inline-item">
+                      <span class="pointer" onClick={linkEvent(this, this.handleAddAdmin)}>{this.isAdmin ? i18n.t('remove_as_admin') : i18n.t('appoint_as_admin')}</span>
+                    </li>
+                  }
+                </>
+              }
+              {/* Site Creator can transfer to another admin */}
+              {this.amSiteCreator && this.isAdmin &&
+                <li className="list-inline-item">
+                  {!this.state.showConfirmTransferSite ?
+                  <span class="pointer" onClick={linkEvent(this, this.handleShowConfirmTransferSite)}><T i18nKey="transfer_site">#</T>
+                </span> : <>
+                  <span class="d-inline-block mr-1"><T i18nKey="are_you_sure">#</T></span>
+                  <span class="pointer d-inline-block mr-1" onClick={linkEvent(this, this.handleTransferSite)}><T i18nKey="yes">#</T></span>
+                  <span class="pointer d-inline-block" onClick={linkEvent(this, this.handleCancelShowConfirmTransferSite)}><T i18nKey="no">#</T></span>
+                </>
+                  }
+                </li>
               }
             </ul>
           }
@@ -189,7 +266,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               <button type="submit" class="btn btn-secondary"><T i18nKey="remove_post">#</T></button>
             </form>
           }
-          {this.props.showBody && this.props.post.body && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />}
+          {this.state.showBanDialog && 
+            <form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
+              <div class="form-group row">
+                <label class="col-form-label"><T i18nKey="reason">#</T></label>
+                <input type="text" class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
+              </div>
+              {/* TODO hold off on expires until later */}
+              {/* <div class="form-group row"> */}
+                {/*   <label class="col-form-label">Expires</label> */}
+                {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
+                {/* </div> */}
+                <div class="form-group row">
+                  <button type="submit" class="btn btn-secondary">{i18n.t('ban')} {post.creator_name}</button>
+                </div>
+              </form>
+          }
+          {this.props.showBody && post.body && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />}
         </div>
       </div>
     )
@@ -218,6 +311,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     } else return false;
   }
 
+  get canAdmin(): boolean {
+    return this.props.admins && canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.post.creator_id);
+  }
+
+  get amCommunityCreator(): boolean {
+    return this.props.moderators && 
+      UserService.Instance.user && 
+      (this.props.post.creator_id != UserService.Instance.user.id) &&
+      (UserService.Instance.user.id == this.props.moderators[0].user_id);
+  }
+
+  get amSiteCreator(): boolean {
+    return this.props.admins && 
+      UserService.Instance.user && 
+      (this.props.post.creator_id != UserService.Instance.user.id) &&
+      (UserService.Instance.user.id == this.props.admins[0].id);
+  }
+
   handlePostLike(i: PostListing) {
 
     let form: CreatePostLikeForm = {
@@ -328,6 +439,124 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     WebSocketService.Instance.editPost(form);
   }
 
+  handleModBanFromCommunityShow(i: PostListing) {
+    i.state.showBanDialog = true;
+    i.state.banType = BanType.Community;
+    i.setState(i.state);
+  }
+
+  handleModBanShow(i: PostListing) {
+    i.state.showBanDialog = true;
+    i.state.banType = BanType.Site;
+    i.setState(i.state);
+  }
+
+  handleModBanReasonChange(i: PostListing, event: any) {
+    i.state.banReason = event.target.value;
+    i.setState(i.state);
+  }
+
+  handleModBanExpiresChange(i: PostListing, event: any) {
+    i.state.banExpires = event.target.value;
+    i.setState(i.state);
+  }
+
+  handleModBanFromCommunitySubmit(i: PostListing) {
+    i.state.banType = BanType.Community;
+    i.setState(i.state);
+    i.handleModBanBothSubmit(i);
+  }
+
+  handleModBanSubmit(i: PostListing) {
+    i.state.banType = BanType.Site;
+    i.setState(i.state);
+    i.handleModBanBothSubmit(i);
+  }
+
+  handleModBanBothSubmit(i: PostListing) {
+    event.preventDefault();
+
+    if (i.state.banType == BanType.Community) {
+      let form: BanFromCommunityForm = {
+        user_id: i.props.post.creator_id,
+        community_id: i.props.post.community_id,
+        ban: !i.props.post.banned_from_community,
+        reason: i.state.banReason,
+        expires: getUnixTime(i.state.banExpires),
+      };
+      WebSocketService.Instance.banFromCommunity(form);
+    } else {
+      let form: BanUserForm = {
+        user_id: i.props.post.creator_id,
+        ban: !i.props.post.banned,
+        reason: i.state.banReason,
+        expires: getUnixTime(i.state.banExpires),
+      };
+      WebSocketService.Instance.banUser(form);
+    }
+
+    i.state.showBanDialog = false;
+    i.setState(i.state);
+  }
+
+  handleAddModToCommunity(i: PostListing) {
+    let form: AddModToCommunityForm = {
+      user_id: i.props.post.creator_id,
+      community_id: i.props.post.community_id,
+      added: !i.isMod,
+    };
+    WebSocketService.Instance.addModToCommunity(form);
+    i.setState(i.state);
+  }
+
+  handleAddAdmin(i: PostListing) {
+    let form: AddAdminForm = {
+      user_id: i.props.post.creator_id,
+      added: !i.isAdmin,
+    };
+    WebSocketService.Instance.addAdmin(form);
+    i.setState(i.state);
+  }
+
+  handleShowConfirmTransferCommunity(i: PostListing) { 
+    i.state.showConfirmTransferCommunity = true;
+    i.setState(i.state);
+  }
+
+  handleCancelShowConfirmTransferCommunity(i: PostListing) { 
+    i.state.showConfirmTransferCommunity = false;
+    i.setState(i.state);
+  }
+
+  handleTransferCommunity(i: PostListing) {
+    let form: TransferCommunityForm = {
+      community_id: i.props.post.community_id,
+      user_id: i.props.post.creator_id,
+    };
+    WebSocketService.Instance.transferCommunity(form);
+    i.state.showConfirmTransferCommunity = false;
+    i.setState(i.state);
+  }
+
+  handleShowConfirmTransferSite(i: PostListing) { 
+    i.state.showConfirmTransferSite = true;
+    i.setState(i.state);
+  }
+
+  handleCancelShowConfirmTransferSite(i: PostListing) { 
+    i.state.showConfirmTransferSite = false;
+    i.setState(i.state);
+  }
+
+  handleTransferSite(i: PostListing) {
+    let form: TransferSiteForm = {
+      user_id: i.props.post.creator_id,
+    };
+    WebSocketService.Instance.transferSite(form);
+    i.state.showConfirmTransferSite = false;
+    i.setState(i.state);
+  }
+
   handleImageExpandClick(i: PostListing) {
     i.state.imageExpanded = !i.state.imageExpanded;
     i.setState(i.state);
index 91f8f4db76b73e90916f9ebefeb5f739e0385610..7e2dbd621ea40cd28f6875c2e153c92abf6c78f2 100644 (file)
@@ -351,6 +351,9 @@ export class Post extends Component<any, PostState> {
       let res: BanFromCommunityResponse = msg;
       this.state.comments.filter(c => c.creator_id == res.user.id)
       .forEach(c => c.banned_from_community = res.banned);
+      if (this.state.post.creator_id == res.user.id) {  
+        this.state.post.banned_from_community = res.banned;
+      }
       this.setState(this.state);
     } else if (op == UserOperation.AddModToCommunity) {
       let res: AddModToCommunityResponse = msg;
@@ -360,6 +363,9 @@ export class Post extends Component<any, PostState> {
       let res: BanUserResponse = msg;
       this.state.comments.filter(c => c.creator_id == res.user.id)
       .forEach(c => c.banned = res.banned);
+      if (this.state.post.creator_id == res.user.id) {  
+        this.state.post.banned = res.banned;
+      }
       this.setState(this.state);
     } else if (op == UserOperation.AddAdmin) {
       let res: AddAdminResponse = msg;
index 8b78917ed89250f87db507d9228b5c5c81fb0d67..c5ba974f0be78dd52df032d98b535cfa936f66b0 100644 (file)
@@ -45,6 +45,7 @@ export class User extends Component<any, UserState> {
       post_score: null,
       number_of_comments: null,
       comment_score: null,
+      banned: null,
     },
     user_id: null,
     username: null,
@@ -234,7 +235,14 @@ export class User extends Component<any, UserState> {
       <div>
         <div class="card border-secondary mb-3">
           <div class="card-body">
-            <h5>{user.name}</h5>
+            <h5>
+              <ul class="list-inline mb-0">
+                <li className="list-inline-item">{user.name}</li>
+                {user.banned &&  
+                  <li className="list-inline-item badge badge-danger"><T i18nKey="banned">#</T></li>
+                }
+              </ul>
+            </h5>
             <div>{i18n.t('joined')} <MomentTime data={user} /></div>
             <div class="table-responsive">
               <table class="table table-bordered table-sm mt-2 mb-0">
index c9a647d61c3e72a686bdf6a1633f9784398ef564..f2675eb30b19553b9707b48656857c000f91ac80 100644 (file)
@@ -34,6 +34,7 @@ export interface UserView {
   post_score: number;
   number_of_comments: number;
   comment_score: number;
+  banned: boolean;
 }
 
 export interface CommunityUser {
@@ -77,6 +78,8 @@ export interface Post {
   deleted: boolean;
   locked: boolean;
   nsfw: boolean;
+  banned: boolean;
+  banned_from_community: boolean;
   published: string;
   updated?: string;
   creator_name: string;
@@ -138,6 +141,8 @@ export interface Site {
   number_of_communities: number;
 }
 
+export enum BanType {Community, Site};
+
 export interface FollowCommunityForm {
   community_id: number;
   follow: boolean;
index 1ddf087da6db5336f3e4af5d4e0d240366260fed..2d3523e968a40db5af1c649b78d8e0c9f070bd1d 100644 (file)
@@ -56,6 +56,7 @@ export const en = {
     ban_from_site: 'ban from site',
     unban: 'unban',
     unban_from_site: 'unban from site',
+    banned: 'banned',
     save: 'save',
     unsave: 'unsave',
     create: 'create',