Add FeaturedPost Support (#873)

This commit is contained in:
Anon 2022-12-14 09:03:18 -06:00 committed by GitHub
parent a6c0643428
commit 003b177f6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 108 additions and 50 deletions

@ -1 +1 @@
Subproject commit 46f4b3e8676c23d4a8100ce78330eceed7bcf053 Subproject commit a2f59fcbf7529a1f7dd5cda894381ef2000b9ef5

View file

@ -47,7 +47,7 @@
"inferno-server": "^8.0.3", "inferno-server": "^8.0.3",
"isomorphic-cookie": "^1.2.4", "isomorphic-cookie": "^1.2.4",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"lemmy-js-client": "0.17.0-rc.54", "lemmy-js-client": "0.17.0-rc.56",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0", "markdown-it-container": "^3.0.0",
"markdown-it-footnote": "^3.0.3", "markdown-it-footnote": "^3.0.3",

View file

@ -584,7 +584,7 @@ export class Community extends Component<any, State> {
op == UserOperation.DeletePost || op == UserOperation.DeletePost ||
op == UserOperation.RemovePost || op == UserOperation.RemovePost ||
op == UserOperation.LockPost || op == UserOperation.LockPost ||
op == UserOperation.StickyPost || op == UserOperation.FeaturePost ||
op == UserOperation.SavePost op == UserOperation.SavePost
) { ) {
let data = wsJsonToRes<PostResponse>(msg, PostResponse); let data = wsJsonToRes<PostResponse>(msg, PostResponse);

View file

@ -140,7 +140,7 @@ export class Home extends Component<any, HomeState> {
dataType: getDataTypeFromProps(this.props), dataType: getDataTypeFromProps(this.props),
sort: getSortTypeFromProps(this.props), sort: getSortTypeFromProps(this.props),
page: getPageFromProps(this.props), page: getPageFromProps(this.props),
tagline: None tagline: None,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -179,7 +179,7 @@ export class Home extends Component<any, HomeState> {
...this.state, ...this.state,
trendingCommunities: trendingRes.communities, trendingCommunities: trendingRes.communities,
loading: false, loading: false,
tagline: taglines.map(tls => getRandomFromList(tls).content) tagline: taglines.map(tls => getRandomFromList(tls).content),
}; };
} else { } else {
this.fetchTrendingCommunities(); this.fetchTrendingCommunities();
@ -336,7 +336,12 @@ export class Home extends Component<any, HomeState> {
<div className="row"> <div className="row">
<main role="main" className="col-12 col-md-8"> <main role="main" className="col-12 col-md-8">
{this.state.tagline.match({ {this.state.tagline.match({
some: tagline => <div id="tagline" dangerouslySetInnerHTML={mdToHtml(tagline)}></div>, some: tagline => (
<div
id="tagline"
dangerouslySetInnerHTML={mdToHtml(tagline)}
></div>
),
none: <></>, none: <></>,
})} })}
<div className="d-block d-md-none">{this.mobileView()}</div> <div className="d-block d-md-none">{this.mobileView()}</div>
@ -804,7 +809,7 @@ export class Home extends Component<any, HomeState> {
op == UserOperation.DeletePost || op == UserOperation.DeletePost ||
op == UserOperation.RemovePost || op == UserOperation.RemovePost ||
op == UserOperation.LockPost || op == UserOperation.LockPost ||
op == UserOperation.StickyPost || op == UserOperation.FeaturePost ||
op == UserOperation.SavePost op == UserOperation.SavePost
) { ) {
let data = wsJsonToRes<PostResponse>(msg, PostResponse); let data = wsJsonToRes<PostResponse>(msg, PostResponse);

View file

@ -16,12 +16,12 @@ import {
ModAddView, ModAddView,
ModBanFromCommunityView, ModBanFromCommunityView,
ModBanView, ModBanView,
ModFeaturePostView,
ModLockPostView, ModLockPostView,
ModlogActionType, ModlogActionType,
ModRemoveCommentView, ModRemoveCommentView,
ModRemoveCommunityView, ModRemoveCommunityView,
ModRemovePostView, ModRemovePostView,
ModStickyPostView,
ModTransferCommunityView, ModTransferCommunityView,
PersonSafe, PersonSafe,
toUndefined, toUndefined,
@ -61,7 +61,7 @@ type ModlogType = {
view: view:
| ModRemovePostView | ModRemovePostView
| ModLockPostView | ModLockPostView
| ModStickyPostView | ModFeaturePostView
| ModRemoveCommentView | ModRemoveCommentView
| ModRemoveCommunityView | ModRemoveCommunityView
| ModBanFromCommunityView | ModBanFromCommunityView
@ -182,12 +182,12 @@ export class Modlog extends Component<any, ModlogState> {
when_: r.mod_lock_post.when_, when_: r.mod_lock_post.when_,
})); }));
let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({ let featured_posts: ModlogType[] = res.featured_posts.map(r => ({
id: r.mod_sticky_post.id, id: r.mod_feature_post.id,
type_: ModlogActionType.ModStickyPost, type_: ModlogActionType.ModFeaturePost,
view: r, view: r,
moderator: r.moderator, moderator: r.moderator,
when_: r.mod_sticky_post.when_, when_: r.mod_feature_post.when_,
})); }));
let removed_comments: ModlogType[] = res.removed_comments.map(r => ({ let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
@ -287,7 +287,7 @@ export class Modlog extends Component<any, ModlogState> {
combined.push(...removed_posts); combined.push(...removed_posts);
combined.push(...locked_posts); combined.push(...locked_posts);
combined.push(...stickied_posts); combined.push(...featured_posts);
combined.push(...removed_comments); combined.push(...removed_comments);
combined.push(...removed_communities); combined.push(...removed_communities);
combined.push(...banned_from_community); combined.push(...banned_from_community);
@ -344,18 +344,21 @@ export class Modlog extends Component<any, ModlogState> {
</> </>
); );
} }
case ModlogActionType.ModStickyPost: { case ModlogActionType.ModFeaturePost: {
let mspv = i.view as ModStickyPostView; let mspv = i.view as ModFeaturePostView;
return ( return (
<> <>
<span> <span>
{mspv.mod_sticky_post.stickied.unwrapOr(false) {mspv.mod_feature_post.featured ? "Featured " : "Unfeatured "}
? "Stickied "
: "Unstickied "}
</span> </span>
<span> <span>
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link> Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
</span> </span>
<span>
{mspv.mod_feature_post.is_featured_community
? " In Community"
: " In Local"}
</span>
</> </>
); );
} }
@ -679,8 +682,8 @@ export class Modlog extends Component<any, ModlogState> {
<option value={ModlogActionType.ModLockPost}> <option value={ModlogActionType.ModLockPost}>
Locking Posts Locking Posts
</option> </option>
<option value={ModlogActionType.ModStickyPost}> <option value={ModlogActionType.ModFeaturePost}>
Stickying Posts Featuring Posts
</option> </option>
<option value={ModlogActionType.ModRemoveComment}> <option value={ModlogActionType.ModRemoveComment}>
Removing Comments Removing Comments

View file

@ -864,7 +864,7 @@ export class Profile extends Component<any, ProfileState> {
op == UserOperation.DeletePost || op == UserOperation.DeletePost ||
op == UserOperation.RemovePost || op == UserOperation.RemovePost ||
op == UserOperation.LockPost || op == UserOperation.LockPost ||
op == UserOperation.StickyPost || op == UserOperation.FeaturePost ||
op == UserOperation.SavePost op == UserOperation.SavePost
) { ) {
let data = wsJsonToRes<PostResponse>(msg, PostResponse); let data = wsJsonToRes<PostResponse>(msg, PostResponse);

View file

@ -12,15 +12,16 @@ import {
CreatePostLike, CreatePostLike,
CreatePostReport, CreatePostReport,
DeletePost, DeletePost,
FeaturePost,
Language, Language,
LockPost, LockPost,
PersonViewSafe, PersonViewSafe,
PostFeatureType,
PostView, PostView,
PurgePerson, PurgePerson,
PurgePost, PurgePost,
RemovePost, RemovePost,
SavePost, SavePost,
StickyPost,
toUndefined, toUndefined,
TransferCommunity, TransferCommunity,
} from "lemmy-js-client"; } from "lemmy-js-client";
@ -29,6 +30,7 @@ import { i18n } from "../../i18next";
import { BanType, PurgeType } from "../../interfaces"; import { BanType, PurgeType } from "../../interfaces";
import { UserService, WebSocketService } from "../../services"; import { UserService, WebSocketService } from "../../services";
import { import {
amAdmin,
amCommunityCreator, amCommunityCreator,
auth, auth,
canAdmin, canAdmin,
@ -452,7 +454,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
let post = this.props.post_view.post; let post = this.props.post_view.post;
return ( return (
<Link <Link
className={!post.stickied ? "text-body" : "text-primary"} className={
!post.featured_community && !post.featured_local
? "text-body"
: "text-primary"
}
to={`/post/${post.id}`} to={`/post/${post.id}`}
title={i18n.t("comments")} title={i18n.t("comments")}
> >
@ -470,7 +476,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
some: url => some: url =>
this.props.showBody ? ( this.props.showBody ? (
<a <a
className={!post.stickied ? "text-body" : "text-primary"} className={
!post.featured_community && !post.featured_local
? "text-body"
: "text-primary"
}
href={url} href={url}
title={url} title={url}
rel={relTags} rel={relTags}
@ -517,14 +527,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<Icon icon="lock" classes="icon-inline text-danger" /> <Icon icon="lock" classes="icon-inline text-danger" />
</small> </small>
)} )}
{post.stickied && ( {post.featured_community && (
<small <small
className="unselectable pointer ml-2 text-muted font-italic" className="unselectable pointer ml-2 text-muted font-italic"
data-tippy-content={i18n.t("stickied")} data-tippy-content={i18n.t("featured")}
> >
<Icon icon="pin" classes="icon-inline text-primary" /> <Icon icon="pin" classes="icon-inline text-primary" />
</small> </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 && ( {post.nsfw && (
<small className="ml-2 text-muted font-italic"> <small className="ml-2 text-muted font-italic">
{i18n.t("nsfw")} {i18n.t("nsfw")}
@ -617,7 +635,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{this.canModOnSelf_ && ( {this.canModOnSelf_ && (
<> <>
{this.lockButton} {this.lockButton}
{this.stickyButton} {this.featureButton}
</> </>
)} )}
{(this.canMod_ || this.canAdmin_) && <>{this.modRemoveButton}</>} {(this.canMod_ || this.canAdmin_) && <>{this.modRemoveButton}</>}
@ -854,22 +872,48 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
); );
} }
get stickyButton() { get featureButton() {
let stickied = this.props.post_view.post.stickied; const featured_community = this.props.post_view.post.featured_community;
let label = stickied ? i18n.t("unsticky") : i18n.t("sticky"); const label_community = featured_community
? i18n.t("unfeature_from_community")
: i18n.t("feature_in_community");
const is_admin = amAdmin();
const featured_local = this.props.post_view.post.featured_local;
const label_local = featured_local
? i18n.t("unfeature_from_local")
: i18n.t("feature_in_local");
return ( return (
<span>
<button <button
className="btn btn-link btn-animate text-muted py-0" className="btn btn-link btn-animate text-muted py-0 pl-0"
onClick={linkEvent(this, this.handleModSticky)} onClick={() => this.handleModFeaturePost(this, true)}
data-tippy-content={label} data-tippy-content={label_community}
aria-label={label} aria-label={label_community}
> >
<Icon <Icon
icon="pin" icon="pin"
classes={classNames({ "text-success": stickied })} classes={classNames({ "text-success": featured_community })}
inline inline
/> />{" "}
Community
</button> </button>
{is_admin && (
<button
className="btn btn-link btn-animate text-muted py-0"
onClick={() => this.handleModFeaturePost(this, false)}
data-tippy-content={label_local}
aria-label={label_local}
>
<Icon
icon="pin"
classes={classNames({ "text-success": featured_local })}
inline
/>{" "}
Local
</button>
)}
</span>
); );
} }
@ -1491,13 +1535,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
WebSocketService.Instance.send(wsClient.lockPost(form)); WebSocketService.Instance.send(wsClient.lockPost(form));
} }
handleModSticky(i: PostListing) { handleModFeaturePost(i: PostListing, is_community: boolean) {
let form = new StickyPost({ let form = new FeaturePost({
post_id: i.props.post_view.post.id, post_id: i.props.post_view.post.id,
stickied: !i.props.post_view.post.stickied, featured: is_community
? !i.props.post_view.post.featured_community
: !i.props.post_view.post.featured_local,
feature_type: is_community
? PostFeatureType.Community
: PostFeatureType.Local,
auth: auth().unwrap(), auth: auth().unwrap(),
}); });
WebSocketService.Instance.send(wsClient.stickyPost(form)); WebSocketService.Instance.send(wsClient.featurePost(form));
} }
handleModBanFromCommunityShow(i: PostListing) { handleModBanFromCommunityShow(i: PostListing) {

View file

@ -755,7 +755,7 @@ export class Post extends Component<any, PostState> {
op == UserOperation.DeletePost || op == UserOperation.DeletePost ||
op == UserOperation.RemovePost || op == UserOperation.RemovePost ||
op == UserOperation.LockPost || op == UserOperation.LockPost ||
op == UserOperation.StickyPost || op == UserOperation.FeaturePost ||
op == UserOperation.SavePost op == UserOperation.SavePost
) { ) {
let data = wsJsonToRes<PostResponse>(msg, PostResponse); let data = wsJsonToRes<PostResponse>(msg, PostResponse);

View file

@ -968,7 +968,8 @@ export function editPostRes(data: PostView, post: PostView) {
post.post.nsfw = data.post.nsfw; post.post.nsfw = data.post.nsfw;
post.post.deleted = data.post.deleted; post.post.deleted = data.post.deleted;
post.post.removed = data.post.removed; post.post.removed = data.post.removed;
post.post.stickied = data.post.stickied; post.post.featured_community = data.post.featured_community;
post.post.featured_local = data.post.featured_local;
post.post.body = data.post.body; post.post.body = data.post.body;
post.post.locked = data.post.locked; post.post.locked = data.post.locked;
post.saved = data.saved; post.saved = data.saved;
@ -1526,6 +1527,6 @@ export function nsfwCheck(
); );
} }
export function getRandomFromList<T>(list : T[]) : T{ export function getRandomFromList<T>(list: T[]): T {
return list[Math.floor(Math.random() * list.length)]; return list[Math.floor(Math.random() * list.length)];
} }