summaryrefslogtreecommitdiffstats
path: root/ui/src/components/user.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/components/user.tsx')
-rw-r--r--ui/src/components/user.tsx528
1 files changed, 362 insertions, 166 deletions
diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx
index 88476bc8..393d91d5 100644
--- a/ui/src/components/user.tsx
+++ b/ui/src/components/user.tsx
@@ -1,10 +1,32 @@
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse, BanUserResponse, AddAdminResponse, DeleteAccountForm } from '../interfaces';
+import {
+ UserOperation,
+ Post,
+ Comment,
+ CommunityUser,
+ GetUserDetailsForm,
+ SortType,
+ UserDetailsResponse,
+ UserView,
+ CommentResponse,
+ UserSettingsForm,
+ LoginResponse,
+ BanUserResponse,
+ AddAdminResponse,
+ DeleteAccountForm,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter, themes, setTheme } from '../utils';
+import {
+ msgOp,
+ fetchLimit,
+ routeSortTypeToEnum,
+ capitalizeFirstLetter,
+ themes,
+ setTheme,
+} from '../utils';
import { PostListing } from './post-listing';
import { CommentNodes } from './comment-nodes';
import { MomentTime } from './moment-time';
@@ -12,7 +34,10 @@ import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
enum View {
- Overview, Comments, Posts, Saved
+ Overview,
+ Comments,
+ Posts,
+ Saved,
}
interface UserState {
@@ -37,7 +62,6 @@ interface UserState {
}
export class User extends Component<any, UserState> {
-
private subscription: Subscription;
private emptyState: UserState = {
user: {
@@ -72,8 +96,8 @@ export class User extends Component<any, UserState> {
deleteAccountShowConfirm: false,
deleteAccountForm: {
password: null,
- }
- }
+ },
+ };
constructor(props: any, context: any) {
super(props, context);
@@ -84,34 +108,44 @@ export class User extends Component<any, UserState> {
this.state.username = this.props.match.params.username;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
+ .subscribe(
+ msg => this.parseMessage(msg),
+ err => console.error(err),
() => console.log('complete')
- );
+ );
this.refetch();
}
get isCurrentUser() {
- return UserService.Instance.user && UserService.Instance.user.id == this.state.user.id;
+ return (
+ UserService.Instance.user &&
+ UserService.Instance.user.id == this.state.user.id
+ );
}
getViewFromProps(props: any): View {
- return (props.match.params.view) ?
- View[capitalizeFirstLetter(props.match.params.view)] :
- View.Overview;
+ return props.match.params.view
+ ? View[capitalizeFirstLetter(props.match.params.view)]
+ : View.Overview;
}
getSortTypeFromProps(props: any): SortType {
- return (props.match.params.sort) ?
- routeSortTypeToEnum(props.match.params.sort) :
- SortType.New;
+ return props.match.params.sort
+ ? routeSortTypeToEnum(props.match.params.sort)
+ : SortType.New;
}
getPageFromProps(props: any): number {
- return (props.match.params.page) ? Number(props.match.params.page) : 1;
+ return props.match.params.page ? Number(props.match.params.page) : 1;
}
componentWillUnmount() {
@@ -132,68 +166,98 @@ export class User extends Component<any, UserState> {
render() {
return (
<div class="container">
- {this.state.loading ?
- <h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
- <div class="row">
- <div class="col-12 col-md-8">
- <h5>/u/{this.state.user.name}</h5>
- {this.selects()}
- {this.state.view == View.Overview &&
- this.overview()
- }
- {this.state.view == View.Comments &&
- this.comments()
- }
- {this.state.view == View.Posts &&
- this.posts()
- }
- {this.state.view == View.Saved &&
- this.overview()
- }
- {this.paginator()}
- </div>
- <div class="col-12 col-md-4">
- {this.userInfo()}
- {this.isCurrentUser &&
- this.userSettings()
- }
- {this.moderates()}
- {this.follows()}
+ {this.state.loading ? (
+ <h5>
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ </h5>
+ ) : (
+ <div class="row">
+ <div class="col-12 col-md-8">
+ <h5>/u/{this.state.user.name}</h5>
+ {this.selects()}
+ {this.state.view == View.Overview && this.overview()}
+ {this.state.view == View.Comments && this.comments()}
+ {this.state.view == View.Posts && this.posts()}
+ {this.state.view == View.Saved && this.overview()}
+ {this.paginator()}
+ </div>
+ <div class="col-12 col-md-4">
+ {this.userInfo()}
+ {this.isCurrentUser && this.userSettings()}
+ {this.moderates()}
+ {this.follows()}
+ </div>
</div>
- </div>
- }
+ )}
</div>
- )
+ );
}
selects() {
return (
<div className="mb-2">
- <select value={this.state.view} onChange={linkEvent(this, this.handleViewChange)} class="custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="view">#</T></option>
- <option value={View.Overview}><T i18nKey="overview">#</T></option>
- <option value={View.Comments}><T i18nKey="comments">#</T></option>
- <option value={View.Posts}><T i18nKey="posts">#</T></option>
- <option value={View.Saved}><T i18nKey="saved">#</T></option>
+ <select
+ value={this.state.view}
+ onChange={linkEvent(this, this.handleViewChange)}
+ class="custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="view">#</T>
+ </option>
+ <option value={View.Overview}>
+ <T i18nKey="overview">#</T>
+ </option>
+ <option value={View.Comments}>
+ <T i18nKey="comments">#</T>
+ </option>
+ <option value={View.Posts}>
+ <T i18nKey="posts">#</T>
+ </option>
+ <option value={View.Saved}>
+ <T i18nKey="saved">#</T>
+ </option>
</select>
- <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
- <option disabled><T i18nKey="sort_type">#</T></option>
- <option value={SortType.New}><T i18nKey="new">#</T></option>
- <option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
- <option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
- <option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
- <option value={SortType.TopYear}><T i18nKey="year">#</T></option>
- <option value={SortType.TopAll}><T i18nKey="all">#</T></option>
+ <select
+ value={this.state.sort}
+ onChange={linkEvent(this, this.handleSortChange)}
+ class="custom-select custom-select-sm w-auto ml-2"
+ >
+ <option disabled>
+ <T i18nKey="sort_type">#</T>
+ </option>
+ <option value={SortType.New}>
+ <T i18nKey="new">#</T>
+ </option>
+ <option value={SortType.TopDay}>
+ <T i18nKey="top_day">#</T>
+ </option>
+ <option value={SortType.TopWeek}>
+ <T i18nKey="week">#</T>
+ </option>
+ <option value={SortType.TopMonth}>
+ <T i18nKey="month">#</T>
+ </option>
+ <option value={SortType.TopYear}>
+ <T i18nKey="year">#</T>
+ </option>
+ <option value={SortType.TopAll}>
+ <T i18nKey="all">#</T>
+ </option>
</select>
</div>
- )
-
+ );
}
overview() {
- let combined: Array<{type_: string, data: Comment | Post}> = [];
- let comments = this.state.comments.map(e => {return {type_: "comments", data: e}});
- let posts = this.state.posts.map(e => {return {type_: "posts", data: e}});
+ let combined: Array<{ type_: string; data: Comment | Post }> = [];
+ let comments = this.state.comments.map(e => {
+ return { type_: 'comments', data: e };
+ });
+ let posts = this.state.posts.map(e => {
+ return { type_: 'posts', data: e };
+ });
combined.push(...comments);
combined.push(...posts);
@@ -207,35 +271,38 @@ export class User extends Component<any, UserState> {
return (
<div>
- {combined.map(i =>
+ {combined.map(i => (
<div>
- {i.type_ == "posts"
- ? <PostListing
- post={i.data as Post}
- admins={this.state.admins}
- showCommunity
- viewOnly />
- :
- <CommentNodes
- nodes={[{comment: i.data as Comment}]}
+ {i.type_ == 'posts' ? (
+ <PostListing
+ post={i.data as Post}
+ admins={this.state.admins}
+ showCommunity
+ viewOnly
+ />
+ ) : (
+ <CommentNodes
+ nodes={[{ comment: i.data as Comment }]}
admins={this.state.admins}
- noIndent />
- }
+ noIndent
+ />
+ )}
</div>
- )
- }
+ ))}
</div>
- )
+ );
}
comments() {
return (
<div>
- {this.state.comments.map(comment =>
- <CommentNodes nodes={[{comment: comment}]}
+ {this.state.comments.map(comment => (
+ <CommentNodes
+ nodes={[{ comment: comment }]}
admins={this.state.admins}
- noIndent />
- )}
+ noIndent
+ />
+ ))}
</div>
);
}
@@ -243,13 +310,14 @@ export class User extends Component<any, UserState> {
posts() {
return (
<div>
- {this.state.posts.map(post =>
- <PostListing
- post={post}
+ {this.state.posts.map(post => (
+ <PostListing
+ post={post}
admins={this.state.admins}
- showCommunity
- viewOnly />
- )}
+ showCommunity
+ viewOnly
+ />
+ ))}
</div>
);
}
@@ -263,124 +331,245 @@ export class User extends Component<any, UserState> {
<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>
- }
+ {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>
+ {i18n.t('joined')} <MomentTime data={user} />
+ </div>
<div class="table-responsive">
<table class="table table-bordered table-sm mt-2 mb-0">
<tr>
- <td><T i18nKey="number_of_points" interpolation={{count: user.post_score}}>#</T></td>
- <td><T i18nKey="number_of_posts" interpolation={{count: user.number_of_posts}}>#</T></td>
+ <td>
+ <T
+ i18nKey="number_of_points"
+ interpolation={{ count: user.post_score }}
+ >
+ #
+ </T>
+ </td>
+ <td>
+ <T
+ i18nKey="number_of_posts"
+ interpolation={{ count: user.number_of_posts }}
+ >
+ #
+ </T>
+ </td>
</tr>
<tr>
- <td><T i18nKey="number_of_points" interpolation={{count: user.comment_score}}>#</T></td>
- <td><T i18nKey="number_of_comments" interpolation={{count: user.number_of_comments}}>#</T></td>
+ <td>
+ <T
+ i18nKey="number_of_points"
+ interpolation={{ count: user.comment_score }}
+ >
+ #
+ </T>
+ </td>
+ <td>
+ <T
+ i18nKey="number_of_comments"
+ interpolation={{ count: user.number_of_comments }}
+ >
+ #
+ </T>
+ </td>
</tr>
</table>
</div>
</div>
</div>
</div>
- )
+ );
}
- userSettings() {
+ userSettings() {
return (
<div>
<div class="card border-secondary mb-3">
<div class="card-body">
- <h5><T i18nKey="settings">#</T></h5>
+ <h5>
+ <T i18nKey="settings">#</T>
+ </h5>
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
<div class="form-group">
<div class="col-12">
- <label><T i18nKey="theme">#</T></label>
- <select value={this.state.userSettingsForm.theme} onChange={linkEvent(this, this.handleUserSettingsThemeChange)} class="ml-2 custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="theme">#</T></option>
- {themes.map(theme =>
- <option value={theme}>{theme}</option>
+ <label>
+ <T i18nKey="theme">#</T>
+ </label>
+ <select
+ value={this.state.userSettingsForm.theme}
+ onChange={linkEvent(
+ this,
+ this.handleUserSettingsThemeChange
)}
+ class="ml-2 custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="theme">#</T>
+ </option>
+ {themes.map(theme => (
+ <option value={theme}>{theme}</option>
+ ))}
</select>
</div>
</div>
<div class="form-group">
<div class="col-12">
<div class="form-check">
- <input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
- <label class="form-check-label"><T i18nKey="show_nsfw">#</T></label>
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.userSettingsForm.show_nsfw}
+ onChange={linkEvent(
+ this,
+ this.handleUserSettingsShowNsfwChange
+ )}
+ />
+ <label class="form-check-label">
+ <T i18nKey="show_nsfw">#</T>
+ </label>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-12">
- <button type="submit" class="btn btn-secondary mr-4">{this.state.userSettingsLoading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : capitalizeFirstLetter(i18n.t('save'))}</button>
- <button class="btn btn-danger" onClick={linkEvent(this, this.handleDeleteAccountShowConfirmToggle)}><T i18nKey="delete_account">#</T></button>
- {this.state.deleteAccountShowConfirm &&
+ <button type="submit" class="btn btn-secondary mr-4">
+ {this.state.userSettingsLoading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ capitalizeFirstLetter(i18n.t('save'))
+ )}
+ </button>
+ <button
+ class="btn btn-danger"
+ onClick={linkEvent(
+ this,
+ this.handleDeleteAccountShowConfirmToggle
+ )}
+ >
+ <T i18nKey="delete_account">#</T>
+ </button>
+ {this.state.deleteAccountShowConfirm && (
<>
- <div class="my-2 alert alert-danger" role="alert"><T i18nKey="delete_account_confirm">#</T></div>
- <input type="password" value={this.state.deleteAccountForm.password} onInput={linkEvent(this, this.handleDeleteAccountPasswordChange)} class="form-control my-2" />
- <button class="btn btn-danger mr-4" disabled={!this.state.deleteAccountForm.password} onClick={linkEvent(this, this.handleDeleteAccount)}>{this.state.deleteAccountLoading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : capitalizeFirstLetter(i18n.t('delete'))}</button>
- <button class="btn btn-secondary" onClick={linkEvent(this, this.handleDeleteAccountShowConfirmToggle)}><T i18nKey="cancel">#</T></button>
+ <div class="my-2 alert alert-danger" role="alert">
+ <T i18nKey="delete_account_confirm">#</T>
+ </div>
+ <input
+ type="password"
+ value={this.state.deleteAccountForm.password}
+ onInput={linkEvent(
+ this,
+ this.handleDeleteAccountPasswordChange
+ )}
+ class="form-control my-2"
+ />
+ <button
+ class="btn btn-danger mr-4"
+ disabled={!this.state.deleteAccountForm.password}
+ onClick={linkEvent(this, this.handleDeleteAccount)}
+ >
+ {this.state.deleteAccountLoading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ capitalizeFirstLetter(i18n.t('delete'))
+ )}
+ </button>
+ <button
+ class="btn btn-secondary"
+ onClick={linkEvent(
+ this,
+ this.handleDeleteAccountShowConfirmToggle
+ )}
+ >
+ <T i18nKey="cancel">#</T>
+ </button>
</>
- }
+ )}
</div>
</div>
</form>
</div>
</div>
</div>
- )
+ );
}
moderates() {
return (
<div>
- {this.state.moderates.length > 0 &&
+ {this.state.moderates.length > 0 && (
<div class="card border-secondary mb-3">
<div class="card-body">
- <h5><T i18nKey="moderates">#</T></h5>
- <ul class="list-unstyled mb-0">
- {this.state.moderates.map(community =>
- <li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
- )}
+ <h5>
+ <T i18nKey="moderates">#</T>
+ </h5>
+ <ul class="list-unstyled mb-0">
+ {this.state.moderates.map(community => (
+ <li>
+ <Link to={`/c/${community.community_name}`}>
+ {community.community_name}
+ </Link>
+ </li>
+ ))}
</ul>
</div>
</div>
- }
+ )}
</div>
- )
+ );
}
follows() {
return (
<div>
- {this.state.follows.length > 0 &&
+ {this.state.follows.length > 0 && (
<div class="card border-secondary mb-3">
<div class="card-body">
- <h5><T i18nKey="subscribed">#</T></h5>
- <ul class="list-unstyled mb-0">
- {this.state.follows.map(community =>
- <li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
- )}
+ <h5>
+ <T i18nKey="subscribed">#</T>
+ </h5>
+ <ul class="list-unstyled mb-0">
+ {this.state.follows.map(community => (
+ <li>
+ <Link to={`/c/${community.community_name}`}>
+ {community.community_name}
+ </Link>
+ </li>
+ ))}
</ul>
</div>
</div>
- }
+ )}
</div>
- )
+ );
}
paginator() {
return (
<div class="my-2">
- {this.state.page > 1 &&
- <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
- }
- <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
+ {this.state.page > 1 && (
+ <button
+ class="btn btn-sm btn-secondary mr-1"
+ onClick={linkEvent(this, this.prevPage)}
+ >
+ <T i18nKey="prev">#</T>
+ </button>
+ )}
+ <button
+ class="btn btn-sm btn-secondary"
+ onClick={linkEvent(this, this.nextPage)}
+ >
+ <T i18nKey="next">#</T>
+ </button>
</div>
);
}
@@ -388,17 +577,19 @@ export class User extends Component<any, UserState> {
updateUrl() {
let viewStr = View[this.state.view].toLowerCase();
let sortStr = SortType[this.state.sort].toLowerCase();
- this.props.history.push(`/u/${this.state.user.name}/view/${viewStr}/sort/${sortStr}/page/${this.state.page}`);
+ this.props.history.push(
+ `/u/${this.state.user.name}/view/${viewStr}/sort/${sortStr}/page/${this.state.page}`
+ );
}
- nextPage(i: User) {
+ nextPage(i: User) {
i.state.page++;
i.setState(i.state);
i.updateUrl();
i.refetch();
}
- prevPage(i: User) {
+ prevPage(i: User) {
i.state.page--;
i.setState(i.state);
i.updateUrl();
@@ -489,11 +680,14 @@ export class User extends Component<any, UserState> {
this.state.admins = res.admins;
this.state.loading = false;
if (this.isCurrentUser) {
- this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
- this.state.userSettingsForm.theme = UserService.Instance.user.theme ? UserService.Instance.user.theme : 'darkly';
+ this.state.userSettingsForm.show_nsfw =
+ UserService.Instance.user.show_nsfw;
+ this.state.userSettingsForm.theme = UserService.Instance.user.theme
+ ? UserService.Instance.user.theme
+ : 'darkly';
}
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
this.setState(this.state);
} else if (op == UserOperation.EditComment) {
let res: CommentResponse = msg;
@@ -520,36 +714,38 @@ export class User extends Component<any, UserState> {
this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg;
- let found: Comment = this.state.comments.find(c => c.id === res.comment.id);
+ let found: Comment = this.state.comments.find(
+ c => c.id === res.comment.id
+ );
found.score = res.comment.score;
found.upvotes = res.comment.upvotes;
found.downvotes = res.comment.downvotes;
- if (res.comment.my_vote !== null)
- found.my_vote = res.comment.my_vote;
+ if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote;
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.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) {
- this.state = this.emptyState;
- this.state.userSettingsLoading = false;
- this.setState(this.state);
- let res: LoginResponse = msg;
- UserService.Instance.login(res);
+ this.state = this.emptyState;
+ this.state.userSettingsLoading = false;
+ this.setState(this.state);
+ let res: LoginResponse = msg;
+ UserService.Instance.login(res);
} else if (op == UserOperation.DeleteAccount) {
- this.state.deleteAccountLoading = false;
- this.state.deleteAccountShowConfirm = false;
- this.setState(this.state);
- this.context.router.history.push('/');
+ this.state.deleteAccountLoading = false;
+ this.state.deleteAccountShowConfirm = false;
+ this.setState(this.state);
+ this.context.router.history.push('/');
}
}
}
-