1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import { WebSocketService, UserService } from '../services';
14 AddModToCommunityForm,
17 TransferCommunityForm,
18 } from '../interfaces';
19 import { MomentTime } from './moment-time';
20 import { PostForm } from './post-form';
21 import { IFramelyCard } from './iframely-card';
22 import { UserListing } from './user-listing';
23 import { CommunityLink } from './community-link';
38 import { i18n } from '../i18next';
40 interface PostListingState {
42 showRemoveDialog: boolean;
44 showBanDialog: boolean;
48 showConfirmTransferSite: boolean;
49 showConfirmTransferCommunity: boolean;
50 imageExpanded: boolean;
52 showAdvanced: boolean;
59 interface PostListingProps {
61 showCommunity?: boolean;
63 moderators?: Array<CommunityUser>;
64 admins?: Array<UserView>;
65 enableDownvotes: boolean;
69 export class PostListing extends Component<PostListingProps, PostListingState> {
70 private emptyState: PostListingState = {
72 showRemoveDialog: false,
77 banType: BanType.Community,
78 showConfirmTransferSite: false,
79 showConfirmTransferCommunity: false,
83 my_vote: this.props.post.my_vote,
84 score: this.props.post.score,
85 upvotes: this.props.post.upvotes,
86 downvotes: this.props.post.downvotes,
89 constructor(props: any, context: any) {
90 super(props, context);
92 this.state = this.emptyState;
93 this.handlePostLike = this.handlePostLike.bind(this);
94 this.handlePostDisLike = this.handlePostDisLike.bind(this);
95 this.handleEditPost = this.handleEditPost.bind(this);
96 this.handleEditCancel = this.handleEditCancel.bind(this);
99 componentWillReceiveProps(nextProps: PostListingProps) {
100 this.state.my_vote = nextProps.post.my_vote;
101 this.state.upvotes = nextProps.post.upvotes;
102 this.state.downvotes = nextProps.post.downvotes;
103 this.state.score = nextProps.post.score;
104 if (this.props.post.id !== nextProps.post.id) {
105 this.state.imageExpanded = false;
107 this.setState(this.state);
113 {!this.state.showEdit ? (
121 post={this.props.post}
122 onEdit={this.handleEditPost}
123 onCancel={this.handleEditCancel}
124 enableNsfw={this.props.enableNsfw}
125 enableDownvotes={this.props.enableDownvotes}
137 {this.props.post.url &&
138 this.props.showBody &&
139 this.props.post.embed_title && (
140 <IFramelyCard post={this.props.post} />
142 {this.props.showBody && this.props.post.body && (
144 {this.state.viewSource ? (
145 <pre>{this.props.post.body}</pre>
149 dangerouslySetInnerHTML={mdToHtml(this.props.post.body)}
159 imgThumb(src: string) {
160 let post = this.props.post;
163 className={`img-fluid thumbnail rounded ${
164 (post.nsfw || post.community_nsfw) && 'img-blur'
171 getImage(thumbnail: boolean = false) {
172 let post = this.props.post;
173 if (isImage(post.url)) {
174 if (post.url.includes('pictrs')) {
175 return pictrsImage(post.url, thumbnail);
176 } else if (post.thumbnail_url) {
177 return pictrsImage(post.thumbnail_url, thumbnail);
181 } else if (post.thumbnail_url) {
182 return pictrsImage(post.thumbnail_url, thumbnail);
187 let post = this.props.post;
189 if (isImage(post.url)) {
192 class="text-body pointer"
193 data-tippy-content={i18n.t('expand_here')}
194 onClick={linkEvent(this, this.handleImageExpandClick)}
196 {this.imgThumb(this.getImage(true))}
197 <svg class="icon mini-overlay">
198 <use xlinkHref="#icon-image"></use>
202 } else if (post.thumbnail_url) {
205 className="text-body"
211 {this.imgThumb(this.getImage(true))}
212 <svg class="icon mini-overlay">
213 <use xlinkHref="#icon-external-link"></use>
217 } else if (post.url) {
218 if (isVideo(post.url)) {
220 <div class="embed-responsive embed-responsive-16by9">
226 class="embed-responsive-item"
228 <source src={post.url} type="video/mp4" />
235 className="text-body"
241 <svg class="icon thumbnail">
242 <use xlinkHref="#icon-external-link"></use>
250 className="text-body"
251 to={`/post/${post.id}`}
252 title={i18n.t('comments')}
254 <svg class="icon thumbnail">
255 <use xlinkHref="#icon-message-square"></use>
263 let post = this.props.post;
266 <div className={`vote-bar col-1 pr-0 small text-center`}>
268 className={`btn-animate btn btn-link p-0 ${
269 this.state.my_vote == 1 ? 'text-info' : 'text-muted'
271 onClick={linkEvent(this, this.handlePostLike)}
272 data-tippy-content={i18n.t('upvote')}
274 <svg class="icon upvote">
275 <use xlinkHref="#icon-arrow-up1"></use>
279 class={`unselectable pointer font-weight-bold text-muted px-1`}
280 data-tippy-content={this.pointsTippy}
284 {this.props.enableDownvotes && (
286 className={`btn-animate btn btn-link p-0 ${
287 this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
289 onClick={linkEvent(this, this.handlePostDisLike)}
290 data-tippy-content={i18n.t('downvote')}
292 <svg class="icon downvote">
293 <use xlinkHref="#icon-arrow-down1"></use>
298 {!this.state.imageExpanded && (
299 <div class="col-3 col-sm-2 pr-0 mt-1">
300 <div class="position-relative">{this.thumbnail()}</div>
304 class={`${this.state.imageExpanded ? 'col-12' : 'col-8 col-sm-9'}`}
307 <div className="col-12">
308 <div className="post-title">
309 <h5 className="mb-0 d-inline">
310 {this.props.showBody && post.url ? (
312 className="text-body"
322 className="text-body"
323 to={`/post/${post.id}`}
324 title={i18n.t('comments')}
330 {post.url && !(hostname(post.url) == window.location.hostname) && (
331 <small class="d-inline-block">
333 className="ml-2 text-muted font-italic"
340 <svg class="ml-1 icon icon-inline">
341 <use xlinkHref="#icon-external-link"></use>
346 {(isImage(post.url) || this.props.post.thumbnail_url) && (
348 {!this.state.imageExpanded ? (
350 class="text-monospace unselectable pointer ml-2 text-muted small"
351 data-tippy-content={i18n.t('expand_here')}
352 onClick={linkEvent(this, this.handleImageExpandClick)}
354 <svg class="icon icon-inline">
355 <use xlinkHref="#icon-plus-square"></use>
361 class="text-monospace unselectable pointer ml-2 text-muted small"
362 onClick={linkEvent(this, this.handleImageExpandClick)}
364 <svg class="icon icon-inline">
365 <use xlinkHref="#icon-minus-square"></use>
373 this.handleImageExpandClick
377 class="img-fluid img-expanded"
378 src={this.getImage()}
387 <small className="ml-2 text-muted font-italic">
393 className="unselectable pointer ml-2 text-muted font-italic"
394 data-tippy-content={i18n.t('deleted')}
396 <svg class={`icon icon-inline text-danger`}>
397 <use xlinkHref="#icon-trash"></use>
403 className="unselectable pointer ml-2 text-muted font-italic"
404 data-tippy-content={i18n.t('locked')}
406 <svg class={`icon icon-inline text-danger`}>
407 <use xlinkHref="#icon-lock"></use>
413 className="unselectable pointer ml-2 text-muted font-italic"
414 data-tippy-content={i18n.t('stickied')}
416 <svg class={`icon icon-inline text-success`}>
417 <use xlinkHref="#icon-pin"></use>
422 <small className="ml-2 text-muted font-italic">
430 <div className="details col-12">
431 <ul class="list-inline mb-0 text-muted small">
432 <li className="list-inline-item">
433 <span>{i18n.t('by')} </span>
436 name: post.creator_name,
437 avatar: post.creator_avatar,
439 local: post.creator_local,
440 actor_id: post.creator_actor_id,
441 published: post.creator_published,
446 <span className="mx-1 badge badge-light">
451 <span className="mx-1 badge badge-light">
455 {(post.banned_from_community || post.banned) && (
456 <span className="mx-1 badge badge-danger">
460 {this.props.showCommunity && (
462 <span> {i18n.t('to')} </span>
465 name: post.community_name,
466 id: post.community_id,
467 local: post.community_local,
468 actor_id: post.community_actor_id,
474 <li className="list-inline-item">•</li>
475 <li className="list-inline-item">
477 <MomentTime data={post} />
482 <li className="list-inline-item">•</li>
483 <li className="list-inline-item">
484 {/* Using a link with tippy doesn't work on touch devices unfortunately */}
486 className="text-muted"
487 data-tippy-content={md.render(previewLines(post.body))}
488 data-tippy-allowHtml={true}
489 to={`/post/${post.id}`}
491 <svg class="mr-1 icon icon-inline">
492 <use xlinkHref="#icon-book-open"></use>
498 <li className="list-inline-item">•</li>
499 {this.state.upvotes !== this.state.score && (
502 class="unselectable pointer mr-2"
503 data-tippy-content={this.pointsTippy}
505 <li className="list-inline-item">
506 <span className="text-muted">
507 <svg class="small icon icon-inline mr-1">
508 <use xlinkHref="#icon-arrow-up"></use>
513 <li className="list-inline-item">
514 <span className="text-muted">
515 <svg class="small icon icon-inline mr-1">
516 <use xlinkHref="#icon-arrow-down"></use>
518 {this.state.downvotes}
522 <li className="list-inline-item">•</li>
525 <li className="list-inline-item">
527 className="text-muted"
528 title={i18n.t('number_of_comments', {
529 count: post.number_of_comments,
531 to={`/post/${post.id}`}
533 <svg class="mr-1 icon icon-inline">
534 <use xlinkHref="#icon-message-square"></use>
536 {post.number_of_comments}
540 {this.props.post.duplicates && (
541 <ul class="list-inline mb-1 small text-muted">
543 <li className="list-inline-item mr-2">
544 {i18n.t('cross_posted_to')}
546 {this.props.post.duplicates.map(post => (
547 <li className="list-inline-item mr-2">
548 <Link to={`/post/${post.id}`}>
549 {post.community_name}
556 <ul class="list-inline mb-1 text-muted font-weight-bold">
557 {UserService.Instance.user && (
559 {this.props.showBody && (
561 <li className="list-inline-item">
563 class="btn btn-sm btn-link btn-animate text-muted"
564 onClick={linkEvent(this, this.handleSavePostClick)}
566 post.saved ? i18n.t('unsave') : i18n.t('save')
570 class={`icon icon-inline ${
571 post.saved && 'text-warning'
574 <use xlinkHref="#icon-star"></use>
578 <li className="list-inline-item">
580 class="btn btn-sm btn-link btn-animate text-muted"
581 to={`/create_post${this.crossPostParams}`}
582 title={i18n.t('cross_post')}
584 <svg class="icon icon-inline">
585 <use xlinkHref="#icon-copy"></use>
591 {this.myPost && this.props.showBody && (
593 <li className="list-inline-item">
595 class="btn btn-sm btn-link btn-animate text-muted"
596 onClick={linkEvent(this, this.handleEditClick)}
597 data-tippy-content={i18n.t('edit')}
599 <svg class="icon icon-inline">
600 <use xlinkHref="#icon-edit"></use>
604 <li className="list-inline-item">
606 class="btn btn-sm btn-link btn-animate text-muted"
607 onClick={linkEvent(this, this.handleDeleteClick)}
615 class={`icon icon-inline ${
616 post.deleted && 'text-danger'
619 <use xlinkHref="#icon-trash"></use>
626 {!this.state.showAdvanced && this.props.showBody ? (
627 <li className="list-inline-item">
629 class="btn btn-sm btn-link btn-animate text-muted"
630 onClick={linkEvent(this, this.handleShowAdvanced)}
631 data-tippy-content={i18n.t('more')}
633 <svg class="icon icon-inline">
634 <use xlinkHref="#icon-more-vertical"></use>
640 {this.props.showBody && post.body && (
641 <li className="list-inline-item">
643 class="btn btn-sm btn-link btn-animate text-muted"
644 onClick={linkEvent(this, this.handleViewSource)}
645 data-tippy-content={i18n.t('view_source')}
648 class={`icon icon-inline ${
649 this.state.viewSource && 'text-success'
652 <use xlinkHref="#icon-file-text"></use>
657 {this.canModOnSelf && (
659 <li className="list-inline-item">
661 class="btn btn-sm btn-link btn-animate text-muted"
662 onClick={linkEvent(this, this.handleModLock)}
670 class={`icon icon-inline ${
671 post.locked && 'text-danger'
674 <use xlinkHref="#icon-lock"></use>
678 <li className="list-inline-item">
680 class="btn btn-sm btn-link btn-animate text-muted"
681 onClick={linkEvent(this, this.handleModSticky)}
689 class={`icon icon-inline ${
690 post.stickied && 'text-success'
693 <use xlinkHref="#icon-pin"></use>
699 {/* Mods can ban from community, and appoint as mods to community */}
700 {(this.canMod || this.canAdmin) && (
701 <li className="list-inline-item">
707 this.handleModRemoveShow
717 this.handleModRemoveSubmit
728 <li className="list-inline-item">
729 {!post.banned_from_community ? (
734 this.handleModBanFromCommunityShow
744 this.handleModBanFromCommunitySubmit
752 {!post.banned_from_community && (
753 <li className="list-inline-item">
758 this.handleAddModToCommunity
762 ? i18n.t('remove_as_mod')
763 : i18n.t('appoint_as_mod')}
769 {/* Community creators and admins can transfer community to another mod */}
770 {(this.amCommunityCreator || this.canAdmin) &&
772 <li className="list-inline-item">
773 {!this.state.showConfirmTransferCommunity ? (
778 this.handleShowConfirmTransferCommunity
781 {i18n.t('transfer_community')}
785 <span class="d-inline-block mr-1">
786 {i18n.t('are_you_sure')}
789 class="pointer d-inline-block mr-1"
792 this.handleTransferCommunity
798 class="pointer d-inline-block"
802 .handleCancelShowConfirmTransferCommunity
811 {/* Admins can ban from all, and appoint other admins */}
815 <li className="list-inline-item">
821 this.handleModBanShow
824 {i18n.t('ban_from_site')}
831 this.handleModBanSubmit
834 {i18n.t('unban_from_site')}
840 <li className="list-inline-item">
843 onClick={linkEvent(this, this.handleAddAdmin)}
846 ? i18n.t('remove_as_admin')
847 : i18n.t('appoint_as_admin')}
853 {/* Site Creator can transfer to another admin */}
854 {this.amSiteCreator && this.isAdmin && (
855 <li className="list-inline-item">
856 {!this.state.showConfirmTransferSite ? (
861 this.handleShowConfirmTransferSite
864 {i18n.t('transfer_site')}
868 <span class="d-inline-block mr-1">
869 {i18n.t('are_you_sure')}
872 class="pointer d-inline-block mr-1"
875 this.handleTransferSite
881 class="pointer d-inline-block"
884 this.handleCancelShowConfirmTransferSite
898 {this.state.showRemoveDialog && (
901 onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
905 class="form-control mr-2"
906 placeholder={i18n.t('reason')}
907 value={this.state.removeReason}
908 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
910 <button type="submit" class="btn btn-secondary">
911 {i18n.t('remove_post')}
915 {this.state.showBanDialog && (
916 <form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
917 <div class="form-group row">
918 <label class="col-form-label" htmlFor="post-listing-reason">
923 id="post-listing-reason"
924 class="form-control mr-2"
925 placeholder={i18n.t('reason')}
926 value={this.state.banReason}
927 onInput={linkEvent(this, this.handleModBanReasonChange)}
930 {/* TODO hold off on expires until later */}
931 {/* <div class="form-group row"> */}
932 {/* <label class="col-form-label">Expires</label> */}
933 {/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
935 <div class="form-group row">
936 <button type="submit" class="btn btn-secondary">
937 {i18n.t('ban')} {post.creator_name}
949 private get myPost(): boolean {
951 UserService.Instance.user &&
952 this.props.post.creator_id == UserService.Instance.user.id
956 get isMod(): boolean {
958 this.props.moderators &&
960 this.props.moderators.map(m => m.user_id),
961 this.props.post.creator_id
966 get isAdmin(): boolean {
970 this.props.admins.map(a => a.id),
971 this.props.post.creator_id
976 get canMod(): boolean {
977 if (this.props.admins && this.props.moderators) {
978 let adminsThenMods = this.props.admins
980 .concat(this.props.moderators.map(m => m.user_id));
983 UserService.Instance.user,
985 this.props.post.creator_id
992 get canModOnSelf(): boolean {
993 if (this.props.admins && this.props.moderators) {
994 let adminsThenMods = this.props.admins
996 .concat(this.props.moderators.map(m => m.user_id));
999 UserService.Instance.user,
1001 this.props.post.creator_id,
1009 get canAdmin(): boolean {
1011 this.props.admins &&
1013 UserService.Instance.user,
1014 this.props.admins.map(a => a.id),
1015 this.props.post.creator_id
1020 get amCommunityCreator(): boolean {
1022 this.props.moderators &&
1023 UserService.Instance.user &&
1024 this.props.post.creator_id != UserService.Instance.user.id &&
1025 UserService.Instance.user.id == this.props.moderators[0].user_id
1029 get amSiteCreator(): boolean {
1031 this.props.admins &&
1032 UserService.Instance.user &&
1033 this.props.post.creator_id != UserService.Instance.user.id &&
1034 UserService.Instance.user.id == this.props.admins[0].id
1038 handlePostLike(i: PostListing) {
1039 if (!UserService.Instance.user) {
1040 this.context.router.history.push(`/login`);
1043 let new_vote = i.state.my_vote == 1 ? 0 : 1;
1045 if (i.state.my_vote == 1) {
1048 } else if (i.state.my_vote == -1) {
1049 i.state.downvotes--;
1057 i.state.my_vote = new_vote;
1059 let form: CreatePostLikeForm = {
1060 post_id: i.props.post.id,
1061 score: i.state.my_vote,
1064 WebSocketService.Instance.likePost(form);
1065 i.setState(i.state);
1069 handlePostDisLike(i: PostListing) {
1070 if (!UserService.Instance.user) {
1071 this.context.router.history.push(`/login`);
1074 let new_vote = i.state.my_vote == -1 ? 0 : -1;
1076 if (i.state.my_vote == 1) {
1079 i.state.downvotes++;
1080 } else if (i.state.my_vote == -1) {
1081 i.state.downvotes--;
1084 i.state.downvotes++;
1088 i.state.my_vote = new_vote;
1090 let form: CreatePostLikeForm = {
1091 post_id: i.props.post.id,
1092 score: i.state.my_vote,
1095 WebSocketService.Instance.likePost(form);
1096 i.setState(i.state);
1100 handleEditClick(i: PostListing) {
1101 i.state.showEdit = true;
1102 i.setState(i.state);
1105 handleEditCancel() {
1106 this.state.showEdit = false;
1107 this.setState(this.state);
1110 // The actual editing is done in the recieve for post
1112 this.state.showEdit = false;
1113 this.setState(this.state);
1116 handleDeleteClick(i: PostListing) {
1117 let deleteForm: PostFormI = {
1118 body: i.props.post.body,
1119 community_id: i.props.post.community_id,
1120 name: i.props.post.name,
1121 url: i.props.post.url,
1122 edit_id: i.props.post.id,
1123 creator_id: i.props.post.creator_id,
1124 deleted: !i.props.post.deleted,
1125 nsfw: i.props.post.nsfw,
1128 WebSocketService.Instance.editPost(deleteForm);
1131 handleSavePostClick(i: PostListing) {
1132 let saved = i.props.post.saved == undefined ? true : !i.props.post.saved;
1133 let form: SavePostForm = {
1134 post_id: i.props.post.id,
1138 WebSocketService.Instance.savePost(form);
1141 get crossPostParams(): string {
1142 let params = `?title=${this.props.post.name}`;
1143 let post = this.props.post;
1146 params += `&url=${post.url}`;
1148 if (this.props.post.body) {
1149 params += `&body=${this.props.post.body}`;
1154 handleModRemoveShow(i: PostListing) {
1155 i.state.showRemoveDialog = true;
1156 i.setState(i.state);
1159 handleModRemoveReasonChange(i: PostListing, event: any) {
1160 i.state.removeReason = event.target.value;
1161 i.setState(i.state);
1164 handleModRemoveSubmit(i: PostListing) {
1165 event.preventDefault();
1166 let form: PostFormI = {
1167 name: i.props.post.name,
1168 community_id: i.props.post.community_id,
1169 edit_id: i.props.post.id,
1170 creator_id: i.props.post.creator_id,
1171 removed: !i.props.post.removed,
1172 reason: i.state.removeReason,
1173 nsfw: i.props.post.nsfw,
1176 WebSocketService.Instance.editPost(form);
1178 i.state.showRemoveDialog = false;
1179 i.setState(i.state);
1182 handleModLock(i: PostListing) {
1183 let form: PostFormI = {
1184 name: i.props.post.name,
1185 community_id: i.props.post.community_id,
1186 edit_id: i.props.post.id,
1187 creator_id: i.props.post.creator_id,
1188 nsfw: i.props.post.nsfw,
1189 locked: !i.props.post.locked,
1192 WebSocketService.Instance.editPost(form);
1195 handleModSticky(i: PostListing) {
1196 let form: PostFormI = {
1197 name: i.props.post.name,
1198 community_id: i.props.post.community_id,
1199 edit_id: i.props.post.id,
1200 creator_id: i.props.post.creator_id,
1201 nsfw: i.props.post.nsfw,
1202 stickied: !i.props.post.stickied,
1205 WebSocketService.Instance.editPost(form);
1208 handleModBanFromCommunityShow(i: PostListing) {
1209 i.state.showBanDialog = true;
1210 i.state.banType = BanType.Community;
1211 i.setState(i.state);
1214 handleModBanShow(i: PostListing) {
1215 i.state.showBanDialog = true;
1216 i.state.banType = BanType.Site;
1217 i.setState(i.state);
1220 handleModBanReasonChange(i: PostListing, event: any) {
1221 i.state.banReason = event.target.value;
1222 i.setState(i.state);
1225 handleModBanExpiresChange(i: PostListing, event: any) {
1226 i.state.banExpires = event.target.value;
1227 i.setState(i.state);
1230 handleModBanFromCommunitySubmit(i: PostListing) {
1231 i.state.banType = BanType.Community;
1232 i.setState(i.state);
1233 i.handleModBanBothSubmit(i);
1236 handleModBanSubmit(i: PostListing) {
1237 i.state.banType = BanType.Site;
1238 i.setState(i.state);
1239 i.handleModBanBothSubmit(i);
1242 handleModBanBothSubmit(i: PostListing) {
1243 event.preventDefault();
1245 if (i.state.banType == BanType.Community) {
1246 let form: BanFromCommunityForm = {
1247 user_id: i.props.post.creator_id,
1248 community_id: i.props.post.community_id,
1249 ban: !i.props.post.banned_from_community,
1250 reason: i.state.banReason,
1251 expires: getUnixTime(i.state.banExpires),
1253 WebSocketService.Instance.banFromCommunity(form);
1255 let form: BanUserForm = {
1256 user_id: i.props.post.creator_id,
1257 ban: !i.props.post.banned,
1258 reason: i.state.banReason,
1259 expires: getUnixTime(i.state.banExpires),
1261 WebSocketService.Instance.banUser(form);
1264 i.state.showBanDialog = false;
1265 i.setState(i.state);
1268 handleAddModToCommunity(i: PostListing) {
1269 let form: AddModToCommunityForm = {
1270 user_id: i.props.post.creator_id,
1271 community_id: i.props.post.community_id,
1274 WebSocketService.Instance.addModToCommunity(form);
1275 i.setState(i.state);
1278 handleAddAdmin(i: PostListing) {
1279 let form: AddAdminForm = {
1280 user_id: i.props.post.creator_id,
1283 WebSocketService.Instance.addAdmin(form);
1284 i.setState(i.state);
1287 handleShowConfirmTransferCommunity(i: PostListing) {
1288 i.state.showConfirmTransferCommunity = true;
1289 i.setState(i.state);
1292 handleCancelShowConfirmTransferCommunity(i: PostListing) {
1293 i.state.showConfirmTransferCommunity = false;
1294 i.setState(i.state);
1297 handleTransferCommunity(i: PostListing) {
1298 let form: TransferCommunityForm = {
1299 community_id: i.props.post.community_id,
1300 user_id: i.props.post.creator_id,
1302 WebSocketService.Instance.transferCommunity(form);
1303 i.state.showConfirmTransferCommunity = false;
1304 i.setState(i.state);
1307 handleShowConfirmTransferSite(i: PostListing) {
1308 i.state.showConfirmTransferSite = true;
1309 i.setState(i.state);
1312 handleCancelShowConfirmTransferSite(i: PostListing) {
1313 i.state.showConfirmTransferSite = false;
1314 i.setState(i.state);
1317 handleTransferSite(i: PostListing) {
1318 let form: TransferSiteForm = {
1319 user_id: i.props.post.creator_id,
1321 WebSocketService.Instance.transferSite(form);
1322 i.state.showConfirmTransferSite = false;
1323 i.setState(i.state);
1326 handleImageExpandClick(i: PostListing) {
1327 i.state.imageExpanded = !i.state.imageExpanded;
1328 i.setState(i.state);
1331 handleViewSource(i: PostListing) {
1332 i.state.viewSource = !i.state.viewSource;
1333 i.setState(i.state);
1336 handleShowAdvanced(i: PostListing) {
1337 i.state.showAdvanced = !i.state.showAdvanced;
1338 i.setState(i.state);
1342 get pointsTippy(): string {
1343 let points = i18n.t('number_of_points', {
1344 count: this.state.score,
1347 let upvotes = i18n.t('number_of_upvotes', {
1348 count: this.state.upvotes,
1351 let downvotes = i18n.t('number_of_downvotes', {
1352 count: this.state.downvotes,
1355 return `${points} • ${upvotes} • ${downvotes}`;