parent
e154397bb4
commit
fb7f4c3292
5 changed files with 76 additions and 32 deletions
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
ui/src/components/comment-node.tsx
vendored
2
ui/src/components/comment-node.tsx
vendored
|
@ -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> :
|
||||||
|
|
58
ui/src/components/post-listing.tsx
vendored
58
ui/src/components/post-listing.tsx
vendored
|
@ -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,14 +173,18 @@ 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 &&
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item mr-2">
|
{this.props.showBody &&
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? i18n.t('unsave') : i18n.t('save')}</span>
|
<>
|
||||||
</li>
|
<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>
|
||||||
<Link className="text-muted" to={`/create_post${this.crossPostParams}`}><T i18nKey="cross_post">#</T></Link>
|
</li>
|
||||||
</li>
|
<li className="list-inline-item mr-2">
|
||||||
|
<Link className="text-muted" to={`/create_post${this.crossPostParams}`}><T i18nKey="cross_post">#</T></Link>
|
||||||
|
</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.canAdmin) &&
|
||||||
|
<li className="list-inline-item">
|
||||||
|
{!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>
|
||||||
|
}
|
||||||
{this.canMod &&
|
{this.canMod &&
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item">
|
|
||||||
{!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>
|
|
||||||
{!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 {
|
||||||
|
|
38
ui/src/components/user.tsx
vendored
38
ui/src/components/user.tsx
vendored
|
@ -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;
|
||||||
|
|
1
ui/src/interfaces.ts
vendored
1
ui/src/interfaces.ts
vendored
|
@ -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 {
|
||||||
|
|
Reference in a new issue