Adding admin abilities on user pages.

- Fixes #280
This commit is contained in:
Dessalines 2019-10-13 17:36:35 -07:00
parent 2520f3a006
commit 663a601e11
5 changed files with 76 additions and 32 deletions

View File

@ -50,6 +50,7 @@ pub struct GetUserDetailsResponse {
moderates: Vec<CommunityModeratorView>, moderates: Vec<CommunityModeratorView>,
comments: Vec<CommentView>, comments: Vec<CommentView>,
posts: Vec<PostView>, posts: Vec<PostView>,
admins: Vec<UserView>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -367,6 +368,11 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
let follows = CommunityFollowerView::for_user(&conn, user_details_id)?; let follows = CommunityFollowerView::for_user(&conn, user_details_id)?;
let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?; let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?;
let site_creator_id = Site::read(&conn, 1)?.creator_id;
let mut admins = UserView::admins(&conn)?;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
// Return the jwt // Return the jwt
Ok(GetUserDetailsResponse { Ok(GetUserDetailsResponse {
@ -376,6 +382,7 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
moderates: moderates, moderates: moderates,
comments: comments, comments: comments,
posts: posts, posts: posts,
admins: admins,
}) })
} }
} }

View File

@ -137,7 +137,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</> </>
} }
{/* Admins and mods can remove comments */} {/* Admins and mods can remove comments */}
{this.canMod && {(this.canMod || this.canAdmin) &&
<li className="list-inline-item"> <li className="list-inline-item">
{!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.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :

View File

@ -24,7 +24,6 @@ interface PostListingState {
interface PostListingProps { interface PostListingProps {
post: Post; post: Post;
editable?: boolean;
showCommunity?: boolean; showCommunity?: boolean;
showBody?: boolean; showBody?: boolean;
viewOnly?: boolean; viewOnly?: boolean;
@ -174,7 +173,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</li> </li>
</ul> </ul>
<ul class="list-inline mb-1 text-muted small font-weight-bold"> <ul class="list-inline mb-1 text-muted small font-weight-bold">
{UserService.Instance.user && this.props.editable && {UserService.Instance.user &&
<>
{this.props.showBody &&
<> <>
<li className="list-inline-item mr-2"> <li className="list-inline-item mr-2">
<span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? i18n.t('unsave') : i18n.t('save')}</span> <span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? i18n.t('unsave') : i18n.t('save')}</span>
@ -182,6 +183,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<li className="list-inline-item mr-2"> <li className="list-inline-item mr-2">
<Link className="text-muted" to={`/create_post${this.crossPostParams}`}><T i18nKey="cross_post">#</T></Link> <Link className="text-muted" to={`/create_post${this.crossPostParams}`}><T i18nKey="cross_post">#</T></Link>
</li> </li>
</>
}
{this.myPost && {this.myPost &&
<> <>
<li className="list-inline-item"> <li className="list-inline-item">
@ -205,14 +208,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</> </>
} }
{/* Mods can ban from community, and appoint as mods to community */} {/* Mods can ban from community, and appoint as mods to community */}
{this.canMod && {(this.canMod || this.canAdmin) &&
<>
<li className="list-inline-item"> <li className="list-inline-item">
{!post.removed ? {!post.removed ?
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> : <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> <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
} }
</li> </li>
}
{this.canMod &&
<>
{!this.isMod && {!this.isMod &&
<li className="list-inline-item"> <li className="list-inline-item">
{!post.banned_from_community ? {!post.banned_from_community ?
@ -326,23 +331,26 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.post.creator_id); return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.post.creator_id);
} }
get adminsThenMods(): Array<number> {
return this.props.admins.map(a => a.id)
.concat(this.props.moderators.map(m => m.user_id));
}
get canMod(): boolean { get canMod(): boolean {
if (this.props.admins && this.props.moderators) {
let adminsThenMods = this.props.admins.map(a => a.id)
.concat(this.props.moderators.map(m => m.user_id));
if (this.props.editable) { return canMod(UserService.Instance.user, adminsThenMods, this.props.post.creator_id);
return canMod(UserService.Instance.user, this.adminsThenMods, this.props.post.creator_id); } else {
} else return false; return false;
}
} }
get canModOnSelf(): boolean { get canModOnSelf(): boolean {
if (this.props.admins && this.props.moderators) {
let adminsThenMods = this.props.admins.map(a => a.id)
.concat(this.props.moderators.map(m => m.user_id));
if (this.props.editable) { return canMod(UserService.Instance.user, adminsThenMods, this.props.post.creator_id, true);
return canMod(UserService.Instance.user, this.adminsThenMods, this.props.post.creator_id, true); } else {
} else return false; return false;
}
} }
get canAdmin(): boolean { get canAdmin(): boolean {

View File

@ -2,7 +2,7 @@ import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse } from '../interfaces'; import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse, BanUserResponse, AddAdminResponse } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils'; import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
@ -24,6 +24,7 @@ interface UserState {
comments: Array<Comment>; comments: Array<Comment>;
posts: Array<Post>; posts: Array<Post>;
saved?: Array<Post>; saved?: Array<Post>;
admins: Array<UserView>;
view: View; view: View;
sort: SortType; sort: SortType;
page: number; page: number;
@ -53,6 +54,7 @@ export class User extends Component<any, UserState> {
moderates: [], moderates: [],
comments: [], comments: [],
posts: [], posts: [],
admins: [],
loading: true, loading: true,
view: this.getViewFromProps(this.props), view: this.getViewFromProps(this.props),
sort: this.getSortTypeFromProps(this.props), sort: this.getSortTypeFromProps(this.props),
@ -199,8 +201,16 @@ export class User extends Component<any, UserState> {
{combined.map(i => {combined.map(i =>
<div> <div>
{i.type_ == "posts" {i.type_ == "posts"
? <PostListing post={i.data as Post} showCommunity viewOnly /> ? <PostListing
: <CommentNodes nodes={[{comment: i.data as Comment}]} noIndent /> post={i.data as Post}
admins={this.state.admins}
showCommunity
viewOnly />
:
<CommentNodes
nodes={[{comment: i.data as Comment}]}
admins={this.state.admins}
noIndent />
} }
</div> </div>
) )
@ -213,7 +223,9 @@ export class User extends Component<any, UserState> {
return ( return (
<div> <div>
{this.state.comments.map(comment => {this.state.comments.map(comment =>
<CommentNodes nodes={[{comment: comment}]} noIndent viewOnly /> <CommentNodes nodes={[{comment: comment}]}
admins={this.state.admins}
noIndent />
)} )}
</div> </div>
); );
@ -223,7 +235,11 @@ export class User extends Component<any, UserState> {
return ( return (
<div> <div>
{this.state.posts.map(post => {this.state.posts.map(post =>
<PostListing post={post} showCommunity viewOnly /> <PostListing
post={post}
admins={this.state.admins}
showCommunity
viewOnly />
)} )}
</div> </div>
); );
@ -415,6 +431,7 @@ export class User extends Component<any, UserState> {
this.state.follows = res.follows; this.state.follows = res.follows;
this.state.moderates = res.moderates; this.state.moderates = res.moderates;
this.state.posts = res.posts; this.state.posts = res.posts;
this.state.admins = res.admins;
this.state.loading = false; this.state.loading = false;
if (this.isCurrentUser) { if (this.isCurrentUser) {
this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw; this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
@ -454,6 +471,17 @@ export class User extends Component<any, UserState> {
if (res.comment.my_vote !== null) if (res.comment.my_vote !== null)
found.my_vote = res.comment.my_vote; found.my_vote = res.comment.my_vote;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.BanUser) {
let res: BanUserResponse = msg;
this.state.comments.filter(c => c.creator_id == res.user.id)
.forEach(c => c.banned = res.banned);
this.state.posts.filter(c => c.creator_id == res.user.id)
.forEach(c => c.banned = res.banned);
this.setState(this.state);
} else if (op == UserOperation.AddAdmin) {
let res: AddAdminResponse = msg;
this.state.admins = res.admins;
this.setState(this.state);
} else if (op == UserOperation.SaveUserSettings) { } else if (op == UserOperation.SaveUserSettings) {
this.state = this.emptyState; this.state = this.emptyState;
this.state.userSettingsLoading = false; this.state.userSettingsLoading = false;

View File

@ -172,6 +172,7 @@ export interface UserDetailsResponse {
moderates: Array<CommunityUser>; moderates: Array<CommunityUser>;
comments: Array<Comment>; comments: Array<Comment>;
posts: Array<Post>; posts: Array<Post>;
admins: Array<UserView>;
} }
export interface GetRepliesForm { export interface GetRepliesForm {