import { Component, linkEvent } from 'inferno';
-import { CommentNode as CommentNodeI, CommentForm as CommentFormI, SearchForm, SearchType, SortType, UserOperation, SearchResponse } from '../interfaces';
-import { Subscription } from "rxjs";
-import { capitalizeFirstLetter, mentionDropdownFetchLimit, msgOp, mdToHtml, randomStr, markdownHelpUrl } from '../utils';
+import {
+ CommentNode as CommentNodeI,
+ CommentForm as CommentFormI,
+ SearchForm,
+ SearchType,
+ SortType,
+ UserOperation,
+ SearchResponse,
+} from '../interfaces';
+import { Subscription } from 'rxjs';
+import {
+ capitalizeFirstLetter,
+ mentionDropdownFetchLimit,
+ msgOp,
+ mdToHtml,
+ randomStr,
+ markdownHelpUrl,
+} from '../utils';
import { WebSocketService, UserService } from '../services';
import * as autosize from 'autosize';
import { i18n } from '../i18next';
}
export class CommentForm extends Component<CommentFormProps, CommentFormState> {
-
private id = `comment-form-${randomStr()}`;
private userSub: Subscription;
private communitySub: Subscription;
commentForm: {
auth: null,
content: null,
- post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId,
- creator_id: UserService.Instance.user ? UserService.Instance.user.id : null,
+ post_id: this.props.node
+ ? this.props.node.comment.post_id
+ : this.props.postId,
+ creator_id: UserService.Instance.user
+ ? UserService.Instance.user.id
+ : null,
},
- buttonTitle: !this.props.node ? capitalizeFirstLetter(i18n.t('post')) : this.props.edit ? capitalizeFirstLetter(i18n.t('edit')) : capitalizeFirstLetter(i18n.t('reply')),
+ buttonTitle: !this.props.node
+ ? capitalizeFirstLetter(i18n.t('post'))
+ : this.props.edit
+ ? capitalizeFirstLetter(i18n.t('edit'))
+ : capitalizeFirstLetter(i18n.t('reply')),
previewMode: false,
imageLoading: false,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
selectTemplate: (item: any) => {
return `:${item.original.key}:`;
},
- values: Object.entries(emojiShortName).map(e => {return {'key': e[1], 'val': e[0]}}),
+ values: Object.entries(emojiShortName).map(e => {
+ return { key: e[1], val: e[0] };
+ }),
allowSpaces: false,
autocompleteMode: true,
menuItemLimit: mentionDropdownFetchLimit,
allowSpaces: false,
autocompleteMode: true,
menuItemLimit: mentionDropdownFetchLimit,
- }
- ]
+ },
+ ],
});
this.state = this.emptyState;
// A reply gets a new parent id
this.state.commentForm.parent_id = this.props.node.comment.id;
}
- }
+ }
}
componentDidMount() {
<form onSubmit={linkEvent(this, this.handleCommentSubmit)}>
<div class="form-group row">
<div className={`col-sm-12`}>
- <textarea id={this.id} className={`form-control ${this.state.previewMode && 'd-none'}`} value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} required disabled={this.props.disabled} rows={2} maxLength={10000} />
- {this.state.previewMode &&
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(this.state.commentForm.content)} />
- }
+ <textarea
+ id={this.id}
+ className={`form-control ${this.state.previewMode && 'd-none'}`}
+ value={this.state.commentForm.content}
+ onInput={linkEvent(this, this.handleCommentContentChange)}
+ required
+ disabled={this.props.disabled}
+ rows={2}
+ maxLength={10000}
+ />
+ {this.state.previewMode && (
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(
+ this.state.commentForm.content
+ )}
+ />
+ )}
</div>
</div>
<div class="row">
<div class="col-sm-12">
- <button type="submit" class="btn btn-sm btn-secondary mr-2" disabled={this.props.disabled}>{this.state.buttonTitle}</button>
- {this.state.commentForm.content &&
- <button className={`btn btn-sm mr-2 btn-secondary ${this.state.previewMode && 'active'}`} onClick={linkEvent(this, this.handlePreviewToggle)}><T i18nKey="preview">#</T></button>
- }
- {this.props.node && <button type="button" class="btn btn-sm btn-secondary mr-2" onClick={linkEvent(this, this.handleReplyCancel)}><T i18nKey="cancel">#</T></button>}
- <a href={markdownHelpUrl} target="_blank" class="d-inline-block float-right text-muted small font-weight-bold"><T i18nKey="formatting_help">#</T></a>
+ <button
+ type="submit"
+ class="btn btn-sm btn-secondary mr-2"
+ disabled={this.props.disabled}
+ >
+ {this.state.buttonTitle}
+ </button>
+ {this.state.commentForm.content && (
+ <button
+ className={`btn btn-sm mr-2 btn-secondary ${this.state
+ .previewMode && 'active'}`}
+ onClick={linkEvent(this, this.handlePreviewToggle)}
+ >
+ <T i18nKey="preview">#</T>
+ </button>
+ )}
+ {this.props.node && (
+ <button
+ type="button"
+ class="btn btn-sm btn-secondary mr-2"
+ onClick={linkEvent(this, this.handleReplyCancel)}
+ >
+ <T i18nKey="cancel">#</T>
+ </button>
+ )}
+ <a
+ href={markdownHelpUrl}
+ target="_blank"
+ class="d-inline-block float-right text-muted small font-weight-bold"
+ >
+ <T i18nKey="formatting_help">#</T>
+ </a>
<form class="d-inline-block mr-2 float-right text-muted small font-weight-bold">
- <label htmlFor={`file-upload-${this.id}`} className={`${UserService.Instance.user && 'pointer'}`}><T i18nKey="upload_image">#</T></label>
- <input id={`file-upload-${this.id}`} type="file" accept="image/*,video/*" name="file" class="d-none" disabled={!UserService.Instance.user} onChange={linkEvent(this, this.handleImageUpload)} />
+ <label
+ htmlFor={`file-upload-${this.id}`}
+ className={`${UserService.Instance.user && 'pointer'}`}
+ >
+ <T i18nKey="upload_image">#</T>
+ </label>
+ <input
+ id={`file-upload-${this.id}`}
+ type="file"
+ accept="image/*,video/*"
+ name="file"
+ class="d-none"
+ disabled={!UserService.Instance.user}
+ onChange={linkEvent(this, this.handleImageUpload)}
+ />
</form>
- {this.state.imageLoading &&
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg>
- }
+ {this.state.imageLoading && (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ )}
</div>
</div>
</form>
method: 'POST',
body: formData,
})
- .then(res => res.json())
- .then(res => {
- let url = `${window.location.origin}/pictshare/${res.url}`;
- let markdown = (res.filetype == 'mp4') ? `[vid](${url}/raw)` : `![](${url})`;
- let content = i.state.commentForm.content;
- content = (content) ? `${content} ${markdown}` : markdown;
- i.state.commentForm.content = content;
- i.state.imageLoading = false;
- i.setState(i.state);
- })
- .catch((error) => {
- i.state.imageLoading = false;
- i.setState(i.state);
- alert(error);
- })
+ .then(res => res.json())
+ .then(res => {
+ let url = `${window.location.origin}/pictshare/${res.url}`;
+ let markdown =
+ res.filetype == 'mp4' ? `[vid](${url}/raw)` : `![](${url})`;
+ let content = i.state.commentForm.content;
+ content = content ? `${content} ${markdown}` : markdown;
+ i.state.commentForm.content = content;
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ })
+ .catch(error => {
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ alert(error);
+ });
}
-
+
userSearch(text: string, cb: any) {
if (text) {
let form: SearchForm = {
WebSocketService.Instance.search(form);
- this.userSub = WebSocketService.Instance.subject
- .subscribe(
- (msg) => {
+ this.userSub = WebSocketService.Instance.subject.subscribe(
+ msg => {
let op: UserOperation = msgOp(msg);
if (op == UserOperation.Search) {
let res: SearchResponse = msg;
- let users = res.users.map(u => {return {key: u.name}});
+ let users = res.users.map(u => {
+ return { key: u.name };
+ });
cb(users);
this.userSub.unsubscribe();
}
},
- (err) => console.error(err),
+ err => console.error(err),
() => console.log('complete')
);
} else {
WebSocketService.Instance.search(form);
- this.communitySub = WebSocketService.Instance.subject
- .subscribe(
- (msg) => {
+ this.communitySub = WebSocketService.Instance.subject.subscribe(
+ msg => {
let op: UserOperation = msgOp(msg);
if (op == UserOperation.Search) {
let res: SearchResponse = msg;
- let communities = res.communities.map(u => {return {key: u.name}});
+ let communities = res.communities.map(u => {
+ return { key: u.name };
+ });
cb(communities);
this.communitySub.unsubscribe();
}
},
- (err) => console.error(err),
+ err => console.error(err),
() => console.log('complete')
);
} else {
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, BanType } 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';
}
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
-
private emptyState: CommentNodeState = {
showReply: false,
showEdit: false,
viewSource: false,
showConfirmTransferSite: false,
showConfirmTransferCommunity: false,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
render() {
let node = this.props.node;
return (
- <div className={`comment ${node.comment.parent_id && !this.props.noIndent ? 'ml-4' : ''}`}>
- {!this.state.collapsed &&
- <div className={`vote-bar mr-2 float-left small text-center ${this.props.viewOnly && 'no-click'}`}>
- <button className={`btn p-0 ${node.comment.my_vote == 1 ? 'text-info' : 'text-muted'}`} onClick={linkEvent(node, this.handleCommentLike)}>
- <svg class="icon upvote"><use xlinkHref="#icon-arrow-up"></use></svg>
+ <div
+ className={`comment ${
+ node.comment.parent_id && !this.props.noIndent ? 'ml-4' : ''
+ }`}
+ >
+ {!this.state.collapsed && (
+ <div
+ className={`vote-bar mr-2 float-left small text-center ${this.props
+ .viewOnly && 'no-click'}`}
+ >
+ <button
+ className={`btn p-0 ${
+ node.comment.my_vote == 1 ? 'text-info' : 'text-muted'
+ }`}
+ onClick={linkEvent(node, this.handleCommentLike)}
+ >
+ <svg class="icon upvote">
+ <use xlinkHref="#icon-arrow-up"></use>
+ </svg>
</button>
- <div class={`font-weight-bold text-muted`}>{node.comment.score}</div>
- <button className={`btn p-0 ${node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'}`} onClick={linkEvent(node, this.handleCommentDisLike)}>
- <svg class="icon downvote"><use xlinkHref="#icon-arrow-down"></use></svg>
+ <div class={`font-weight-bold text-muted`}>
+ {node.comment.score}
+ </div>
+ <button
+ className={`btn p-0 ${
+ node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
+ }`}
+ onClick={linkEvent(node, this.handleCommentDisLike)}
+ >
+ <svg class="icon downvote">
+ <use xlinkHref="#icon-arrow-down"></use>
+ </svg>
</button>
</div>
- }
- <div id={`comment-${node.comment.id}`} className={`details comment-node ml-4 ${this.isCommentNew ? 'mark' : ''}`}>
+ )}
+ <div
+ id={`comment-${node.comment.id}`}
+ className={`details comment-node ml-4 ${
+ this.isCommentNew ? 'mark' : ''
+ }`}
+ >
<ul class="list-inline mb-0 text-muted small">
<li className="list-inline-item">
- <Link className="text-info" to={`/u/${node.comment.creator_name}`}>{node.comment.creator_name}</Link>
+ <Link
+ className="text-info"
+ to={`/u/${node.comment.creator_name}`}
+ >
+ {node.comment.creator_name}
+ </Link>
</li>
- {this.isMod &&
- <li className="list-inline-item badge badge-light"><T i18nKey="mod">#</T></li>
- }
- {this.isAdmin &&
- <li className="list-inline-item badge badge-light"><T i18nKey="admin">#</T></li>
- }
- {this.isPostCreator &&
- <li className="list-inline-item badge badge-light"><T i18nKey="creator">#</T></li>
- }
- {(node.comment.banned_from_community || node.comment.banned) &&
- <li className="list-inline-item badge badge-danger"><T i18nKey="banned">#</T></li>
- }
+ {this.isMod && (
+ <li className="list-inline-item badge badge-light">
+ <T i18nKey="mod">#</T>
+ </li>
+ )}
+ {this.isAdmin && (
+ <li className="list-inline-item badge badge-light">
+ <T i18nKey="admin">#</T>
+ </li>
+ )}
+ {this.isPostCreator && (
+ <li className="list-inline-item badge badge-light">
+ <T i18nKey="creator">#</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>
+ <span>
+ (<span className="text-info">+{node.comment.upvotes}</span>
<span> | </span>
<span className="text-danger">-{node.comment.downvotes}</span>
<span>) </span>
</span>
</li>
<li className="list-inline-item">
- <span><MomentTime data={node.comment} /></span>
+ <span>
+ <MomentTime data={node.comment} />
+ </span>
</li>
<li className="list-inline-item">
- <div className="pointer text-monospace" onClick={linkEvent(this, this.handleCommentCollapse)}>{this.state.collapsed ? '[+]' : '[-]'}</div>
+ <div
+ className="pointer text-monospace"
+ onClick={linkEvent(this, this.handleCommentCollapse)}
+ >
+ {this.state.collapsed ? '[+]' : '[-]'}
+ </div>
</li>
</ul>
- {this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
- {!this.state.showEdit && !this.state.collapsed &&
+ {this.state.showEdit && (
+ <CommentForm
+ node={node}
+ edit
+ onReplyCancel={this.handleReplyCancel}
+ disabled={this.props.locked}
+ />
+ )}
+ {!this.state.showEdit && !this.state.collapsed && (
<div>
- {this.state.viewSource ? <pre>{this.commentUnlessRemoved}</pre> :
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} />
- }
+ {this.state.viewSource ? (
+ <pre>{this.commentUnlessRemoved}</pre>
+ ) : (
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)}
+ />
+ )}
<ul class="list-inline mb-1 text-muted small font-weight-bold">
- {UserService.Instance.user && !this.props.viewOnly &&
+ {UserService.Instance.user && !this.props.viewOnly && (
<>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}><T i18nKey="reply">#</T></span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleReplyClick)}
+ >
+ <T i18nKey="reply">#</T>
+ </span>
</li>
<li className="list-inline-item mr-2">
- <span class="pointer" onClick={linkEvent(this, this.handleSaveCommentClick)}>{node.comment.saved ? i18n.t('unsave') : i18n.t('save')}</span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleSaveCommentClick)}
+ >
+ {node.comment.saved ? i18n.t('unsave') : i18n.t('save')}
+ </span>
</li>
- {this.myComment &&
+ {this.myComment && (
<>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleEditClick)}
+ >
+ <T i18nKey="edit">#</T>
+ </span>
</li>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
- {!node.comment.deleted ? i18n.t('delete') : i18n.t('restore')}
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleDeleteClick)}
+ >
+ {!node.comment.deleted
+ ? i18n.t('delete')
+ : i18n.t('restore')}
</span>
</li>
</>
- }
+ )}
{/* Admins and mods can remove comments */}
- {(this.canMod || this.canAdmin) &&
+ {(this.canMod || this.canAdmin) && (
<li className="list-inline-item">
- {!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>
- }
+ {!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>
+ )}
</li>
- }
+ )}
{/* Mods can ban from community, and appoint as mods to community */}
- {this.canMod &&
+ {this.canMod && (
<>
- {!this.isMod &&
+ {!this.isMod && (
<li className="list-inline-item">
- {!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>
- }
+ {!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>
- }
- {!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>
+ <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 &&
+ {(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>
- </>
- }
+ {!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.canAdmin && (
<>
- {!this.isAdmin &&
+ {!this.isAdmin && (
<li className="list-inline-item">
- {!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>
- }
+ {!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>
- }
- {!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>
+ <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 &&
+ {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>
- </>
- }
+ {!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>
- }
+ )}
</>
- }
+ )}
<li className="list-inline-item">
- <span className="pointer" onClick={linkEvent(this, this.handleViewSource)}><T i18nKey="view_source">#</T></span>
+ <span
+ className="pointer"
+ onClick={linkEvent(this, this.handleViewSource)}
+ >
+ <T i18nKey="view_source">#</T>
+ </span>
</li>
<li className="list-inline-item">
- <Link className="text-muted" to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}><T i18nKey="link">#</T></Link>
+ <Link
+ className="text-muted"
+ to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
+ >
+ <T i18nKey="link">#</T>
+ </Link>
</li>
- {this.props.markable &&
+ {this.props.markable && (
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleMarkRead)}>{node.comment.read ? i18n.t('mark_as_unread') : i18n.t('mark_as_read')}</span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleMarkRead)}
+ >
+ {node.comment.read
+ ? i18n.t('mark_as_unread')
+ : i18n.t('mark_as_read')}
+ </span>
</li>
- }
+ )}
</ul>
</div>
- }
+ )}
</div>
- {this.state.showRemoveDialog &&
- <form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
- <input type="text" class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
- <button type="submit" class="btn btn-secondary"><T i18nKey="remove_comment">#</T></button>
+ {this.state.showRemoveDialog && (
+ <form
+ class="form-inline"
+ onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
+ >
+ <input
+ type="text"
+ class="form-control mr-2"
+ placeholder={i18n.t('reason')}
+ value={this.state.removeReason}
+ onInput={linkEvent(this, this.handleModRemoveReasonChange)}
+ />
+ <button type="submit" class="btn btn-secondary">
+ <T i18nKey="remove_comment">#</T>
+ </button>
</form>
- }
- {this.state.showBanDialog &&
+ )}
+ {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)} />
+ <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"> */}
{/* <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')} {node.comment.creator_name}</button>
+ <button type="submit" class="btn btn-secondary">
+ {i18n.t('ban')} {node.comment.creator_name}
+ </button>
</div>
</form>
- }
- {this.state.showReply &&
- <CommentForm
- node={node}
- onReplyCancel={this.handleReplyCancel}
- disabled={this.props.locked}
+ )}
+ {this.state.showReply && (
+ <CommentForm
+ node={node}
+ onReplyCancel={this.handleReplyCancel}
+ disabled={this.props.locked}
/>
- }
- {node.children && !this.state.collapsed &&
- <CommentNodes
- nodes={node.children}
- locked={this.props.locked}
+ )}
+ {node.children && !this.state.collapsed && (
+ <CommentNodes
+ nodes={node.children}
+ locked={this.props.locked}
moderators={this.props.moderators}
admins={this.props.admins}
postCreatorId={this.props.postCreatorId}
/>
- }
+ )}
{/* A collapsed clearfix */}
{this.state.collapsed && <div class="row col-12"></div>}
</div>
- )
+ );
}
get myComment(): boolean {
- return UserService.Instance.user && this.props.node.comment.creator_id == UserService.Instance.user.id;
+ return (
+ UserService.Instance.user &&
+ this.props.node.comment.creator_id == UserService.Instance.user.id
+ );
}
get isMod(): boolean {
- return this.props.moderators && isMod(this.props.moderators.map(m => m.user_id), this.props.node.comment.creator_id);
+ return (
+ this.props.moderators &&
+ isMod(
+ this.props.moderators.map(m => m.user_id),
+ this.props.node.comment.creator_id
+ )
+ );
}
get isAdmin(): boolean {
- return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
+ return (
+ this.props.admins &&
+ isMod(
+ this.props.admins.map(a => a.id),
+ this.props.node.comment.creator_id
+ )
+ );
}
get isPostCreator(): 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));
-
- return canMod(UserService.Instance.user, adminsThenMods, this.props.node.comment.creator_id);
- } else {
+ let adminsThenMods = this.props.admins
+ .map(a => a.id)
+ .concat(this.props.moderators.map(m => m.user_id));
+
+ return canMod(
+ UserService.Instance.user,
+ adminsThenMods,
+ this.props.node.comment.creator_id
+ );
+ } else {
return false;
}
}
get canAdmin(): boolean {
- return this.props.admins && canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
+ return (
+ this.props.admins &&
+ canMod(
+ UserService.Instance.user,
+ this.props.admins.map(a => a.id),
+ this.props.node.comment.creator_id
+ )
+ );
}
get amCommunityCreator(): boolean {
- return this.props.moderators &&
- UserService.Instance.user &&
- (this.props.node.comment.creator_id != UserService.Instance.user.id) &&
- (UserService.Instance.user.id == this.props.moderators[0].user_id);
+ return (
+ this.props.moderators &&
+ UserService.Instance.user &&
+ this.props.node.comment.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.node.comment.creator_id != UserService.Instance.user.id) &&
- (UserService.Instance.user.id == this.props.admins[0].id);
+ return (
+ this.props.admins &&
+ UserService.Instance.user &&
+ this.props.node.comment.creator_id != UserService.Instance.user.id &&
+ UserService.Instance.user.id == this.props.admins[0].id
+ );
}
-
- get commentUnlessRemoved(): string {
+
+ get commentUnlessRemoved(): string {
let node = this.props.node;
- return node.comment.removed ? `*${i18n.t('removed')}*` : node.comment.deleted ? `*${i18n.t('deleted')}*` : node.comment.content;
+ return node.comment.removed
+ ? `*${i18n.t('removed')}*`
+ : node.comment.deleted
+ ? `*${i18n.t('deleted')}*`
+ : node.comment.content;
}
handleReplyClick(i: CommentNode) {
post_id: i.props.node.comment.post_id,
parent_id: i.props.node.comment.parent_id,
deleted: !i.props.node.comment.deleted,
- auth: null
+ auth: null,
};
WebSocketService.Instance.editComment(deleteForm);
}
handleSaveCommentClick(i: CommentNode) {
- let saved = (i.props.node.comment.saved == undefined) ? true : !i.props.node.comment.saved;
+ let saved =
+ i.props.node.comment.saved == undefined
+ ? true
+ : !i.props.node.comment.saved;
let form: SaveCommentForm = {
comment_id: i.props.node.comment.id,
- save: saved
+ save: saved,
};
WebSocketService.Instance.saveComment(form);
this.setState(this.state);
}
-
handleCommentLike(i: CommentNodeI) {
-
let form: CommentLikeForm = {
comment_id: i.comment.id,
post_id: i.comment.post_id,
- score: (i.comment.my_vote == 1) ? 0 : 1
+ score: i.comment.my_vote == 1 ? 0 : 1,
};
WebSocketService.Instance.likeComment(form);
}
let form: CommentLikeForm = {
comment_id: i.comment.id,
post_id: i.comment.post_id,
- score: (i.comment.my_vote == -1) ? 0 : -1
+ score: i.comment.my_vote == -1 ? 0 : -1,
};
WebSocketService.Instance.likeComment(form);
}
parent_id: i.props.node.comment.parent_id,
removed: !i.props.node.comment.removed,
reason: i.state.removeReason,
- auth: null
+ auth: null,
};
WebSocketService.Instance.editComment(form);
post_id: i.props.node.comment.post_id,
parent_id: i.props.node.comment.parent_id,
read: !i.props.node.comment.read,
- auth: null
+ auth: null,
};
WebSocketService.Instance.editComment(form);
}
-
handleModBanFromCommunityShow(i: CommentNode) {
i.state.showBanDialog = true;
i.state.banType = BanType.Community;
i.setState(i.state);
}
- handleShowConfirmTransferCommunity(i: CommentNode) {
+ handleShowConfirmTransferCommunity(i: CommentNode) {
i.state.showConfirmTransferCommunity = true;
i.setState(i.state);
}
- handleCancelShowConfirmTransferCommunity(i: CommentNode) {
+ handleCancelShowConfirmTransferCommunity(i: CommentNode) {
i.state.showConfirmTransferCommunity = false;
i.setState(i.state);
}
i.setState(i.state);
}
- handleShowConfirmTransferSite(i: CommentNode) {
+ handleShowConfirmTransferSite(i: CommentNode) {
i.state.showConfirmTransferSite = true;
i.setState(i.state);
}
- handleCancelShowConfirmTransferSite(i: CommentNode) {
+ handleCancelShowConfirmTransferSite(i: CommentNode) {
i.state.showConfirmTransferSite = false;
i.setState(i.state);
}
import { Component } from 'inferno';
-import { CommentNode as CommentNodeI, CommunityUser, UserView } from '../interfaces';
+import {
+ CommentNode as CommentNodeI,
+ CommunityUser,
+ UserView,
+} from '../interfaces';
import { CommentNode } from './comment-node';
-interface CommentNodesState {
-}
+interface CommentNodesState {}
interface CommentNodesProps {
nodes: Array<CommentNodeI>;
markable?: boolean;
}
-export class CommentNodes extends Component<CommentNodesProps, CommentNodesState> {
-
+export class CommentNodes extends Component<
+ CommentNodesProps,
+ CommentNodesState
+> {
constructor(props: any, context: any) {
super(props, context);
}
render() {
return (
<div className="comments">
- {this.props.nodes.map(node =>
- <CommentNode node={node}
- noIndent={this.props.noIndent}
- viewOnly={this.props.viewOnly}
- locked={this.props.locked}
+ {this.props.nodes.map(node => (
+ <CommentNode
+ node={node}
+ noIndent={this.props.noIndent}
+ viewOnly={this.props.viewOnly}
+ locked={this.props.locked}
moderators={this.props.moderators}
admins={this.props.admins}
postCreatorId={this.props.postCreatorId}
markable={this.props.markable}
/>
- )}
+ ))}
</div>
- )
+ );
}
-
}
-
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, Community, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm, ListCommunitiesForm, SortType } from '../interfaces';
+import {
+ UserOperation,
+ Community,
+ ListCommunitiesResponse,
+ CommunityResponse,
+ FollowCommunityForm,
+ ListCommunitiesForm,
+ SortType,
+} from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp } from '../utils';
import { i18n } from '../i18next';
communities: [],
loading: true,
page: this.getPageFromProps(this.props),
- }
+ };
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
.subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
+ msg => this.parseMessage(msg),
+ err => console.error(err),
() => console.log('complete')
);
this.refetch();
-
}
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() {
}
componentDidMount() {
- document.title = `${i18n.t('communities')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('communities')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
// Necessary for back button for some reason
render() {
return (
<div class="container">
- {this.state.loading ?
- <h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
- <div>
- <h5><T i18nKey="list_of_communities">#</T></h5>
- <div class="table-responsive">
- <table id="community_table" class="table table-sm table-hover">
- <thead class="pointer">
- <tr>
- <th><T i18nKey="name">#</T></th>
- <th class="d-none d-lg-table-cell"><T i18nKey="title">#</T></th>
- <th><T i18nKey="category">#</T></th>
- <th class="text-right"><T i18nKey="subscribers">#</T></th>
- <th class="text-right d-none d-lg-table-cell"><T i18nKey="posts">#</T></th>
- <th class="text-right d-none d-lg-table-cell"><T i18nKey="comments">#</T></th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {this.state.communities.map(community =>
+ {this.state.loading ? (
+ <h5 class="">
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ </h5>
+ ) : (
+ <div>
+ <h5>
+ <T i18nKey="list_of_communities">#</T>
+ </h5>
+ <div class="table-responsive">
+ <table id="community_table" class="table table-sm table-hover">
+ <thead class="pointer">
<tr>
- <td><Link to={`/c/${community.name}`}>{community.name}</Link></td>
- <td class="d-none d-lg-table-cell">{community.title}</td>
- <td>{community.category_name}</td>
- <td class="text-right">{community.number_of_subscribers}</td>
- <td class="text-right d-none d-lg-table-cell">{community.number_of_posts}</td>
- <td class="text-right d-none d-lg-table-cell">{community.number_of_comments}</td>
- <td class="text-right">
- {community.subscribed ?
- <span class="pointer btn-link" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></span> :
- <span class="pointer btn-link" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></span>
- }
- </td>
+ <th>
+ <T i18nKey="name">#</T>
+ </th>
+ <th class="d-none d-lg-table-cell">
+ <T i18nKey="title">#</T>
+ </th>
+ <th>
+ <T i18nKey="category">#</T>
+ </th>
+ <th class="text-right">
+ <T i18nKey="subscribers">#</T>
+ </th>
+ <th class="text-right d-none d-lg-table-cell">
+ <T i18nKey="posts">#</T>
+ </th>
+ <th class="text-right d-none d-lg-table-cell">
+ <T i18nKey="comments">#</T>
+ </th>
+ <th></th>
</tr>
- )}
- </tbody>
- </table>
+ </thead>
+ <tbody>
+ {this.state.communities.map(community => (
+ <tr>
+ <td>
+ <Link to={`/c/${community.name}`}>
+ {community.name}
+ </Link>
+ </td>
+ <td class="d-none d-lg-table-cell">{community.title}</td>
+ <td>{community.category_name}</td>
+ <td class="text-right">
+ {community.number_of_subscribers}
+ </td>
+ <td class="text-right d-none d-lg-table-cell">
+ {community.number_of_posts}
+ </td>
+ <td class="text-right d-none d-lg-table-cell">
+ {community.number_of_comments}
+ </td>
+ <td class="text-right">
+ {community.subscribed ? (
+ <span
+ class="pointer btn-link"
+ onClick={linkEvent(
+ community.id,
+ this.handleUnsubscribe
+ )}
+ >
+ <T i18nKey="unsubscribe">#</T>
+ </span>
+ ) : (
+ <span
+ class="pointer btn-link"
+ onClick={linkEvent(
+ community.id,
+ this.handleSubscribe
+ )}
+ >
+ <T i18nKey="subscribe">#</T>
+ </span>
+ )}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </div>
+ {this.paginator()}
</div>
- {this.paginator()}
- </div>
- }
+ )}
</div>
);
}
paginator() {
return (
<div class="mt-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>
);
}
this.props.history.push(`/communities/page/${this.state.page}`);
}
- nextPage(i: Communities) {
+ nextPage(i: Communities) {
i.state.page++;
i.setState(i.state);
i.updateUrl();
i.refetch();
}
- prevPage(i: Communities) {
+ prevPage(i: Communities) {
i.state.page--;
i.setState(i.state);
i.updateUrl();
handleUnsubscribe(communityId: number) {
let form: FollowCommunityForm = {
community_id: communityId,
- follow: false
+ follow: false,
};
WebSocketService.Instance.followCommunity(form);
}
handleSubscribe(communityId: number) {
let form: FollowCommunityForm = {
community_id: communityId,
- follow: true
+ follow: true,
};
WebSocketService.Instance.followCommunity(form);
}
sort: SortType[SortType.TopAll],
limit: 100,
page: this.state.page,
- }
+ };
WebSocketService.Instance.listCommunities(listCommunitiesForm);
-
}
parseMessage(msg: any) {
} else if (op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg;
this.state.communities = res.communities;
- this.state.communities.sort((a, b) => b.number_of_subscribers - a.number_of_subscribers);
+ this.state.communities.sort(
+ (a, b) => b.number_of_subscribers - a.number_of_subscribers
+ );
this.state.loading = false;
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
this.setState(this.state);
let table = document.querySelector('#community_table');
Sortable.initTable(table);
found.subscribed = res.community.subscribed;
found.number_of_subscribers = res.community.number_of_subscribers;
this.setState(this.state);
- }
+ }
}
}
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { CommunityForm as CommunityFormI, UserOperation, Category, ListCategoriesResponse, CommunityResponse } from '../interfaces';
+import {
+ CommunityForm as CommunityFormI,
+ UserOperation,
+ Category,
+ ListCategoriesResponse,
+ CommunityResponse,
+} from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp, capitalizeFirstLetter } from '../utils';
import * as autosize from 'autosize';
loading: boolean;
}
-export class CommunityForm extends Component<CommunityFormProps, CommunityFormState> {
+export class CommunityForm extends Component<
+ CommunityFormProps,
+ CommunityFormState
+> {
private subscription: Subscription;
private emptyState: CommunityFormState = {
nsfw: false,
},
categories: [],
- loading: false
- }
+ loading: false,
+ };
constructor(props: any, context: any) {
super(props, context);
description: this.props.community.description,
edit_id: this.props.community.id,
nsfw: this.props.community.nsfw,
- auth: null
- }
+ auth: null,
+ };
}
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
.subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
- () => console.log("complete")
+ msg => this.parseMessage(msg),
+ err => console.error(err),
+ () => console.log('complete')
);
WebSocketService.Instance.listCategories();
this.subscription.unsubscribe();
}
-
render() {
return (
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="name">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="name">#</T>
+ </label>
<div class="col-12">
- <input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} maxLength={20} pattern="[a-z0-9_]+" title={i18n.t('community_reqs')}/>
+ <input
+ type="text"
+ class="form-control"
+ value={this.state.communityForm.name}
+ onInput={linkEvent(this, this.handleCommunityNameChange)}
+ required
+ minLength={3}
+ maxLength={20}
+ pattern="[a-z0-9_]+"
+ title={i18n.t('community_reqs')}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="title">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="title">#</T>
+ </label>
<div class="col-12">
- <input type="text" value={this.state.communityForm.title} onInput={linkEvent(this, this.handleCommunityTitleChange)} class="form-control" required minLength={3} maxLength={100} />
+ <input
+ type="text"
+ value={this.state.communityForm.title}
+ onInput={linkEvent(this, this.handleCommunityTitleChange)}
+ class="form-control"
+ required
+ minLength={3}
+ maxLength={100}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="sidebar">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="sidebar">#</T>
+ </label>
<div class="col-12">
- <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={3} maxLength={10000} />
+ <textarea
+ value={this.state.communityForm.description}
+ onInput={linkEvent(this, this.handleCommunityDescriptionChange)}
+ class="form-control"
+ rows={3}
+ maxLength={10000}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="category">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="category">#</T>
+ </label>
<div class="col-12">
- <select class="form-control" value={this.state.communityForm.category_id} onInput={linkEvent(this, this.handleCommunityCategoryChange)}>
- {this.state.categories.map(category =>
+ <select
+ class="form-control"
+ value={this.state.communityForm.category_id}
+ onInput={linkEvent(this, this.handleCommunityCategoryChange)}
+ >
+ {this.state.categories.map(category => (
<option value={category.id}>{category.name}</option>
- )}
+ ))}
</select>
</div>
</div>
<div class="form-group row">
<div class="col-12">
<div class="form-check">
- <input class="form-check-input" type="checkbox" checked={this.state.communityForm.nsfw} onChange={linkEvent(this, this.handleCommunityNsfwChange)}/>
- <label class="form-check-label"><T i18nKey="nsfw">#</T></label>
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.communityForm.nsfw}
+ onChange={linkEvent(this, this.handleCommunityNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="nsfw">#</T>
+ </label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
- {this.state.loading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
- this.props.community ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('create'))}</button>
- {this.props.community && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
+ {this.state.loading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : this.props.community ? (
+ capitalizeFirstLetter(i18n.t('save'))
+ ) : (
+ capitalizeFirstLetter(i18n.t('create'))
+ )}
+ </button>
+ {this.props.community && (
+ <button
+ type="button"
+ class="btn btn-secondary"
+ onClick={linkEvent(this, this.handleCancel)}
+ >
+ <T i18nKey="cancel">#</T>
+ </button>
+ )}
</div>
</div>
</form>
this.state.loading = false;
this.setState(this.state);
return;
- } else if (op == UserOperation.ListCategories){
+ } else if (op == UserOperation.ListCategories) {
let res: ListCategoriesResponse = msg;
this.state.categories = res.categories;
if (!this.props.community) {
let res: CommunityResponse = msg;
this.state.loading = false;
this.props.onCreate(res.community);
- }
+ }
// TODO is ths necessary
else if (op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg;
this.props.onEdit(res.community);
}
}
-
}
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, CommunityUser, UserView, SortType, Post, GetPostsForm, ListingType, GetPostsResponse, CreatePostLikeResponse } from '../interfaces';
+import {
+ UserOperation,
+ Community as CommunityI,
+ GetCommunityResponse,
+ CommunityResponse,
+ CommunityUser,
+ UserView,
+ SortType,
+ Post,
+ GetPostsForm,
+ ListingType,
+ GetPostsResponse,
+ CreatePostLikeResponse,
+} from '../interfaces';
import { WebSocketService } from '../services';
import { PostListings } from './post-listings';
import { Sidebar } from './sidebar';
-import { msgOp, routeSortTypeToEnum, fetchLimit, postRefetchSeconds } from '../utils';
+import {
+ msgOp,
+ routeSortTypeToEnum,
+ fetchLimit,
+ postRefetchSeconds,
+} from '../utils';
import { T, i18n } from 'inferno-i18next';
interface State {
}
export class Community extends Component<any, State> {
-
private subscription: Subscription;
private postFetcher: any;
private emptyState: State = {
posts: [],
sort: this.getSortTypeFromProps(this.props),
page: this.getPageFromProps(this.props),
- }
+ };
getSortTypeFromProps(props: any): SortType {
- return (props.match.params.sort) ?
- routeSortTypeToEnum(props.match.params.sort) :
- SortType.Hot;
+ return props.match.params.sort
+ ? routeSortTypeToEnum(props.match.params.sort)
+ : SortType.Hot;
}
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;
}
constructor(props: any, context: any) {
this.state = this.emptyState;
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')
- );
+ );
if (this.state.communityId) {
WebSocketService.Instance.getCommunity(this.state.communityId);
} else if (this.state.communityName) {
WebSocketService.Instance.getCommunityByName(this.state.communityName);
}
-
}
componentWillUnmount() {
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>{this.state.community.title}
- {this.state.community.removed &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
- }
- {this.state.community.nsfw &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
- }
+ {this.state.loading ? (
+ <h5>
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
</h5>
- {this.selects()}
- <PostListings posts={this.state.posts} />
- {this.paginator()}
- </div>
- <div class="col-12 col-md-4">
- <Sidebar
- community={this.state.community}
- moderators={this.state.moderators}
- admins={this.state.admins}
- />
+ ) : (
+ <div class="row">
+ <div class="col-12 col-md-8">
+ <h5>
+ {this.state.community.title}
+ {this.state.community.removed && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="removed">#</T>
+ </small>
+ )}
+ {this.state.community.nsfw && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="nsfw">#</T>
+ </small>
+ )}
+ </h5>
+ {this.selects()}
+ <PostListings posts={this.state.posts} />
+ {this.paginator()}
+ </div>
+ <div class="col-12 col-md-4">
+ <Sidebar
+ community={this.state.community}
+ moderators={this.state.moderators}
+ admins={this.state.admins}
+ />
+ </div>
</div>
- </div>
- }
+ )}
</div>
- )
+ );
}
selects() {
return (
<div className="mb-2">
- <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="sort_type">#</T></option>
- <option value={SortType.Hot}><T i18nKey="hot">#</T></option>
- <option value={SortType.New}><T i18nKey="new">#</T></option>
+ <select
+ value={this.state.sort}
+ onChange={linkEvent(this, this.handleSortChange)}
+ class="custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="sort_type">#</T>
+ </option>
+ <option value={SortType.Hot}>
+ <T i18nKey="hot">#</T>
+ </option>
+ <option value={SortType.New}>
+ <T i18nKey="new">#</T>
+ </option>
<option disabled>──────</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>
+ <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>
- )
+ );
}
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>
);
}
- nextPage(i: Community) {
+ nextPage(i: Community) {
i.state.page++;
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
- prevPage(i: Community) {
+ prevPage(i: Community) {
i.state.page--;
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
handleSortChange(i: Community, event: any) {
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
updateUrl() {
let sortStr = SortType[this.state.sort].toLowerCase();
- this.props.history.push(`/c/${this.state.community.name}/sort/${sortStr}/page/${this.state.page}`);
+ this.props.history.push(
+ `/c/${this.state.community.name}/sort/${sortStr}/page/${this.state.page}`
+ );
}
keepFetchingPosts() {
sort: SortType[this.state.sort],
type_: ListingType[ListingType.Community],
community_id: this.state.community.id,
- }
+ };
WebSocketService.Instance.getPosts(getPostsForm);
}
} else if (op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg;
this.state.community.subscribed = res.community.subscribed;
- this.state.community.number_of_subscribers = res.community.number_of_subscribers;
+ this.state.community.number_of_subscribers =
+ res.community.number_of_subscribers;
this.setState(this.state);
} else if (op == UserOperation.GetPosts) {
let res: GetPostsResponse = msg;
}
}
}
-
import { T } from 'inferno-i18next';
export class CreateCommunity extends Component<any, any> {
-
constructor(props: any, context: any) {
super(props, context);
this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
}
componentDidMount() {
- document.title = `${i18n.t('create_community')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('create_community')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
render() {
<div class="container">
<div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
- <h5><T i18nKey="create_community">#</T></h5>
- <CommunityForm onCreate={this.handleCommunityCreate}/>
+ <h5>
+ <T i18nKey="create_community">#</T>
+ </h5>
+ <CommunityForm onCreate={this.handleCommunityCreate} />
</div>
</div>
</div>
- )
+ );
}
handleCommunityCreate(community: Community) {
this.props.history.push(`/c/${community.name}`);
}
}
-
-
import { T } from 'inferno-i18next';
export class CreatePost extends Component<any, any> {
-
constructor(props: any, context: any) {
super(props, context);
this.handlePostCreate = this.handlePostCreate.bind(this);
}
componentDidMount() {
- document.title = `${i18n.t('create_post')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('create_post')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
render() {
<div class="container">
<div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
- <h5><T i18nKey="create_post">#</T></h5>
+ <h5>
+ <T i18nKey="create_post">#</T>
+ </h5>
<PostForm onCreate={this.handlePostCreate} params={this.params} />
</div>
</div>
</div>
- )
+ );
}
get params(): PostFormParams {
let urlParams = new URLSearchParams(this.props.location.search);
let params: PostFormParams = {
- name: urlParams.get("name"),
- community: urlParams.get("community") || this.prevCommunityName,
- body: urlParams.get("body"),
- url: urlParams.get("url"),
+ name: urlParams.get('name'),
+ community: urlParams.get('community') || this.prevCommunityName,
+ body: urlParams.get('body'),
+ url: urlParams.get('url'),
};
return params;
return this.props.match.params.name;
} else if (this.props.location.state) {
let lastLocation = this.props.location.state.prevPath;
- if (lastLocation.includes("/c/")) {
- return lastLocation.split("/c/")[1];
- }
+ if (lastLocation.includes('/c/')) {
+ return lastLocation.split('/c/')[1];
+ }
}
return undefined;
}
this.props.history.push(`/post/${id}`);
}
}
-
-
import { T } from 'inferno-i18next';
export class Footer extends Component<any, any> {
-
-
constructor(props: any, context: any) {
super(props, context);
}
<span class="navbar-text">{version}</span>
</li>
<li class="nav-item">
- <Link class="nav-link" to="/modlog"><T i18nKey="modlog">#</T></Link>
+ <Link class="nav-link" to="/modlog">
+ <T i18nKey="modlog">#</T>
+ </Link>
</li>
<li class="nav-item">
- <a class="nav-link" href={`${repoUrl}/blob/master/docs/api.md`}><T i18nKey="api">#</T></a>
+ <a class="nav-link" href={`${repoUrl}/blob/master/docs/api.md`}>
+ <T i18nKey="api">#</T>
+ </a>
</li>
<li class="nav-item">
- <Link class="nav-link" to="/sponsors"><T i18nKey="sponsors">#</T></Link>
+ <Link class="nav-link" to="/sponsors">
+ <T i18nKey="sponsors">#</T>
+ </Link>
</li>
<li class="nav-item">
- <a class="nav-link" href={repoUrl}><T i18nKey="code">#</T></a>
+ <a class="nav-link" href={repoUrl}>
+ <T i18nKey="code">#</T>
+ </a>
</li>
</ul>
</div>
);
}
}
-
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, Comment, SortType, GetRepliesForm, GetRepliesResponse, CommentResponse } from '../interfaces';
+import {
+ UserOperation,
+ Comment,
+ SortType,
+ GetRepliesForm,
+ GetRepliesResponse,
+ CommentResponse,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { msgOp } from '../utils';
import { CommentNodes } from './comment-nodes';
import { T } from 'inferno-i18next';
enum UnreadType {
- Unread, All
+ Unread,
+ All,
}
interface InboxState {
}
export class Inbox extends Component<any, InboxState> {
-
private subscription: Subscription;
private emptyState: InboxState = {
unreadType: UnreadType.Unread,
replies: [],
sort: SortType.New,
page: 1,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
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();
}
}
componentDidMount() {
- document.title = `/u/${UserService.Instance.user.username} ${i18n.t('inbox')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `/u/${UserService.Instance.user.username} ${i18n.t(
+ 'inbox'
+ )} - ${WebSocketService.Instance.site.name}`;
}
render() {
<div class="row">
<div class="col-12">
<h5 class="mb-0">
- <span><T i18nKey="inbox_for" interpolation={{user: user.username}}>#<Link to={`/u/${user.username}`}>#</Link></T></span>
+ <span>
+ <T i18nKey="inbox_for" interpolation={{ user: user.username }}>
+ #<Link to={`/u/${user.username}`}>#</Link>
+ </T>
+ </span>
</h5>
- {this.state.replies.length > 0 && this.state.unreadType == UnreadType.Unread &&
- <ul class="list-inline mb-1 text-muted small font-weight-bold">
- <li className="list-inline-item">
- <span class="pointer" onClick={this.markAllAsRead}><T i18nKey="mark_all_as_read">#</T></span>
- </li>
- </ul>
- }
+ {this.state.replies.length > 0 &&
+ this.state.unreadType == UnreadType.Unread && (
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ <li className="list-inline-item">
+ <span class="pointer" onClick={this.markAllAsRead}>
+ <T i18nKey="mark_all_as_read">#</T>
+ </span>
+ </li>
+ </ul>
+ )}
{this.selects()}
{this.replies()}
{this.paginator()}
</div>
</div>
</div>
- )
+ );
}
selects() {
return (
<div className="mb-2">
- <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="type">#</T></option>
- <option value={UnreadType.Unread}><T i18nKey="unread">#</T></option>
- <option value={UnreadType.All}><T i18nKey="all">#</T></option>
+ <select
+ value={this.state.unreadType}
+ onChange={linkEvent(this, this.handleUnreadTypeChange)}
+ class="custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="type">#</T>
+ </option>
+ <option value={UnreadType.Unread}>
+ <T i18nKey="unread">#</T>
+ </option>
+ <option value={UnreadType.All}>
+ <T i18nKey="all">#</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>
- )
-
+ );
}
replies() {
return (
<div>
- {this.state.replies.map(reply =>
- <CommentNodes nodes={[{comment: reply}]} noIndent markable />
- )}
+ {this.state.replies.map(reply => (
+ <CommentNodes nodes={[{ comment: reply }]} noIndent markable />
+ ))}
</div>
);
}
paginator() {
return (
<div class="mt-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>
);
}
- nextPage(i: Inbox) {
+ nextPage(i: Inbox) {
i.state.page++;
i.setState(i.state);
i.refetch();
}
- prevPage(i: Inbox) {
+ prevPage(i: Inbox) {
i.state.page--;
i.setState(i.state);
i.refetch();
refetch() {
let form: GetRepliesForm = {
sort: SortType[this.state.sort],
- unread_only: (this.state.unreadType == UnreadType.Unread),
+ unread_only: this.state.unreadType == UnreadType.Unread,
page: this.state.page,
limit: 9999,
};
if (msg.error) {
alert(i18n.t(msg.error));
return;
- } else if (op == UserOperation.GetReplies || op == UserOperation.MarkAllAsRead) {
+ } else if (
+ op == UserOperation.GetReplies ||
+ op == UserOperation.MarkAllAsRead
+ ) {
let res: GetRepliesResponse = msg;
this.state.replies = res.replies;
this.sendRepliesCount();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
this.setState(this.state);
} else if (op == UserOperation.EditComment) {
let res: CommentResponse = msg;
// If youre in the unread view, just remove it from the list
if (this.state.unreadType == UnreadType.Unread && res.comment.read) {
- this.state.replies = this.state.replies.filter(r => r.id !== res.comment.id);
+ this.state.replies = this.state.replies.filter(
+ r => r.id !== res.comment.id
+ );
} else {
let found = this.state.replies.find(c => c.id == res.comment.id);
found.read = res.comment.read;
this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg;
- let found: Comment = this.state.replies.find(c => c.id === res.comment.id);
+ let found: Comment = this.state.replies.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);
}
}
sendRepliesCount() {
- UserService.Instance.sub.next({user: UserService.Instance.user, unreadCount: this.state.replies.filter(r => !r.read).length});
+ UserService.Instance.sub.next({
+ user: UserService.Instance.user,
+ unreadCount: this.state.replies.filter(r => !r.read).length,
+ });
}
}
-
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { LoginForm, RegisterForm, LoginResponse, UserOperation } from '../interfaces';
+import {
+ LoginForm,
+ RegisterForm,
+ LoginResponse,
+ UserOperation,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { msgOp } from '../utils';
import { i18n } from '../i18next';
registerLoading: boolean;
}
-
export class Login extends Component<any, State> {
private subscription: Subscription;
emptyState: State = {
loginForm: {
username_or_email: undefined,
- password: undefined
+ password: undefined,
},
registerForm: {
username: undefined,
},
loginLoading: false,
registerLoading: false,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
- () => console.log("complete")
- );
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
+ .subscribe(
+ msg => this.parseMessage(msg),
+ err => console.error(err),
+ () => console.log('complete')
+ );
}
componentWillUnmount() {
}
componentDidMount() {
- document.title = `${i18n.t('login')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('login')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
render() {
return (
<div class="container">
<div class="row">
- <div class="col-12 col-lg-6 mb-4">
- {this.loginForm()}
- </div>
- <div class="col-12 col-lg-6">
- {this.registerForm()}
- </div>
+ <div class="col-12 col-lg-6 mb-4">{this.loginForm()}</div>
+ <div class="col-12 col-lg-6">{this.registerForm()}</div>
</div>
</div>
- )
+ );
}
loginForm() {
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
<h5>Login</h5>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="email_or_username">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="email_or_username">#</T>
+ </label>
<div class="col-sm-10">
- <input type="text" class="form-control" value={this.state.loginForm.username_or_email} onInput={linkEvent(this, this.handleLoginUsernameChange)} required minLength={3} />
+ <input
+ type="text"
+ class="form-control"
+ value={this.state.loginForm.username_or_email}
+ onInput={linkEvent(this, this.handleLoginUsernameChange)}
+ required
+ minLength={3}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="password">#</T>
+ </label>
<div class="col-sm-10">
- <input type="password" value={this.state.loginForm.password} onInput={linkEvent(this, this.handleLoginPasswordChange)} class="form-control" required />
+ <input
+ type="password"
+ value={this.state.loginForm.password}
+ onInput={linkEvent(this, this.handleLoginPasswordChange)}
+ class="form-control"
+ required
+ />
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">{this.state.loginLoading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('login')}</button>
+ <button type="submit" class="btn btn-secondary">
+ {this.state.loginLoading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ i18n.t('login')
+ )}
+ </button>
</div>
</div>
</form>
registerForm() {
return (
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
- <h5><T i18nKey="sign_up">#</T></h5>
+ <h5>
+ <T i18nKey="sign_up">#</T>
+ </h5>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="username">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="username">#</T>
+ </label>
<div class="col-sm-10">
- <input type="text" class="form-control" value={this.state.registerForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} maxLength={20} pattern="[a-zA-Z0-9_]+" />
+ <input
+ type="text"
+ class="form-control"
+ value={this.state.registerForm.username}
+ onInput={linkEvent(this, this.handleRegisterUsernameChange)}
+ required
+ minLength={3}
+ maxLength={20}
+ pattern="[a-zA-Z0-9_]+"
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="email">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="email">#</T>
+ </label>
<div class="col-sm-10">
- <input type="email" class="form-control" placeholder={i18n.t('optional')} value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
+ <input
+ type="email"
+ class="form-control"
+ placeholder={i18n.t('optional')}
+ value={this.state.registerForm.email}
+ onInput={linkEvent(this, this.handleRegisterEmailChange)}
+ minLength={3}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="password">#</T>
+ </label>
<div class="col-sm-10">
- <input type="password" value={this.state.registerForm.password} onInput={linkEvent(this, this.handleRegisterPasswordChange)} class="form-control" required />
+ <input
+ type="password"
+ value={this.state.registerForm.password}
+ onInput={linkEvent(this, this.handleRegisterPasswordChange)}
+ class="form-control"
+ required
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="verify_password">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="verify_password">#</T>
+ </label>
<div class="col-sm-10">
- <input type="password" value={this.state.registerForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
+ <input
+ type="password"
+ value={this.state.registerForm.password_verify}
+ onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
+ class="form-control"
+ required
+ />
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<div class="form-check">
- <input class="form-check-input" type="checkbox" checked={this.state.registerForm.show_nsfw} onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}/>
- <label class="form-check-label"><T i18nKey="show_nsfw">#</T></label>
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.registerForm.show_nsfw}
+ onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="show_nsfw">#</T>
+ </label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">{this.state.registerLoading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
+ <button type="submit" class="btn btn-secondary">
+ {this.state.registerLoading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ i18n.t('sign_up')
+ )}
+ </button>
</div>
</div>
</form>
}
}
}
-
}
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, CommunityUser, GetFollowedCommunitiesResponse, ListCommunitiesForm, ListCommunitiesResponse, Community, SortType, GetSiteResponse, ListingType, SiteResponse, GetPostsResponse, CreatePostLikeResponse, Post, GetPostsForm } from '../interfaces';
+import {
+ UserOperation,
+ CommunityUser,
+ GetFollowedCommunitiesResponse,
+ ListCommunitiesForm,
+ ListCommunitiesResponse,
+ Community,
+ SortType,
+ GetSiteResponse,
+ ListingType,
+ SiteResponse,
+ GetPostsResponse,
+ CreatePostLikeResponse,
+ Post,
+ GetPostsForm,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { PostListings } from './post-listings';
import { SiteForm } from './site-form';
-import { msgOp, repoUrl, mdToHtml, fetchLimit, routeSortTypeToEnum, routeListingTypeToEnum, postRefetchSeconds } from '../utils';
+import {
+ msgOp,
+ repoUrl,
+ mdToHtml,
+ fetchLimit,
+ routeSortTypeToEnum,
+ routeListingTypeToEnum,
+ postRefetchSeconds,
+} from '../utils';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
}
export class Main extends Component<any, MainState> {
-
private subscription: Subscription;
private postFetcher: any;
private emptyState: MainState = {
type_: this.getListingTypeFromProps(this.props),
sort: this.getSortTypeFromProps(this.props),
page: this.getPageFromProps(this.props),
- }
+ };
getListingTypeFromProps(props: any): ListingType {
- return (props.match.params.type) ?
- routeListingTypeToEnum(props.match.params.type) :
- UserService.Instance.user ?
- ListingType.Subscribed :
- ListingType.All;
+ return props.match.params.type
+ ? routeListingTypeToEnum(props.match.params.type)
+ : UserService.Instance.user
+ ? ListingType.Subscribed
+ : ListingType.All;
}
getSortTypeFromProps(props: any): SortType {
- return (props.match.params.sort) ?
- routeSortTypeToEnum(props.match.params.sort) :
- SortType.Hot;
+ return props.match.params.sort
+ ? routeSortTypeToEnum(props.match.params.sort)
+ : SortType.Hot;
}
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;
}
constructor(props: any, context: any) {
this.handleEditCancel = this.handleEditCancel.bind(this);
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')
- );
+ );
WebSocketService.Instance.getSite();
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.Hot],
- limit: 6
- }
+ limit: 6,
+ };
WebSocketService.Instance.listCommunities(listCommunitiesForm);
// Necessary for back button for some reason
componentWillReceiveProps(nextProps: any) {
- if (nextProps.history.action == 'POP' || nextProps.history.action == 'PUSH') {
+ if (
+ nextProps.history.action == 'POP' ||
+ nextProps.history.action == 'PUSH'
+ ) {
this.state.type_ = this.getListingTypeFromProps(nextProps);
this.state.sort = this.getSortTypeFromProps(nextProps);
this.state.page = this.getPageFromProps(nextProps);
return (
<div class="container">
<div class="row">
- <div class="col-12 col-md-8">
- {this.posts()}
- </div>
- <div class="col-12 col-md-4">
- {this.my_sidebar()}
- </div>
+ <div class="col-12 col-md-8">{this.posts()}</div>
+ <div class="col-12 col-md-4">{this.my_sidebar()}</div>
</div>
</div>
- )
+ );
}
-
+
my_sidebar() {
- return(
+ return (
<div>
- {!this.state.loading &&
+ {!this.state.loading && (
<div>
<div class="card border-secondary mb-3">
<div class="card-body">
{this.trendingCommunities()}
- {UserService.Instance.user && this.state.subscribedCommunities.length > 0 &&
- <div>
- <h5>
- <T i18nKey="subscribed_to_communities">#<Link class="text-white" to="/communities">#</Link></T>
- </h5>
- <ul class="list-inline">
- {this.state.subscribedCommunities.map(community =>
- <li class="list-inline-item"><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
- )}
- </ul>
- </div>
- }
- <Link class="btn btn-sm btn-secondary btn-block"
- to="/create_community">
+ {UserService.Instance.user &&
+ this.state.subscribedCommunities.length > 0 && (
+ <div>
+ <h5>
+ <T i18nKey="subscribed_to_communities">
+ #
+ <Link class="text-white" to="/communities">
+ #
+ </Link>
+ </T>
+ </h5>
+ <ul class="list-inline">
+ {this.state.subscribedCommunities.map(community => (
+ <li class="list-inline-item">
+ <Link to={`/c/${community.community_name}`}>
+ {community.community_name}
+ </Link>
+ </li>
+ ))}
+ </ul>
+ </div>
+ )}
+ <Link
+ class="btn btn-sm btn-secondary btn-block"
+ to="/create_community"
+ >
<T i18nKey="create_a_community">#</T>
</Link>
</div>
{this.sidebar()}
{this.landing()}
</div>
- }
+ )}
</div>
- )
+ );
}
trendingCommunities() {
return (
<div>
<h5>
- <T i18nKey="trending_communities">#<Link class="text-white" to="/communities">#</Link></T>
+ <T i18nKey="trending_communities">
+ #
+ <Link class="text-white" to="/communities">
+ #
+ </Link>
+ </T>
</h5>
- <ul class="list-inline">
- {this.state.trendingCommunities.map(community =>
- <li class="list-inline-item"><Link to={`/c/${community.name}`}>{community.name}</Link></li>
- )}
+ <ul class="list-inline">
+ {this.state.trendingCommunities.map(community => (
+ <li class="list-inline-item">
+ <Link to={`/c/${community.name}`}>{community.name}</Link>
+ </li>
+ ))}
</ul>
</div>
- )
+ );
}
sidebar() {
return (
<div>
- {!this.state.showEditSite ?
- this.siteInfo() :
+ {!this.state.showEditSite ? (
+ this.siteInfo()
+ ) : (
<SiteForm
- site={this.state.site.site}
- onCancel={this.handleEditCancel}
+ site={this.state.site.site}
+ onCancel={this.handleEditCancel}
/>
- }
+ )}
</div>
- )
+ );
}
updateUrl() {
let typeStr = ListingType[this.state.type_].toLowerCase();
let sortStr = SortType[this.state.sort].toLowerCase();
- this.props.history.push(`/home/type/${typeStr}/sort/${sortStr}/page/${this.state.page}`);
+ this.props.history.push(
+ `/home/type/${typeStr}/sort/${sortStr}/page/${this.state.page}`
+ );
}
siteInfo() {
<div class="card border-secondary mb-3">
<div class="card-body">
<h5 class="mb-0">{`${this.state.site.site.name}`}</h5>
- {this.canAdmin &&
- <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ {this.canAdmin && (
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleEditClick)}
+ >
<T i18nKey="edit">#</T>
</span>
</li>
</ul>
- }
+ )}
<ul class="my-2 list-inline">
<li className="list-inline-item badge badge-secondary">
- <T i18nKey="number_online" interpolation={{count: this.state.site.online}}>#</T>
+ <T
+ i18nKey="number_online"
+ interpolation={{ count: this.state.site.online }}
+ >
+ #
+ </T>
</li>
<li className="list-inline-item badge badge-secondary">
- <T i18nKey="number_of_users" interpolation={{count: this.state.site.site.number_of_users}}>#</T>
+ <T
+ i18nKey="number_of_users"
+ interpolation={{
+ count: this.state.site.site.number_of_users,
+ }}
+ >
+ #
+ </T>
</li>
<li className="list-inline-item badge badge-secondary">
- <T i18nKey="number_of_communities" interpolation={{count: this.state.site.site.number_of_communities}}>#</T>
+ <T
+ i18nKey="number_of_communities"
+ interpolation={{
+ count: this.state.site.site.number_of_communities,
+ }}
+ >
+ #
+ </T>
</li>
<li className="list-inline-item badge badge-secondary">
- <T i18nKey="number_of_posts" interpolation={{count: this.state.site.site.number_of_posts}}>#</T>
+ <T
+ i18nKey="number_of_posts"
+ interpolation={{
+ count: this.state.site.site.number_of_posts,
+ }}
+ >
+ #
+ </T>
</li>
<li className="list-inline-item badge badge-secondary">
- <T i18nKey="number_of_comments" interpolation={{count: this.state.site.site.number_of_comments}}>#</T>
+ <T
+ i18nKey="number_of_comments"
+ interpolation={{
+ count: this.state.site.site.number_of_comments,
+ }}
+ >
+ #
+ </T>
</li>
<li className="list-inline-item">
<Link className="badge badge-secondary" to="/modlog">
</Link>
</li>
</ul>
- <ul class="mt-1 list-inline small mb-0">
+ <ul class="mt-1 list-inline small mb-0">
<li class="list-inline-item">
- <T i18nKey="admins" class="d-inline">#</T>:
+ <T i18nKey="admins" class="d-inline">
+ #
+ </T>
+ :
+ </li>
+ {this.state.site.admins.map(admin => (
+ <li class="list-inline-item">
+ <Link class="text-info" to={`/u/${admin.name}`}>
+ {admin.name}
+ </Link>
</li>
- {this.state.site.admins.map(admin =>
- <li class="list-inline-item"><Link class="text-info" to={`/u/${admin.name}`}>{admin.name}</Link></li>
+ ))}
+ </ul>
+ </div>
+ </div>
+ {this.state.site.site.description && (
+ <div class="card border-secondary mb-3">
+ <div class="card-body">
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(
+ this.state.site.site.description
)}
- </ul>
+ />
</div>
</div>
- {this.state.site.site.description &&
- <div class="card border-secondary mb-3">
- <div class="card-body">
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(this.state.site.site.description)} />
- </div>
- </div>
- }
- </div>
- )
+ )}
+ </div>
+ );
}
landing() {
<div class="card border-secondary">
<div class="card-body">
<h5>
- <T i18nKey="powered_by" class="d-inline">#</T>
- <svg class="icon mx-2"><use xlinkHref="#icon-mouse">#</use></svg>
- <a href={repoUrl}>Lemmy<sup>beta</sup></a>
+ <T i18nKey="powered_by" class="d-inline">
+ #
+ </T>
+ <svg class="icon mx-2">
+ <use xlinkHref="#icon-mouse">#</use>
+ </svg>
+ <a href={repoUrl}>
+ Lemmy<sup>beta</sup>
+ </a>
</h5>
<p class="mb-0">
- <T i18nKey="landing_0">#<a href="https://en.wikipedia.org/wiki/Social_network_aggregation">#</a><a href="https://en.wikipedia.org/wiki/Fediverse">#</a><br></br><code>#</code><br></br><b>#</b><br></br><a href={repoUrl}>#</a><br></br><a href="https://www.rust-lang.org">#</a><a href="https://actix.rs/">#</a><a href="https://infernojs.org">#</a><a href="https://www.typescriptlang.org/">#</a>
- </T>
- </p>
+ <T i18nKey="landing_0">
+ #
+ <a href="https://en.wikipedia.org/wiki/Social_network_aggregation">
+ #
+ </a>
+ <a href="https://en.wikipedia.org/wiki/Fediverse">#</a>
+ <br></br>
+ <code>#</code>
+ <br></br>
+ <b>#</b>
+ <br></br>
+ <a href={repoUrl}>#</a>
+ <br></br>
+ <a href="https://www.rust-lang.org">#</a>
+ <a href="https://actix.rs/">#</a>
+ <a href="https://infernojs.org">#</a>
+ <a href="https://www.typescriptlang.org/">#</a>
+ </T>
+ </p>
+ </div>
</div>
- </div>
- )
+ );
}
posts() {
return (
<div>
- {this.state.loading ?
- <h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
- <div>
- {this.selects()}
- <PostListings posts={this.state.posts} showCommunity />
- {this.paginator()}
- </div>
- }
+ {this.state.loading ? (
+ <h5>
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ </h5>
+ ) : (
+ <div>
+ {this.selects()}
+ <PostListings posts={this.state.posts} showCommunity />
+ {this.paginator()}
+ </div>
+ )}
</div>
- )
+ );
}
selects() {
return (
<div className="mb-3">
<div class="btn-group btn-group-toggle">
- <label className={`btn btn-sm btn-secondary
+ <label
+ className={`btn btn-sm btn-secondary
${this.state.type_ == ListingType.Subscribed && 'active'}
${UserService.Instance.user == undefined ? 'disabled' : 'pointer'}
- `}>
- <input type="radio"
+ `}
+ >
+ <input
+ type="radio"
value={ListingType.Subscribed}
checked={this.state.type_ == ListingType.Subscribed}
onChange={linkEvent(this, this.handleTypeChange)}
/>
{i18n.t('subscribed')}
</label>
- <label className={`pointer btn btn-sm btn-secondary ${this.state.type_ == ListingType.All && 'active'}`}>
- <input type="radio"
+ <label
+ className={`pointer btn btn-sm btn-secondary ${this.state.type_ ==
+ ListingType.All && 'active'}`}
+ >
+ <input
+ type="radio"
value={ListingType.All}
checked={this.state.type_ == ListingType.All}
onChange={linkEvent(this, this.handleTypeChange)}
- />
+ />
{i18n.t('all')}
</label>
</div>
- <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="ml-2 custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="sort_type">#</T></option>
- <option value={SortType.Hot}><T i18nKey="hot">#</T></option>
- <option value={SortType.New}><T i18nKey="new">#</T></option>
+ <select
+ value={this.state.sort}
+ onChange={linkEvent(this, this.handleSortChange)}
+ class="ml-2 custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="sort_type">#</T>
+ </option>
+ <option value={SortType.Hot}>
+ <T i18nKey="hot">#</T>
+ </option>
+ <option value={SortType.New}>
+ <T i18nKey="new">#</T>
+ </option>
<option disabled>─────</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>
+ <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>
- )
+ );
}
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>
);
}
get canAdmin(): boolean {
- return UserService.Instance.user && this.state.site.admins.map(a => a.id).includes(UserService.Instance.user.id);
+ return (
+ UserService.Instance.user &&
+ this.state.site.admins
+ .map(a => a.id)
+ .includes(UserService.Instance.user.id)
+ );
}
handleEditClick(i: Main) {
this.setState(this.state);
}
- nextPage(i: Main) {
+ nextPage(i: Main) {
i.state.page++;
i.state.loading = true;
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
- prevPage(i: Main) {
+ prevPage(i: Main) {
i.state.page--;
i.state.loading = true;
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
handleSortChange(i: Main, event: any) {
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
handleTypeChange(i: Main, event: any) {
i.setState(i.state);
i.updateUrl();
i.fetchPosts();
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
}
keepFetchingPosts() {
page: this.state.page,
limit: fetchLimit,
sort: SortType[this.state.sort],
- type_: ListingType[this.state.type_]
- }
+ type_: ListingType[this.state.type_],
+ };
WebSocketService.Instance.getPosts(getPostsForm);
}
// This means it hasn't been set up yet
if (!res.site) {
- this.context.router.history.push("/setup");
+ this.context.router.history.push('/setup');
}
this.state.site.admins = res.admins;
this.state.site.site = res.site;
this.state.site.online = res.online;
this.setState(this.state);
document.title = `${WebSocketService.Instance.site.name}`;
-
} else if (op == UserOperation.EditSite) {
let res: SiteResponse = msg;
this.state.site.site = res.site;
}
}
}
-
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, GetModlogForm, GetModlogResponse, ModRemovePost, ModLockPost, ModStickyPost, ModRemoveComment, ModRemoveCommunity, ModBanFromCommunity, ModBan, ModAddCommunity, ModAdd } from '../interfaces';
+import {
+ UserOperation,
+ GetModlogForm,
+ GetModlogResponse,
+ ModRemovePost,
+ ModLockPost,
+ ModStickyPost,
+ ModRemoveComment,
+ ModRemoveCommunity,
+ ModBanFromCommunity,
+ ModBan,
+ ModAddCommunity,
+ ModAdd,
+} from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp, addTypeInfo, fetchLimit } from '../utils';
import { MomentTime } from './moment-time';
import { i18n } from '../i18next';
interface ModlogState {
- combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModStickyPost | ModRemoveCommunity | ModAdd | ModBan}>,
- communityId?: number,
- communityName?: string,
+ combined: Array<{
+ type_: string;
+ data:
+ | ModRemovePost
+ | ModLockPost
+ | ModStickyPost
+ | ModRemoveCommunity
+ | ModAdd
+ | ModBan;
+ }>;
+ communityId?: number;
+ communityName?: string;
page: number;
loading: boolean;
}
combined: [],
page: 1,
loading: true,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
- this.state.communityId = this.props.match.params.community_id ? Number(this.props.match.params.community_id) : undefined;
+ this.state.communityId = this.props.match.params.community_id
+ ? Number(this.props.match.params.community_id)
+ : undefined;
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();
}
}
setCombined(res: GetModlogResponse) {
- let removed_posts = addTypeInfo(res.removed_posts, "removed_posts");
- let locked_posts = addTypeInfo(res.locked_posts, "locked_posts");
- let stickied_posts = addTypeInfo(res.stickied_posts, "stickied_posts");
- let removed_comments = addTypeInfo(res.removed_comments, "removed_comments");
- let removed_communities = addTypeInfo(res.removed_communities, "removed_communities");
- let banned_from_community = addTypeInfo(res.banned_from_community, "banned_from_community");
- let added_to_community = addTypeInfo(res.added_to_community, "added_to_community");
- let added = addTypeInfo(res.added, "added");
- let banned = addTypeInfo(res.banned, "banned");
+ let removed_posts = addTypeInfo(res.removed_posts, 'removed_posts');
+ let locked_posts = addTypeInfo(res.locked_posts, 'locked_posts');
+ let stickied_posts = addTypeInfo(res.stickied_posts, 'stickied_posts');
+ let removed_comments = addTypeInfo(
+ res.removed_comments,
+ 'removed_comments'
+ );
+ let removed_communities = addTypeInfo(
+ res.removed_communities,
+ 'removed_communities'
+ );
+ let banned_from_community = addTypeInfo(
+ res.banned_from_community,
+ 'banned_from_community'
+ );
+ let added_to_community = addTypeInfo(
+ res.added_to_community,
+ 'added_to_community'
+ );
+ let added = addTypeInfo(res.added, 'added');
+ let banned = addTypeInfo(res.banned, 'banned');
this.state.combined = [];
this.state.combined.push(...removed_posts);
this.state.combined.push(...banned);
if (this.state.communityId && this.state.combined.length > 0) {
- this.state.communityName = (this.state.combined[0].data as ModRemovePost).community_name;
+ this.state.communityName = (this.state.combined[0]
+ .data as ModRemovePost).community_name;
}
// Sort them by time
- this.state.combined.sort((a, b) => b.data.when_.localeCompare(a.data.when_));
+ this.state.combined.sort((a, b) =>
+ b.data.when_.localeCompare(a.data.when_)
+ );
this.setState(this.state);
}
combined() {
return (
<tbody>
- {this.state.combined.map(i =>
+ {this.state.combined.map(i => (
<tr>
- <td><MomentTime data={i.data} /></td>
- <td><Link to={`/u/${i.data.mod_user_name}`}>{i.data.mod_user_name}</Link></td>
<td>
- {i.type_ == 'removed_posts' &&
+ <MomentTime data={i.data} />
+ </td>
+ <td>
+ <Link to={`/u/${i.data.mod_user_name}`}>
+ {i.data.mod_user_name}
+ </Link>
+ </td>
+ <td>
+ {i.type_ == 'removed_posts' && (
<>
- {(i.data as ModRemovePost).removed? 'Removed' : 'Restored'}
- <span> Post <Link to={`/post/${(i.data as ModRemovePost).post_id}`}>{(i.data as ModRemovePost).post_name}</Link></span>
- <div>{(i.data as ModRemovePost).reason && ` reason: ${(i.data as ModRemovePost).reason}`}</div>
+ {(i.data as ModRemovePost).removed ? 'Removed' : 'Restored'}
+ <span>
+ {' '}
+ Post{' '}
+ <Link to={`/post/${(i.data as ModRemovePost).post_id}`}>
+ {(i.data as ModRemovePost).post_name}
+ </Link>
+ </span>
+ <div>
+ {(i.data as ModRemovePost).reason &&
+ ` reason: ${(i.data as ModRemovePost).reason}`}
+ </div>
</>
- }
- {i.type_ == 'locked_posts' &&
+ )}
+ {i.type_ == 'locked_posts' && (
<>
- {(i.data as ModLockPost).locked? 'Locked' : 'Unlocked'}
- <span> Post <Link to={`/post/${(i.data as ModLockPost).post_id}`}>{(i.data as ModLockPost).post_name}</Link></span>
+ {(i.data as ModLockPost).locked ? 'Locked' : 'Unlocked'}
+ <span>
+ {' '}
+ Post{' '}
+ <Link to={`/post/${(i.data as ModLockPost).post_id}`}>
+ {(i.data as ModLockPost).post_name}
+ </Link>
+ </span>
</>
- }
- {i.type_ == 'stickied_posts' &&
+ )}
+ {i.type_ == 'stickied_posts' && (
<>
- {(i.data as ModStickyPost).stickied? 'Stickied' : 'Unstickied'}
- <span> Post <Link to={`/post/${(i.data as ModStickyPost).post_id}`}>{(i.data as ModStickyPost).post_name}</Link></span>
+ {(i.data as ModStickyPost).stickied
+ ? 'Stickied'
+ : 'Unstickied'}
+ <span>
+ {' '}
+ Post{' '}
+ <Link to={`/post/${(i.data as ModStickyPost).post_id}`}>
+ {(i.data as ModStickyPost).post_name}
+ </Link>
+ </span>
</>
- }
- {i.type_ == 'removed_comments' &&
+ )}
+ {i.type_ == 'removed_comments' && (
<>
- {(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'}
- <span> Comment <Link to={`/post/${(i.data as ModRemoveComment).post_id}/comment/${(i.data as ModRemoveComment).comment_id}`}>{(i.data as ModRemoveComment).comment_content}</Link></span>
- <span> by <Link to={`/u/${(i.data as ModRemoveComment).comment_user_name}`}>{(i.data as ModRemoveComment).comment_user_name}</Link></span>
- <div>{(i.data as ModRemoveComment).reason && ` reason: ${(i.data as ModRemoveComment).reason}`}</div>
+ {(i.data as ModRemoveComment).removed
+ ? 'Removed'
+ : 'Restored'}
+ <span>
+ {' '}
+ Comment{' '}
+ <Link
+ to={`/post/${
+ (i.data as ModRemoveComment).post_id
+ }/comment/${(i.data as ModRemoveComment).comment_id}`}
+ >
+ {(i.data as ModRemoveComment).comment_content}
+ </Link>
+ </span>
+ <span>
+ {' '}
+ by{' '}
+ <Link
+ to={`/u/${
+ (i.data as ModRemoveComment).comment_user_name
+ }`}
+ >
+ {(i.data as ModRemoveComment).comment_user_name}
+ </Link>
+ </span>
+ <div>
+ {(i.data as ModRemoveComment).reason &&
+ ` reason: ${(i.data as ModRemoveComment).reason}`}
+ </div>
</>
- }
- {i.type_ == 'removed_communities' &&
+ )}
+ {i.type_ == 'removed_communities' && (
<>
- {(i.data as ModRemoveCommunity).removed ? 'Removed' : 'Restored'}
- <span> Community <Link to={`/c/${(i.data as ModRemoveCommunity).community_name}`}>{(i.data as ModRemoveCommunity).community_name}</Link></span>
- <div>{(i.data as ModRemoveCommunity).reason && ` reason: ${(i.data as ModRemoveCommunity).reason}`}</div>
- <div>{(i.data as ModRemoveCommunity).expires && ` expires: ${moment.utc((i.data as ModRemoveCommunity).expires).fromNow()}`}</div>
+ {(i.data as ModRemoveCommunity).removed
+ ? 'Removed'
+ : 'Restored'}
+ <span>
+ {' '}
+ Community{' '}
+ <Link
+ to={`/c/${(i.data as ModRemoveCommunity).community_name}`}
+ >
+ {(i.data as ModRemoveCommunity).community_name}
+ </Link>
+ </span>
+ <div>
+ {(i.data as ModRemoveCommunity).reason &&
+ ` reason: ${(i.data as ModRemoveCommunity).reason}`}
+ </div>
+ <div>
+ {(i.data as ModRemoveCommunity).expires &&
+ ` expires: ${moment
+ .utc((i.data as ModRemoveCommunity).expires)
+ .fromNow()}`}
+ </div>
</>
- }
- {i.type_ == 'banned_from_community' &&
+ )}
+ {i.type_ == 'banned_from_community' && (
<>
- <span>{(i.data as ModBanFromCommunity).banned ? 'Banned ' : 'Unbanned '} </span>
- <span><Link to={`/u/${(i.data as ModBanFromCommunity).other_user_name}`}>{(i.data as ModBanFromCommunity).other_user_name}</Link></span>
+ <span>
+ {(i.data as ModBanFromCommunity).banned
+ ? 'Banned '
+ : 'Unbanned '}{' '}
+ </span>
+ <span>
+ <Link
+ to={`/u/${
+ (i.data as ModBanFromCommunity).other_user_name
+ }`}
+ >
+ {(i.data as ModBanFromCommunity).other_user_name}
+ </Link>
+ </span>
<span> from the community </span>
- <span><Link to={`/c/${(i.data as ModBanFromCommunity).community_name}`}>{(i.data as ModBanFromCommunity).community_name}</Link></span>
- <div>{(i.data as ModBanFromCommunity).reason && ` reason: ${(i.data as ModBanFromCommunity).reason}`}</div>
- <div>{(i.data as ModBanFromCommunity).expires && ` expires: ${moment.utc((i.data as ModBanFromCommunity).expires).fromNow()}`}</div>
+ <span>
+ <Link
+ to={`/c/${
+ (i.data as ModBanFromCommunity).community_name
+ }`}
+ >
+ {(i.data as ModBanFromCommunity).community_name}
+ </Link>
+ </span>
+ <div>
+ {(i.data as ModBanFromCommunity).reason &&
+ ` reason: ${(i.data as ModBanFromCommunity).reason}`}
+ </div>
+ <div>
+ {(i.data as ModBanFromCommunity).expires &&
+ ` expires: ${moment
+ .utc((i.data as ModBanFromCommunity).expires)
+ .fromNow()}`}
+ </div>
</>
- }
- {i.type_ == 'added_to_community' &&
+ )}
+ {i.type_ == 'added_to_community' && (
<>
- <span>{(i.data as ModAddCommunity).removed ? 'Removed ' : 'Appointed '} </span>
- <span><Link to={`/u/${(i.data as ModAddCommunity).other_user_name}`}>{(i.data as ModAddCommunity).other_user_name}</Link></span>
+ <span>
+ {(i.data as ModAddCommunity).removed
+ ? 'Removed '
+ : 'Appointed '}{' '}
+ </span>
+ <span>
+ <Link
+ to={`/u/${(i.data as ModAddCommunity).other_user_name}`}
+ >
+ {(i.data as ModAddCommunity).other_user_name}
+ </Link>
+ </span>
<span> as a mod to the community </span>
- <span><Link to={`/c/${(i.data as ModAddCommunity).community_name}`}>{(i.data as ModAddCommunity).community_name}</Link></span>
+ <span>
+ <Link
+ to={`/c/${(i.data as ModAddCommunity).community_name}`}
+ >
+ {(i.data as ModAddCommunity).community_name}
+ </Link>
+ </span>
</>
- }
- {i.type_ == 'banned' &&
+ )}
+ {i.type_ == 'banned' && (
<>
- <span>{(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '} </span>
- <span><Link to={`/u/${(i.data as ModBan).other_user_name}`}>{(i.data as ModBan).other_user_name}</Link></span>
- <div>{(i.data as ModBan).reason && ` reason: ${(i.data as ModBan).reason}`}</div>
- <div>{(i.data as ModBan).expires && ` expires: ${moment.utc((i.data as ModBan).expires).fromNow()}`}</div>
+ <span>
+ {(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '}{' '}
+ </span>
+ <span>
+ <Link to={`/u/${(i.data as ModBan).other_user_name}`}>
+ {(i.data as ModBan).other_user_name}
+ </Link>
+ </span>
+ <div>
+ {(i.data as ModBan).reason &&
+ ` reason: ${(i.data as ModBan).reason}`}
+ </div>
+ <div>
+ {(i.data as ModBan).expires &&
+ ` expires: ${moment
+ .utc((i.data as ModBan).expires)
+ .fromNow()}`}
+ </div>
</>
- }
- {i.type_ == 'added' &&
+ )}
+ {i.type_ == 'added' && (
<>
- <span>{(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '} </span>
- <span><Link to={`/u/${(i.data as ModAdd).other_user_name}`}>{(i.data as ModAdd).other_user_name}</Link></span>
+ <span>
+ {(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '}{' '}
+ </span>
+ <span>
+ <Link to={`/u/${(i.data as ModAdd).other_user_name}`}>
+ {(i.data as ModAdd).other_user_name}
+ </Link>
+ </span>
<span> as an admin </span>
</>
- }
+ )}
</td>
</tr>
- )
- }
-
+ ))}
</tbody>
);
-
}
render() {
return (
<div class="container">
- {this.state.loading ?
- <h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
- <div>
- <h5>
- {this.state.communityName && <Link className="text-white" to={`/c/${this.state.communityName}`}>/c/{this.state.communityName} </Link>}
- <span>Modlog</span>
+ {this.state.loading ? (
+ <h5 class="">
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
</h5>
- <div class="table-responsive">
- <table id="modlog_table" class="table table-sm table-hover">
- <thead class="pointer">
- <tr>
- <th>Time</th>
- <th>Mod</th>
- <th>Action</th>
- </tr>
- </thead>
- {this.combined()}
- </table>
- {this.paginator()}
+ ) : (
+ <div>
+ <h5>
+ {this.state.communityName && (
+ <Link
+ className="text-white"
+ to={`/c/${this.state.communityName}`}
+ >
+ /c/{this.state.communityName}{' '}
+ </Link>
+ )}
+ <span>Modlog</span>
+ </h5>
+ <div class="table-responsive">
+ <table id="modlog_table" class="table table-sm table-hover">
+ <thead class="pointer">
+ <tr>
+ <th>Time</th>
+ <th>Mod</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ {this.combined()}
+ </table>
+ {this.paginator()}
+ </div>
</div>
- </div>
- }
+ )}
</div>
);
}
paginator() {
return (
<div class="mt-2">
- {this.state.page > 1 &&
- <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
- }
- <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
+ {this.state.page > 1 && (
+ <button
+ class="btn btn-sm btn-secondary mr-1"
+ onClick={linkEvent(this, this.prevPage)}
+ >
+ Prev
+ </button>
+ )}
+ <button
+ class="btn btn-sm btn-secondary"
+ onClick={linkEvent(this, this.nextPage)}
+ >
+ Next
+ </button>
</div>
);
}
- nextPage(i: Modlog) {
+ nextPage(i: Modlog) {
i.state.page++;
i.setState(i.state);
i.refetch();
}
- prevPage(i: Modlog) {
+ prevPage(i: Modlog) {
i.state.page--;
i.setState(i.state);
i.refetch();
}
- refetch(){
+ refetch() {
let modlogForm: GetModlogForm = {
community_id: this.state.communityId,
page: this.state.page,
} else if (op == UserOperation.GetModlog) {
let res: GetModlogResponse = msg;
this.state.loading = false;
- window.scrollTo(0,0);
+ window.scrollTo(0, 0);
this.setCombined(res);
- }
+ }
}
}
published?: string;
when_?: string;
updated?: string;
- }
+ };
}
export class MomentTime extends Component<MomentTimeProps, any> {
-
constructor(props: any, context: any) {
super(props, context);
render() {
if (this.props.data.updated) {
return (
- <span title={this.props.data.updated} className="font-italics">{i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()}</span>
- )
+ <span title={this.props.data.updated} className="font-italics">
+ {i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()}
+ </span>
+ );
} else {
let str = this.props.data.published || this.props.data.when_;
- return (
- <span title={str}>{moment.utc(str).fromNow()}</span>
- )
+ return <span title={str}>{moment.utc(str).fromNow()}</span>;
}
}
}
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 { WebSocketService, UserService } from '../services';
-import { UserOperation, GetRepliesForm, GetRepliesResponse, SortType, GetSiteResponse, Comment} from '../interfaces';
+import {
+ UserOperation,
+ GetRepliesForm,
+ GetRepliesResponse,
+ SortType,
+ GetSiteResponse,
+ Comment,
+} from '../interfaces';
import { msgOp } from '../utils';
import { version } from '../version';
import { i18n } from '../i18next';
isLoggedIn: boolean;
expanded: boolean;
expandUserDropdown: boolean;
- replies: Array<Comment>,
- fetchCount: number,
+ replies: Array<Comment>;
+ fetchCount: number;
unreadCount: number;
siteName: string;
}
private wsSub: Subscription;
private userSub: Subscription;
emptyState: NavbarState = {
- isLoggedIn: (UserService.Instance.user !== undefined),
+ isLoggedIn: UserService.Instance.user !== undefined,
unreadCount: 0,
fetchCount: 0,
replies: [],
expanded: false,
expandUserDropdown: false,
- siteName: undefined
- }
+ siteName: undefined,
+ };
constructor(props: any, context: any) {
super(props, context);
});
this.wsSub = 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')
- );
+ );
if (this.state.isLoggedIn) {
this.requestNotificationPermission();
}
render() {
- return (
- <div>{this.navbar()}</div>
- )
+ return <div>{this.navbar()}</div>;
}
componentWillUnmount() {
<Link title={version} class="navbar-brand" to="/">
{this.state.siteName}
</Link>
- <button class="navbar-toggler" type="button" onClick={linkEvent(this, this.expandNavbar)}>
+ <button
+ class="navbar-toggler"
+ type="button"
+ onClick={linkEvent(this, this.expandNavbar)}
+ >
<span class="navbar-toggler-icon"></span>
</button>
- <div className={`${!this.state.expanded && 'collapse'} navbar-collapse`}>
+ <div
+ className={`${!this.state.expanded && 'collapse'} navbar-collapse`}
+ >
<ul class="navbar-nav mr-auto">
<li class="nav-item">
- <Link class="nav-link" to="/communities"><T i18nKey="communities">#</T></Link>
+ <Link class="nav-link" to="/communities">
+ <T i18nKey="communities">#</T>
+ </Link>
</li>
<li class="nav-item">
- <Link class="nav-link" to="/search"><T i18nKey="search">#</T></Link>
+ <Link class="nav-link" to="/search">
+ <T i18nKey="search">#</T>
+ </Link>
</li>
<li class="nav-item">
- <Link class="nav-link" to={{pathname: '/create_post', state: { prevPath: this.currentLocation }}}><T i18nKey="create_post">#</T></Link>
+ <Link
+ class="nav-link"
+ to={{
+ pathname: '/create_post',
+ state: { prevPath: this.currentLocation },
+ }}
+ >
+ <T i18nKey="create_post">#</T>
+ </Link>
</li>
<li class="nav-item">
- <Link class="nav-link" to="/create_community"><T i18nKey="create_community">#</T></Link>
+ <Link class="nav-link" to="/create_community">
+ <T i18nKey="create_community">#</T>
+ </Link>
</li>
</ul>
<ul class="navbar-nav ml-auto mr-2">
- {this.state.isLoggedIn ?
- <>
- {
- <li className="nav-item">
- <Link class="nav-link" to="/inbox">
- <svg class="icon"><use xlinkHref="#icon-mail"></use></svg>
- {this.state.unreadCount> 0 && <span class="ml-1 badge badge-light">{this.state.unreadCount}</span>}
- </Link>
+ {this.state.isLoggedIn ? (
+ <>
+ {
+ <li className="nav-item">
+ <Link class="nav-link" to="/inbox">
+ <svg class="icon">
+ <use xlinkHref="#icon-mail"></use>
+ </svg>
+ {this.state.unreadCount > 0 && (
+ <span class="ml-1 badge badge-light">
+ {this.state.unreadCount}
+ </span>
+ )}
+ </Link>
+ </li>
+ }
+ <li
+ className={`nav-item dropdown ${this.state
+ .expandUserDropdown && 'show'}`}
+ >
+ <a
+ class="pointer nav-link dropdown-toggle"
+ onClick={linkEvent(this, this.expandUserDropdown)}
+ role="button"
+ >
+ {UserService.Instance.user.username}
+ </a>
+ <div
+ className={`dropdown-menu dropdown-menu-right ${this.state
+ .expandUserDropdown && 'show'}`}
+ >
+ <a
+ role="button"
+ class="dropdown-item pointer"
+ onClick={linkEvent(this, this.handleOverviewClick)}
+ >
+ <T i18nKey="overview">#</T>
+ </a>
+ <a
+ role="button"
+ class="dropdown-item pointer"
+ onClick={linkEvent(this, this.handleLogoutClick)}
+ >
+ <T i18nKey="logout">#</T>
+ </a>
+ </div>
</li>
- }
- <li className={`nav-item dropdown ${this.state.expandUserDropdown && 'show'}`}>
- <a class="pointer nav-link dropdown-toggle" onClick={linkEvent(this, this.expandUserDropdown)} role="button">
- {UserService.Instance.user.username}
- </a>
- <div className={`dropdown-menu dropdown-menu-right ${this.state.expandUserDropdown && 'show'}`}>
- <a role="button" class="dropdown-item pointer" onClick={linkEvent(this, this.handleOverviewClick)}><T i18nKey="overview">#</T></a>
- <a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }><T i18nKey="logout">#</T></a>
- </div>
- </li>
- </>
- :
- <Link class="nav-link" to="/login"><T i18nKey="login_sign_up">#</T></Link>
- }
+ </>
+ ) : (
+ <Link class="nav-link" to="/login">
+ <T i18nKey="login_sign_up">#</T>
+ </Link>
+ )}
</ul>
</div>
</nav>
parseMessage(msg: any) {
let op: UserOperation = msgOp(msg);
if (msg.error) {
- if (msg.error == "not_logged_in") {
+ if (msg.error == 'not_logged_in') {
UserService.Instance.logout();
location.reload();
}
} else if (op == UserOperation.GetReplies) {
let res: GetRepliesResponse = msg;
let unreadReplies = res.replies.filter(r => !r.read);
- if (unreadReplies.length > 0 && this.state.fetchCount > 1 &&
- (JSON.stringify(this.state.replies) !== JSON.stringify(unreadReplies))) {
+ if (
+ unreadReplies.length > 0 &&
+ this.state.fetchCount > 1 &&
+ JSON.stringify(this.state.replies) !== JSON.stringify(unreadReplies)
+ ) {
this.notify(unreadReplies);
}
WebSocketService.Instance.site = res.site;
this.setState(this.state);
}
- }
+ }
}
keepFetchingReplies() {
page: 1,
limit: 9999,
};
- if (this.currentLocation !=='/inbox') {
+ if (this.currentLocation !== '/inbox') {
WebSocketService.Instance.getReplies(repliesForm);
this.state.fetchCount++;
}
}
sendRepliesCount(res: GetRepliesResponse) {
- UserService.Instance.sub.next({user: UserService.Instance.user, unreadCount: res.replies.filter(r => !r.read).length});
+ UserService.Instance.sub.next({
+ user: UserService.Instance.user,
+ unreadCount: res.replies.filter(r => !r.read).length,
+ });
}
requestNotificationPermission() {
if (UserService.Instance.user) {
- document.addEventListener('DOMContentLoaded', function () {
- if (!Notification) {
- alert(i18n.t('notifications_error'));
- return;
- }
+ document.addEventListener('DOMContentLoaded', function() {
+ if (!Notification) {
+ alert(i18n.t('notifications_error'));
+ return;
+ }
- if (Notification.permission !== 'granted')
- Notification.requestPermission();
- });
+ if (Notification.permission !== 'granted')
+ Notification.requestPermission();
+ });
}
}
notify(replies: Array<Comment>) {
let recentReply = replies[0];
- if (Notification.permission !== 'granted')
- Notification.requestPermission();
+ if (Notification.permission !== 'granted') Notification.requestPermission();
else {
- var notification = new Notification(`${replies.length} ${i18n.t('unread_messages')}`, {
- icon: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`,
- body: `${recentReply.creator_name}: ${recentReply.content}`
- });
+ var notification = new Notification(
+ `${replies.length} ${i18n.t('unread_messages')}`,
+ {
+ icon: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`,
+ body: `${recentReply.creator_name}: ${recentReply.content}`,
+ }
+ );
notification.onclick = () => {
- this.context.router.history.push(`/post/${recentReply.post_id}/comment/${recentReply.id}`);
+ this.context.router.history.push(
+ `/post/${recentReply.post_id}/comment/${recentReply.id}`
+ );
};
-
}
}
}
import { Component, linkEvent } from 'inferno';
import { PostListings } from './post-listings';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { PostForm as PostFormI, PostFormParams, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse, ListCommunitiesForm, SortType, SearchForm, SearchType, SearchResponse } from '../interfaces';
+import {
+ PostForm as PostFormI,
+ PostFormParams,
+ Post,
+ PostResponse,
+ UserOperation,
+ Community,
+ ListCommunitiesResponse,
+ ListCommunitiesForm,
+ SortType,
+ SearchForm,
+ SearchType,
+ SearchResponse,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { msgOp, getPageTitle, debounce, validURL, capitalizeFirstLetter, markdownHelpUrl, mdToHtml } from '../utils';
+import {
+ msgOp,
+ getPageTitle,
+ debounce,
+ validURL,
+ capitalizeFirstLetter,
+ markdownHelpUrl,
+ mdToHtml,
+} from '../utils';
import * as autosize from 'autosize';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
}
export class PostForm extends Component<PostFormProps, PostFormState> {
-
private subscription: Subscription;
private emptyState: PostFormState = {
postForm: {
nsfw: false,
auth: null,
community_id: null,
- creator_id: (UserService.Instance.user) ? UserService.Instance.user.id : null,
+ creator_id: UserService.Instance.user
+ ? UserService.Instance.user.id
+ : null,
},
communities: [],
loading: false,
suggestedTitle: undefined,
suggestedPosts: [],
crossPosts: [],
- }
+ };
constructor(props: any, context: any) {
super(props, context);
creator_id: this.props.post.creator_id,
url: this.props.post.url,
nsfw: this.props.post.nsfw,
- auth: null
- }
+ auth: null,
+ };
}
if (this.props.params) {
}
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')
- );
+ );
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.TopAll],
limit: 9999,
- }
+ };
WebSocketService.Instance.listCommunities(listCommunitiesForm);
}
<div>
<form onSubmit={linkEvent(this, this.handlePostSubmit)}>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="url">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="url">#</T>
+ </label>
<div class="col-sm-10">
- <input type="url" class="form-control" value={this.state.postForm.url} onInput={linkEvent(this, this.handlePostUrlChange)} />
- {this.state.suggestedTitle &&
- <div class="mt-1 text-muted small font-weight-bold pointer" onClick={linkEvent(this, this.copySuggestedTitle)}><T i18nKey="copy_suggested_title" interpolation={{title: this.state.suggestedTitle}}>#</T></div>
- }
+ <input
+ type="url"
+ class="form-control"
+ value={this.state.postForm.url}
+ onInput={linkEvent(this, this.handlePostUrlChange)}
+ />
+ {this.state.suggestedTitle && (
+ <div
+ class="mt-1 text-muted small font-weight-bold pointer"
+ onClick={linkEvent(this, this.copySuggestedTitle)}
+ >
+ <T
+ i18nKey="copy_suggested_title"
+ interpolation={{ title: this.state.suggestedTitle }}
+ >
+ #
+ </T>
+ </div>
+ )}
<form>
- <label htmlFor="file-upload" className={`${UserService.Instance.user && 'pointer'} d-inline-block mr-2 float-right text-muted small font-weight-bold`}><T i18nKey="upload_image">#</T></label>
- <input id="file-upload" type="file" accept="image/*,video/*" name="file" class="d-none" disabled={!UserService.Instance.user} onChange={linkEvent(this, this.handleImageUpload)} />
+ <label
+ htmlFor="file-upload"
+ className={`${UserService.Instance.user &&
+ 'pointer'} d-inline-block mr-2 float-right text-muted small font-weight-bold`}
+ >
+ <T i18nKey="upload_image">#</T>
+ </label>
+ <input
+ id="file-upload"
+ type="file"
+ accept="image/*,video/*"
+ name="file"
+ class="d-none"
+ disabled={!UserService.Instance.user}
+ onChange={linkEvent(this, this.handleImageUpload)}
+ />
</form>
- {this.state.imageLoading &&
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg>
- }
- {this.state.crossPosts.length > 0 &&
+ {this.state.imageLoading && (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ )}
+ {this.state.crossPosts.length > 0 && (
<>
- <div class="my-1 text-muted small font-weight-bold"><T i18nKey="cross_posts">#</T></div>
+ <div class="my-1 text-muted small font-weight-bold">
+ <T i18nKey="cross_posts">#</T>
+ </div>
<PostListings showCommunity posts={this.state.crossPosts} />
</>
- }
+ )}
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="title">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="title">#</T>
+ </label>
<div class="col-sm-10">
- <textarea value={this.state.postForm.name} onInput={linkEvent(this, this.handlePostNameChange)} class="form-control" required rows={2} minLength={3} maxLength={100} />
- {this.state.suggestedPosts.length > 0 &&
+ <textarea
+ value={this.state.postForm.name}
+ onInput={linkEvent(this, this.handlePostNameChange)}
+ class="form-control"
+ required
+ rows={2}
+ minLength={3}
+ maxLength={100}
+ />
+ {this.state.suggestedPosts.length > 0 && (
<>
- <div class="my-1 text-muted small font-weight-bold"><T i18nKey="related_posts">#</T></div>
+ <div class="my-1 text-muted small font-weight-bold">
+ <T i18nKey="related_posts">#</T>
+ </div>
<PostListings posts={this.state.suggestedPosts} />
</>
- }
+ )}
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="body">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="body">#</T>
+ </label>
<div class="col-sm-10">
- <textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} className={`form-control ${this.state.previewMode && 'd-none'}`} rows={4} maxLength={10000} />
- {this.state.previewMode &&
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(this.state.postForm.body)} />
- }
- {this.state.postForm.body &&
- <button className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state.previewMode && 'active'}`} onClick={linkEvent(this, this.handlePreviewToggle)}><T i18nKey="preview">#</T></button>
- }
- <a href={markdownHelpUrl} target="_blank" class="d-inline-block float-right text-muted small font-weight-bold"><T i18nKey="formatting_help">#</T></a>
+ <textarea
+ value={this.state.postForm.body}
+ onInput={linkEvent(this, this.handlePostBodyChange)}
+ className={`form-control ${this.state.previewMode && 'd-none'}`}
+ rows={4}
+ maxLength={10000}
+ />
+ {this.state.previewMode && (
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(this.state.postForm.body)}
+ />
+ )}
+ {this.state.postForm.body && (
+ <button
+ className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state
+ .previewMode && 'active'}`}
+ onClick={linkEvent(this, this.handlePreviewToggle)}
+ >
+ <T i18nKey="preview">#</T>
+ </button>
+ )}
+ <a
+ href={markdownHelpUrl}
+ target="_blank"
+ class="d-inline-block float-right text-muted small font-weight-bold"
+ >
+ <T i18nKey="formatting_help">#</T>
+ </a>
</div>
</div>
- {!this.props.post &&
+ {!this.props.post && (
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="community">#</T></label>
- <div class="col-sm-10">
- <select class="form-control" value={this.state.postForm.community_id} onInput={linkEvent(this, this.handlePostCommunityChange)}>
- {this.state.communities.map(community =>
- <option value={community.id}>{community.name}</option>
- )}
- </select>
- </div>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="community">#</T>
+ </label>
+ <div class="col-sm-10">
+ <select
+ class="form-control"
+ value={this.state.postForm.community_id}
+ onInput={linkEvent(this, this.handlePostCommunityChange)}
+ >
+ {this.state.communities.map(community => (
+ <option value={community.id}>{community.name}</option>
+ ))}
+ </select>
+ </div>
</div>
- }
+ )}
<div class="form-group row">
<div class="col-sm-10">
<div class="form-check">
- <input class="form-check-input" type="checkbox" checked={this.state.postForm.nsfw} onChange={linkEvent(this, this.handlePostNsfwChange)}/>
- <label class="form-check-label"><T i18nKey="nsfw">#</T></label>
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.postForm.nsfw}
+ onChange={linkEvent(this, this.handlePostNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="nsfw">#</T>
+ </label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary mr-2">
- {this.state.loading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
- this.props.post ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('create'))}</button>
- {this.props.post && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
+ {this.state.loading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : this.props.post ? (
+ capitalizeFirstLetter(i18n.t('save'))
+ ) : (
+ capitalizeFirstLetter(i18n.t('create'))
+ )}
+ </button>
+ {this.props.post && (
+ <button
+ type="button"
+ class="btn btn-secondary"
+ onClick={linkEvent(this, this.handleCancel)}
+ >
+ <T i18nKey="cancel">#</T>
+ </button>
+ )}
</div>
</div>
</form>
handlePostUrlChange(i: PostForm, event: any) {
i.state.postForm.url = event.target.value;
if (validURL(i.state.postForm.url)) {
-
let form: SearchForm = {
q: i.state.postForm.url,
type_: SearchType[SearchType.Url],
method: 'POST',
body: formData,
})
- .then(res => res.json())
- .then(res => {
- let url = `${window.location.origin}/pictshare/${res.url}`;
- if (res.filetype == 'mp4') {
- url += '/raw';
- }
- i.state.postForm.url = url;
- i.state.imageLoading = false;
- i.setState(i.state);
- })
- .catch((error) => {
- i.state.imageLoading = false;
- i.setState(i.state);
- alert(error);
- })
+ .then(res => res.json())
+ .then(res => {
+ let url = `${window.location.origin}/pictshare/${res.url}`;
+ if (res.filetype == 'mp4') {
+ url += '/raw';
+ }
+ i.state.postForm.url = url;
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ })
+ .catch(error => {
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ alert(error);
+ });
}
parseMessage(msg: any) {
if (this.props.post) {
this.state.postForm.community_id = this.props.post.community_id;
} else if (this.props.params && this.props.params.community) {
- let foundCommunityId = res.communities.find(r => r.name == this.props.params.community).id;
+ let foundCommunityId = res.communities.find(
+ r => r.name == this.props.params.community
+ ).id;
this.state.postForm.community_id = foundCommunityId;
} else {
this.state.postForm.community_id = res.communities[0].id;
this.props.onEdit(res.post);
} else if (op == UserOperation.Search) {
let res: SearchResponse = msg;
-
+
if (res.type_ == SearchType[SearchType.Posts]) {
this.state.suggestedPosts = res.posts;
} else if (res.type_ == SearchType[SearchType.Url]) {
this.setState(this.state);
}
}
-
}
-
-
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { WebSocketService, UserService } from '../services';
-import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm, CommunityUser, UserView, BanType, BanFromCommunityForm, BanUserForm, AddModToCommunityForm, AddAdminForm, TransferSiteForm, TransferCommunityForm } 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, isVideo, getUnixTime } from '../utils';
+import {
+ mdToHtml,
+ canMod,
+ isMod,
+ isImage,
+ isVideo,
+ getUnixTime,
+} from '../utils';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
}
export class PostListing extends Component<PostListingProps, PostListingState> {
-
private emptyState: PostListingState = {
showEdit: false,
showRemoveDialog: false,
showConfirmTransferCommunity: false,
imageExpanded: false,
viewSource: false,
- }
+ };
constructor(props: any, context: any) {
super(props, context);
render() {
return (
<div class="row">
- {!this.state.showEdit
- ? this.listing()
- :
+ {!this.state.showEdit ? (
+ this.listing()
+ ) : (
<div class="col-12">
- <PostForm post={this.props.post} onEdit={this.handleEditPost} onCancel={this.handleEditCancel}/>
+ <PostForm
+ post={this.props.post}
+ onEdit={this.handleEditPost}
+ onCancel={this.handleEditCancel}
+ />
</div>
- }
+ )}
</div>
- )
+ );
}
listing() {
let post = this.props.post;
return (
<div class="listing col-12">
- <div className={`vote-bar mr-2 float-left small text-center ${this.props.viewOnly && 'no-click'}`}>
- <button className={`btn p-0 ${post.my_vote == 1 ? 'text-info' : 'text-muted'}`} onClick={linkEvent(this, this.handlePostLike)}>
- <svg class="icon upvote"><use xlinkHref="#icon-arrow-up"></use></svg>
+ <div
+ className={`vote-bar mr-2 float-left small text-center ${this.props
+ .viewOnly && 'no-click'}`}
+ >
+ <button
+ className={`btn p-0 ${
+ post.my_vote == 1 ? 'text-info' : 'text-muted'
+ }`}
+ onClick={linkEvent(this, this.handlePostLike)}
+ >
+ <svg class="icon upvote">
+ <use xlinkHref="#icon-arrow-up"></use>
+ </svg>
</button>
<div class={`font-weight-bold text-muted`}>{post.score}</div>
- <button className={`btn p-0 ${post.my_vote == -1 ? 'text-danger' : 'text-muted'}`} onClick={linkEvent(this, this.handlePostDisLike)}>
- <svg class="icon downvote"><use xlinkHref="#icon-arrow-down"></use></svg>
+ <button
+ className={`btn p-0 ${
+ post.my_vote == -1 ? 'text-danger' : 'text-muted'
+ }`}
+ onClick={linkEvent(this, this.handlePostDisLike)}
+ >
+ <svg class="icon downvote">
+ <use xlinkHref="#icon-arrow-down"></use>
+ </svg>
</button>
</div>
- {post.url && isImage(post.url) &&
- <span title={i18n.t('expand_here')} class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)}><img class="mx-2 mt-1 float-left img-fluid thumbnail rounded" src={post.url} /></span>
- }
- {post.url && isVideo(post.url) &&
- <video playsinline muted loop controls class="mx-2 mt-1 float-left" height="100" width="150">
+ {post.url && isImage(post.url) && (
+ <span
+ title={i18n.t('expand_here')}
+ class="pointer"
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ <img
+ class="mx-2 mt-1 float-left img-fluid thumbnail rounded"
+ src={post.url}
+ />
+ </span>
+ )}
+ {post.url && isVideo(post.url) && (
+ <video
+ playsinline
+ muted
+ loop
+ controls
+ class="mx-2 mt-1 float-left"
+ height="100"
+ width="150"
+ >
<source src={post.url} type="video/mp4" />
</video>
- }
+ )}
<div className="ml-4">
<div className="post-title">
<h5 className="mb-0 d-inline">
- {post.url ?
- <a className="text-body" href={post.url} target="_blank" title={post.url}>{post.name}</a> :
- <Link className="text-body" to={`/post/${post.id}`} title={i18n.t('comments')}>{post.name}</Link>
- }
+ {post.url ? (
+ <a
+ className="text-body"
+ href={post.url}
+ target="_blank"
+ title={post.url}
+ >
+ {post.name}
+ </a>
+ ) : (
+ <Link
+ className="text-body"
+ to={`/post/${post.id}`}
+ title={i18n.t('comments')}
+ >
+ {post.name}
+ </Link>
+ )}
</h5>
- {post.url &&
+ {post.url && (
<small>
- <a className="ml-2 text-muted font-italic" href={post.url} target="_blank" title={post.url}>{(new URL(post.url)).hostname}</a>
+ <a
+ className="ml-2 text-muted font-italic"
+ href={post.url}
+ target="_blank"
+ title={post.url}
+ >
+ {new URL(post.url).hostname}
+ </a>
</small>
- }
- { post.url && isImage(post.url) &&
+ )}
+ {post.url && isImage(post.url) && (
<>
- { !this.state.imageExpanded
- ? <span class="text-monospace pointer ml-2 text-muted small" title={i18n.t('expand_here')} onClick={linkEvent(this, this.handleImageExpandClick)}>[+]</span>
- :
+ {!this.state.imageExpanded ? (
+ <span
+ class="text-monospace pointer ml-2 text-muted small"
+ title={i18n.t('expand_here')}
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ [+]
+ </span>
+ ) : (
<span>
- <span class="text-monospace pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleImageExpandClick)}>[-]</span>
+ <span
+ class="text-monospace pointer ml-2 text-muted small"
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ [-]
+ </span>
<div>
- <span class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)}><img class="img-fluid" src={post.url} /></span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ <img class="img-fluid" src={post.url} />
+ </span>
</div>
</span>
- }
+ )}
</>
- }
- {post.removed &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
- }
- {post.deleted &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
- }
- {post.locked &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="locked">#</T></small>
- }
- {post.stickied &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="stickied">#</T></small>
- }
- {post.nsfw &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
- }
+ )}
+ {post.removed && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="removed">#</T>
+ </small>
+ )}
+ {post.deleted && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="deleted">#</T>
+ </small>
+ )}
+ {post.locked && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="locked">#</T>
+ </small>
+ )}
+ {post.stickied && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="stickied">#</T>
+ </small>
+ )}
+ {post.nsfw && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="nsfw">#</T>
+ </small>
+ )}
</div>
</div>
<div className="details ml-4">
<ul class="list-inline mb-0 text-muted small">
<li className="list-inline-item">
<span>{i18n.t('by')} </span>
- <Link className="text-info" to={`/u/${post.creator_name}`}>{post.creator_name}</Link>
- {this.isMod &&
- <span className="mx-1 badge badge-light"><T i18nKey="mod">#</T></span>
- }
- {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 &&
+ <Link className="text-info" to={`/u/${post.creator_name}`}>
+ {post.creator_name}
+ </Link>
+ {this.isMod && (
+ <span className="mx-1 badge badge-light">
+ <T i18nKey="mod">#</T>
+ </span>
+ )}
+ {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>
- <Link to={`/c/${post.community_name}`}>{post.community_name}</Link>
+ <Link to={`/c/${post.community_name}`}>
+ {post.community_name}
+ </Link>
</span>
- }
+ )}
</li>
<li className="list-inline-item">
- <span><MomentTime data={post} /></span>
+ <span>
+ <MomentTime data={post} />
+ </span>
</li>
<li className="list-inline-item">
- <span>(
- <span className="text-info">+{post.upvotes}</span>
+ <span>
+ (<span className="text-info">+{post.upvotes}</span>
<span> | </span>
<span className="text-danger">-{post.downvotes}</span>
<span>) </span>
</span>
</li>
<li className="list-inline-item">
- <Link className="text-muted" to={`/post/${post.id}`}><T i18nKey="number_of_comments" interpolation={{count: post.number_of_comments}}>#</T></Link>
+ <Link className="text-muted" to={`/post/${post.id}`}>
+ <T
+ i18nKey="number_of_comments"
+ interpolation={{ count: post.number_of_comments }}
+ >
+ #
+ </T>
+ </Link>
</li>
</ul>
- <ul class="list-inline mb-1 text-muted small font-weight-bold">
- {UserService.Instance.user &&
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ {UserService.Instance.user && (
<>
- {this.props.showBody &&
+ {this.props.showBody && (
<>
<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>
</li>
<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>
</>
- }
- {this.myPost &&
+ )}
+ {this.myPost && (
<>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleEditClick)}
+ >
+ <T i18nKey="edit">#</T>
+ </span>
</li>
<li className="list-inline-item mr-2">
- <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleDeleteClick)}
+ >
{!post.deleted ? i18n.t('delete') : i18n.t('restore')}
</span>
</li>
</>
- }
- {this.canModOnSelf &&
+ )}
+ {this.canModOnSelf && (
<>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{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>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleModSticky)}>{post.stickied ? i18n.t('unsticky') : i18n.t('sticky')}</span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleModSticky)}
+ >
+ {post.stickied ? i18n.t('unsticky') : i18n.t('sticky')}
+ </span>
</li>
</>
- }
+ )}
{/* Mods can ban from community, and appoint as mods to community */}
- {(this.canMod || this.canAdmin) &&
+ {(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>
- }
+ {!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 && (
<>
- {!this.isMod &&
+ {!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>
- }
+ {!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 &&
+ )}
+ {!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>
+ <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 &&
+ {(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>
- </>
- }
+ {!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.canAdmin && (
<>
- {!this.isAdmin &&
+ {!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>
- }
+ {!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 &&
+ )}
+ {!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>
+ <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 &&
+ {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>
- </>
- }
+ {!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>
- }
+ )}
</>
- }
- {this.props.showBody && post.body &&
+ )}
+ {this.props.showBody && post.body && (
<li className="list-inline-item">
- <span className="pointer" onClick={linkEvent(this, this.handleViewSource)}><T i18nKey="view_source">#</T></span>
+ <span
+ className="pointer"
+ onClick={linkEvent(this, this.handleViewSource)}
+ >
+ <T i18nKey="view_source">#</T>
+ </span>
</li>
- }
+ )}
</ul>
- {this.state.showRemoveDialog &&
- <form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
- <input type="text" class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
- <button type="submit" class="btn btn-secondary"><T i18nKey="remove_post">#</T></button>
+ {this.state.showRemoveDialog && (
+ <form
+ class="form-inline"
+ onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
+ >
+ <input
+ type="text"
+ class="form-control mr-2"
+ placeholder={i18n.t('reason')}
+ value={this.state.removeReason}
+ onInput={linkEvent(this, this.handleModRemoveReasonChange)}
+ />
+ <button type="submit" class="btn btn-secondary">
+ <T i18nKey="remove_post">#</T>
+ </button>
</form>
- }
- {this.state.showBanDialog &&
+ )}
+ {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)} />
+ <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 &&
- <>
- {this.state.viewSource ? <pre>{post.body}</pre> :
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />
- }
- </>
- }
+ {/* <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 && (
+ <>
+ {this.state.viewSource ? (
+ <pre>{post.body}</pre>
+ ) : (
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(post.body)}
+ />
+ )}
+ </>
+ )}
</div>
</div>
- )
+ );
}
private get myPost(): boolean {
- return UserService.Instance.user && this.props.post.creator_id == UserService.Instance.user.id;
+ return (
+ UserService.Instance.user &&
+ this.props.post.creator_id == UserService.Instance.user.id
+ );
}
get isMod(): boolean {
- return this.props.moderators && isMod(this.props.moderators.map(m => m.user_id), this.props.post.creator_id);
+ return (
+ this.props.moderators &&
+ isMod(
+ this.props.moderators.map(m => m.user_id),
+ this.props.post.creator_id
+ )
+ );
}
get isAdmin(): boolean {
- 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 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));
-
- return canMod(UserService.Instance.user, adminsThenMods, this.props.post.creator_id);
- } else {
+ let adminsThenMods = this.props.admins
+ .map(a => a.id)
+ .concat(this.props.moderators.map(m => m.user_id));
+
+ return canMod(
+ UserService.Instance.user,
+ adminsThenMods,
+ this.props.post.creator_id
+ );
+ } else {
return false;
}
}
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));
-
- return canMod(UserService.Instance.user, adminsThenMods, this.props.post.creator_id, true);
- } else {
+ let adminsThenMods = this.props.admins
+ .map(a => a.id)
+ .concat(this.props.moderators.map(m => m.user_id));
+
+ return canMod(
+ UserService.Instance.user,
+ adminsThenMods,
+ this.props.post.creator_id,
+ true
+ );
+ } 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);
+ 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);
+ 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);
+ 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 = {
post_id: i.props.post.id,
- score: (i.props.post.my_vote == 1) ? 0 : 1
+ score: i.props.post.my_vote == 1 ? 0 : 1,
};
WebSocketService.Instance.likePost(form);
}
handlePostDisLike(i: PostListing) {
let form: CreatePostLikeForm = {
post_id: i.props.post.id,
- score: (i.props.post.my_vote == -1) ? 0 : -1
+ score: i.props.post.my_vote == -1 ? 0 : -1,
};
WebSocketService.Instance.likePost(form);
}
creator_id: i.props.post.creator_id,
deleted: !i.props.post.deleted,
nsfw: i.props.post.nsfw,
- auth: null
+ auth: null,
};
WebSocketService.Instance.editPost(deleteForm);
}
handleSavePostClick(i: PostListing) {
- let saved = (i.props.post.saved == undefined) ? true : !i.props.post.saved;
+ let saved = i.props.post.saved == undefined ? true : !i.props.post.saved;
let form: SavePostForm = {
post_id: i.props.post.id,
- save: saved
+ save: saved,
};
WebSocketService.Instance.savePost(form);
i.setState(i.state);
}
- handleShowConfirmTransferCommunity(i: PostListing) {
+ handleShowConfirmTransferCommunity(i: PostListing) {
i.state.showConfirmTransferCommunity = true;
i.setState(i.state);
}
- handleCancelShowConfirmTransferCommunity(i: PostListing) {
+ handleCancelShowConfirmTransferCommunity(i: PostListing) {
i.state.showConfirmTransferCommunity = false;
i.setState(i.state);
}
i.setState(i.state);
}
- handleShowConfirmTransferSite(i: PostListing) {
+ handleShowConfirmTransferSite(i: PostListing) {
i.state.showConfirmTransferSite = true;
i.setState(i.state);
}
- handleCancelShowConfirmTransferSite(i: PostListing) {
+ handleCancelShowConfirmTransferSite(i: PostListing) {
i.state.showConfirmTransferSite = false;
i.setState(i.state);
}
i.setState(i.state);
}
}
-
}
export class PostListings extends Component<PostListingsProps, any> {
-
constructor(props: any, context: any) {
super(props, context);
}
render() {
return (
<div>
- {this.props.posts.length > 0 ? this.props.posts.map(post =>
- <>
- <PostListing post={post} showCommunity={this.props.showCommunity} />
- <hr class="d-md-none my-2" />
- <div class="d-none d-md-block my-2"></div>
- </>
- ) :
+ {this.props.posts.length > 0 ? (
+ this.props.posts.map(post => (
+ <>
+ <PostListing
+ post={post}
+ showCommunity={this.props.showCommunity}
+ />
+ <hr class="d-md-none my-2" />
+ <div class="d-none d-md-block my-2"></div>
+ </>
+ ))
+ ) : (
<>
- <div><T i18nKey="no_posts">#</T></div>
- {this.props.showCommunity !== undefined && <div><T i18nKey="subscribe_to_communities">#<Link to="/communities">#</Link></T></div>}
+ <div>
+ <T i18nKey="no_posts">#</T>
+ </div>
+ {this.props.showCommunity !== undefined && (
+ <div>
+ <T i18nKey="subscribe_to_communities">
+ #<Link to="/communities">#</Link>
+ </T>
+ </div>
+ )}
</>
- }
+ )}
</div>
- )
+ );
}
}
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, BanUserResponse, AddModToCommunityResponse, AddAdminResponse, UserView, SearchType, SortType, SearchForm, SearchResponse, GetSiteResponse, GetCommunityResponse } from '../interfaces';
+import {
+ UserOperation,
+ Community,
+ Post as PostI,
+ GetPostResponse,
+ PostResponse,
+ Comment,
+ CommentForm as CommentFormI,
+ CommentResponse,
+ CommentSortType,
+ CreatePostLikeResponse,
+ CommunityUser,
+ CommunityResponse,
+ CommentNode as CommentNodeI,
+ BanFromCommunityResponse,
+ BanUserResponse,
+ AddModToCommunityResponse,
+ AddAdminResponse,
+ UserView,
+ SearchType,
+ SortType,
+ SearchForm,
+ SearchResponse,
+ GetSiteResponse,
+ GetCommunityResponse,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank } from '../utils';
import { PostListing } from './post-listing';
}
export class Post extends Component<any, PostState> {
-
private subscription: Subscription;
private emptyState: PostState = {
post: null,
community: null,
moderators: [],
admins: [],
- scrolled: false,
+ scrolled: false,
loading: true,
crossPosts: [],
- }
+ };
constructor(props: any, context: any) {
super(props, context);
}
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
.subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
+ msg => this.parseMessage(msg),
+ err => console.error(err),
() => console.log('complete')
);
}
componentDidUpdate(_lastProps: any, lastState: PostState, _snapshot: any) {
- if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) {
- var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`);
- elmnt.scrollIntoView();
- elmnt.classList.add("mark");
+ if (
+ this.state.scrolled_comment_id &&
+ !this.state.scrolled &&
+ lastState.comments.length > 0
+ ) {
+ var elmnt = document.getElementById(
+ `comment-${this.state.scrolled_comment_id}`
+ );
+ elmnt.scrollIntoView();
+ elmnt.classList.add('mark');
this.state.scrolled = true;
this.markScrolledAsRead(this.state.scrolled_comment_id);
}
// this.context.router.history.push('/sponsors');
// this.context.refresh();
// this.context.router.history.push(_lastProps.location.pathname);
-
}
}
markScrolledAsRead(commentId: number) {
let found = this.state.comments.find(c => c.id == commentId);
let parent = this.state.comments.find(c => found.parent_id == c.id);
- let parent_user_id = parent ? parent.creator_id : this.state.post.creator_id;
-
- if (UserService.Instance.user && UserService.Instance.user.id == parent_user_id) {
-
+ let parent_user_id = parent
+ ? parent.creator_id
+ : this.state.post.creator_id;
+
+ if (
+ UserService.Instance.user &&
+ UserService.Instance.user.id == parent_user_id
+ ) {
let form: CommentFormI = {
content: found.content,
edit_id: found.id,
post_id: found.post_id,
parent_id: found.parent_id,
read: true,
- auth: null
+ auth: null,
};
WebSocketService.Instance.editComment(form);
}
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">
+ {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 mb-3">
- <PostListing
- post={this.state.post}
- showBody
- showCommunity
- editable
- moderators={this.state.moderators}
+ <PostListing
+ post={this.state.post}
+ showBody
+ showCommunity
+ editable
+ moderators={this.state.moderators}
admins={this.state.admins}
/>
- {this.state.crossPosts.length > 0 &&
+ {this.state.crossPosts.length > 0 && (
<>
- <div class="my-1 text-muted small font-weight-bold"><T i18nKey="cross_posts">#</T></div>
+ <div class="my-1 text-muted small font-weight-bold">
+ <T i18nKey="cross_posts">#</T>
+ </div>
<PostListings showCommunity posts={this.state.crossPosts} />
</>
- }
+ )}
<div className="mb-2" />
- <CommentForm postId={this.state.post.id} disabled={this.state.post.locked} />
+ <CommentForm
+ postId={this.state.post.id}
+ disabled={this.state.post.locked}
+ />
{this.sortRadios()}
{this.commentsTree()}
</div>
{this.sidebar()}
</div>
</div>
- }
+ )}
</div>
- )
+ );
}
sortRadios() {
return (
<div class="btn-group btn-group-toggle mb-3">
- <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
+ 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
+ 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
+ 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>
</div>
- )
+ );
}
newComments() {
return (
<div class="d-none d-md-block new-comments mb-3 card border-secondary">
<div class="card-body small">
- <h6><T i18nKey="recent_comments">#</T></h6>
- {this.state.comments.map(comment =>
- <CommentNodes
- nodes={[{comment: comment}]}
- noIndent
- locked={this.state.post.locked}
- moderators={this.state.moderators}
+ <h6>
+ <T i18nKey="recent_comments">#</T>
+ </h6>
+ {this.state.comments.map(comment => (
+ <CommentNodes
+ nodes={[{ comment: comment }]}
+ noIndent
+ locked={this.state.post.locked}
+ moderators={this.state.moderators}
admins={this.state.admins}
postCreatorId={this.state.post.creator_id}
/>
- )}
+ ))}
</div>
</div>
- )
+ );
}
sidebar() {
- return (
+ return (
<div class="mb-3">
- <Sidebar
- community={this.state.community}
- moderators={this.state.moderators}
+ <Sidebar
+ community={this.state.community}
+ moderators={this.state.moderators}
admins={this.state.admins}
/>
</div>
);
}
-
+
handleCommentSortChange(i: Post, event: any) {
i.state.commentSort = Number(event.target.value);
i.setState(i.state);
for (let comment of this.state.comments) {
let node: CommentNodeI = {
comment: comment,
- children: []
+ children: [],
};
map.set(comment.id, { ...node });
}
let tree: Array<CommentNodeI> = [];
for (let comment of this.state.comments) {
- if( comment.parent_id ) {
+ if (comment.parent_id) {
map.get(comment.parent_id).children.push(map.get(comment.id));
- }
- else {
+ } else {
tree.push(map.get(comment.id));
}
}
}
sortTree(tree: Array<CommentNodeI>) {
-
// First, put removed and deleted comments at the bottom, then do your other sorts
if (this.state.commentSort == CommentSortType.Top) {
- tree.sort((a, b) => (+a.comment.removed - +b.comment.removed) ||
- (+a.comment.deleted - +b.comment.deleted ) ||
- (b.comment.score - a.comment.score));
+ tree.sort(
+ (a, b) =>
+ +a.comment.removed - +b.comment.removed ||
+ +a.comment.deleted - +b.comment.deleted ||
+ b.comment.score - a.comment.score
+ );
} else if (this.state.commentSort == CommentSortType.New) {
- tree.sort((a, b) => (+a.comment.removed - +b.comment.removed) ||
- (+a.comment.deleted - +b.comment.deleted ) ||
- (b.comment.published.localeCompare(a.comment.published)));
+ tree.sort(
+ (a, b) =>
+ +a.comment.removed - +b.comment.removed ||
+ +a.comment.deleted - +b.comment.deleted ||
+ b.comment.published.localeCompare(a.comment.published)
+ );
} else if (this.state.commentSort == CommentSortType.Hot) {
- tree.sort((a, b) => (+a.comment.removed - +b.comment.removed) ||
- (+a.comment.deleted - +b.comment.deleted ) ||
- (hotRank(b.comment) - hotRank(a.comment)));
+ tree.sort(
+ (a, b) =>
+ +a.comment.removed - +b.comment.removed ||
+ +a.comment.deleted - +b.comment.deleted ||
+ hotRank(b.comment) - hotRank(a.comment)
+ );
}
for (let node of tree) {
this.sortTree(node.children);
}
-
}
commentsTree() {
let nodes = this.buildCommentsTree();
return (
<div>
- <CommentNodes
- nodes={nodes}
- locked={this.state.post.locked}
- moderators={this.state.moderators}
+ <CommentNodes
+ nodes={nodes}
+ locked={this.state.post.locked}
+ moderators={this.state.moderators}
admins={this.state.admins}
postCreatorId={this.state.post.creator_id}
/>
this.state.loading = false;
document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`;
- // Get cross-posts
+ // Get cross-posts
if (this.state.post.url) {
let form: SearchForm = {
q: this.state.post.url,
};
WebSocketService.Instance.search(form);
}
-
+
this.setState(this.state);
} else if (op == UserOperation.CreateComment) {
let res: CommentResponse = msg;
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.CreatePostLike) {
let res: CreatePostLikeResponse = msg;
} else if (op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg;
this.state.community.subscribed = res.community.subscribed;
- this.state.community.number_of_subscribers = res.community.number_of_subscribers;
+ this.state.community.number_of_subscribers =
+ res.community.number_of_subscribers;
this.setState(this.state);
} else if (op == UserOperation.BanFromCommunity) {
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.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);
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);
- if (this.state.post.creator_id == res.user.id) {
+ 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);
let res: SearchResponse = msg;
this.state.crossPosts = res.posts.filter(p => p.id != this.state.post.id);
this.setState(this.state);
- } else if (op == UserOperation.TransferSite) {
+ } else if (op == UserOperation.TransferSite) {
let res: GetSiteResponse = msg;
this.state.admins = res.admins;
this.setState(this.state);
- } else if (op == UserOperation.TransferCommunity) {
+ } else if (op == UserOperation.TransferCommunity) {
let res: GetCommunityResponse = msg;
this.state.community = res.community;
this.state.moderators = res.moderators;
this.state.admins = res.admins;
this.setState(this.state);
}
-
}
}
-
-
-
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, Community, UserView, SortType, SearchForm, SearchResponse, SearchType } from '../interfaces';
+import {
+ UserOperation,
+ Post,
+ Comment,
+ Community,
+ UserView,
+ SortType,
+ SearchForm,
+ SearchResponse,
+ SearchType,
+} from '../interfaces';
import { WebSocketService } from '../services';
-import { msgOp, fetchLimit, routeSearchTypeToEnum, routeSortTypeToEnum } from '../utils';
+import {
+ msgOp,
+ fetchLimit,
+ routeSearchTypeToEnum,
+ routeSortTypeToEnum,
+} from '../utils';
import { PostListing } from './post-listing';
import { CommentNodes } from './comment-nodes';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
interface SearchState {
- q: string,
- type_: SearchType,
- sort: SortType,
- page: number,
+ q: string;
+ type_: SearchType;
+ sort: SortType;
+ page: number;
searchResponse: SearchResponse;
loading: boolean;
}
export class Search extends Component<any, SearchState> {
-
private subscription: Subscription;
private emptyState: SearchState = {
q: this.getSearchQueryFromProps(this.props),
users: [],
},
loading: false,
- }
+ };
getSearchQueryFromProps(props: any): string {
- return (props.match.params.q) ? props.match.params.q : '';
+ return props.match.params.q ? props.match.params.q : '';
}
getSearchTypeFromProps(props: any): SearchType {
- return (props.match.params.type) ?
- routeSearchTypeToEnum(props.match.params.type) :
- SearchType.All;
+ return props.match.params.type
+ ? routeSearchTypeToEnum(props.match.params.type)
+ : SearchType.All;
}
getSortTypeFromProps(props: any): SortType {
- return (props.match.params.sort) ?
- routeSortTypeToEnum(props.match.params.sort) :
- SortType.TopAll;
+ return props.match.params.sort
+ ? routeSortTypeToEnum(props.match.params.sort)
+ : SortType.TopAll;
}
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;
}
constructor(props: any, context: any) {
this.state = this.emptyState;
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')
- );
-
+ );
+
if (this.state.q) {
this.search();
}
-
}
componentWillUnmount() {
// Necessary for back button for some reason
componentWillReceiveProps(nextProps: any) {
- if (nextProps.history.action == 'POP' || nextProps.history.action == 'PUSH') {
+ if (
+ nextProps.history.action == 'POP' ||
+ nextProps.history.action == 'PUSH'
+ ) {
this.state = this.emptyState;
this.state.q = this.getSearchQueryFromProps(nextProps);
this.state.type_ = this.getSearchTypeFromProps(nextProps);
}
componentDidMount() {
- document.title = `${i18n.t('search')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('search')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
render() {
<div class="container">
<div class="row">
<div class="col-12">
- <h5><T i18nKey="search">#</T></h5>
+ <h5>
+ <T i18nKey="search">#</T>
+ </h5>
{this.selects()}
{this.searchForm()}
- {this.state.type_ == SearchType.All &&
- this.all()
- }
- {this.state.type_ == SearchType.Comments &&
- this.comments()
- }
- {this.state.type_ == SearchType.Posts &&
- this.posts()
- }
- {this.state.type_ == SearchType.Communities &&
- this.communities()
- }
- {this.state.type_ == SearchType.Users &&
- this.users()
- }
+ {this.state.type_ == SearchType.All && this.all()}
+ {this.state.type_ == SearchType.Comments && this.comments()}
+ {this.state.type_ == SearchType.Posts && this.posts()}
+ {this.state.type_ == SearchType.Communities && this.communities()}
+ {this.state.type_ == SearchType.Users && this.users()}
{this.noResults()}
{this.paginator()}
</div>
</div>
</div>
- )
+ );
}
searchForm() {
return (
- <form class="form-inline" onSubmit={linkEvent(this, this.handleSearchSubmit)}>
- <input type="text" class="form-control mr-2" value={this.state.q} placeholder={`${i18n.t('search')}...`} onInput={linkEvent(this, this.handleQChange)} required minLength={3} />
+ <form
+ class="form-inline"
+ onSubmit={linkEvent(this, this.handleSearchSubmit)}
+ >
+ <input
+ type="text"
+ class="form-control mr-2"
+ value={this.state.q}
+ placeholder={`${i18n.t('search')}...`}
+ onInput={linkEvent(this, this.handleQChange)}
+ required
+ minLength={3}
+ />
<button type="submit" class="btn btn-secondary mr-2">
- {this.state.loading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
- <span><T i18nKey="search">#</T></span>
- }
+ {this.state.loading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ <span>
+ <T i18nKey="search">#</T>
+ </span>
+ )}
</button>
</form>
- )
+ );
}
selects() {
return (
<div className="mb-2">
- <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select custom-select-sm w-auto">
- <option disabled><T i18nKey="type">#</T></option>
- <option value={SearchType.All}><T i18nKey="all">#</T></option>
- <option value={SearchType.Comments}><T i18nKey="comments">#</T></option>
- <option value={SearchType.Posts}><T i18nKey="posts">#</T></option>
- <option value={SearchType.Communities}><T i18nKey="communities">#</T></option>
- <option value={SearchType.Users}><T i18nKey="users">#</T></option>
+ <select
+ value={this.state.type_}
+ onChange={linkEvent(this, this.handleTypeChange)}
+ class="custom-select custom-select-sm w-auto"
+ >
+ <option disabled>
+ <T i18nKey="type">#</T>
+ </option>
+ <option value={SearchType.All}>
+ <T i18nKey="all">#</T>
+ </option>
+ <option value={SearchType.Comments}>
+ <T i18nKey="comments">#</T>
+ </option>
+ <option value={SearchType.Posts}>
+ <T i18nKey="posts">#</T>
+ </option>
+ <option value={SearchType.Communities}>
+ <T i18nKey="communities">#</T>
+ </option>
+ <option value={SearchType.Users}>
+ <T i18nKey="users">#</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>
- )
-
+ );
}
all() {
- let combined: Array<{type_: string, data: Comment | Post | Community | UserView}> = [];
- let comments = this.state.searchResponse.comments.map(e => {return {type_: "comments", data: e}});
- let posts = this.state.searchResponse.posts.map(e => {return {type_: "posts", data: e}});
- let communities = this.state.searchResponse.communities.map(e => {return {type_: "communities", data: e}});
- let users = this.state.searchResponse.users.map(e => {return {type_: "users", data: e}});
+ let combined: Array<{
+ type_: string;
+ data: Comment | Post | Community | UserView;
+ }> = [];
+ let comments = this.state.searchResponse.comments.map(e => {
+ return { type_: 'comments', data: e };
+ });
+ let posts = this.state.searchResponse.posts.map(e => {
+ return { type_: 'posts', data: e };
+ });
+ let communities = this.state.searchResponse.communities.map(e => {
+ return { type_: 'communities', data: e };
+ });
+ let users = this.state.searchResponse.users.map(e => {
+ return { type_: 'users', data: e };
+ });
combined.push(...comments);
combined.push(...posts);
if (this.state.sort == SortType.New) {
combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
} else {
- combined.sort((a, b) => ((b.data as Comment | Post).score
- | (b.data as Community).number_of_subscribers
- | (b.data as UserView).comment_score)
- - ((a.data as Comment | Post).score
- | (a.data as Community).number_of_subscribers
- | (a.data as UserView).comment_score));
+ combined.sort(
+ (a, b) =>
+ ((b.data as Comment | Post).score |
+ (b.data as Community).number_of_subscribers |
+ (b.data as UserView).comment_score) -
+ ((a.data as Comment | Post).score |
+ (a.data as Community).number_of_subscribers |
+ (a.data as UserView).comment_score)
+ );
}
return (
<div>
- {combined.map(i =>
+ {combined.map(i => (
<div>
- {i.type_ == "posts" &&
+ {i.type_ == 'posts' && (
<PostListing post={i.data as Post} showCommunity viewOnly />
- }
- {i.type_ == "comments" &&
- <CommentNodes nodes={[{comment: i.data as Comment}]} viewOnly noIndent />
- }
- {i.type_ == "communities" &&
+ )}
+ {i.type_ == 'comments' && (
+ <CommentNodes
+ nodes={[{ comment: i.data as Comment }]}
+ viewOnly
+ noIndent
+ />
+ )}
+ {i.type_ == 'communities' && (
<div>
- <span><Link to={`/c/${(i.data as Community).name}`}>{`/c/${(i.data as Community).name}`}</Link></span>
- <span>{` - ${(i.data as Community).title} - ${(i.data as Community).number_of_subscribers} subscribers`}</span>
+ <span>
+ <Link to={`/c/${(i.data as Community).name}`}>{`/c/${
+ (i.data as Community).name
+ }`}</Link>
+ </span>
+ <span>{` - ${(i.data as Community).title} - ${
+ (i.data as Community).number_of_subscribers
+ } subscribers`}</span>
</div>
- }
- {i.type_ == "users" &&
+ )}
+ {i.type_ == 'users' && (
<div>
- <span><Link className="text-info" to={`/u/${(i.data as UserView).name}`}>{`/u/${(i.data as UserView).name}`}</Link></span>
- <span>{` - ${(i.data as UserView).comment_score} comment karma`}</span>
+ <span>
+ <Link
+ className="text-info"
+ to={`/u/${(i.data as UserView).name}`}
+ >{`/u/${(i.data as UserView).name}`}</Link>
+ </span>
+ <span>{` - ${
+ (i.data as UserView).comment_score
+ } comment karma`}</span>
</div>
- }
+ )}
</div>
- )
- }
+ ))}
</div>
- )
+ );
}
comments() {
return (
<div>
- {this.state.searchResponse.comments.map(comment =>
- <CommentNodes nodes={[{comment: comment}]} noIndent viewOnly />
- )}
+ {this.state.searchResponse.comments.map(comment => (
+ <CommentNodes nodes={[{ comment: comment }]} noIndent viewOnly />
+ ))}
</div>
);
}
posts() {
return (
<div>
- {this.state.searchResponse.posts.map(post =>
+ {this.state.searchResponse.posts.map(post => (
<PostListing post={post} showCommunity viewOnly />
- )}
+ ))}
</div>
);
}
communities() {
return (
<div>
- {this.state.searchResponse.communities.map(community =>
+ {this.state.searchResponse.communities.map(community => (
<div>
- <span><Link to={`/c/${community.name}`}>{`/c/${community.name}`}</Link></span>
+ <span>
+ <Link to={`/c/${community.name}`}>{`/c/${community.name}`}</Link>
+ </span>
<span>{` - ${community.title} - ${community.number_of_subscribers} subscribers`}</span>
</div>
- )}
+ ))}
</div>
);
}
users() {
return (
<div>
- {this.state.searchResponse.users.map(user =>
+ {this.state.searchResponse.users.map(user => (
<div>
- <span><Link className="text-info" to={`/u/${user.name}`}>{`/u/${user.name}`}</Link></span>
+ <span>
+ <Link
+ className="text-info"
+ to={`/u/${user.name}`}
+ >{`/u/${user.name}`}</Link>
+ </span>
<span>{` - ${user.comment_score} comment karma`}</span>
</div>
- )}
+ ))}
</div>
);
}
paginator() {
return (
<div class="mt-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>
);
}
let res = this.state.searchResponse;
return (
<div>
- {res && res.op && res.posts.length == 0 && res.comments.length == 0 &&
- <span><T i18nKey="no_results">#</T></span>
- }
+ {res && res.op && res.posts.length == 0 && res.comments.length == 0 && (
+ <span>
+ <T i18nKey="no_results">#</T>
+ </span>
+ )}
</div>
- )
+ );
}
- nextPage(i: Search) {
+ nextPage(i: Search) {
i.state.page++;
i.setState(i.state);
i.updateUrl();
i.search();
}
- prevPage(i: Search) {
+ prevPage(i: Search) {
i.state.page--;
i.setState(i.state);
i.updateUrl();
updateUrl() {
let typeStr = SearchType[this.state.type_].toLowerCase();
let sortStr = SortType[this.state.sort].toLowerCase();
- this.props.history.push(`/search/q/${this.state.q}/type/${typeStr}/sort/${sortStr}/page/${this.state.page}`);
+ this.props.history.push(
+ `/search/q/${this.state.q}/type/${typeStr}/sort/${sortStr}/page/${this.state.page}`
+ );
}
parseMessage(msg: any) {
let res: SearchResponse = msg;
this.state.searchResponse = res;
this.state.loading = false;
- document.title = `${i18n.t('search')} - ${this.state.q} - ${WebSocketService.Instance.site.name}`;
- window.scrollTo(0,0);
+ document.title = `${i18n.t('search')} - ${this.state.q} - ${
+ WebSocketService.Instance.site.name
+ }`;
+ window.scrollTo(0, 0);
this.setState(this.state);
}
}
}
-
import { Component, linkEvent } from 'inferno';
-import { Subscription } from "rxjs";
+import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
import { RegisterForm, LoginResponse, UserOperation } from '../interfaces';
import { WebSocketService, UserService } from '../services';
},
doneRegisteringUser: false,
userLoading: false,
- }
-
+ };
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
- (err) => console.error(err),
- () => console.log("complete")
- );
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
+ .subscribe(
+ msg => this.parseMessage(msg),
+ err => console.error(err),
+ () => console.log('complete')
+ );
}
componentWillUnmount() {
<div class="container">
<div class="row">
<div class="col-12 offset-lg-3 col-lg-6">
- <h3><T i18nKey="lemmy_instance_setup">#</T></h3>
- {!this.state.doneRegisteringUser ? this.registerUser() : <SiteForm />}
+ <h3>
+ <T i18nKey="lemmy_instance_setup">#</T>
+ </h3>
+ {!this.state.doneRegisteringUser ? (
+ this.registerUser()
+ ) : (
+ <SiteForm />
+ )}
</div>
</div>
</div>
- )
+ );
}
registerUser() {
return (
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
- <h5><T i18nKey="setup_admin">#</T></h5>
+ <h5>
+ <T i18nKey="setup_admin">#</T>
+ </h5>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="username">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="username">#</T>
+ </label>
<div class="col-sm-10">
- <input type="text" class="form-control" value={this.state.userForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} maxLength={20} pattern="[a-zA-Z0-9_]+" />
+ <input
+ type="text"
+ class="form-control"
+ value={this.state.userForm.username}
+ onInput={linkEvent(this, this.handleRegisterUsernameChange)}
+ required
+ minLength={3}
+ maxLength={20}
+ pattern="[a-zA-Z0-9_]+"
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="email">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="email">#</T>
+ </label>
<div class="col-sm-10">
- <input type="email" class="form-control" placeholder={i18n.t('optional')} value={this.state.userForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
+ <input
+ type="email"
+ class="form-control"
+ placeholder={i18n.t('optional')}
+ value={this.state.userForm.email}
+ onInput={linkEvent(this, this.handleRegisterEmailChange)}
+ minLength={3}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="password">#</T>
+ </label>
<div class="col-sm-10">
- <input type="password" value={this.state.userForm.password} onInput={linkEvent(this, this.handleRegisterPasswordChange)} class="form-control" required />
+ <input
+ type="password"
+ value={this.state.userForm.password}
+ onInput={linkEvent(this, this.handleRegisterPasswordChange)}
+ class="form-control"
+ required
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-sm-2 col-form-label"><T i18nKey="verify_password">#</T></label>
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="verify_password">#</T>
+ </label>
<div class="col-sm-10">
- <input type="password" value={this.state.userForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
+ <input
+ type="password"
+ value={this.state.userForm.password_verify}
+ onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
+ class="form-control"
+ required
+ />
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">{this.state.userLoading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
-
+ <button type="submit" class="btn btn-secondary">
+ {this.state.userLoading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ i18n.t('sign_up')
+ )}
+ </button>
</div>
</div>
</form>
);
}
-
handleRegisterSubmit(i: Setup, event: any) {
event.preventDefault();
i.state.userLoading = true;
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
-import { Community, CommunityUser, FollowCommunityForm, CommunityForm as CommunityFormI, UserView } from '../interfaces';
+import {
+ Community,
+ CommunityUser,
+ FollowCommunityForm,
+ CommunityForm as CommunityFormI,
+ UserView,
+} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { mdToHtml, getUnixTime } from '../utils';
import { CommunityForm } from './community-form';
}
export class Sidebar extends Component<SidebarProps, SidebarState> {
-
private emptyState: SidebarState = {
showEdit: false,
showRemoveDialog: false,
removeReason: null,
- removeExpires: null
- }
+ removeExpires: null,
+ };
constructor(props: any, context: any) {
super(props, context);
render() {
return (
<div>
- {!this.state.showEdit
- ? this.sidebar()
- : <CommunityForm
- community={this.props.community}
- onEdit={this.handleEditCommunity}
- onCancel={this.handleEditCancel} />
- }
+ {!this.state.showEdit ? (
+ this.sidebar()
+ ) : (
+ <CommunityForm
+ community={this.props.community}
+ onEdit={this.handleEditCommunity}
+ onCancel={this.handleEditCancel}
+ />
+ )}
</div>
- )
+ );
}
sidebar() {
<div class="card-body">
<h5 className="mb-0">
<span>{community.title}</span>
- {community.removed &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
- }
- {community.deleted &&
- <small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
- }
+ {community.removed && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="removed">#</T>
+ </small>
+ )}
+ {community.deleted && (
+ <small className="ml-2 text-muted font-italic">
+ <T i18nKey="deleted">#</T>
+ </small>
+ )}
</h5>
- <Link className="text-muted" to={`/c/${community.name}`}>/c/{community.name}</Link>
- <ul class="list-inline mb-1 text-muted small font-weight-bold">
- {this.canMod &&
+ <Link className="text-muted" to={`/c/${community.name}`}>
+ /c/{community.name}
+ </Link>
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ {this.canMod && (
<>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleEditClick)}
+ >
+ <T i18nKey="edit">#</T>
+ </span>
</li>
- {this.amCreator &&
+ {this.amCreator && (
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
- {!community.deleted ? i18n.t('delete') : i18n.t('restore')}
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.handleDeleteClick)}
+ >
+ {!community.deleted
+ ? i18n.t('delete')
+ : i18n.t('restore')}
</span>
</li>
- }
+ )}
</>
- }
- {this.canAdmin &&
+ )}
+ {this.canAdmin && (
<li className="list-inline-item">
- {!this.props.community.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>
- }
+ {!this.props.community.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>
-
- }
+ )}
</ul>
- {this.state.showRemoveDialog &&
+ {this.state.showRemoveDialog && (
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
<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('optional')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
+ <label class="col-form-label">
+ <T i18nKey="reason">#</T>
+ </label>
+ <input
+ type="text"
+ class="form-control mr-2"
+ placeholder={i18n.t('optional')}
+ value={this.state.removeReason}
+ onInput={linkEvent(this, this.handleModRemoveReasonChange)}
+ />
</div>
{/* TODO hold off on expires for now */}
{/* <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.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
- {/* </div> */}
- <div class="form-group row">
- <button type="submit" class="btn btn-secondary"><T i18nKey="remove_community">#</T></button>
- </div>
- </form>
- }
+ {/* <label class="col-form-label">Expires</label> */}
+ {/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
+ {/* </div> */}
+ <div class="form-group row">
+ <button type="submit" class="btn btn-secondary">
+ <T i18nKey="remove_community">#</T>
+ </button>
+ </div>
+ </form>
+ )}
<ul class="my-1 list-inline">
- <li className="list-inline-item"><Link className="badge badge-secondary" to="/communities">{community.category_name}</Link></li>
- <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_subscribers" interpolation={{count: community.number_of_subscribers}}>#</T></li>
- <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_posts" interpolation={{count: community.number_of_posts}}>#</T></li>
- <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_comments" interpolation={{count: community.number_of_comments}}>#</T></li>
- <li className="list-inline-item"><Link className="badge badge-secondary" to={`/modlog/community/${this.props.community.id}`}><T i18nKey="modlog">#</T></Link></li>
+ <li className="list-inline-item">
+ <Link className="badge badge-secondary" to="/communities">
+ {community.category_name}
+ </Link>
+ </li>
+ <li className="list-inline-item badge badge-secondary">
+ <T
+ i18nKey="number_of_subscribers"
+ interpolation={{ count: community.number_of_subscribers }}
+ >
+ #
+ </T>
+ </li>
+ <li className="list-inline-item badge badge-secondary">
+ <T
+ i18nKey="number_of_posts"
+ interpolation={{ count: community.number_of_posts }}
+ >
+ #
+ </T>
+ </li>
+ <li className="list-inline-item badge badge-secondary">
+ <T
+ i18nKey="number_of_comments"
+ interpolation={{ count: community.number_of_comments }}
+ >
+ #
+ </T>
+ </li>
+ <li className="list-inline-item">
+ <Link
+ className="badge badge-secondary"
+ to={`/modlog/community/${this.props.community.id}`}
+ >
+ <T i18nKey="modlog">#</T>
+ </Link>
+ </li>
</ul>
- <ul class="list-inline small">
+ <ul class="list-inline small">
<li class="list-inline-item">{i18n.t('mods')}: </li>
- {this.props.moderators.map(mod =>
- <li class="list-inline-item"><Link class="text-info" to={`/u/${mod.user_name}`}>{mod.user_name}</Link></li>
- )}
+ {this.props.moderators.map(mod => (
+ <li class="list-inline-item">
+ <Link class="text-info" to={`/u/${mod.user_name}`}>
+ {mod.user_name}
+ </Link>
+ </li>
+ ))}
</ul>
- <Link class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted || community.removed) && 'no-click'}`}
- to={`/create_post?community=${community.name}`}><T i18nKey="create_a_post">#</T></Link>
- <div>
- {community.subscribed
- ? <button class="btn btn-sm btn-secondary btn-block" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></button>
- : <button class="btn btn-sm btn-secondary btn-block" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></button>
- }
+ <Link
+ class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
+ community.removed) &&
+ 'no-click'}`}
+ to={`/create_post?community=${community.name}`}
+ >
+ <T i18nKey="create_a_post">#</T>
+ </Link>
+ <div>
+ {community.subscribed ? (
+ <button
+ class="btn btn-sm btn-secondary btn-block"
+ onClick={linkEvent(community.id, this.handleUnsubscribe)}
+ >
+ <T i18nKey="unsubscribe">#</T>
+ </button>
+ ) : (
+ <button
+ class="btn btn-sm btn-secondary btn-block"
+ onClick={linkEvent(community.id, this.handleSubscribe)}
+ >
+ <T i18nKey="subscribe">#</T>
+ </button>
+ )}
+ </div>
</div>
</div>
- </div>
- {community.description &&
+ {community.description && (
<div class="card border-secondary">
<div class="card-body">
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
+ <div
+ className="md-div"
+ dangerouslySetInnerHTML={mdToHtml(community.description)}
+ />
</div>
</div>
- }
- </div>
+ )}
+ </div>
);
}
handleUnsubscribe(communityId: number) {
let form: FollowCommunityForm = {
community_id: communityId,
- follow: false
+ follow: false,
};
WebSocketService.Instance.followCommunity(form);
}
handleSubscribe(communityId: number) {
let form: FollowCommunityForm = {
community_id: communityId,
- follow: true
+ follow: true,
};
WebSocketService.Instance.followCommunity(form);
}
}
get canMod(): boolean {
- return UserService.Instance.user && this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
+ return (
+ UserService.Instance.user &&
+ this.props.moderators
+ .map(m => m.user_id)
+ .includes(UserService.Instance.user.id)
+ );
}
get canAdmin(): boolean {
- return UserService.Instance.user && this.props.admins.map(a => a.id).includes(UserService.Instance.user.id);
+ return (
+ UserService.Instance.user &&
+ this.props.admins.map(a => a.id).includes(UserService.Instance.user.id)
+ );
}
handleModRemoveShow(i: Sidebar) {
}
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
- private emptyState: SiteFormState ={
+ private emptyState: SiteFormState = {
siteForm: {
- name: null
+ name: null,
},
- loading: false
- }
+ loading: false,
+ };
constructor(props: any, context: any) {
super(props, context);
this.state.siteForm = {
name: this.props.site.name,
description: this.props.site.description,
- }
+ };
}
}
render() {
return (
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
- <h5>{`${this.props.site ? capitalizeFirstLetter(i18n.t('edit')) : capitalizeFirstLetter(i18n.t('name'))} ${i18n.t('your_site')}`}</h5>
+ <h5>{`${
+ this.props.site
+ ? capitalizeFirstLetter(i18n.t('edit'))
+ : capitalizeFirstLetter(i18n.t('name'))
+ } ${i18n.t('your_site')}`}</h5>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="name">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="name">#</T>
+ </label>
<div class="col-12">
- <input type="text" class="form-control" value={this.state.siteForm.name} onInput={linkEvent(this, this.handleSiteNameChange)} required minLength={3} maxLength={20} />
+ <input
+ type="text"
+ class="form-control"
+ value={this.state.siteForm.name}
+ onInput={linkEvent(this, this.handleSiteNameChange)}
+ required
+ minLength={3}
+ maxLength={20}
+ />
</div>
</div>
<div class="form-group row">
- <label class="col-12 col-form-label"><T i18nKey="sidebar">#</T></label>
+ <label class="col-12 col-form-label">
+ <T i18nKey="sidebar">#</T>
+ </label>
<div class="col-12">
- <textarea value={this.state.siteForm.description} onInput={linkEvent(this, this.handleSiteDescriptionChange)} class="form-control" rows={3} maxLength={10000} />
+ <textarea
+ value={this.state.siteForm.description}
+ onInput={linkEvent(this, this.handleSiteDescriptionChange)}
+ class="form-control"
+ rows={3}
+ maxLength={10000}
+ />
</div>
</div>
<div class="form-group row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
- {this.state.loading ?
- <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
- this.props.site ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('create'))}</button>
- {this.props.site && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
+ {this.state.loading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : this.props.site ? (
+ capitalizeFirstLetter(i18n.t('save'))
+ ) : (
+ capitalizeFirstLetter(i18n.t('create'))
+ )}
+ </button>
+ {this.props.site && (
+ <button
+ type="button"
+ class="btn btn-secondary"
+ onClick={linkEvent(this, this.handleCancel)}
+ >
+ <T i18nKey="cancel">#</T>
+ </button>
+ )}
</div>
</div>
</form>
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
-let general = [
- "riccardo","NotTooHighToHack",
-];
+let general = ['riccardo', 'NotTooHighToHack'];
// let highlighted = [];
// let silver = [];
// let gold = [];
// let latinum = [];
export class Sponsors extends Component<any, any> {
-
constructor(props: any, context: any) {
super(props, context);
-
}
componentDidMount() {
- document.title = `${i18n.t('sponsors')} - ${WebSocketService.Instance.site.name}`;
+ document.title = `${i18n.t('sponsors')} - ${
+ WebSocketService.Instance.site.name
+ }`;
}
render() {
<hr />
{this.bitcoin()}
</div>
- )
+ );
}
topMessage() {
return (
<div>
- <h5><T i18nKey="sponsors_of_lemmy">#</T></h5>
+ <h5>
+ <T i18nKey="sponsors_of_lemmy">#</T>
+ </h5>
<p>
- <T i18nKey="sponsor_message">#<a href="https://github.com/dessalines/lemmy">#</a></T>
+ <T i18nKey="sponsor_message">
+ #<a href="https://github.com/dessalines/lemmy">#</a>
+ </T>
</p>
- <a class="btn btn-secondary" href="https://www.patreon.com/dessalines"><T i18nKey="support_on_patreon">#</T></a>
+ <a class="btn btn-secondary" href="https://www.patreon.com/dessalines">
+ <T i18nKey="support_on_patreon">#</T>
+ </a>
</div>
- )
+ );
}
sponsors() {
return (
<div class="container">
- <h5><T i18nKey="sponsors">#</T></h5>
- <p><T i18nKey="general_sponsors">#</T></p>
+ <h5>
+ <T i18nKey="sponsors">#</T>
+ </h5>
+ <p>
+ <T i18nKey="general_sponsors">#</T>
+ </p>
<div class="row card-columns">
- {general.map(s =>
+ {general.map(s => (
<div class="card col-12 col-md-2">
<div>{s}</div>
</div>
- )}
+ ))}
</div>
</div>
- )
+ );
}
bitcoin() {
return (
<div>
- <h5><T i18nKey="crypto">#</T></h5>
- <div class="table-responsive">
- <table class="table table-hover text-center">
- <tbody>
- <tr>
- <td><T i18nKey="bitcoin">#</T></td>
- <td><code>1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK</code></td>
- </tr>
- <tr>
- <td><T i18nKey="ethereum">#</T></td>
- <td><code>0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01</code></td>
- </tr>
- <tr>
- <td><T i18nKey="monero">#</T></td>
- <td>
- <code>41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV</code>
- </td>
- </tr>
- </tbody>
- </table>
+ <h5>
+ <T i18nKey="crypto">#</T>
+ </h5>
+ <div class="table-responsive">
+ <table class="table table-hover text-center">
+ <tbody>
+ <tr>
+ <td>
+ <T i18nKey="bitcoin">#</T>
+ </td>
+ <td>
+ <code>1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <T i18nKey="ethereum">#</T>
+ </td>
+ <td>
+ <code>0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <T i18nKey="monero">#</T>
+ </td>
+ <td>
+ <code>
+ 41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV
+ </code>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
- </div>
- )
+ );
}
}
-
import { Component } from 'inferno';
export class Symbols extends Component<any, any> {
-
constructor(props: any, context: any) {
super(props, context);
}
render() {
return (
- <svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
+ <svg
+ aria-hidden="true"
+ style="position: absolute; width: 0; height: 0; overflow: hidden;"
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlnsXlink="http://www.w3.org/1999/xlink"
+ >
<defs>
<symbol id="icon-arrow-down" viewBox="0 0 26 28">
<title>arrow-down</title>
<symbol id="icon-arrow-up" viewBox="0 0 26 28">
<title>arrow-up</title>
<path d="M25.172 15.172c0 0.531-0.219 1.031-0.578 1.406l-1.172 1.172c-0.375 0.375-0.891 0.594-1.422 0.594s-1.047-0.219-1.406-0.594l-4.594-4.578v11c0 1.125-0.938 1.828-2 1.828h-2c-1.062 0-2-0.703-2-1.828v-11l-4.594 4.578c-0.359 0.375-0.875 0.594-1.406 0.594s-1.047-0.219-1.406-0.594l-1.172-1.172c-0.375-0.375-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l10.172-10.172c0.359-0.375 0.875-0.578 1.406-0.578s1.047 0.203 1.422 0.578l10.172 10.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path>
- </symbol>
+ </symbol>
<symbol id="icon-mail" viewBox="0 0 32 32">
<title>mail</title>
<path d="M28 5h-24c-2.209 0-4 1.792-4 4v13c0 2.209 1.791 4 4 4h24c2.209 0 4-1.791 4-4v-13c0-2.208-1.791-4-4-4zM2 10.25l6.999 5.25-6.999 5.25v-10.5zM30 22c0 1.104-0.898 2-2 2h-24c-1.103 0-2-0.896-2-2l7.832-5.875 4.368 3.277c0.533 0.398 1.166 0.6 1.8 0.6 0.633 0 1.266-0.201 1.799-0.6l4.369-3.277 7.832 5.875zM30 20.75l-7-5.25 7-5.25v10.5zM17.199 18.602c-0.349 0.262-0.763 0.4-1.199 0.4s-0.851-0.139-1.2-0.4l-12.8-9.602c0-1.103 0.897-2 2-2h24c1.102 0 2 0.897 2 2l-12.801 9.602z"></path>
- </symbol>
- <symbol id="icon-mouse" version="1.1" x="0px" y="0px"
- viewBox="0 0 1024 1024">
- <g
- id="layer1"
- transform="translate(0,-26.066658)"
- style="display:inline">
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- d="m 167.03908,270.78735 c -0.94784,-0.002 -1.8939,0.004 -2.83789,0.0215 -4.31538,0.0778 -8.58934,0.3593 -12.8125,0.8457 -33.78522,3.89116 -64.215716,21.86394 -82.871086,53.27344 -18.27982,30.77718 -22.77749,64.66635 -13.46094,96.06837 9.31655,31.40203 31.88488,59.93174 65.296886,82.5332 0.20163,0.13618 0.40678,0.26709 0.61523,0.39258 28.65434,17.27768 57.18167,28.93179 87.74218,34.95508 -0.74566,12.61339 -0.72532,25.5717 0.082,38.84375 2.43989,40.10943 16.60718,77.03742 38.0957,109.67187 l -77.00781,31.4375 c -8.30605,3.25932 -12.34178,12.68234 -8.96967,20.94324 3.37211,8.2609 12.84919,12.16798 21.06342,8.68371 l 84.69727,-34.57617 c 15.70675,18.72702 33.75346,35.68305 53.12109,50.57032 0.74013,0.56891 1.4904,1.12236 2.23437,1.68554 l -49.61132,65.69141 c -5.45446,7.0474 -4.10058,17.19288 3.01098,22.5634 7.11156,5.37052 17.24028,3.89649 22.52612,-3.27824 l 50.38672,-66.71876 c 27.68572,17.53469 57.07524,31.20388 86.07227,40.25196 14.88153,27.28008 43.96965,44.64648 77.58789,44.64648 33.93762,0 63.04252,-18.68693 77.80082,-45.4375 28.7072,-9.21295 57.7527,-22.93196 85.1484,-40.40234 l 51.0977,67.66016 c 5.2858,7.17473 15.4145,8.64876 22.5261,3.27824 7.1115,-5.37052 8.4654,-15.516 3.011,-22.5634 l -50.3614,-66.68555 c 0.334,-0.25394 0.6727,-0.50077 1.0059,-0.75586 19.1376,-14.64919 37.0259,-31.28581 52.7031,-49.63476 l 82.5625,33.70507 c 8.2143,3.48427 17.6913,-0.42281 21.0634,-8.68371 3.3722,-8.2609 -0.6636,-17.68392 -8.9696,-20.94324 l -74.5391,-30.42773 c 22.1722,-32.82971 37.0383,-70.03397 40.1426,-110.46094 1.0253,-13.35251 1.2292,-26.42535 0.6387,-39.17578 30.3557,-6.05408 58.7164,-17.66833 87.2011,-34.84375 0.2085,-0.12549 0.4136,-0.2564 0.6153,-0.39258 33.412,-22.60147 55.9803,-51.13117 65.2968,-82.5332 9.3166,-31.40202 4.8189,-65.29118 -13.4609,-96.06837 -18.6553,-31.40951 -49.0859,-49.38228 -82.8711,-53.27344 -4.2231,-0.4864 -8.4971,-0.76791 -12.8125,-0.8457 -30.2077,-0.54448 -62.4407,8.82427 -93.4316,26.71484 -22.7976,13.16063 -43.3521,33.31423 -59.4375,55.30469 -44.9968,-25.75094 -103.5444,-40.25065 -175.4785,-41.43945 -6.4522,-0.10663 -13.0125,-0.10696 -19.67974,0.002 -80.18875,1.30929 -144.38284,16.5086 -192.87109,43.9922 -0.11914,-0.19111 -0.24287,-0.37932 -0.37109,-0.56446 -16.29,-22.764 -37.41085,-43.73706 -60.89649,-57.29493 -30.02247,-17.33149 -61.21051,-26.66489 -90.59375,-26.73633 z"
- id="path817-3"
- />
- <path
- id="path1087"
- style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 716.85595,362.96478 c 15.29075,-21.36763 35.36198,-41.10921 56.50979,-53.31749 66.66377,-38.48393 137.02617,-33.22172 170.08018,22.43043 33.09493,55.72093 14.98656,117.48866 -47.64399,159.85496 -31.95554,19.26819 -62.93318,30.92309 -97.22892,35.54473 M 307.14407,362.96478 C 291.85332,341.59715 271.78209,321.85557 250.63429,309.64729 183.97051,271.16336 113.60811,276.42557 80.554051,332.07772 47.459131,387.79865 65.56752,449.56638 128.19809,491.93268 c 31.95554,19.26819 62.93319,30.92309 97.22893,35.54473"
- />
- <path
- style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 801.23205,576.8699 C 812.73478,427.06971 720.58431,321.98291 511.99999,325.38859 303.41568,328.79426 213.71393,428.0311 222.76794,576.8699 c 8.64289,142.08048 176.80223,246.40388 288.12038,246.40388 111.31815,0 279.45076,-104.5447 290.34373,-246.40388 z"
- id="path969"
- />
- <path
- id="path1084"
- style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 610.4991,644.28932 c 0,23.11198 18.70595,41.84795 41.78091,41.84795 23.07495,0 41.7809,-18.73597 41.7809,-41.84795 0,-23.112 -18.70594,-41.84796 -41.7809,-41.84796 -23.07496,0 -41.78091,18.73596 -41.78091,41.84796 z m -280.56002,0 c 0,23.32492 18.87829,42.23352 42.16586,42.23352 23.28755,0 42.16585,-18.9086 42.16585,-42.23352 0,-23.32494 -18.87829,-42.23353 -42.16585,-42.23353 -23.28757,0 -42.16586,18.90859 -42.16586,42.23353 z"
- />
- <path
- id="path1008"
- style="display:inline;opacity:1;fill:none;stroke:#000000;stroke-width:32;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 339.72919,769.2467 -54.54422,72.22481 m 399.08582,-72.22481 54.54423,72.22481 M 263.68341,697.82002 175.92752,733.64353 m 579.85765,-35.82351 87.7559,35.82351"
- />
- <path
- style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 512.00082,713.08977 c -45.86417,0 -75.13006,31.84485 -74.14159,71.10084 1.07048,42.51275 32.46865,71.10323 74.14159,71.10323 41.67296,0 74.05118,-32.99608 74.14161,-71.10323 0.0932,-39.26839 -28.27742,-71.10084 -74.14161,-71.10084 z"
- id="path1115"
- />
- </g>
- </symbol>
- <symbol id="icon-search" viewBox="0 0 32 32">
- <title>search</title>
- <path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path>
- </symbol>
- <symbol id="icon-github" viewBox="0 0 32 32">
- <title>github</title>
- <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
- </symbol>
- <symbol id="icon-spinner" viewBox="0 0 32 32">
- <title>spinner</title>
- <path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"></path>
- </symbol>
- </defs>
- </svg>
+ </symbol>
+ <symbol
+ id="icon-mouse"
+ version="1.1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 1024 1024"
+ >
+ <g
+ id="layer1"
+ transform="translate(0,-26.066658)"
+ style="display:inline"
+ >
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 167.03908,270.78735 c -0.94784,-0.002 -1.8939,0.004 -2.83789,0.0215 -4.31538,0.0778 -8.58934,0.3593 -12.8125,0.8457 -33.78522,3.89116 -64.215716,21.86394 -82.871086,53.27344 -18.27982,30.77718 -22.77749,64.66635 -13.46094,96.06837 9.31655,31.40203 31.88488,59.93174 65.296886,82.5332 0.20163,0.13618 0.40678,0.26709 0.61523,0.39258 28.65434,17.27768 57.18167,28.93179 87.74218,34.95508 -0.74566,12.61339 -0.72532,25.5717 0.082,38.84375 2.43989,40.10943 16.60718,77.03742 38.0957,109.67187 l -77.00781,31.4375 c -8.30605,3.25932 -12.34178,12.68234 -8.96967,20.94324 3.37211,8.2609 12.84919,12.16798 21.06342,8.68371 l 84.69727,-34.57617 c 15.70675,18.72702 33.75346,35.68305 53.12109,50.57032 0.74013,0.56891 1.4904,1.12236 2.23437,1.68554 l -49.61132,65.69141 c -5.45446,7.0474 -4.10058,17.19288 3.01098,22.5634 7.11156,5.37052 17.24028,3.89649 22.52612,-3.27824 l 50.38672,-66.71876 c 27.68572,17.53469 57.07524,31.20388 86.07227,40.25196 14.88153,27.28008 43.96965,44.64648 77.58789,44.64648 33.93762,0 63.04252,-18.68693 77.80082,-45.4375 28.7072,-9.21295 57.7527,-22.93196 85.1484,-40.40234 l 51.0977,67.66016 c 5.2858,7.17473 15.4145,8.64876 22.5261,3.27824 7.1115,-5.37052 8.4654,-15.516 3.011,-22.5634 l -50.3614,-66.68555 c 0.334,-0.25394 0.6727,-0.50077 1.0059,-0.75586 19.1376,-14.64919 37.0259,-31.28581 52.7031,-49.63476 l 82.5625,33.70507 c 8.2143,3.48427 17.6913,-0.42281 21.0634,-8.68371 3.3722,-8.2609 -0.6636,-17.68392 -8.9696,-20.94324 l -74.5391,-30.42773 c 22.1722,-32.82971 37.0383,-70.03397 40.1426,-110.46094 1.0253,-13.35251 1.2292,-26.42535 0.6387,-39.17578 30.3557,-6.05408 58.7164,-17.66833 87.2011,-34.84375 0.2085,-0.12549 0.4136,-0.2564 0.6153,-0.39258 33.412,-22.60147 55.9803,-51.13117 65.2968,-82.5332 9.3166,-31.40202 4.8189,-65.29118 -13.4609,-96.06837 -18.6553,-31.40951 -49.0859,-49.38228 -82.8711,-53.27344 -4.2231,-0.4864 -8.4971,-0.76791 -12.8125,-0.8457 -30.2077,-0.54448 -62.4407,8.82427 -93.4316,26.71484 -22.7976,13.16063 -43.3521,33.31423 -59.4375,55.30469 -44.9968,-25.75094 -103.5444,-40.25065 -175.4785,-41.43945 -6.4522,-0.10663 -13.0125,-0.10696 -19.67974,0.002 -80.18875,1.30929 -144.38284,16.5086 -192.87109,43.9922 -0.11914,-0.19111 -0.24287,-0.37932 -0.37109,-0.56446 -16.29,-22.764 -37.41085,-43.73706 -60.89649,-57.29493 -30.02247,-17.33149 -61.21051,-26.66489 -90.59375,-26.73633 z"
+ id="path817-3"
+ />
+ <path
+ id="path1087"
+ style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 716.85595,362.96478 c 15.29075,-21.36763 35.36198,-41.10921 56.50979,-53.31749 66.66377,-38.48393 137.02617,-33.22172 170.08018,22.43043 33.09493,55.72093 14.98656,117.48866 -47.64399,159.85496 -31.95554,19.26819 -62.93318,30.92309 -97.22892,35.54473 M 307.14407,362.96478 C 291.85332,341.59715 271.78209,321.85557 250.63429,309.64729 183.97051,271.16336 113.60811,276.42557 80.554051,332.07772 47.459131,387.79865 65.56752,449.56638 128.19809,491.93268 c 31.95554,19.26819 62.93319,30.92309 97.22893,35.54473"
+ />
+ <path
+ style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 801.23205,576.8699 C 812.73478,427.06971 720.58431,321.98291 511.99999,325.38859 303.41568,328.79426 213.71393,428.0311 222.76794,576.8699 c 8.64289,142.08048 176.80223,246.40388 288.12038,246.40388 111.31815,0 279.45076,-104.5447 290.34373,-246.40388 z"
+ id="path969"
+ />
+ <path
+ id="path1084"
+ style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 610.4991,644.28932 c 0,23.11198 18.70595,41.84795 41.78091,41.84795 23.07495,0 41.7809,-18.73597 41.7809,-41.84795 0,-23.112 -18.70594,-41.84796 -41.7809,-41.84796 -23.07496,0 -41.78091,18.73596 -41.78091,41.84796 z m -280.56002,0 c 0,23.32492 18.87829,42.23352 42.16586,42.23352 23.28755,0 42.16585,-18.9086 42.16585,-42.23352 0,-23.32494 -18.87829,-42.23353 -42.16585,-42.23353 -23.28757,0 -42.16586,18.90859 -42.16586,42.23353 z"
+ />
+ <path
+ id="path1008"
+ style="display:inline;opacity:1;fill:none;stroke:#000000;stroke-width:32;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 339.72919,769.2467 -54.54422,72.22481 m 399.08582,-72.22481 54.54423,72.22481 M 263.68341,697.82002 175.92752,733.64353 m 579.85765,-35.82351 87.7559,35.82351"
+ />
+ <path
+ style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:28;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 512.00082,713.08977 c -45.86417,0 -75.13006,31.84485 -74.14159,71.10084 1.07048,42.51275 32.46865,71.10323 74.14159,71.10323 41.67296,0 74.05118,-32.99608 74.14161,-71.10323 0.0932,-39.26839 -28.27742,-71.10084 -74.14161,-71.10084 z"
+ id="path1115"
+ />
+ </g>
+ </symbol>
+ <symbol id="icon-search" viewBox="0 0 32 32">
+ <title>search</title>
+ <path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path>
+ </symbol>
+ <symbol id="icon-github" viewBox="0 0 32 32">
+ <title>github</title>
+ <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
+ </symbol>
+ <symbol id="icon-spinner" viewBox="0 0 32 32">
+ <title>spinner</title>
+ <path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"></path>
+ </symbol>
+ </defs>
+ </svg>
);
}
}
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';
import { T } from 'inferno-i18next';
enum View {
- Overview, Comments, Posts, Saved
+ Overview,
+ Comments,
+ Posts,
+ Saved,
}
interface UserState {
}
export class User extends Component<any, UserState> {
-
private subscription: Subscription;
private emptyState: UserState = {
user: {
deleteAccountShowConfirm: false,
deleteAccountForm: {
password: null,
- }
- }
+ },
+ };
constructor(props: any, context: any) {
super(props, context);
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() {
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);
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>
);
}
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>
);
}
<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>
);
}
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();
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;
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('/');
}
}
}
-
let host = `${window.location.hostname}`;
-let port = `${window.location.port == "4444" ? '8536' : window.location.port}`;
+let port = `${window.location.port == '4444' ? '8536' : window.location.port}`;
let endpoint = `${host}:${port}`;
-export let wsUri = `${(window.location.protocol=='https:') ? 'wss://' : 'ws://'}${endpoint}/api/v1/ws`;
+export let wsUri = `${
+ window.location.protocol == 'https:' ? 'wss://' : 'ws://'
+}${endpoint}/api/v1/ws`;
sv,
ru,
nl,
-}
+};
function format(value: any, format: any, lng: any) {
- if (format === 'uppercase') return value.toUpperCase();
- return value;
+ if (format === 'uppercase') return value.toUpperCase();
+ return value;
}
-i18n
-.init({
+i18n.init({
debug: true,
// load: 'languageOnly',
// initImmediate: false,
lng: getLanguage(),
fallbackLng: 'en',
- resources,
- interpolation: {
- format: format
-
- }
+ resources,
+ interpolation: {
+ format: format,
+ },
});
export { i18n, resources };
const container = document.getElementById('app');
class Index extends Component<any, any> {
-
constructor(props: any, context: any) {
super(props, context);
WebSocketService.Instance;
<Navbar />
<div class="mt-4 p-0">
<Switch>
- <Route path={`/home/type/:type/sort/:sort/page/:page`} component={Main} />
+ <Route
+ path={`/home/type/:type/sort/:sort/page/:page`}
+ component={Main}
+ />
<Route exact path={`/`} component={Main} />
<Route path={`/login`} component={Login} />
<Route path={`/create_post`} component={CreatePost} />
<Route path={`/communities`} component={Communities} />
<Route path={`/post/:id/comment/:comment_id`} component={Post} />
<Route path={`/post/:id`} component={Post} />
- <Route path={`/c/:name/sort/:sort/page/:page`} component={Community} />
+ <Route
+ path={`/c/:name/sort/:sort/page/:page`}
+ component={Community}
+ />
<Route path={`/community/:id`} component={Community} />
<Route path={`/c/:name`} component={Community} />
- <Route path={`/u/:username/view/:view/sort/:sort/page/:page`} component={User} />
+ <Route
+ path={`/u/:username/view/:view/sort/:sort/page/:page`}
+ component={User}
+ />
<Route path={`/user/:id`} component={User} />
<Route path={`/u/:username`} component={User} />
<Route path={`/inbox`} component={Inbox} />
- <Route path={`/modlog/community/:community_id`} component={Modlog} />
+ <Route
+ path={`/modlog/community/:community_id`}
+ component={Modlog}
+ />
<Route path={`/modlog`} component={Modlog} />
<Route path={`/setup`} component={Setup} />
- <Route path={`/search/q/:q/type/:type/sort/:sort/page/:page`} component={Search} />
+ <Route
+ path={`/search/q/:q/type/:type/sort/:sort/page/:page`}
+ component={Search}
+ />
<Route path={`/search`} component={Search} />
<Route path={`/sponsors`} component={Sponsors} />
</Switch>
</Provider>
);
}
-
}
render(<Index />, container);
export enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings, TransferCommunity, TransferSite, DeleteAccount
+ Login,
+ Register,
+ CreateCommunity,
+ CreatePost,
+ ListCommunities,
+ ListCategories,
+ GetPost,
+ GetCommunity,
+ CreateComment,
+ EditComment,
+ SaveComment,
+ CreateCommentLike,
+ GetPosts,
+ CreatePostLike,
+ EditPost,
+ SavePost,
+ EditCommunity,
+ FollowCommunity,
+ GetFollowedCommunities,
+ GetUserDetails,
+ GetReplies,
+ GetModlog,
+ BanFromCommunity,
+ AddModToCommunity,
+ CreateSite,
+ EditSite,
+ GetSite,
+ AddAdmin,
+ BanUser,
+ Search,
+ MarkAllAsRead,
+ SaveUserSettings,
+ TransferCommunity,
+ TransferSite,
+ DeleteAccount,
}
export enum CommentSortType {
- Hot, Top, New
+ Hot,
+ Top,
+ New,
}
export enum ListingType {
- All, Subscribed, Community
+ All,
+ Subscribed,
+ Community,
}
export enum SortType {
- Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll
+ Hot,
+ New,
+ TopDay,
+ TopWeek,
+ TopMonth,
+ TopYear,
+ TopAll,
}
export enum SearchType {
- All, Comments, Posts, Communities, Users, Url
+ All,
+ Comments,
+ Posts,
+ Communities,
+ Users,
+ Url,
}
export interface User {
export interface Comment {
id: number;
creator_id: number;
- post_id: number,
+ post_id: number;
parent_id?: number;
content: string;
removed: boolean;
read: boolean;
published: string;
updated?: string;
- community_id: number,
+ community_id: number;
banned: boolean;
banned_from_community: boolean;
creator_name: string;
number_of_communities: number;
}
-export enum BanType {Community, Site};
+export enum BanType {
+ Community,
+ Site,
+}
export interface FollowCommunityForm {
community_id: number;
community_id: number;
user_id: number;
ban: boolean;
- reason?: string,
- expires?: number,
+ reason?: string;
+ expires?: number;
auth?: string;
}
export interface BanFromCommunityResponse {
op: string;
- user: UserView,
- banned: boolean,
+ user: UserView;
+ banned: boolean;
}
export interface AddModToCommunityForm {
export interface GetModlogResponse {
op: string;
- removed_posts: Array<ModRemovePost>,
- locked_posts: Array<ModLockPost>,
- stickied_posts: Array<ModStickyPost>,
- removed_comments: Array<ModRemoveComment>,
- removed_communities: Array<ModRemoveCommunity>,
- banned_from_community: Array<ModBanFromCommunity>,
- banned: Array<ModBan>,
- added_to_community: Array<ModAddCommunity>,
- added: Array<ModAdd>,
+ removed_posts: Array<ModRemovePost>;
+ locked_posts: Array<ModLockPost>;
+ stickied_posts: Array<ModStickyPost>;
+ removed_comments: Array<ModRemoveComment>;
+ removed_communities: Array<ModRemoveCommunity>;
+ banned_from_community: Array<ModBanFromCommunity>;
+ banned: Array<ModBan>;
+ added_to_community: Array<ModAddCommunity>;
+ added: Array<ModAdd>;
}
export interface ModRemovePost {
post_id: number;
reason?: string;
removed?: boolean;
- when_: string
+ when_: string;
mod_user_name: string;
post_name: string;
community_id: number;
}
export interface ModLockPost {
- id: number,
- mod_user_id: number,
- post_id: number,
- locked?: boolean,
- when_: string,
- mod_user_name: string,
- post_name: string,
- community_id: number,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ post_id: number;
+ locked?: boolean;
+ when_: string;
+ mod_user_name: string;
+ post_name: string;
+ community_id: number;
+ community_name: string;
}
export interface ModStickyPost {
- id: number,
- mod_user_id: number,
- post_id: number,
- stickied?: boolean,
- when_: string,
- mod_user_name: string,
- post_name: string,
- community_id: number,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ post_id: number;
+ stickied?: boolean;
+ when_: string;
+ mod_user_name: string;
+ post_name: string;
+ community_id: number;
+ community_name: string;
}
export interface ModRemoveComment {
- id: number,
- mod_user_id: number,
- comment_id: number,
- reason?: string,
- removed?: boolean,
- when_: string,
- mod_user_name: string,
- comment_user_id: number,
- comment_user_name: string,
- comment_content: string,
- post_id: number,
- post_name: string,
- community_id: number,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ comment_id: number;
+ reason?: string;
+ removed?: boolean;
+ when_: string;
+ mod_user_name: string;
+ comment_user_id: number;
+ comment_user_name: string;
+ comment_content: string;
+ post_id: number;
+ post_name: string;
+ community_id: number;
+ community_name: string;
}
export interface ModRemoveCommunity {
- id: number,
- mod_user_id: number,
- community_id: number,
- reason?: string,
- removed?: boolean,
- expires?: number,
- when_: string,
- mod_user_name: string,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ community_id: number;
+ reason?: string;
+ removed?: boolean;
+ expires?: number;
+ when_: string;
+ mod_user_name: string;
+ community_name: string;
}
export interface ModBanFromCommunity {
- id: number,
- mod_user_id: number,
- other_user_id: number,
- community_id: number,
- reason?: string,
- banned?: boolean,
- expires?: number,
- when_: string,
- mod_user_name: string,
- other_user_name: string,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ other_user_id: number;
+ community_id: number;
+ reason?: string;
+ banned?: boolean;
+ expires?: number;
+ when_: string;
+ mod_user_name: string;
+ other_user_name: string;
+ community_name: string;
}
export interface ModBan {
- id: number,
- mod_user_id: number,
- other_user_id: number,
- reason?: string,
- banned?: boolean,
- expires?: number,
- when_: string,
- mod_user_name: string,
- other_user_name: string,
+ id: number;
+ mod_user_id: number;
+ other_user_id: number;
+ reason?: string;
+ banned?: boolean;
+ expires?: number;
+ when_: string;
+ mod_user_name: string;
+ other_user_name: string;
}
export interface ModAddCommunity {
- id: number,
- mod_user_id: number,
- other_user_id: number,
- community_id: number,
- removed?: boolean,
- when_: string,
- mod_user_name: string,
- other_user_name: string,
- community_name: string,
+ id: number;
+ mod_user_id: number;
+ other_user_id: number;
+ community_id: number;
+ removed?: boolean;
+ when_: string;
+ mod_user_name: string;
+ other_user_name: string;
+ community_name: string;
}
export interface ModAdd {
- id: number,
- mod_user_id: number,
- other_user_id: number,
- removed?: boolean,
- when_: string,
- mod_user_name: string,
- other_user_name: string,
+ id: number;
+ mod_user_id: number;
+ other_user_id: number;
+ removed?: boolean;
+ when_: string;
+ mod_user_name: string;
+ other_user_name: string;
}
export interface LoginForm {
export interface CommunityForm {
name: string;
title: string;
- description?: string,
- category_id: number,
+ description?: string;
+ category_id: number;
edit_id?: number;
removed?: boolean;
deleted?: boolean;
admins: Array<UserView>;
}
-
export interface CommunityResponse {
op: string;
community: Community;
export interface SiteForm {
name: string;
- description?: string,
+ description?: string;
removed?: boolean;
reason?: string;
expires?: number;
online: number;
}
-
export interface SiteResponse {
op: string;
site: Site;
export interface BanUserForm {
user_id: number;
ban: boolean;
- reason?: string,
- expires?: number,
+ reason?: string;
+ expires?: number;
auth?: string;
}
export interface BanUserResponse {
op: string;
- user: UserView,
- banned: boolean,
+ user: UserView;
+ banned: boolean;
}
export interface AddAdminForm {
type_: string;
posts?: Array<Post>;
comments?: Array<Comment>;
- communities: Array<Community>;
+ communities: Array<Community>;
users: Array<UserView>;
}
import { Subject } from 'rxjs';
export class UserService {
-
private static _instance: UserService;
public user: User;
- public sub: Subject<{user: User, unreadCount: number}> = new Subject<{user: User, unreadCount: number}>();
+ public sub: Subject<{ user: User; unreadCount: number }> = new Subject<{
+ user: User;
+ unreadCount: number;
+ }>();
private constructor() {
- let jwt = Cookies.get("jwt");
+ let jwt = Cookies.get('jwt');
if (jwt) {
this.setUser(jwt);
} else {
public login(res: LoginResponse) {
this.setUser(res.jwt);
- Cookies.set("jwt", res.jwt, { expires: 365 });
- console.log("jwt cookie set");
+ Cookies.set('jwt', res.jwt, { expires: 365 });
+ console.log('jwt cookie set');
}
public logout() {
this.user = undefined;
- Cookies.remove("jwt");
+ Cookies.remove('jwt');
setTheme();
- this.sub.next({user: undefined, unreadCount: 0});
- console.log("Logged out.");
+ this.sub.next({ user: undefined, unreadCount: 0 });
+ console.log('Logged out.');
}
public get auth(): string {
- return Cookies.get("jwt");
+ return Cookies.get('jwt');
}
private setUser(jwt: string) {
this.user = jwt_decode(jwt);
setTheme(this.user.theme);
- this.sub.next({user: this.user, unreadCount: 0});
+ this.sub.next({ user: this.user, unreadCount: 0 });
console.log(this.user);
}
- public static get Instance(){
+ public static get Instance() {
return this._instance || (this._instance = new this());
}
}
import { wsUri } from '../env';
-import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, TransferCommunityForm, AddAdminForm, TransferSiteForm, BanUserForm, SiteForm, Site, UserView, GetRepliesForm, SearchForm, UserSettingsForm, DeleteAccountForm } from '../interfaces';
+import {
+ LoginForm,
+ RegisterForm,
+ UserOperation,
+ CommunityForm,
+ PostForm,
+ SavePostForm,
+ CommentForm,
+ SaveCommentForm,
+ CommentLikeForm,
+ GetPostsForm,
+ CreatePostLikeForm,
+ FollowCommunityForm,
+ GetUserDetailsForm,
+ ListCommunitiesForm,
+ GetModlogForm,
+ BanFromCommunityForm,
+ AddModToCommunityForm,
+ TransferCommunityForm,
+ AddAdminForm,
+ TransferSiteForm,
+ BanUserForm,
+ SiteForm,
+ Site,
+ UserView,
+ GetRepliesForm,
+ SearchForm,
+ UserSettingsForm,
+ DeleteAccountForm,
+} from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
// Necessary to not keep reconnecting
this.subject
- .pipe(retryWhen(errors => errors.pipe(delay(60000), take(999))))
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(60000),
+ take(999)
+ )
+ )
+ )
.subscribe();
- console.log(`Connected to ${wsUri}`);
+ console.log(`Connected to ${wsUri}`);
}
- public static get Instance(){
+ public static get Instance() {
return this._instance || (this._instance = new this());
}
-
+
public login(loginForm: LoginForm) {
this.subject.next(this.wsSendWrapper(UserOperation.Login, loginForm));
}
public createCommunity(communityForm: CommunityForm) {
this.setAuth(communityForm);
- this.subject.next(this.wsSendWrapper(UserOperation.CreateCommunity, communityForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.CreateCommunity, communityForm)
+ );
}
public editCommunity(communityForm: CommunityForm) {
this.setAuth(communityForm);
- this.subject.next(this.wsSendWrapper(UserOperation.EditCommunity, communityForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.EditCommunity, communityForm)
+ );
}
public followCommunity(followCommunityForm: FollowCommunityForm) {
this.setAuth(followCommunityForm);
- this.subject.next(this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm)
+ );
}
public listCommunities(form: ListCommunitiesForm) {
}
public getFollowedCommunities() {
- let data = {auth: UserService.Instance.auth };
- this.subject.next(this.wsSendWrapper(UserOperation.GetFollowedCommunities, data));
+ let data = { auth: UserService.Instance.auth };
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.GetFollowedCommunities, data)
+ );
}
public listCategories() {
- this.subject.next(this.wsSendWrapper(UserOperation.ListCategories, undefined));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.ListCategories, undefined)
+ );
}
public createPost(postForm: PostForm) {
}
public getPost(postId: number) {
- let data = {id: postId, auth: UserService.Instance.auth };
+ let data = { id: postId, auth: UserService.Instance.auth };
this.subject.next(this.wsSendWrapper(UserOperation.GetPost, data));
}
public getCommunity(communityId: number) {
- let data = {id: communityId, auth: UserService.Instance.auth };
+ let data = { id: communityId, auth: UserService.Instance.auth };
this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data));
}
public getCommunityByName(name: string) {
- let data = {name: name, auth: UserService.Instance.auth };
+ let data = { name: name, auth: UserService.Instance.auth };
this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data));
}
public createComment(commentForm: CommentForm) {
this.setAuth(commentForm);
- this.subject.next(this.wsSendWrapper(UserOperation.CreateComment, commentForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.CreateComment, commentForm)
+ );
}
public editComment(commentForm: CommentForm) {
this.setAuth(commentForm);
- this.subject.next(this.wsSendWrapper(UserOperation.EditComment, commentForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.EditComment, commentForm)
+ );
}
public likeComment(form: CommentLikeForm) {
this.setAuth(form);
- this.subject.next(this.wsSendWrapper(UserOperation.CreateCommentLike, form));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.CreateCommentLike, form)
+ );
}
public saveComment(form: SaveCommentForm) {
public addModToCommunity(form: AddModToCommunityForm) {
this.setAuth(form);
- this.subject.next(this.wsSendWrapper(UserOperation.AddModToCommunity, form));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.AddModToCommunity, form)
+ );
}
public transferCommunity(form: TransferCommunityForm) {
this.setAuth(form);
- this.subject.next(this.wsSendWrapper(UserOperation.TransferCommunity, form));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.TransferCommunity, form)
+ );
}
public transferSite(form: TransferSiteForm) {
this.setAuth(form);
this.subject.next(this.wsSendWrapper(UserOperation.TransferSite, form));
}
-
+
public banUser(form: BanUserForm) {
this.setAuth(form);
this.subject.next(this.wsSendWrapper(UserOperation.BanUser, form));
public saveUserSettings(userSettingsForm: UserSettingsForm) {
this.setAuth(userSettingsForm);
- this.subject.next(this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm));
+ this.subject.next(
+ this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm)
+ );
}
public deleteAccount(form: DeleteAccountForm) {
obj.auth = UserService.Instance.auth;
if (obj.auth == null && throwErr) {
alert(i18n.t('not_logged_in'));
- throw "Not logged in";
+ throw 'Not logged in';
}
}
}
-window.onbeforeunload = (() => {
+window.onbeforeunload = () => {
WebSocketService.Instance.subject.unsubscribe();
WebSocketService.Instance.subject = null;
-});
-
+};
no_posts: 'Keine Beiträge.',
create_a_post: 'Einen Beitrag anlegen',
create_post: 'Beitrag anlegen',
- number_of_posts:'{{count}} Beiträge',
+ number_of_posts: '{{count}} Beiträge',
posts: 'Beiträge',
related_posts: 'Diese Beiträge könnten verwandt sein',
comments: 'Kommentare',
- number_of_comments:'{{count}} Kommentare',
+ number_of_comments: '{{count}} Kommentare',
remove_comment: 'Kommentar löschen',
communities: 'Communities',
create_a_community: 'Eine community anlegen',
create_community: 'Community anlegen',
remove_community: 'Community entfernen',
- subscribed_to_communities:'Abonnierte <1>communities</1>',
- trending_communities:'Trending <1>communities</1>',
+ subscribed_to_communities: 'Abonnierte <1>communities</1>',
+ trending_communities: 'Trending <1>communities</1>',
list_of_communities: 'Liste von communities',
community_reqs: 'Kleinbuchstaben, Großbuchstaben und keine Leerzeichen.',
edit: 'editieren',
create: 'anlegen',
username: 'Username',
email_or_username: 'Email oder Username',
- number_of_users:'{{count}} Benutzer',
- number_of_subscribers:'{{count}} Abonnenten',
- number_of_points:'{{count}} Punkte',
+ number_of_users: '{{count}} Benutzer',
+ number_of_subscribers: '{{count}} Abonnenten',
+ number_of_points: '{{count}} Punkte',
name: 'Name',
title: 'Titel',
category: 'Kategorie',
view: 'Ansicht',
logout: 'Ausloggen',
login_sign_up: 'Einloggen / Registrieren',
- notifications_error: 'Desktop-Benachrichtigungen sind in deinem browser nicht verfügbar. Versuche Firefox oder Chrome.',
+ notifications_error:
+ 'Desktop-Benachrichtigungen sind in deinem browser nicht verfügbar. Versuche Firefox oder Chrome.',
unread_messages: 'Ungelesene Nachrichten',
password: 'Passwort',
verify_password: 'Passwort überprüfen',
modified: 'verändert',
sponsors: 'Sponsoren',
sponsors_of_lemmy: 'Sponsoren von Lemmy',
- sponsor_message: 'Lemmy ist freie <1>Open-Source</1> Software, also ohne Werbung, Monetarisierung oder Venturekapital, Punkt. Deine Spenden gehen direkt an die Vollzeit Entwicklung des Projekts. Vielen Dank an die folgenden Personen:',
+ sponsor_message:
+ 'Lemmy ist freie <1>Open-Source</1> Software, also ohne Werbung, Monetarisierung oder Venturekapital, Punkt. Deine Spenden gehen direkt an die Vollzeit Entwicklung des Projekts. Vielen Dank an die folgenden Personen:',
support_on_patreon: 'Auf Patreon unterstützen',
- general_sponsors:'Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.',
+ general_sponsors:
+ 'Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
code: 'Code',
powered_by: 'Bereitgestellt durch',
- landing_0: 'Lemmy ist ein <1>Link Aggregator</1> / Reddit Alternative im <2>Fediverse</2>.<3></3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB</4>). Federation in das ActivityPub Netzwerk ist geplant. <5></5>Dies ist eine <6>sehr frühe Beta Version</6>, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7></7>Schlage neue Features vor oder melde Bugs <8>hier.</8><9></9>Gebaut mit <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy ist ein <1>Link Aggregator</1> / Reddit Alternative im <2>Fediverse</2>.<3></3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB</4>). Federation in das ActivityPub Netzwerk ist geplant. <5></5>Dies ist eine <6>sehr frühe Beta Version</6>, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7></7>Schlage neue Features vor oder melde Bugs <8>hier.</8><9></9>Gebaut mit <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'Nicht eingeloggt.',
community_ban: 'Du wurdest von dieser Community gebannt.',
site_ban: 'Du wurdest von dieser Seite gebannt',
couldnt_find_community: 'Konnte Community nicht finden.',
couldnt_update_community: 'Konnte Community nicht aktualisieren.',
community_already_exists: 'Community existiert bereits.',
- community_moderator_already_exists: 'Community Moderator existiert bereits.',
+ community_moderator_already_exists:
+ 'Community Moderator existiert bereits.',
community_follower_already_exists: 'Community Follower existiert bereits.',
community_user_already_banned: 'Community Nutzer schon gebannt.',
couldnt_create_post: 'Konnte Beitrag nicht anlegen.',
not_an_admin: 'Kein Administrator.',
site_already_exists: 'Seite existiert bereits.',
couldnt_update_site: 'Konnte Seite nicht aktualisieren.',
- couldnt_find_that_username_or_email: 'Konnte Username oder E-Mail nicht finden.',
+ couldnt_find_that_username_or_email:
+ 'Konnte Username oder E-Mail nicht finden.',
password_incorrect: 'Passwort falsch.',
passwords_dont_match: 'Passwörter stimmen nicht überein.',
admin_already_created: 'Entschuldigung, es gibt schon einen Administrator.',
user_already_exists: 'Nutzer existiert bereits.',
couldnt_update_user: 'Konnte Nutzer nicht aktualisieren',
- system_err_login: 'Systemfehler. Versuche dich aus- und wieder einzuloggen.',
+ system_err_login:
+ 'Systemfehler. Versuche dich aus- und wieder einzuloggen.',
},
-}
-
+};
delete: 'delete',
deleted: 'deleted',
delete_account: 'Delete Account',
- delete_account_confirm: 'Warning: this will permanently delete all your data. Enter your password to confirm.',
+ delete_account_confirm:
+ 'Warning: this will permanently delete all your data. Enter your password to confirm.',
restore: 'restore',
ban: 'ban',
ban_from_site: 'ban from site',
login_sign_up: 'Login / Sign up',
login: 'Login',
sign_up: 'Sign Up',
- notifications_error: 'Desktop notifications not available in your browser. Try Firefox or Chrome.',
+ notifications_error:
+ 'Desktop notifications not available in your browser. Try Firefox or Chrome.',
unread_messages: 'Unread Messages',
password: 'Password',
verify_password: 'Verify Password',
theme: 'Theme',
sponsors: 'Sponsors',
sponsors_of_lemmy: 'Sponsors of Lemmy',
- sponsor_message: 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
+ sponsor_message:
+ 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
support_on_patreon: 'Support on Patreon',
- general_sponsors: 'General Sponsors are those that pledged $10 to $39 to Lemmy.',
+ general_sponsors:
+ 'General Sponsors are those that pledged $10 to $39 to Lemmy.',
crypto: 'Crypto',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
yes: 'yes',
no: 'no',
powered_by: 'Powered by',
- landing_0: 'Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It\'s self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
not_logged_in: 'Not logged in.',
community_ban: 'You have been banned from this community.',
site_ban: 'You have been banned from the site',
- couldnt_create_comment: 'Couldn\'t create comment.',
- couldnt_like_comment: 'Couldn\'t like comment.',
- couldnt_update_comment: 'Couldn\'t update comment.',
- couldnt_save_comment: 'Couldn\'t save comment.',
+ couldnt_create_comment: "Couldn't create comment.",
+ couldnt_like_comment: "Couldn't like comment.",
+ couldnt_update_comment: "Couldn't update comment.",
+ couldnt_save_comment: "Couldn't save comment.",
no_comment_edit_allowed: 'Not allowed to edit comment.',
no_post_edit_allowed: 'Not allowed to edit post.',
no_community_edit_allowed: 'Not allowed to edit community.',
- couldnt_find_community: 'Couldn\'t find community.',
- couldnt_update_community: 'Couldn\'t update Community.',
+ couldnt_find_community: "Couldn't find community.",
+ couldnt_update_community: "Couldn't update Community.",
community_already_exists: 'Community already exists.',
community_moderator_already_exists: 'Community moderator already exists.',
community_follower_already_exists: 'Community follower already exists.',
community_user_already_banned: 'Community user already banned.',
- couldnt_create_post: 'Couldn\'t create post.',
- couldnt_like_post: 'Couldn\'t like post.',
- couldnt_find_post: 'Couldn\'t find post.',
- couldnt_get_posts: 'Couldn\'t get posts',
- couldnt_update_post: 'Couldn\'t update post',
- couldnt_save_post: 'Couldn\'t save post.',
+ couldnt_create_post: "Couldn't create post.",
+ couldnt_like_post: "Couldn't like post.",
+ couldnt_find_post: "Couldn't find post.",
+ couldnt_get_posts: "Couldn't get posts",
+ couldnt_update_post: "Couldn't update post",
+ couldnt_save_post: "Couldn't save post.",
no_slurs: 'No slurs.',
not_an_admin: 'Not an admin.',
site_already_exists: 'Site already exists.',
- couldnt_update_site: 'Couldn\'t update site.',
- couldnt_find_that_username_or_email: 'Couldn\'t find that username or email.',
+ couldnt_update_site: "Couldn't update site.",
+ couldnt_find_that_username_or_email:
+ "Couldn't find that username or email.",
password_incorrect: 'Password incorrect.',
passwords_dont_match: 'Passwords do not match.',
- admin_already_created: 'Sorry, there\'s already an admin.',
+ admin_already_created: "Sorry, there's already an admin.",
user_already_exists: 'User already exists.',
- couldnt_update_user: 'Couldn\'t update user.',
+ couldnt_update_user: "Couldn't update user.",
system_err_login: 'System error. Try logging out and back in.',
},
-}
-
+};
no_posts: 'Ne Poŝtoj.',
create_a_post: 'Verki Poŝton',
create_post: 'Verki Poŝton',
- number_of_posts:'{{count}} Poŝtoj',
+ number_of_posts: '{{count}} Poŝtoj',
posts: 'Poŝtoj',
related_posts: 'Tiuj poŝtoj eble rilatas',
cross_posts: 'Tiuj ligilo ankaŭ estas poŝtinta al:',
cross_post: 'laŭapoŝto',
comments: 'Komentoj',
- number_of_comments:'{{count}} Komentoj',
+ number_of_comments: '{{count}} Komentoj',
remove_comment: 'Fortiri Komentojn',
communities: 'Komunumoj',
users: 'Uzantoj',
create_a_community: 'Krei komunumon',
create_community: 'Krei Komunumon',
remove_community: 'Forigi Komunumon',
- subscribed_to_communities:'Abonita al <1>komunumoj</1>',
- trending_communities:'Furora <1>komunumoj</1>',
+ subscribed_to_communities: 'Abonita al <1>komunumoj</1>',
+ trending_communities: 'Furora <1>komunumoj</1>',
list_of_communities: 'Listo de komunumoj',
community_reqs: 'minusklaj leteroj, substrekoj, kaj ne spacetoj.',
edit: 'redakti',
create: 'krei',
username: 'Uzantnomo',
email_or_username: 'Retadreso aŭ Uzantnomo',
- number_of_users:'{{count}} Uzantoj',
- number_of_subscribers:'{{count}} Abonantoj',
- number_of_points:'{{count}} Voĉdonoj',
+ number_of_users: '{{count}} Uzantoj',
+ number_of_subscribers: '{{count}} Abonantoj',
+ number_of_points: '{{count}} Voĉdonoj',
name: 'Nomo',
title: 'Titolo',
category: 'Kategorio',
login_sign_up: 'Ensaluti / Registriĝi',
login: 'Ensaluti',
sign_up: 'Registriĝi',
- notifications_error: 'Labortablaj avizoj estas nehavebla en via retumilo. Provu Firefox-on aŭ Chrome-on.',
+ notifications_error:
+ 'Labortablaj avizoj estas nehavebla en via retumilo. Provu Firefox-on aŭ Chrome-on.',
unread_messages: 'Nelegitaj Mesaĝoj',
password: 'Pasvorto',
verify_password: 'Konfirmu Vian Pasvorton',
show_nsfw: 'Vidigi NSFW-an enhavon',
sponsors: 'Subtenantoj',
sponsors_of_lemmy: 'Subtenantoj de Lemmy',
- sponsor_message: 'Lemmy estas senpaga, <1>liberkoda</1> programaro. Tio signifas ne reklami, pagigi, aŭ riska kapitalo, ĉiam. Viaj donacoj rekte subtenas plentempan evoluon de la projekto. Dankon al tiuj homoj:',
+ sponsor_message:
+ 'Lemmy estas senpaga, <1>liberkoda</1> programaro. Tio signifas ne reklami, pagigi, aŭ riska kapitalo, ĉiam. Viaj donacoj rekte subtenas plentempan evoluon de la projekto. Dankon al tiuj homoj:',
support_on_patreon: 'Subteni per Patreon',
- general_sponsors:'Ĝeneralaj Subtenantoj estas tiuj ke donacis inter $10 kaj $39 al Lemmy.',
+ general_sponsors:
+ 'Ĝeneralaj Subtenantoj estas tiuj ke donacis inter $10 kaj $39 al Lemmy.',
crypto: 'Crypto',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
transfer_community: 'transdoni la komunumon',
transfer_site: 'transdoni la retejon',
powered_by: 'Konstruis per',
- landing_0: 'Lemmy estas <1>ligila agregatilo</1> / Reddit anstataŭo ke intenciĝas funkci en la <2>federacio-universo</2>.<3></3>ĝi estas mem-gastigebla, havas nuna-ĝisdatigajn komentarojn, kaj estas malgrandega (<4>~80kB</4>). Federacio en la ActivityPub-an reton estas planizita. <5></5>Estas <6>fruega beta versio</6>, kaj multaj trajtoj estas nune difektaj aŭ mankaj. <7></7>Sugestias novajn trajtojn aŭ raportas cimojn <8>ĉi tie.</8><9></9>Faris per <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy estas <1>ligila agregatilo</1> / Reddit anstataŭo ke intenciĝas funkci en la <2>federacio-universo</2>.<3></3>ĝi estas mem-gastigebla, havas nuna-ĝisdatigajn komentarojn, kaj estas malgrandega (<4>~80kB</4>). Federacio en la ActivityPub-an reton estas planizita. <5></5>Estas <6>fruega beta versio</6>, kaj multaj trajtoj estas nune difektaj aŭ mankaj. <7></7>Sugestias novajn trajtojn aŭ raportas cimojn <8>ĉi tie.</8><9></9>Faris per <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'Ne estas ensalutinta.',
community_ban: 'Vi estas forbarita de la komunumo.',
site_ban: 'Vi estas forbarita de la retejo',
not_an_admin: 'Ne estas administranto.',
site_already_exists: 'Retejo jam ekzistas.',
couldnt_update_site: 'Ne povis ĝisdatigi la retejon.',
- couldnt_find_that_username_or_email: 'Ne povis trovi tiun uzantnomon aŭ retadreson.',
+ couldnt_find_that_username_or_email:
+ 'Ne povis trovi tiun uzantnomon aŭ retadreson.',
password_incorrect: 'Pasvorto malĝustas.',
passwords_dont_match: 'Pasvortoj ne samas.',
admin_already_created: 'Pardonu, jam estas administranto.',
couldnt_update_user: 'Ne povis ĝisdatigi la uzanton.',
system_err_login: 'Sistema eraro. Provu elsaluti kaj ensaluti.',
},
-}
-
+};
no_posts: 'Sin publicaciones.',
create_a_post: 'Crear una publicación',
create_post: 'Crear Publicación',
- number_of_posts:'{{count}} Publicaciones',
+ number_of_posts: '{{count}} Publicaciones',
posts: 'Publicaciones',
related_posts: 'Estas publicaciones podrían estar relacionadas',
cross_posts: 'Este link también ha sido publicado en:',
cross_post: 'cross-post',
comments: 'Comentarios',
- number_of_comments:'{{count}} Comentarios',
+ number_of_comments: '{{count}} Comentarios',
remove_comment: 'Remover Comentarios',
communities: 'Comunidades',
users: 'Usuarios',
create_a_community: 'Crear una comunidad',
create_community: 'Crear Comunidad',
remove_community: 'Remover Comunidad',
- subscribed_to_communities:'Suscrito a <1>comunidades</1>',
- trending_communities:'<1>Comunidades</1> en tendencia',
+ subscribed_to_communities: 'Suscrito a <1>comunidades</1>',
+ trending_communities: '<1>Comunidades</1> en tendencia',
list_of_communities: 'Lista de comunidades',
- number_of_communities:'{{count}} Comunidades',
+ number_of_communities: '{{count}} Comunidades',
community_reqs: 'minúsculas, guión bajo, y sin espacios.',
edit: 'editar',
reply: 'responder',
delete: 'eliminar',
deleted: 'eliminado',
delete_account: 'Eliminar Cuenta',
- delete_account_confirm: 'Peligro: esta acción eliminará permanentemente tu información. ¿Estás seguro?',
+ delete_account_confirm:
+ 'Peligro: esta acción eliminará permanentemente tu información. ¿Estás seguro?',
restore: 'restaurar',
ban: 'expulsar',
ban_from_site: 'expulsar del sitio',
creator: 'creador',
username: 'Nombre de Usuario',
email_or_username: 'Correo electrónico o Nombre de Usuario',
- number_of_users:'{{count}} Usuarios',
- number_of_subscribers:'{{count}} Suscriptores',
- number_of_points:'{{count}} Puntos',
+ number_of_users: '{{count}} Usuarios',
+ number_of_subscribers: '{{count}} Suscriptores',
+ number_of_points: '{{count}} Puntos',
number_online: '{{count}} Usaurios En Línea',
name: 'Nombre',
title: 'Titulo',
login_sign_up: 'Iniciar sesión / Crear cuenta',
login: 'Iniciar sesión',
sign_up: 'Crear cuenta',
- notifications_error: 'Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.',
+ notifications_error:
+ 'Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.',
unread_messages: 'Mensajes no leídos',
password: 'Contraseña',
verify_password: 'Verificar contraseña',
theme: 'Tema',
sponsors: 'Patrocinadores',
sponsors_of_lemmy: 'Patrocinadores de Lemmy',
- sponsor_message: 'Lemmy es software libre y de <1>código abierto</1>, lo que significa que no tendrá publicidades, monetización, ni capitales emprendedores, nunca. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:',
+ sponsor_message:
+ 'Lemmy es software libre y de <1>código abierto</1>, lo que significa que no tendrá publicidades, monetización, ni capitales emprendedores, nunca. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:',
support_on_patreon: 'Apoyo en Patreon',
- general_sponsors:'Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.',
+ general_sponsors:
+ 'Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.',
crypto: 'Crypto',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
yes: 'sí',
no: 'no',
powered_by: 'Impulsado por',
- landing_0: 'Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'No has iniciado sesión.',
community_ban: 'Has sido expulsado de esta comunidad.',
site_ban: 'Has sido expulsado del sitio',
couldnt_find_community: 'No se pudo encontrar la comunidad.',
couldnt_update_community: 'No se pudo actualizar la comunidad.',
community_already_exists: 'Esta comunidad ya existe.',
- community_moderator_already_exists: 'Este moderador de la comunidad ya existe.',
- community_follower_already_exists: 'Este seguidor de la comunidad ya existe.',
- community_user_already_banned: 'Este usuario de la comunidad ya fue expulsado.',
+ community_moderator_already_exists:
+ 'Este moderador de la comunidad ya existe.',
+ community_follower_already_exists:
+ 'Este seguidor de la comunidad ya existe.',
+ community_user_already_banned:
+ 'Este usuario de la comunidad ya fue expulsado.',
couldnt_create_post: 'No se pudo crear la publicación.',
couldnt_like_post: 'No se pudo gustar la publicación.',
couldnt_find_post: 'No se pudo encontrar la publicación.',
not_an_admin: 'No es un administrador.',
site_already_exists: 'El sitio ya existe.',
couldnt_update_site: 'No se pudo actualizar el sitio.',
- couldnt_find_that_username_or_email: 'No se pudo encontrar ese nombre de usuario o correo electrónico.',
+ couldnt_find_that_username_or_email:
+ 'No se pudo encontrar ese nombre de usuario o correo electrónico.',
password_incorrect: 'Contraseña incorrecta.',
passwords_dont_match: 'Las contraseñas no coinciden.',
admin_already_created: 'Lo sentimos, ya hay un adminisitrador.',
user_already_exists: 'El usuario ya existe.',
couldnt_update_user: 'No se pudo actualizar el usuario.',
- system_err_login: 'Error del sistema. Intente cerrar sesión e ingresar de nuevo.',
+ system_err_login:
+ 'Error del sistema. Intente cerrar sesión e ingresar de nuevo.',
},
-}
-
+};
no_posts: 'Pas de sujets.',
create_a_post: 'Créer un sujet',
create_post: 'Créer le sujet',
- number_of_posts:'{{count}} Sujets',
+ number_of_posts: '{{count}} Sujets',
posts: 'Sujets',
related_posts: 'Ces sujets peuvent être corrélés',
cross_posts: 'Ce sujet a également été posté sur :',
cross_post: 'crossposter',
comments: 'Commentaires',
- number_of_comments:'{{count}} Commentaires',
+ number_of_comments: '{{count}} Commentaires',
remove_comment: 'Supprimer le commentaire',
communities: 'Communautés',
users: 'Utilisateurs',
create_a_community: 'Créer une communauté',
create_community: 'Créer la communauté',
remove_community: 'Supprimer la Communauté',
- subscribed_to_communities:'Abonné à ces <1>communautés</1>',
- trending_communities:'<1>Communauté</1> en vogue',
+ subscribed_to_communities: 'Abonné à ces <1>communautés</1>',
+ trending_communities: '<1>Communauté</1> en vogue',
list_of_communities: 'Liste des communautés',
number_of_communities: '{{count}} communautés',
community_reqs: 'en minuscule, sans espace et avec tiret du bas.',
delete: 'supprimer',
deleted: 'supprimé',
delete_account: 'Supprimer le compte',
- delete_account_confirm: 'Attention: cette action supprime toutes vos données de façons permanente. Entrez votre mot de passe pour confirmer.',
+ delete_account_confirm:
+ 'Attention: cette action supprime toutes vos données de façons permanente. Entrez votre mot de passe pour confirmer.',
restore: 'restaurer',
ban: 'bannir',
ban_from_site: 'bannir du site',
unsave: 'retirer',
create: 'créer',
creator: 'createur',
- username: 'Nom d\'utilisateur',
- email_or_username: 'Email ou Nom d\'utilisateur',
- number_of_users:'{{count}} Utilisateurs',
- number_of_subscribers:'{{count}} Abonnés',
- number_of_points:'{{count}} Points',
+ username: "Nom d'utilisateur",
+ email_or_username: "Email ou Nom d'utilisateur",
+ number_of_users: '{{count}} Utilisateurs',
+ number_of_subscribers: '{{count}} Abonnés',
+ number_of_points: '{{count}} Points',
number_online: '{{count}} Utilisateurs en ligne',
name: 'Nom',
title: 'Titre',
both: 'Les deux',
saved: 'Sauvegardé',
unsubscribe: 'Se désincrire',
- subscribe: 'S\'inscrire',
+ subscribe: "S'inscrire",
subscribed: 'Inscris',
prev: 'Précédent',
next: 'Suivant',
overview: 'Général',
view: 'Voir',
logout: 'Se déconnecter',
- login_sign_up: 'Se connecter / S\'inscrire',
+ login_sign_up: "Se connecter / S'inscrire",
login: 'Se connecter',
- sign_up: 'S\'inscrire',
- notifications_error: 'Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.',
+ sign_up: "S'inscrire",
+ notifications_error:
+ 'Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.',
unread_messages: 'Messages non-lu',
password: 'Mot de passe',
verify_password: 'Vérifiez le mot de passe',
copy_suggested_title: 'Ajouter le titre suggéré: {{title}}',
community: 'Communauté',
expand_here: 'Développer ici',
- subscribe_to_communities: 'S\'abonner à quelques <1>communautés</1>.',
+ subscribe_to_communities: "S'abonner à quelques <1>communautés</1>.",
chat: 'Chat',
recent_comments: 'Commentaires récents',
no_results: 'Pas de résultats.',
setup: 'Installation',
- lemmy_instance_setup: 'Installation d\'une instance Lemmy',
+ lemmy_instance_setup: "Installation d'une instance Lemmy",
setup_admin: 'Créer un administrateur',
your_site: 'votre site',
modified: 'modifié',
theme: 'Thème',
sponsors: 'Sponsors',
sponsors_of_lemmy: 'Sponsors de Lemmy',
- sponsor_message: 'Lemmy est gratuit et <1>open-source</1>, c\'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.',
+ sponsor_message:
+ "Lemmy est gratuit et <1>open-source</1>, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.",
support_on_patreon: 'Soutenir sur Patreon',
- general_sponsors:'General Sponsors are those that pledged $10 to $39 to Lemmy.',
+ general_sponsors:
+ 'General Sponsors are those that pledged $10 to $39 to Lemmy.',
crypto: 'Crypto',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
yes: 'oui',
no: 'non',
powered_by: 'Propulsé par',
- landing_0: 'Lemmy est un <1>aggrégateur de lien</1>, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse</2>.<3></3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB</4>). La fédération via Activitypub est prévue sur la feuille de route. <5></5>Lemmy est une <6>version beta très précoce</6>, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7></7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.</8><9></9>Crée avec <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
- not_logged_in: 'Vous n\'êtes pas connecté.',
+ landing_0:
+ 'Lemmy est un <1>aggrégateur de lien</1>, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse</2>.<3></3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB</4>). La fédération via Activitypub est prévue sur la feuille de route. <5></5>Lemmy est une <6>version beta très précoce</6>, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7></7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.</8><9></9>Crée avec <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ not_logged_in: "Vous n'êtes pas connecté.",
community_ban: 'Vous avez été banni de cette communauté.',
site_ban: 'Vous avez été banni du site',
couldnt_create_comment: 'Impossible de poster le commentaire.',
- couldnt_like_comment: 'Impossible d\'aimer le commentaire.',
+ couldnt_like_comment: "Impossible d'aimer le commentaire.",
couldnt_update_comment: 'Impossible de mettre à jour le commentaire.',
couldnt_save_comment: 'Impossible de sauvegarder le commentaire.',
- no_comment_edit_allowed: 'Vous n\'êtes pas autorisé à éditer ce commentaire.',
- no_post_edit_allowed: 'ous n\'êtes pas autorisé à éditer sujet.',
- no_community_edit_allowed: 'ous n\'êtes pas autorisé à éditer cette communauté.',
+ no_comment_edit_allowed:
+ "Vous n'êtes pas autorisé à éditer ce commentaire.",
+ no_post_edit_allowed: "ous n'êtes pas autorisé à éditer sujet.",
+ no_community_edit_allowed:
+ "ous n'êtes pas autorisé à éditer cette communauté.",
couldnt_find_community: 'Impossible de trouver cette communauté.',
- couldnt_update_community: 'Impossible d\'éditer cette communauté.',
+ couldnt_update_community: "Impossible d'éditer cette communauté.",
community_already_exists: 'Cette communauté existe déjà.',
community_moderator_already_exists: 'Ce membre est déjà modérateur.',
community_follower_already_exists: 'Ce membre est déjà abonné.',
community_user_already_banned: 'Ce membre est déjà banni.',
couldnt_create_post: 'Impossible dae créer le sujet.',
- couldnt_like_post: 'Impossible d\'aimer le sujet.',
+ couldnt_like_post: "Impossible d'aimer le sujet.",
couldnt_find_post: 'Impossible de trouver le sujet.',
- couldnt_get_posts: 'Impossible d\'obtenir les sujets',
+ couldnt_get_posts: "Impossible d'obtenir les sujets",
couldnt_update_post: 'Impossible de mettre à jour le sujet',
couldnt_save_post: 'Impossible de sauvegarder le sujet.',
- no_slurs: 'Pas d\'insultes.',
+ no_slurs: "Pas d'insultes.",
not_an_admin: 'Pas administrateur.',
site_already_exists: 'Le site existe déjà.',
couldnt_update_site: 'Impossible de mettre à jour le site.',
- couldnt_find_that_username_or_email: 'Impossible de trouver cet utilisateur ou cet email.',
+ couldnt_find_that_username_or_email:
+ 'Impossible de trouver cet utilisateur ou cet email.',
password_incorrect: 'Mot de passe incorrect.',
passwords_dont_match: 'Les mots de passes ne correspondent pas..',
admin_already_created: 'Désolé, il y a déjà un admin.',
- user_already_exists: 'L\'utilisateur existe déjà.',
- couldnt_update_user: 'Impossible de mettre à jour l\'utilisateur.',
- system_err_login: 'Erreur système. Essayez de vous déconneter puis de vous reconnecter.',
+ user_already_exists: "L'utilisateur existe déjà.",
+ couldnt_update_user: "Impossible de mettre à jour l'utilisateur.",
+ system_err_login:
+ 'Erreur système. Essayez de vous déconneter puis de vous reconnecter.',
},
-}
+};
no_posts: 'Geen posts.',
create_a_post: 'Plaats een post',
create_post: 'Plaats post',
- number_of_posts:'{{count}} posts',
+ number_of_posts: '{{count}} posts',
posts: 'posts',
related_posts: 'Deze posts kunnen gerelateerd zijn',
cross_posts: 'Deze link is ook geplaatst in:',
cross_post: 'cross-post',
comments: 'Reacties',
- number_of_comments:'{{count}} reacties',
+ number_of_comments: '{{count}} reacties',
remove_comment: 'Verwijder reactie',
communities: 'Communities',
users: 'Gebruikers',
create_a_community: 'Maak een community',
create_community: 'Maak community',
remove_community: 'Verwijder community',
- subscribed_to_communities:'Geabonneerd op <1>communities</1>',
- trending_communities:'Populaire <1>communities</1>',
+ subscribed_to_communities: 'Geabonneerd op <1>communities</1>',
+ trending_communities: 'Populaire <1>communities</1>',
list_of_communities: 'Lijst van communities',
- number_of_communities:'{{count}} communities',
+ number_of_communities: '{{count}} communities',
community_reqs: 'kleine letters, onderstrepingsteken en geen spaties',
edit: 'bewerk',
reply: 'reageer',
create: 'maak',
username: 'Gebruikersnaam',
email_or_username: 'E-mail of gebruikersnaam',
- number_of_users:'{{count}} gebruikers',
- number_of_subscribers:'{{count}} abonnees',
- number_of_points:'{{count}} punten',
+ number_of_users: '{{count}} gebruikers',
+ number_of_subscribers: '{{count}} abonnees',
+ number_of_points: '{{count}} punten',
name: 'Naam',
title: 'Titel',
category: 'Categorie',
login_sign_up: 'Log in / Aanmelden',
login: 'Log in',
sign_up: 'Aanmelden',
- notifications_error: 'Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.',
+ notifications_error:
+ 'Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.',
unread_messages: 'Ongelezen berichten',
password: 'Wachtwoord',
verify_password: 'Herhaal wachtwoord',
show_nsfw: 'Laat NSFW-inhoud zien',
sponsors: 'Sponsoren',
sponsors_of_lemmy: 'Sponsoren van Lemmy',
- sponsor_message: 'Lemmy is vrije, <1>open-source</1> software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:',
+ sponsor_message:
+ 'Lemmy is vrije, <1>open-source</1> software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:',
support_on_patreon: 'Ondersteun op Patreon',
- general_sponsors:'Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.',
+ general_sponsors:
+ 'Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.',
crypto: 'Cryptovaluta',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
yes: 'ja',
no: 'nee',
powered_by: 'Mogelijk gemaakt door',
- landing_0: 'Lemmy is een <1>linkverzameler</1> / reddit-alternatief, bedoeld om in de <2>fediverse</2> te werken.<3></3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB</4>). Federatie in hte ActivityPub-netwerk is gepland. <5></5>Dit is een <6>erg vroege bèta-versie</6>, en een hoop functies zijn stuk of afwezig. <7></7>Stel nieuwe functies voor of meldt fouten <8>hier</8>.<9></9>Gemaakt met <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> en <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy is een <1>linkverzameler</1> / reddit-alternatief, bedoeld om in de <2>fediverse</2> te werken.<3></3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB</4>). Federatie in hte ActivityPub-netwerk is gepland. <5></5>Dit is een <6>erg vroege bèta-versie</6>, en een hoop functies zijn stuk of afwezig. <7></7>Stel nieuwe functies voor of meldt fouten <8>hier</8>.<9></9>Gemaakt met <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> en <13>Typescript</13>.',
not_logged_in: 'Niet ingelogd.',
community_ban: 'Je bent verbannen uit deze community.',
site_ban: 'Je bent verbannen van deze site.',
not_an_admin: 'Niet een beheerder.',
site_already_exists: 'Site bestaat al.',
couldnt_update_site: 'Kon site niet bijwerken.',
- couldnt_find_that_username_or_email: 'Kon gebruikersnaam of e-mailadres niet vinden.',
+ couldnt_find_that_username_or_email:
+ 'Kon gebruikersnaam of e-mailadres niet vinden.',
password_incorrect: 'Wachtwoord incorrect.',
passwords_dont_match: 'Wachtwoorden zijn niet gelijk.',
admin_already_created: 'Sorry, er is al een beheerder.',
user_already_exists: 'Gebruiker bestaat al.',
couldnt_update_user: 'Kon gebruiker niet bijwerken.',
- system_err_login: 'Systeemfout. Probeer uit te loggen en weer in te loggen.',
+ system_err_login:
+ 'Systeemfout. Probeer uit te loggen en weer in te loggen.',
},
-}
+};
no_posts: 'Нет записей.',
create_a_post: 'Создать запись',
create_post: 'Создать запись',
- number_of_posts:'{{count}} записей',
+ number_of_posts: '{{count}} записей',
posts: 'Записи',
related_posts: 'Эти записи могут быть связаны',
comments: 'Комментарии',
- number_of_comments:'{{count}} комментариев',
+ number_of_comments: '{{count}} комментариев',
remove_comment: 'Удалить комментарий',
communities: 'Сообщества',
users: 'Пользователи',
create_a_community: 'Создать сообщество',
create_community: 'Создать сообщество',
remove_community: 'Удалить сообщество',
- subscribed_to_communities:'Подписаны на <1>сообщества</1>',
- trending_communities:'<1>Сообщества</1> в тренде',
+ subscribed_to_communities: 'Подписаны на <1>сообщества</1>',
+ trending_communities: '<1>Сообщества</1> в тренде',
list_of_communities: 'Список сообществ',
community_reqs: 'строчными буквами, подчеркиваниями и без пробелов.',
edit: 'редактировать',
create: 'создать',
username: 'Имя пользователя',
email_or_username: 'Электронная почта или имя пользователя',
- number_of_users:'{{count}} пользователей',
- number_of_subscribers:'{{count}} подписчиков',
- number_of_points:'{{count}} баллов',
+ number_of_users: '{{count}} пользователей',
+ number_of_subscribers: '{{count}} подписчиков',
+ number_of_points: '{{count}} баллов',
name: 'Имя',
title: 'Название',
category: 'Категория',
login_sign_up: 'Войти / Регистрация',
login: 'Авторизация',
sign_up: 'Регистрация',
- notifications_error: 'Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.',
+ notifications_error:
+ 'Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.',
unread_messages: 'Непрочитанные сообщения',
password: 'Пароль',
verify_password: 'Повторите пароль',
show_nsfw: 'Показывать NSFW-контент',
sponsors: 'Спонсоры',
sponsors_of_lemmy: 'Спонсоры Lemmy',
- sponsor_message: 'Lemmy это бесплатное, <1>открытое</1> программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:',
+ sponsor_message:
+ 'Lemmy это бесплатное, <1>открытое</1> программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:',
support_on_patreon: 'Поддержать на Patreon',
- general_sponsors:'Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.',
+ general_sponsors:
+ 'Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.',
crypto: 'Крипто',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
code: 'Код',
joined: 'Присоединился',
powered_by: 'Работает на',
- landing_0: 'Lemmy - это <1>агрегатор ссылок</1> / альтернатива reddit, предназначенный для работы в <2>федиверсе</2>.<3></3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб</4>). Федерация в сети ActivityPub находится в разработке. <5></5>Это <6>очень ранняя бета-версия</6>, и многие функции в настоящее время сломаны или отсутствуют. <7></7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.</8><9></9>Сделано на <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy - это <1>агрегатор ссылок</1> / альтернатива reddit, предназначенный для работы в <2>федиверсе</2>.<3></3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб</4>). Федерация в сети ActivityPub находится в разработке. <5></5>Это <6>очень ранняя бета-версия</6>, и многие функции в настоящее время сломаны или отсутствуют. <7></7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.</8><9></9>Сделано на <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'Не авторизованы.',
community_ban: 'Вы были заблокированы на данном сообществе.',
site_ban: 'Вы были заблокированы на данном сайте',
not_an_admin: 'Не администратор.',
site_already_exists: 'Сайт уже существует.',
couldnt_update_site: 'Не получилось обновить сайт.',
- couldnt_find_that_username_or_email: 'Не получилось найти данное имя пользователя или электронную почту.',
+ couldnt_find_that_username_or_email:
+ 'Не получилось найти данное имя пользователя или электронную почту.',
password_incorrect: 'Неверный пароль.',
passwords_dont_match: 'Пароли не совпадают.',
admin_already_created: 'Извините, уже есть администратор.',
user_already_exists: 'Пользователь уже существует.',
couldnt_update_user: 'Не получилось обновить пользователя.',
- system_err_login: 'Системная ошибка. Попробуйте выйти из системы и вернуться обратно.',
+ system_err_login:
+ 'Системная ошибка. Попробуйте выйти из системы и вернуться обратно.',
},
-}
-
+};
delete: 'radera',
deleted: 'raderad',
delete_account: 'Ta bort konto',
- delete_account_confirm: 'Varning: den här åtgärden kommer radera alla dina data permanent. Är du säker?',
+ delete_account_confirm:
+ 'Varning: den här åtgärden kommer radera alla dina data permanent. Är du säker?',
restore: 'återställ',
ban: 'blockera',
ban_from_site: 'blockera från webbplats',
login_sign_up: 'Logga in eller skapa konto',
login: 'Logga in',
sign_up: 'Skapa konto',
- notifications_error: 'Din webbläsare har inte stöd för skrivbordsaviseringar. Testa Firefox eller Chrome.',
+ notifications_error:
+ 'Din webbläsare har inte stöd för skrivbordsaviseringar. Testa Firefox eller Chrome.',
unread_messages: 'Olästa meddelanden',
password: 'Lösenord',
verify_password: 'Bekräfta lösenord',
theme: 'Utseende',
sponsors: 'Sponsorer',
sponsors_of_lemmy: 'Lemmys sponsorer',
- sponsor_message: 'Lemmy är fri mjukvara med <1>öppen källkod</1>, vilket innebär att ingen reklam, vinstindrivning eller venturekapital förekommer, någonsin. Dina donationer går direkt till att stöda utvecklingen av projektet. Stort tack till följande personer:',
+ sponsor_message:
+ 'Lemmy är fri mjukvara med <1>öppen källkod</1>, vilket innebär att ingen reklam, vinstindrivning eller venturekapital förekommer, någonsin. Dina donationer går direkt till att stöda utvecklingen av projektet. Stort tack till följande personer:',
support_on_patreon: 'Stöd på Patreon',
- general_sponsors: 'Allmänna sponsorer är dem som givit mellan 10 och 39\u00a0dollar till Lemmy.',
+ general_sponsors:
+ 'Allmänna sponsorer är dem som givit mellan 10 och 39\u00a0dollar till Lemmy.',
crypto: 'Kryptovaluta',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
yes: 'ja',
no: 'nej',
powered_by: 'Drivs av',
- landing_0: 'Lemmy är en <1>länksamlare</1> och alternativ till reddit, ämnad att fungera i <2>Fediversumet</2>.<3></3>Lemmy kan drivas av vem som helst, har kommentarstrådar som updateras i realid och är mycket liten (<4>ca 80\u00a0kB</4>). Federering med ActivityPub-nätverket är planerat. <5></5>Detta är en <6>väldigt tidig betaversion</6> och många funktioner saknas därför eller är trasiga.<7></7>Föreslå nya funktioner eller anmäl buggar <8>här</8>.<9></9>Skapad i <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> och <13>Typescript</13>.',
+ landing_0:
+ 'Lemmy är en <1>länksamlare</1> och alternativ till reddit, ämnad att fungera i <2>Fediversumet</2>.<3></3>Lemmy kan drivas av vem som helst, har kommentarstrådar som updateras i realid och är mycket liten (<4>ca 80\u00a0kB</4>). Federering med ActivityPub-nätverket är planerat. <5></5>Detta är en <6>väldigt tidig betaversion</6> och många funktioner saknas därför eller är trasiga.<7></7>Föreslå nya funktioner eller anmäl buggar <8>här</8>.<9></9>Skapad i <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> och <13>Typescript</13>.',
not_logged_in: 'Inte inloggad.',
community_ban: 'Du har blockerats från den här gemenskapen.',
site_ban: 'Du har blockerats från webbplatsen.',
not_an_admin: 'Inte en administratör.',
site_already_exists: 'Webbplatsen finns redan.',
couldnt_update_site: 'Kunde inte uppdatera webbplats.',
- couldnt_find_that_username_or_email: 'Kunde inte hitta det användarnamnet eller e-postadressen.',
+ couldnt_find_that_username_or_email:
+ 'Kunde inte hitta det användarnamnet eller e-postadressen.',
password_incorrect: 'Ogiltigt lösenord.',
passwords_dont_match: 'Lösenorden stämmer inte överens.',
admin_already_created: 'Beklagar, men det finns redan en administratör.',
couldnt_update_user: 'Kunde inte uppdatera användare.',
system_err_login: 'Systemfel. Försök att logga ut och sedan in igen.',
},
-}
+};
no_posts: '没有帖子.',
create_a_post: '创建新帖子',
create_post: '创建帖子',
- number_of_posts:'{{count}} 帖子',
+ number_of_posts: '{{count}} 帖子',
posts: '帖子',
related_posts: '相关的帖子',
comments: '评论',
- number_of_comments:'{{count}} 评论',
+ number_of_comments: '{{count}} 评论',
remove_comment: '移除评论',
communities: '节点',
create_a_community: '创建新节点',
create_community: '创建节点',
remove_community: '移除节点',
- subscribed_to_communities:'订阅新 <1>节点</1>',
- trending_communities:'<1>节点</1>趋势',
+ subscribed_to_communities: '订阅新 <1>节点</1>',
+ trending_communities: '<1>节点</1>趋势',
list_of_communities: '节点列表',
community_reqs: '包含小写与下划线且没有空格的字符串.',
edit: '编辑',
create: '创建',
username: '用户名',
email_or_username: '邮箱或用户名',
- number_of_users:'{{count}} 用户',
- number_of_subscribers:'{{count}} 订阅',
- number_of_points:'{{count}} 分',
+ number_of_users: '{{count}} 用户',
+ number_of_subscribers: '{{count}} 订阅',
+ number_of_points: '{{count}} 分',
name: '名字',
title: '标题',
category: '分类',
modified: '修改',
sponsors: 'Sponsors',
sponsors_of_lemmy: 'Sponsors of Lemmy',
- sponsor_message: 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
+ sponsor_message:
+ 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
support_on_patreon: 'Support on Patreon',
- general_sponsors:'General Sponsors are those that pledged $10 to $39 to Lemmy.',
+ general_sponsors:
+ 'General Sponsors are those that pledged $10 to $39 to Lemmy.',
crypto: '加密',
bitcoin: '比特币',
ethereum: '以太币',
code: '代码',
joined: '已加入',
powered_by: '保留所有权利',
- landing_0: 'Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It\'s self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
+ landing_0:
+ "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
not_logged_in: '未登录.',
community_ban: '你被此节点禁止.',
site_ban: '你被此站点禁止',
couldnt_update_user: '不可以更新用户.',
system_err_login: '系统错误. 尝试注销再登录',
},
-}
-
+};
import 'moment/locale/ru';
import 'moment/locale/nl';
-import { UserOperation, Comment, User, SortType, ListingType, SearchType } from './interfaces';
+import {
+ UserOperation,
+ Comment,
+ User,
+ SortType,
+ ListingType,
+ SearchType,
+} from './interfaces';
import * as markdown_it from 'markdown-it';
import * as markdownitEmoji from 'markdown-it-emoji/light';
import * as markdown_it_container from 'markdown-it-container';
export const repoUrl = 'https://github.com/dessalines/lemmy';
export const markdownHelpUrl = 'https://commonmark.org/help/';
-export const postRefetchSeconds: number = 60*1000;
+export const postRefetchSeconds: number = 60 * 1000;
export const fetchLimit: number = 20;
export const mentionDropdownFetchLimit = 6;
-export function randomStr() {return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10)}
+export function randomStr() {
+ return Math.random()
+ .toString(36)
+ .replace(/[^a-z]+/g, '')
+ .substr(2, 10);
+}
export function msgOp(msg: any): UserOperation {
let opStr: string = msg.op;
export const md = new markdown_it({
html: false,
linkify: true,
- typographer: true
-}).use(markdown_it_container, 'spoiler', {
- validate: function(params: any) {
- return params.trim().match(/^spoiler\s+(.*)$/);
- },
-
- render: function (tokens: any, idx: any) {
- var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
-
- if (tokens[idx].nesting === 1) {
- // opening tag
- return '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n';
-
- } else {
- // closing tag
- return '</details>\n';
- }
- }
-}).use(markdownitEmoji, {
- defs: objectFlip(emojiShortName)
-});
+ typographer: true,
+})
+ .use(markdown_it_container, 'spoiler', {
+ validate: function(params: any) {
+ return params.trim().match(/^spoiler\s+(.*)$/);
+ },
+
+ render: function(tokens: any, idx: any) {
+ var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
+
+ if (tokens[idx].nesting === 1) {
+ // opening tag
+ return (
+ '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n'
+ );
+ } else {
+ // closing tag
+ return '</details>\n';
+ }
+ },
+ })
+ .use(markdownitEmoji, {
+ defs: objectFlip(emojiShortName),
+ });
md.renderer.rules.emoji = function(token, idx) {
return twemoji.parse(token[idx].content);
let now: Date = new Date();
let hoursElapsed: number = (now.getTime() - date.getTime()) / 36e5;
- let rank = (10000 * Math.log10(Math.max(1, 3 + comment.score))) / Math.pow(hoursElapsed + 2, 1.8);
+ let rank =
+ (10000 * Math.log10(Math.max(1, 3 + comment.score))) /
+ Math.pow(hoursElapsed + 2, 1.8);
// console.log(`Comment: ${comment.content}\nRank: ${rank}\nScore: ${comment.score}\nHours: ${hoursElapsed}`);
}
export function mdToHtml(text: string) {
- return {__html: md.render(text)};
+ return { __html: md.render(text) };
}
-export function getUnixTime(text: string): number {
- return text ? new Date(text).getTime()/1000 : undefined;
+export function getUnixTime(text: string): number {
+ return text ? new Date(text).getTime() / 1000 : undefined;
}
-export function addTypeInfo<T>(arr: Array<T>, name: string): Array<{type_: string, data: T}> {
- return arr.map(e => {return {type_: name, data: e}});
+export function addTypeInfo<T>(
+ arr: Array<T>,
+ name: string
+): Array<{ type_: string; data: T }> {
+ return arr.map(e => {
+ return { type_: name, data: e };
+ });
}
-export function canMod(user: User, modIds: Array<number>, creator_id: number, onSelf: boolean = false): boolean {
+export function canMod(
+ user: User,
+ modIds: Array<number>,
+ creator_id: number,
+ onSelf: boolean = false
+): boolean {
// You can do moderator actions only on the mods added after you.
if (user) {
let yourIndex = modIds.findIndex(id => id == user.id);
if (yourIndex == -1) {
return false;
- } else {
+ } else {
// onSelf +1 on mod actions not for yourself, IE ban, remove, etc
- modIds = modIds.slice(0, yourIndex+(onSelf ? 0 : 1));
+ modIds = modIds.slice(0, yourIndex + (onSelf ? 0 : 1));
return !modIds.includes(creator_id);
}
} else {
return modIds.includes(creator_id);
}
-
-var imageRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:png|jpg|jpeg|gif|png|svg))`);
+var imageRegex = new RegExp(
+ `(http)?s?:?(\/\/[^"']*\.(?:png|jpg|jpeg|gif|png|svg))`
+);
var videoRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:mp4))`);
export function isImage(url: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
-
export function routeSortTypeToEnum(sort: string): SortType {
if (sort == 'new') {
return SortType.New;
return data;
}
-export function debounce(func: any, wait: number = 500, immediate: boolean = false) {
+export function debounce(
+ func: any,
+ wait: number = 500,
+ immediate: boolean = false
+) {
// 'private' variable for instance
// The returned function will be able to reference this due to closure.
// Each call to the returned function will share this common timer.
return function() {
// reference the context and args for the setTimeout function
var context = this,
- args = arguments;
-
- // Should the function be called now? If immediate is true
- // and not already in a timeout then the answer is: Yes
- var callNow = immediate && !timeout;
-
- // This is the basic debounce behaviour where you can call this
- // function several times, but it will only execute once
- // [before or after imposing a delay].
- // Each time the returned function is called, the timer starts over.
- clearTimeout(timeout);
-
- // Set the new timeout
- timeout = setTimeout(function() {
-
- // Inside the timeout function, clear the timeout variable
- // which will let the next execution run when in 'immediate' mode
- timeout = null;
-
- // Check if the function already ran with the immediate flag
- if (!immediate) {
- // Call the original function with apply
- // apply lets you define the 'this' object as well as the arguments
- // (both captured before setTimeout)
- func.apply(context, args);
- }
- }, wait);
-
- // Immediate mode and no wait timer? Execute the function..
- if (callNow) func.apply(context, args);
- }
+ args = arguments;
+
+ // Should the function be called now? If immediate is true
+ // and not already in a timeout then the answer is: Yes
+ var callNow = immediate && !timeout;
+
+ // This is the basic debounce behaviour where you can call this
+ // function several times, but it will only execute once
+ // [before or after imposing a delay].
+ // Each time the returned function is called, the timer starts over.
+ clearTimeout(timeout);
+
+ // Set the new timeout
+ timeout = setTimeout(function() {
+ // Inside the timeout function, clear the timeout variable
+ // which will let the next execution run when in 'immediate' mode
+ timeout = null;
+
+ // Check if the function already ran with the immediate flag
+ if (!immediate) {
+ // Call the original function with apply
+ // apply lets you define the 'this' object as well as the arguments
+ // (both captured before setTimeout)
+ func.apply(context, args);
+ }
+ }, wait);
+
+ // Immediate mode and no wait timer? Execute the function..
+ if (callNow) func.apply(context, args);
+ };
}
export function getLanguage(): string {
- return (navigator.language || navigator.userLanguage);
+ return navigator.language || navigator.userLanguage;
}
export function objectFlip(obj: any) {
const ret = {};
- Object.keys(obj).forEach((key) => {
+ Object.keys(obj).forEach(key => {
ret[obj[key]] = key;
});
return ret;
return lang;
}
-export const themes = ['litera', 'minty', 'solar', 'united', 'cyborg','darkly', 'journal', 'sketchy'];
+export const themes = [
+ 'litera',
+ 'minty',
+ 'solar',
+ 'united',
+ 'cyborg',
+ 'darkly',
+ 'journal',
+ 'sketchy',
+];
export function setTheme(theme: string = 'darkly') {
- for (var i=0; i < themes.length; i++) {
-
+ for (var i = 0; i < themes.length; i++) {
let styleSheet = document.getElementById(themes[i]);
if (themes[i] == theme) {
- styleSheet.removeAttribute("disabled");
+ styleSheet.removeAttribute('disabled');
} else {
- styleSheet.setAttribute("disabled", "disabled");
- }
+ styleSheet.setAttribute('disabled', 'disabled');
+ }
}
}
-export let version: string = "v0.3.0.4-0-gce10a34";
\ No newline at end of file
+export let version: string = 'v0.3.0.4-0-gce10a34';