mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-01 01:59:56 +00:00
Add FeaturedPost Support (#873)
This commit is contained in:
parent
a6c0643428
commit
003b177f6e
9 changed files with 108 additions and 50 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 46f4b3e8676c23d4a8100ce78330eceed7bcf053
|
Subproject commit a2f59fcbf7529a1f7dd5cda894381ef2000b9ef5
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 (
|
||||||
<button
|
<span>
|
||||||
className="btn btn-link btn-animate text-muted py-0"
|
<button
|
||||||
onClick={linkEvent(this, this.handleModSticky)}
|
className="btn btn-link btn-animate text-muted py-0 pl-0"
|
||||||
data-tippy-content={label}
|
onClick={() => this.handleModFeaturePost(this, true)}
|
||||||
aria-label={label}
|
data-tippy-content={label_community}
|
||||||
>
|
aria-label={label_community}
|
||||||
<Icon
|
>
|
||||||
icon="pin"
|
<Icon
|
||||||
classes={classNames({ "text-success": stickied })}
|
icon="pin"
|
||||||
inline
|
classes={classNames({ "text-success": featured_community })}
|
||||||
/>
|
inline
|
||||||
</button>
|
/>{" "}
|
||||||
|
Community
|
||||||
|
</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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue