CreatePostLike,
CreatePostReport,
DeletePost,
+ EditPost,
FeaturePost,
Language,
LockPost,
} from "lemmy-js-client";
import { getExternalHost, getHttpBase } from "../../env";
import { i18n } from "../../i18next";
-import { BanType, PostFormParams, PurgeType } from "../../interfaces";
-import { UserService, WebSocketService } from "../../services";
+import { BanType, PostFormParams, PurgeType, VoteType } from "../../interfaces";
+import { UserService } from "../../services";
import {
- amAdmin,
- amCommunityCreator,
- amMod,
- canAdmin,
- canMod,
- canShare,
futureDaysToUnixTime,
hostname,
- isAdmin,
- isBanned,
isImage,
- isMod,
isVideo,
mdNoImages,
mdToHtml,
mdToHtmlInline,
- myAuth,
+ myAuthRequired,
+ newVote,
numToSI,
relTags,
setupTippy,
- share,
showScores,
- wsClient,
} from "../../utils";
+import { canShare } from "../../utils/browser/can-share";
+import { share } from "../../utils/browser/share";
+import { amAdmin } from "../../utils/roles/am-admin";
+import { amCommunityCreator } from "../../utils/roles/am-community-creator";
+import { amMod } from "../../utils/roles/am-mod";
+import { canAdmin } from "../../utils/roles/can-admin";
+import { canMod } from "../../utils/roles/can-mod";
+import { isAdmin } from "../../utils/roles/is-admin";
+import { isBanned } from "../../utils/roles/is-banned";
+import { isMod } from "../../utils/roles/is-mod";
import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
import { PictrsImage } from "../common/pictrs-image";
showBody: boolean;
showReportDialog: boolean;
reportReason?: string;
- my_vote?: number;
- score: number;
- upvotes: number;
- downvotes: number;
+ upvoteLoading: boolean;
+ downvoteLoading: boolean;
+ reportLoading: boolean;
+ blockLoading: boolean;
+ lockLoading: boolean;
+ deleteLoading: boolean;
+ removeLoading: boolean;
+ saveLoading: boolean;
+ featureCommunityLoading: boolean;
+ featureLocalLoading: boolean;
+ banLoading: boolean;
+ addModLoading: boolean;
+ addAdminLoading: boolean;
+ transferLoading: boolean;
}
interface PostListingProps {
post_view: PostView;
- duplicates?: PostView[];
+ crossPosts?: PostView[];
moderators?: CommunityModeratorView[];
admins?: PersonView[];
allLanguages: Language[];
enableDownvotes?: boolean;
enableNsfw?: boolean;
viewOnly?: boolean;
+ onPostEdit(form: EditPost): void;
+ onPostVote(form: CreatePostLike): void;
+ onPostReport(form: CreatePostReport): void;
+ onBlockPerson(form: BlockPerson): void;
+ onLockPost(form: LockPost): void;
+ onDeletePost(form: DeletePost): void;
+ onRemovePost(form: RemovePost): void;
+ onSavePost(form: SavePost): void;
+ onFeaturePost(form: FeaturePost): void;
+ onPurgePerson(form: PurgePerson): void;
+ onPurgePost(form: PurgePost): void;
+ onBanPersonFromCommunity(form: BanFromCommunity): void;
+ onBanPerson(form: BanPerson): void;
+ onAddModToCommunity(form: AddModToCommunity): void;
+ onAddAdmin(form: AddAdmin): void;
+ onTransferCommunity(form: TransferCommunity): void;
}
export class PostListing extends Component<PostListingProps, PostListingState> {
showRemoveDialog: false,
showPurgeDialog: false,
purgeType: PurgeType.Person,
- purgeLoading: false,
showBanDialog: false,
banType: BanType.Community,
removeData: false,
showMoreMobile: false,
showBody: false,
showReportDialog: false,
- my_vote: this.props.post_view.my_vote,
- score: this.props.post_view.counts.score,
- upvotes: this.props.post_view.counts.upvotes,
- downvotes: this.props.post_view.counts.downvotes,
+ upvoteLoading: false,
+ downvoteLoading: false,
+ purgeLoading: false,
+ reportLoading: false,
+ blockLoading: false,
+ lockLoading: false,
+ deleteLoading: false,
+ removeLoading: false,
+ saveLoading: false,
+ featureCommunityLoading: false,
+ featureLocalLoading: false,
+ banLoading: false,
+ addModLoading: false,
+ addAdminLoading: false,
+ transferLoading: false,
};
constructor(props: any, context: any) {
super(props, context);
- this.handlePostLike = this.handlePostLike.bind(this);
- this.handlePostDisLike = this.handlePostDisLike.bind(this);
this.handleEditPost = this.handleEditPost.bind(this);
this.handleEditCancel = this.handleEditCancel.bind(this);
}
componentWillReceiveProps(nextProps: PostListingProps) {
- this.setState({
- my_vote: nextProps.post_view.my_vote,
- upvotes: nextProps.post_view.counts.upvotes,
- downvotes: nextProps.post_view.counts.downvotes,
- score: nextProps.post_view.counts.score,
- });
- if (this.props.post_view.post.id !== nextProps.post_view.post.id) {
- this.setState({ imageExpanded: false });
+ if (this.props !== nextProps) {
+ this.setState({
+ upvoteLoading: false,
+ downvoteLoading: false,
+ purgeLoading: false,
+ reportLoading: false,
+ blockLoading: false,
+ lockLoading: false,
+ deleteLoading: false,
+ removeLoading: false,
+ saveLoading: false,
+ featureCommunityLoading: false,
+ featureLocalLoading: false,
+ banLoading: false,
+ addModLoading: false,
+ addAdminLoading: false,
+ transferLoading: false,
+ imageExpanded: false,
+ });
}
}
+ get postView(): PostView {
+ return this.props.post_view;
+ }
+
render() {
- const post = this.props.post_view.post;
+ const post = this.postView.post;
return (
- <div className="post-listing">
+ <div className="post-listing mt-2">
{!this.state.showEdit ? (
<>
{this.listing()}
{this.state.imageExpanded && !this.props.hideImage && this.img}
- {post.url && this.showBody && post.embed_title && (
+ {post.url && this.state.showBody && post.embed_title && (
<MetadataCard post={post} />
)}
{this.showBody && this.body()}
</>
) : (
- <div className="col-12">
- <PostForm
- post_view={this.props.post_view}
- onEdit={this.handleEditPost}
- onCancel={this.handleEditCancel}
- enableNsfw={this.props.enableNsfw}
- enableDownvotes={this.props.enableDownvotes}
- allLanguages={this.props.allLanguages}
- siteLanguages={this.props.siteLanguages}
- />
- </div>
+ <PostForm
+ post_view={this.postView}
+ crossPosts={this.props.crossPosts}
+ onEdit={this.handleEditPost}
+ onCancel={this.handleEditCancel}
+ enableNsfw={this.props.enableNsfw}
+ enableDownvotes={this.props.enableDownvotes}
+ allLanguages={this.props.allLanguages}
+ siteLanguages={this.props.siteLanguages}
+ />
)}
</div>
);
}
body() {
- let body = this.props.post_view.post.body;
+ const body = this.postView.post.body;
return body ? (
- <div className="col-12 card my-2 p-2">
+ <article id="postContent" className="col-12 card my-2 p-2">
{this.state.viewSource ? (
<pre>{body}</pre>
) : (
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(body)} />
)}
- </div>
+ </article>
) : (
<></>
);
}
get img() {
- let src = this.imageSrc;
- return src ? (
+ return this.imageSrc ? (
<>
<div className="offset-sm-3 my-2 d-none d-sm-block">
- <a href={src} className="d-inline-block">
- <PictrsImage src={src} />
+ <a href={this.imageSrc} className="d-inline-block">
+ <PictrsImage src={this.imageSrc} />
</a>
</div>
<div className="my-2 d-block d-sm-none">
className="d-inline-block"
onClick={linkEvent(this, this.handleImageExpandClick)}
>
- <PictrsImage src={src} />
+ <PictrsImage src={this.imageSrc} />
</a>
</div>
</>
}
imgThumb(src: string) {
- let post_view = this.props.post_view;
+ const post_view = this.postView;
return (
<PictrsImage
src={src}
}
get imageSrc(): string | undefined {
- let post = this.props.post_view.post;
- let url = post.url;
- let thumbnail = post.thumbnail_url;
+ const post = this.postView.post;
+ const url = post.url;
+ const thumbnail = post.thumbnail_url;
if (url && isImage(url)) {
if (url.includes("pictrs")) {
}
thumbnail() {
- let post = this.props.post_view.post;
- let url = post.url;
- let thumbnail = post.thumbnail_url;
+ const post = this.postView.post;
+ const url = post.url;
+ const thumbnail = post.thumbnail_url;
if (!this.props.hideImage && url && isImage(url) && this.imageSrc) {
return (
}
createdLine() {
- let post_view = this.props.post_view;
- let url = post_view.post.url;
- let body = post_view.post.body;
+ const post_view = this.postView;
+ const url = post_view.post.url;
+ const body = post_view.post.body;
return (
<ul className="list-inline mb-1 text-muted small">
<li className="list-inline-item">
</span>
)}
{this.props.showCommunity && (
- <span>
- <span className="mx-1"> {i18n.t("to")} </span>
- <CommunityLink community={post_view.community} />
- </span>
+ <>
+ {" "}
+ {i18n.t("to")} <CommunityLink community={post_view.community} />
+ </>
)}
</li>
{post_view.post.language_id !== 0 && (
<div className={`vote-bar col-1 pr-0 small text-center`}>
<button
className={`btn-animate btn btn-link p-0 ${
- this.state.my_vote === 1 ? "text-info" : "text-muted"
+ this.postView.my_vote == 1 ? "text-info" : "text-muted"
}`}
- onClick={this.handlePostLike}
+ onClick={linkEvent(this, this.handleUpvote)}
data-tippy-content={i18n.t("upvote")}
aria-label={i18n.t("upvote")}
- aria-pressed={this.state.my_vote === 1}
+ aria-pressed={this.postView.my_vote === 1}
>
- <Icon icon="arrow-up1" classes="upvote" />
+ {this.state.upvoteLoading ? (
+ <Spinner />
+ ) : (
+ <Icon icon="arrow-up1" classes="upvote" />
+ )}
</button>
{showScores() ? (
<div
- className={`unselectable pointer font-weight-bold text-muted px-1`}
+ className={`unselectable pointer font-weight-bold text-muted px-1 post-score`}
data-tippy-content={this.pointsTippy}
>
- {numToSI(this.state.score)}
+ {numToSI(this.postView.counts.score)}
</div>
) : (
<div className="p-1"></div>
{this.props.enableDownvotes && (
<button
className={`btn-animate btn btn-link p-0 ${
- this.state.my_vote === -1 ? "text-danger" : "text-muted"
+ this.postView.my_vote == -1 ? "text-danger" : "text-muted"
}`}
- onClick={this.handlePostDisLike}
+ onClick={linkEvent(this, this.handleDownvote)}
data-tippy-content={i18n.t("downvote")}
aria-label={i18n.t("downvote")}
- aria-pressed={this.state.my_vote === -1}
+ aria-pressed={this.postView.my_vote === -1}
>
- <Icon icon="arrow-down1" classes="downvote" />
+ {this.state.downvoteLoading ? (
+ <Spinner />
+ ) : (
+ <Icon icon="arrow-down1" classes="downvote" />
+ )}
</button>
)}
</div>
}
get postLink() {
- let post = this.props.post_view.post;
+ const post = this.postView.post;
return (
<Link
- className={`d-inline-block ${
+ className={`d-inline ${
!post.featured_community && !post.featured_local
? "text-body"
: "text-primary"
to={`/post/${post.id}`}
title={i18n.t("comments")}
>
- <div
- className="d-inline-block"
+ <span
+ className="d-inline"
dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
/>
</Link>
}
postTitleLine() {
- let post = this.props.post_view.post;
- let url = post.url;
+ const post = this.postView.post;
+ const url = post.url;
return (
<div className="post-title overflow-hidden">
- <h5>
- {url ? (
- this.props.showBody ? (
- <a
- className={`d-inline-block ${
- !post.featured_community && !post.featured_local
- ? "text-body"
- : "text-primary"
- }`}
- href={url}
- title={url}
- rel={relTags}
- >
- <div
- className="d-inline-block"
- dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
- />
- </a>
- ) : (
- this.postLink
- )
+ <h5 className="d-inline">
+ {url && this.props.showBody ? (
+ <a
+ className={
+ !post.featured_community && !post.featured_local
+ ? "text-body"
+ : "text-primary"
+ }
+ href={url}
+ title={url}
+ rel={relTags}
+ dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
+ ></a>
) : (
this.postLink
)}
- {(url && isImage(url)) ||
- (post.thumbnail_url && (
- <button
- className="btn btn-link text-monospace text-muted small d-inline-block"
- data-tippy-content={i18n.t("expand_here")}
- onClick={linkEvent(this, this.handleImageExpandClick)}
- >
- <Icon
- icon={
- !this.state.imageExpanded ? "plus-square" : "minus-square"
- }
- classes="icon-inline"
- />
- </button>
- ))}
- {post.removed && (
- <small className="ml-2 text-muted font-italic">
- {i18n.t("removed")}
- </small>
- )}
- {post.deleted && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("deleted")}
- >
- <Icon icon="trash" classes="icon-inline text-danger" />
- </small>
- )}
- {post.locked && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("locked")}
- >
- <Icon icon="lock" classes="icon-inline text-danger" />
- </small>
- )}
- {post.featured_community && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("featured")}
- >
- <Icon icon="pin" classes="icon-inline text-primary" />
- </small>
- )}
- {post.featured_local && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("featured")}
- >
- <Icon icon="pin" classes="icon-inline text-secondary" />
- </small>
- )}
- {post.nsfw && (
- <small className="ml-2 text-muted font-italic">
- {i18n.t("nsfw")}
- </small>
- )}
</h5>
+ {(url && isImage(url)) ||
+ (post.thumbnail_url && (
+ <button
+ className="btn btn-link text-monospace text-muted small d-inline-block"
+ data-tippy-content={i18n.t("expand_here")}
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ <Icon
+ icon={
+ !this.state.imageExpanded ? "plus-square" : "minus-square"
+ }
+ classes="icon-inline"
+ />
+ </button>
+ ))}
+ {post.removed && (
+ <small className="ml-2 badge text-bg-secondary">
+ {i18n.t("removed")}
+ </small>
+ )}
+ {post.deleted && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("deleted")}
+ >
+ <Icon icon="trash" classes="icon-inline text-danger" />
+ </small>
+ )}
+ {post.locked && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("locked")}
+ >
+ <Icon icon="lock" classes="icon-inline text-danger" />
+ </small>
+ )}
+ {post.featured_community && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("featured")}
+ >
+ <Icon icon="pin" classes="icon-inline text-primary" />
+ </small>
+ )}
+ {post.featured_local && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("featured")}
+ >
+ <Icon icon="pin" classes="icon-inline text-secondary" />
+ </small>
+ )}
+ {post.nsfw && (
+ <small className="ml-2 badge text-bg-danger">{i18n.t("nsfw")}</small>
+ )}
</div>
);
}
duplicatesLine() {
- let dupes = this.props.duplicates;
+ const dupes = this.props.crossPosts;
return dupes && dupes.length > 0 ? (
<ul className="list-inline mb-1 small text-muted">
<>
}
commentsLine(mobile = false) {
- let post = this.props.post_view.post;
+ const post = this.postView.post;
return (
- <div className="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
+ <div className="d-flex align-items-center justify-content-start flex-wrap text-muted font-weight-bold">
{this.commentsButton}
{canShare() && (
<button
- className="btn btn-link"
+ className="btn btn-sm btn-link"
onClick={linkEvent(this, this.handleShare)}
type="button"
>
{mobile && !this.props.viewOnly && this.mobileVotes}
{UserService.Instance.myUserInfo &&
!this.props.viewOnly &&
- this.postActions(mobile)}
+ this.postActions()}
</div>
);
}
- postActions(mobile = false) {
+ get hasAdvancedButtons() {
+ return (
+ this.myPost ||
+ (this.showBody && this.postView.post.body) ||
+ amMod(this.props.moderators) ||
+ amAdmin() ||
+ this.canMod_ ||
+ this.canAdmin_
+ );
+ }
+
+ postActions() {
// Possible enhancement: Priority+ pattern instead of just hard coding which get hidden behind the show more button.
// Possible enhancement: Make each button a component.
- let post_view = this.props.post_view;
+ const post_view = this.postView;
return (
<>
{this.saveButton}
{this.crossPostButton}
- {mobile && this.showMoreButton}
- {(!mobile || this.state.showAdvanced) && (
- <>
- {!this.myPost && (
- <>
- {this.reportButton}
- {this.blockButton}
- </>
- )}
- {this.myPost && (this.showBody || this.state.showAdvanced) && (
- <>
- {this.editButton}
- {this.deleteButton}
- </>
- )}
- </>
- )}
- {this.state.showAdvanced && (
- <>
- {this.showBody && post_view.post.body && this.viewSourceButton}
- {/* Any mod can do these, not limited to hierarchy*/}
- {(amMod(this.props.moderators) || amAdmin()) && (
- <>
- {this.lockButton}
- {this.featureButton}
- </>
- )}
- {(this.canMod_ || this.canAdmin_) && <>{this.modRemoveButton}</>}
- </>
+
+ {this.showBody && post_view.post.body && this.viewSourceButton}
+
+ {this.hasAdvancedButtons && (
+ <div className="dropdown">
+ <button
+ className="btn btn-link btn-animate text-muted py-0 dropdown-toggle"
+ onClick={linkEvent(this, this.handleShowAdvanced)}
+ data-tippy-content={i18n.t("more")}
+ data-bs-toggle="dropdown"
+ aria-expanded="false"
+ aria-controls="advancedButtonsDropdown"
+ aria-label={i18n.t("more")}
+ >
+ <Icon icon="more-vertical" inline />
+ </button>
+
+ <ul className="dropdown-menu" id="advancedButtonsDropdown">
+ {!this.myPost ? (
+ <>
+ <li>{this.reportButton}</li>
+ <li>{this.blockButton}</li>
+ </>
+ ) : (
+ <>
+ <li>{this.editButton}</li>
+ <li>{this.deleteButton}</li>
+ </>
+ )}
+
+ {/* Any mod can do these, not limited to hierarchy*/}
+ {(amMod(this.props.moderators) || amAdmin()) && (
+ <>
+ <li>
+ <hr className="dropdown-divider" />
+ </li>
+ <li>{this.lockButton}</li>
+ {this.featureButtons}
+ </>
+ )}
+
+ {(this.canMod_ || this.canAdmin_) && (
+ <li>{this.modRemoveButton}</li>
+ )}
+ </ul>
+ </div>
)}
- {!mobile && this.showMoreButton}
</>
);
}
get commentsButton() {
- let post_view = this.props.post_view;
+ const post_view = this.postView;
return (
- <button className="btn btn-link text-muted py-0 pl-0">
- <Link
- className="text-muted"
- title={i18n.t("number_of_comments", {
+ <Link
+ className="btn btn-link text-muted pl-0 text-muted"
+ title={i18n.t("number_of_comments", {
+ count: Number(post_view.counts.comments),
+ formattedCount: Number(post_view.counts.comments),
+ })}
+ to={`/post/${post_view.post.id}?scrollToComments=true`}
+ >
+ <Icon icon="message-square" classes="mr-1" inline />
+ <span className="mr-2">
+ {i18n.t("number_of_comments", {
count: Number(post_view.counts.comments),
- formattedCount: Number(post_view.counts.comments),
+ formattedCount: numToSI(post_view.counts.comments),
})}
- to={`/post/${post_view.post.id}?scrollToComments=true`}
- >
- <Icon icon="message-square" classes="mr-1" inline />
- <span className="mr-2">
- {i18n.t("number_of_comments", {
- count: Number(post_view.counts.comments),
- formattedCount: numToSI(post_view.counts.comments),
- })}
+ </span>
+ {this.unreadCount && (
+ <span className="small text-warning">
+ ({this.unreadCount} {i18n.t("new")})
</span>
- {this.unreadCount && (
- <span className="small text-warning">
- ({this.unreadCount} {i18n.t("new")})
- </span>
- )}
- </Link>
- </button>
+ )}
+ </Link>
);
}
get unreadCount(): number | undefined {
- let pv = this.props.post_view;
+ const pv = this.postView;
return pv.unread_comments == pv.counts.comments || pv.unread_comments == 0
? undefined
: pv.unread_comments;
get mobileVotes() {
// TODO: make nicer
- let tippy = showScores() ? { "data-tippy-content": this.pointsTippy } : {};
+ const tippy = showScores()
+ ? { "data-tippy-content": this.pointsTippy }
+ : {};
return (
<>
<div>
<button
className={`btn-animate btn py-0 px-1 ${
- this.state.my_vote === 1 ? "text-info" : "text-muted"
+ this.postView.my_vote === 1 ? "text-info" : "text-muted"
}`}
{...tippy}
- onClick={this.handlePostLike}
+ onClick={linkEvent(this, this.handleUpvote)}
aria-label={i18n.t("upvote")}
- aria-pressed={this.state.my_vote === 1}
+ aria-pressed={this.postView.my_vote === 1}
>
- <Icon icon="arrow-up1" classes="icon-inline small" />
- {showScores() && (
- <span className="ml-2">{numToSI(this.state.upvotes)}</span>
+ {this.state.upvoteLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon icon="arrow-up1" classes="icon-inline small" />
+ {showScores() && (
+ <span className="ml-2">
+ {numToSI(this.postView.counts.upvotes)}
+ </span>
+ )}
+ </>
)}
</button>
{this.props.enableDownvotes && (
<button
className={`ml-2 btn-animate btn py-0 px-1 ${
- this.state.my_vote === -1 ? "text-danger" : "text-muted"
+ this.postView.my_vote === -1 ? "text-danger" : "text-muted"
}`}
- onClick={this.handlePostDisLike}
+ onClick={linkEvent(this, this.handleDownvote)}
{...tippy}
aria-label={i18n.t("downvote")}
- aria-pressed={this.state.my_vote === -1}
+ aria-pressed={this.postView.my_vote === -1}
>
- <Icon icon="arrow-down1" classes="icon-inline small" />
- {showScores() && (
- <span
- className={classNames("ml-2", {
- invisible: this.state.downvotes === 0,
- })}
- >
- {numToSI(this.state.downvotes)}
- </span>
+ {this.state.downvoteLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon icon="arrow-down1" classes="icon-inline small" />
+ {showScores() && (
+ <span
+ className={classNames("ml-2", {
+ invisible: this.postView.counts.downvotes === 0,
+ })}
+ >
+ {numToSI(this.postView.counts.downvotes)}
+ </span>
+ )}
+ </>
)}
</button>
)}
}
get saveButton() {
- let saved = this.props.post_view.saved;
- let label = saved ? i18n.t("unsave") : i18n.t("save");
+ const saved = this.postView.saved;
+ const label = saved ? i18n.t("unsave") : i18n.t("save");
return (
<button
className="btn btn-link btn-animate text-muted py-0"
data-tippy-content={label}
aria-label={label}
>
- <Icon
- icon="star"
- classes={classNames({ "text-warning": saved })}
- inline
- />
+ {this.state.saveLoading ? (
+ <Spinner />
+ ) : (
+ <Icon
+ icon="star"
+ classes={classNames({ "text-warning": saved })}
+ inline
+ />
+ )}
</button>
);
}
search: "",
}}
title={i18n.t("cross_post")}
+ data-tippy-content={i18n.t("cross_post")}
+ aria-label={i18n.t("cross_post")}
>
<Icon icon="copy" inline />
</Link>
get reportButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleShowReportDialog)}
- data-tippy-content={i18n.t("show_report_dialog")}
aria-label={i18n.t("show_report_dialog")}
>
- <Icon icon="flag" inline />
+ <Icon classes="mr-1" icon="flag" inline />
+ {i18n.t("create_report")}
</button>
);
}
get blockButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleBlockUserClick)}
- data-tippy-content={i18n.t("block_user")}
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
+ onClick={linkEvent(this, this.handleBlockPersonClick)}
aria-label={i18n.t("block_user")}
>
- <Icon icon="slash" inline />
+ {this.state.blockLoading ? (
+ <Spinner />
+ ) : (
+ <Icon classes="mr-1" icon="slash" inline />
+ )}
+ {i18n.t("block_user")}
</button>
);
}
get editButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleEditClick)}
- data-tippy-content={i18n.t("edit")}
aria-label={i18n.t("edit")}
>
- <Icon icon="edit" inline />
+ <Icon classes="mr-1" icon="edit" inline />
+ {i18n.t("edit")}
</button>
);
}
get deleteButton() {
- let deleted = this.props.post_view.post.deleted;
- let label = !deleted ? i18n.t("delete") : i18n.t("restore");
+ const deleted = this.postView.post.deleted;
+ const label = !deleted ? i18n.t("delete") : i18n.t("restore");
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleDeleteClick)}
- data-tippy-content={label}
aria-label={label}
>
- <Icon
- icon="trash"
- classes={classNames({ "text-danger": deleted })}
- inline
- />
- </button>
- );
- }
-
- get showMoreButton() {
- return (
- <button
- className="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleShowAdvanced)}
- data-tippy-content={i18n.t("more")}
- aria-label={i18n.t("more")}
- >
- <Icon icon="more-vertical" inline />
+ {this.state.deleteLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon
+ icon="trash"
+ classes={classNames("mr-1", { "text-danger": deleted })}
+ inline
+ />
+ {label}
+ </>
+ )}
</button>
);
}
}
get lockButton() {
- let locked = this.props.post_view.post.locked;
- let label = locked ? i18n.t("unlock") : i18n.t("lock");
+ const locked = this.postView.post.locked;
+ const label = locked ? i18n.t("unlock") : i18n.t("lock");
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleModLock)}
- data-tippy-content={label}
aria-label={label}
>
- <Icon
- icon="lock"
- classes={classNames({ "text-danger": locked })}
- inline
- />
+ {this.state.lockLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon
+ icon="lock"
+ classes={classNames("mr-1", { "text-danger": locked })}
+ inline
+ />
+ {label}
+ </>
+ )}
</button>
);
}
- get featureButton() {
- const featuredCommunity = this.props.post_view.post.featured_community;
+ get featureButtons() {
+ const featuredCommunity = this.postView.post.featured_community;
const labelCommunity = featuredCommunity
? i18n.t("unfeature_from_community")
: i18n.t("feature_in_community");
- const featuredLocal = this.props.post_view.post.featured_local;
+ const featuredLocal = this.postView.post.featured_local;
const labelLocal = featuredLocal
? i18n.t("unfeature_from_local")
: i18n.t("feature_in_local");
return (
- <span>
- <button
- className="btn btn-link btn-animate text-muted py-0 pl-0"
- onClick={linkEvent(this, this.handleModFeaturePostCommunity)}
- data-tippy-content={labelCommunity}
- aria-label={labelCommunity}
- >
- <Icon
- icon="pin"
- classes={classNames({ "text-success": featuredCommunity })}
- inline
- />{" "}
- Community
- </button>
- {amAdmin() && (
+ <>
+ <li>
<button
- className="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleModFeaturePostLocal)}
- data-tippy-content={labelLocal}
- aria-label={labelLocal}
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
+ onClick={linkEvent(this, this.handleModFeaturePostCommunity)}
+ data-tippy-content={labelCommunity}
+ aria-label={labelCommunity}
>
- <Icon
- icon="pin"
- classes={classNames({ "text-success": featuredLocal })}
- inline
- />{" "}
- Local
+ {this.state.featureCommunityLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon
+ icon="pin"
+ classes={classNames("mr-1", {
+ "text-success": featuredCommunity,
+ })}
+ inline
+ />
+ {i18n.t("community")}
+ </>
+ )}
</button>
- )}
- </span>
+ </li>
+ <li>
+ {amAdmin() && (
+ <button
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
+ onClick={linkEvent(this, this.handleModFeaturePostLocal)}
+ data-tippy-content={labelLocal}
+ aria-label={labelLocal}
+ >
+ {this.state.featureLocalLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon
+ icon="pin"
+ classes={classNames("mr-1", {
+ "text-success": featuredLocal,
+ })}
+ inline
+ />
+ {i18n.t("local")}
+ </>
+ )}
+ </button>
+ )}
+ </li>
+ </>
);
}
get modRemoveButton() {
- let removed = this.props.post_view.post.removed;
+ const removed = this.postView.post.removed;
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(
this,
!removed ? this.handleModRemoveShow : this.handleModRemoveSubmit
)}
>
{/* TODO: Find an icon for this. */}
- {!removed ? i18n.t("remove") : i18n.t("restore")}
+ {this.state.removeLoading ? (
+ <Spinner />
+ ) : !removed ? (
+ i18n.t("remove")
+ ) : (
+ i18n.t("restore")
+ )}
</button>
);
}
*/
userActionsLine() {
// TODO: make nicer
- let post_view = this.props.post_view;
+ const post_view = this.postView;
return (
this.state.showAdvanced && (
<>
)}
aria-label={i18n.t("unban")}
>
- {i18n.t("unban")}
+ {this.state.banLoading ? <Spinner /> : i18n.t("unban")}
</button>
))}
{!post_view.creator_banned_from_community && (
: i18n.t("appoint_as_mod")
}
>
- {this.creatorIsMod_
- ? i18n.t("remove_as_mod")
- : i18n.t("appoint_as_mod")}
+ {this.state.addModLoading ? (
+ <Spinner />
+ ) : this.creatorIsMod_ ? (
+ i18n.t("remove_as_mod")
+ ) : (
+ i18n.t("appoint_as_mod")
+ )}
</button>
)}
</>
aria-label={i18n.t("yes")}
onClick={linkEvent(this, this.handleTransferCommunity)}
>
- {i18n.t("yes")}
+ {this.state.transferLoading ? <Spinner /> : i18n.t("yes")}
</button>
<button
className="btn btn-link btn-animate text-muted py-0 d-inline-block"
onClick={linkEvent(this, this.handleModBanSubmit)}
aria-label={i18n.t("unban_from_site")}
>
- {i18n.t("unban_from_site")}
+ {this.state.banLoading ? (
+ <Spinner />
+ ) : (
+ i18n.t("unban_from_site")
+ )}
</button>
)}
<button
: i18n.t("appoint_as_admin")
}
>
- {this.creatorIsAdmin_
- ? i18n.t("remove_as_admin")
- : i18n.t("appoint_as_admin")}
+ {this.state.addAdminLoading ? (
+ <Spinner />
+ ) : this.creatorIsAdmin_ ? (
+ i18n.t("remove_as_admin")
+ ) : (
+ i18n.t("appoint_as_admin")
+ )}
</button>
)}
</>
}
removeAndBanDialogs() {
- let post = this.props.post_view;
- let purgeTypeText =
+ const post = this.postView;
+ const purgeTypeText =
this.state.purgeType == PurgeType.Post
? i18n.t("purge_post")
: `${i18n.t("purge")} ${post.creator.name}`;
className="btn btn-secondary"
aria-label={i18n.t("remove_post")}
>
- {i18n.t("remove_post")}
+ {this.state.removeLoading ? <Spinner /> : i18n.t("remove_post")}
</button>
</form>
)}
className="btn btn-secondary"
aria-label={i18n.t("ban")}
>
- {i18n.t("ban")} {post.creator.name}
+ {this.state.banLoading ? (
+ <Spinner />
+ ) : (
+ <span>
+ {i18n.t("ban")} {post.creator.name}
+ </span>
+ )}
</button>
</div>
</form>
className="btn btn-secondary"
aria-label={i18n.t("create_report")}
>
- {i18n.t("create_report")}
+ {this.state.reportLoading ? <Spinner /> : i18n.t("create_report")}
</button>
</form>
)}
className="btn btn-secondary"
aria-label={purgeTypeText}
>
- {purgeTypeText}
+ {this.state.purgeLoading ? <Spinner /> : { purgeTypeText }}
</button>
)}
</form>
}
mobileThumbnail() {
- let post = this.props.post_view.post;
+ const post = this.postView.post;
return post.thumbnail_url || (post.url && isImage(post.url)) ? (
<div className="row">
<div className={`${this.state.imageExpanded ? "col-12" : "col-8"}`}>
}
showMobilePreview() {
- let body = this.props.post_view.post.body;
+ const { body, id } = this.postView.post;
+
return !this.showBody && body ? (
- <div className="md-div mb-1 preview-lines">{body}</div>
+ <Link className="text-body" to={`/post/${id}`}>
+ <div className="md-div mb-1 preview-lines">{body}</div>
+ </Link>
) : (
<></>
);
<>
{/* The mobile view*/}
<div className="d-block d-sm-none">
- <div className="row">
+ <article className="row post-container">
<div className="col-12">
{this.createdLine()}
{this.duplicatesLine()}
{this.removeAndBanDialogs()}
</div>
- </div>
+ </article>
</div>
{/* The larger view*/}
<div className="d-none d-sm-block">
- <div className="row">
+ <article className="row post-container">
{!this.props.viewOnly && this.voteBar()}
- <div className="col-sm-2 pr-0">
+ <div className="col-sm-2 pr-0 post-media">
<div className="">{this.thumbnail()}</div>
</div>
<div className="col-12 col-sm-9">
</div>
</div>
</div>
- </div>
+ </article>
</div>
</>
);
private get myPost(): boolean {
return (
- this.props.post_view.creator.id ==
+ this.postView.creator.id ==
UserService.Instance.myUserInfo?.local_user_view.person.id
);
}
-
- handlePostLike(event: any) {
- event.preventDefault();
- if (!UserService.Instance.myUserInfo) {
- this.context.router.history.push(`/login`);
- }
-
- let myVote = this.state.my_vote;
- let newVote = myVote == 1 ? 0 : 1;
-
- if (myVote == 1) {
- this.setState({
- score: this.state.score - 1,
- upvotes: this.state.upvotes - 1,
- });
- } else if (myVote == -1) {
- this.setState({
- score: this.state.score + 2,
- upvotes: this.state.upvotes + 1,
- downvotes: this.state.downvotes - 1,
- });
- } else {
- this.setState({
- score: this.state.score + 1,
- upvotes: this.state.upvotes + 1,
- });
- }
-
- this.setState({ my_vote: newVote });
-
- let auth = myAuth();
- if (auth) {
- let form: CreatePostLike = {
- post_id: this.props.post_view.post.id,
- score: newVote,
- auth,
- };
-
- WebSocketService.Instance.send(wsClient.likePost(form));
- this.setState(this.state);
- }
- setupTippy();
- }
-
- handlePostDisLike(event: any) {
- event.preventDefault();
- if (!UserService.Instance.myUserInfo) {
- this.context.router.history.push(`/login`);
- }
-
- let myVote = this.state.my_vote;
- let newVote = myVote == -1 ? 0 : -1;
-
- if (myVote == 1) {
- this.setState({
- score: this.state.score - 2,
- upvotes: this.state.upvotes - 1,
- downvotes: this.state.downvotes + 1,
- });
- } else if (myVote == -1) {
- this.setState({
- score: this.state.score + 1,
- downvotes: this.state.downvotes - 1,
- });
- } else {
- this.setState({
- score: this.state.score - 1,
- downvotes: this.state.downvotes + 1,
- });
- }
-
- this.setState({ my_vote: newVote });
-
- let auth = myAuth();
- if (auth) {
- let form: CreatePostLike = {
- post_id: this.props.post_view.post.id,
- score: newVote,
- auth,
- };
-
- WebSocketService.Instance.send(wsClient.likePost(form));
- this.setState(this.state);
- }
- setupTippy();
- }
-
handleEditClick(i: PostListing) {
i.setState({ showEdit: true });
}
this.setState({ showEdit: false });
}
- // The actual editing is done in the recieve for post
- handleEditPost() {
+ // The actual editing is done in the receive for post
+ handleEditPost(form: EditPost) {
this.setState({ showEdit: false });
+ this.props.onPostEdit(form);
}
handleShare(i: PostListing) {
handleReportSubmit(i: PostListing, event: any) {
event.preventDefault();
- let auth = myAuth();
- let reason = i.state.reportReason;
- if (auth && reason) {
- let form: CreatePostReport = {
- post_id: i.props.post_view.post.id,
- reason,
- auth,
- };
- WebSocketService.Instance.send(wsClient.createPostReport(form));
-
- i.setState({ showReportDialog: false });
- }
+ i.setState({ reportLoading: true });
+ i.props.onPostReport({
+ post_id: i.postView.post.id,
+ reason: i.state.reportReason ?? "",
+ auth: myAuthRequired(),
+ });
}
- handleBlockUserClick(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let blockUserForm: BlockPerson = {
- person_id: i.props.post_view.creator.id,
- block: true,
- auth,
- };
- WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
- }
+ handleBlockPersonClick(i: PostListing) {
+ i.setState({ blockLoading: true });
+ i.props.onBlockPerson({
+ person_id: i.postView.creator.id,
+ block: true,
+ auth: myAuthRequired(),
+ });
}
handleDeleteClick(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let deleteForm: DeletePost = {
- post_id: i.props.post_view.post.id,
- deleted: !i.props.post_view.post.deleted,
- auth,
- };
- WebSocketService.Instance.send(wsClient.deletePost(deleteForm));
- }
+ i.setState({ deleteLoading: true });
+ i.props.onDeletePost({
+ post_id: i.postView.post.id,
+ deleted: !i.postView.post.deleted,
+ auth: myAuthRequired(),
+ });
}
handleSavePostClick(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let saved =
- i.props.post_view.saved == undefined ? true : !i.props.post_view.saved;
- let form: SavePost = {
- post_id: i.props.post_view.post.id,
- save: saved,
- auth,
- };
- WebSocketService.Instance.send(wsClient.savePost(form));
- }
+ i.setState({ saveLoading: true });
+ i.props.onSavePost({
+ post_id: i.postView.post.id,
+ save: !i.postView.saved,
+ auth: myAuthRequired(),
+ });
}
get crossPostParams(): PostFormParams {
const queryParams: PostFormParams = {};
- const { name, url } = this.props.post_view.post;
+ const { name, url } = this.postView.post;
queryParams.name = name;
}
crossPostBody(): string | undefined {
- let post = this.props.post_view.post;
- let body = post.body;
+ const post = this.postView.post;
+ const body = post.body;
return body
? `${i18n.t("cross_posted_from")} ${post.ap_id}\n\n${body.replace(
handleModRemoveSubmit(i: PostListing, event: any) {
event.preventDefault();
-
- let auth = myAuth();
- if (auth) {
- let form: RemovePost = {
- post_id: i.props.post_view.post.id,
- removed: !i.props.post_view.post.removed,
- reason: i.state.removeReason,
- auth,
- };
- WebSocketService.Instance.send(wsClient.removePost(form));
- i.setState({ showRemoveDialog: false });
- }
+ i.setState({ removeLoading: true });
+ i.props.onRemovePost({
+ post_id: i.postView.post.id,
+ removed: !i.postView.post.removed,
+ auth: myAuthRequired(),
+ });
}
handleModLock(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: LockPost = {
- post_id: i.props.post_view.post.id,
- locked: !i.props.post_view.post.locked,
- auth,
- };
- WebSocketService.Instance.send(wsClient.lockPost(form));
- }
+ i.setState({ lockLoading: true });
+ i.props.onLockPost({
+ post_id: i.postView.post.id,
+ locked: !i.postView.post.locked,
+ auth: myAuthRequired(),
+ });
}
handleModFeaturePostLocal(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: FeaturePost = {
- post_id: i.props.post_view.post.id,
- feature_type: "Local",
- featured: !i.props.post_view.post.featured_local,
- auth,
- };
- WebSocketService.Instance.send(wsClient.featurePost(form));
- }
+ i.setState({ featureLocalLoading: true });
+ i.props.onFeaturePost({
+ post_id: i.postView.post.id,
+ featured: !i.postView.post.featured_local,
+ feature_type: "Local",
+ auth: myAuthRequired(),
+ });
}
handleModFeaturePostCommunity(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: FeaturePost = {
- post_id: i.props.post_view.post.id,
- feature_type: "Community",
- featured: !i.props.post_view.post.featured_community,
- auth,
- };
- WebSocketService.Instance.send(wsClient.featurePost(form));
- }
+ i.setState({ featureCommunityLoading: true });
+ i.props.onFeaturePost({
+ post_id: i.postView.post.id,
+ featured: !i.postView.post.featured_community,
+ feature_type: "Community",
+ auth: myAuthRequired(),
+ });
}
handleModBanFromCommunityShow(i: PostListing) {
handlePurgeSubmit(i: PostListing, event: any) {
event.preventDefault();
-
- let auth = myAuth();
- if (auth) {
- if (i.state.purgeType == PurgeType.Person) {
- let form: PurgePerson = {
- person_id: i.props.post_view.creator.id,
- reason: i.state.purgeReason,
- auth,
- };
- WebSocketService.Instance.send(wsClient.purgePerson(form));
- } else if (i.state.purgeType == PurgeType.Post) {
- let form: PurgePost = {
- post_id: i.props.post_view.post.id,
- reason: i.state.purgeReason,
- auth,
- };
- WebSocketService.Instance.send(wsClient.purgePost(form));
- }
-
- i.setState({ purgeLoading: true });
+ i.setState({ purgeLoading: true });
+ if (i.state.purgeType == PurgeType.Person) {
+ i.props.onPurgePerson({
+ person_id: i.postView.creator.id,
+ reason: i.state.purgeReason,
+ auth: myAuthRequired(),
+ });
+ } else if (i.state.purgeType == PurgeType.Post) {
+ i.props.onPurgePost({
+ post_id: i.postView.post.id,
+ reason: i.state.purgeReason,
+ auth: myAuthRequired(),
+ });
}
}
i.setState({ banExpireDays: event.target.value });
}
- handleModBanFromCommunitySubmit(i: PostListing) {
+ handleModBanFromCommunitySubmit(i: PostListing, event: any) {
i.setState({ banType: BanType.Community });
- i.handleModBanBothSubmit(i);
+ i.handleModBanBothSubmit(i, event);
}
- handleModBanSubmit(i: PostListing) {
+ handleModBanSubmit(i: PostListing, event: any) {
i.setState({ banType: BanType.Site });
- i.handleModBanBothSubmit(i);
- }
-
- handleModBanBothSubmit(i: PostListing, event?: any) {
- if (event) event.preventDefault();
- let auth = myAuth();
- if (auth) {
- let ban = !i.props.post_view.creator_banned_from_community;
- let person_id = i.props.post_view.creator.id;
- let remove_data = i.state.removeData;
- let reason = i.state.banReason;
- let expires = futureDaysToUnixTime(i.state.banExpireDays);
-
- if (i.state.banType == BanType.Community) {
- // If its an unban, restore all their data
- if (ban == false) {
- i.setState({ removeData: false });
- }
-
- let form: BanFromCommunity = {
- person_id,
- community_id: i.props.post_view.community.id,
- ban,
- remove_data,
- reason,
- expires,
- auth,
- };
- WebSocketService.Instance.send(wsClient.banFromCommunity(form));
- } else {
- // If its an unban, restore all their data
- let ban = !i.props.post_view.creator.banned;
- if (ban == false) {
- i.setState({ removeData: false });
- }
- let form: BanPerson = {
- person_id,
- ban,
- remove_data,
- reason,
- expires,
- auth,
- };
- WebSocketService.Instance.send(wsClient.banPerson(form));
- }
+ i.handleModBanBothSubmit(i, event);
+ }
+
+ handleModBanBothSubmit(i: PostListing, event: any) {
+ event.preventDefault();
+ i.setState({ banLoading: true });
- i.setState({ showBanDialog: false });
+ const ban = !i.props.post_view.creator_banned_from_community;
+ // If its an unban, restore all their data
+ if (ban == false) {
+ i.setState({ removeData: false });
+ }
+ const person_id = i.props.post_view.creator.id;
+ const remove_data = i.state.removeData;
+ const reason = i.state.banReason;
+ const expires = futureDaysToUnixTime(i.state.banExpireDays);
+
+ if (i.state.banType == BanType.Community) {
+ const community_id = i.postView.community.id;
+ i.props.onBanPersonFromCommunity({
+ community_id,
+ person_id,
+ ban,
+ remove_data,
+ reason,
+ expires,
+ auth: myAuthRequired(),
+ });
+ } else {
+ i.props.onBanPerson({
+ person_id,
+ ban,
+ remove_data,
+ reason,
+ expires,
+ auth: myAuthRequired(),
+ });
}
}
handleAddModToCommunity(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: AddModToCommunity = {
- person_id: i.props.post_view.creator.id,
- community_id: i.props.post_view.community.id,
- added: !i.creatorIsMod_,
- auth,
- };
- WebSocketService.Instance.send(wsClient.addModToCommunity(form));
- i.setState(i.state);
- }
+ i.setState({ addModLoading: true });
+ i.props.onAddModToCommunity({
+ community_id: i.postView.community.id,
+ person_id: i.postView.creator.id,
+ added: !i.creatorIsMod_,
+ auth: myAuthRequired(),
+ });
}
handleAddAdmin(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: AddAdmin = {
- person_id: i.props.post_view.creator.id,
- added: !i.creatorIsAdmin_,
- auth,
- };
- WebSocketService.Instance.send(wsClient.addAdmin(form));
- i.setState(i.state);
- }
+ i.setState({ addAdminLoading: true });
+ i.props.onAddAdmin({
+ person_id: i.postView.creator.id,
+ added: !i.creatorIsAdmin_,
+ auth: myAuthRequired(),
+ });
}
handleShowConfirmTransferCommunity(i: PostListing) {
}
handleTransferCommunity(i: PostListing) {
- let auth = myAuth();
- if (auth) {
- let form: TransferCommunity = {
- community_id: i.props.post_view.community.id,
- person_id: i.props.post_view.creator.id,
- auth,
- };
- WebSocketService.Instance.send(wsClient.transferCommunity(form));
- i.setState({ showConfirmTransferCommunity: false });
- }
+ i.setState({ transferLoading: true });
+ i.props.onTransferCommunity({
+ community_id: i.postView.community.id,
+ person_id: i.postView.creator.id,
+ auth: myAuthRequired(),
+ });
}
handleShowConfirmTransferSite(i: PostListing) {
setupTippy();
}
+ handleUpvote(i: PostListing) {
+ i.setState({ upvoteLoading: true });
+ i.props.onPostVote({
+ post_id: i.postView.post.id,
+ score: newVote(VoteType.Upvote, i.props.post_view.my_vote),
+ auth: myAuthRequired(),
+ });
+ }
+
+ handleDownvote(i: PostListing) {
+ i.setState({ downvoteLoading: true });
+ i.props.onPostVote({
+ post_id: i.postView.post.id,
+ score: newVote(VoteType.Downvote, i.props.post_view.my_vote),
+ auth: myAuthRequired(),
+ });
+ }
+
get pointsTippy(): string {
- let points = i18n.t("number_of_points", {
- count: Number(this.state.score),
- formattedCount: Number(this.state.score),
+ const points = i18n.t("number_of_points", {
+ count: Number(this.postView.counts.score),
+ formattedCount: Number(this.postView.counts.score),
});
- let upvotes = i18n.t("number_of_upvotes", {
- count: Number(this.state.upvotes),
- formattedCount: Number(this.state.upvotes),
+ const upvotes = i18n.t("number_of_upvotes", {
+ count: Number(this.postView.counts.upvotes),
+ formattedCount: Number(this.postView.counts.upvotes),
});
- let downvotes = i18n.t("number_of_downvotes", {
- count: Number(this.state.downvotes),
- formattedCount: Number(this.state.downvotes),
+ const downvotes = i18n.t("number_of_downvotes", {
+ count: Number(this.postView.counts.downvotes),
+ formattedCount: Number(this.postView.counts.downvotes),
});
return `${points} • ${upvotes} • ${downvotes}`;
get canModOnSelf_(): boolean {
return canMod(
- this.props.post_view.creator.id,
+ this.postView.creator.id,
this.props.moderators,
this.props.admins,
undefined,
get canMod_(): boolean {
return canMod(
- this.props.post_view.creator.id,
+ this.postView.creator.id,
this.props.moderators,
this.props.admins
);
}
get canAdmin_(): boolean {
- return canAdmin(this.props.post_view.creator.id, this.props.admins);
+ return canAdmin(this.postView.creator.id, this.props.admins);
}
get creatorIsMod_(): boolean {
- return isMod(this.props.post_view.creator.id, this.props.moderators);
+ return isMod(this.postView.creator.id, this.props.moderators);
}
get creatorIsAdmin_(): boolean {
- return isAdmin(this.props.post_view.creator.id, this.props.admins);
+ return isAdmin(this.postView.creator.id, this.props.admins);
}
}