Merge branch 'main' into content-warning

This commit is contained in:
SleeplessOne1917 2024-04-12 14:00:13 +00:00 committed by GitHub
commit 588bacb7b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 242 additions and 14 deletions

View file

@ -54,6 +54,7 @@ export type ContentPostProps = {
onLock: () => Promise<void>;
onFeatureLocal: () => Promise<void>;
onFeatureCommunity: () => Promise<void>;
onHidePost: () => Promise<void>;
} & ContentActionDropdownPropsBase;
type ContentActionDropdownProps = ContentCommentProps | ContentPostProps;
@ -178,6 +179,17 @@ export default class ContentActionDropdown extends Component<
</button>
<ul className="dropdown-menu" id={dropdownId}>
{type === "post" && (
<li>
<ActionButton
icon={this.props.postView.hidden ? "eye" : "eye-slash"}
label={I18NextService.i18n.t(
this.props.postView.hidden ? "unhide_post" : "hide_post",
)}
onClick={this.props.onHidePost}
/>
</li>
)}
{this.amCreator ? (
<>
<li>

View file

@ -0,0 +1,71 @@
import { StringBoolean } from "@utils/types";
import classNames from "classnames";
import { Icon } from "./icon";
import { tippyMixin } from "../mixins/tippy-mixin";
import { Component, linkEvent } from "inferno";
import { I18NextService } from "../../services/I18NextService";
// Need to disable this rule because ESLint flat out lies about labels not
// having an associated control in this component
/* eslint-disable jsx-a11y/label-has-associated-control */
interface PostHiddenSelectProps {
showHidden?: StringBoolean;
onShowHiddenChange: (hidden?: StringBoolean) => void;
}
function handleShowHiddenChange(i: PostHiddenSelect, event: any) {
i.props.onShowHiddenChange(event.target.value);
}
@tippyMixin
export default class PostHiddenSelect extends Component<
PostHiddenSelectProps,
never
> {
render() {
const { showHidden } = this.props;
return (
<div
className="show-hidden-select btn-group btn-group-toggle flex-wrap"
role="group"
>
<label
htmlFor="show-hidden"
className={classNames("pointer btn btn-outline-secondary", {
active: showHidden === "true",
})}
data-tippy-content={I18NextService.i18n.t("show_hidden_posts")}
>
<Icon icon="eye" inline />
<input
id="show-hidden"
type="radio"
className="btn-check"
value="true"
checked={showHidden === "true"}
onChange={linkEvent(this, handleShowHiddenChange)}
/>
</label>
<label
htmlFor="hide-hidden"
className={classNames("pointer btn btn-outline-secondary", {
active: showHidden !== "true",
})}
data-tippy-content={I18NextService.i18n.t("hide_hidden_posts")}
>
<Icon icon="eye-slash" inline />
<input
id="hide-hidden"
type="radio"
className="btn-check"
value="false"
checked={showHidden !== "true"}
onChange={linkEvent(this, handleShowHiddenChange)}
/>
</label>
</div>
);
}
}

View file

@ -47,7 +47,7 @@ export default class UrlListTextarea extends Component<
UrlListTextareaState
> {
state: UrlListTextareaState = {
text: "",
text: this.props.urls.join("\n"),
};
render() {

View file

@ -20,7 +20,7 @@ import {
resourcesSettled,
} from "@utils/helpers";
import { scrollMixin } from "../mixins/scroll-mixin";
import type { QueryParams } from "@utils/types";
import type { QueryParams, StringBoolean } from "@utils/types";
import { RouteDataResponse } from "@utils/types";
import { Component, RefObject, createRef, linkEvent } from "inferno";
import { RouteComponentProps } from "inferno-router/dist/Route";
@ -59,6 +59,7 @@ import {
GetPosts,
GetPostsResponse,
GetSiteResponse,
HidePost,
LemmyHttp,
LockPost,
MarkCommentReplyAsRead,
@ -111,6 +112,7 @@ import {
} from "../common/loading-skeleton";
import { Sidebar } from "./sidebar";
import { IRoutePropsWithFetch } from "../../routes";
import PostHiddenSelect from "../common/post-hidden-select";
type CommunityData = RouteDataResponse<{
communityRes: GetCommunityResponse;
@ -132,6 +134,7 @@ interface CommunityProps {
dataType: DataType;
sort: SortType;
pageCursor?: PaginationCursor;
showHidden?: StringBoolean;
}
type Fallbacks = { sort: SortType };
@ -148,6 +151,7 @@ export function getCommunityQueryParams(
dataType: getDataTypeFromQuery,
pageCursor: (cursor?: string) => cursor,
sort: getSortTypeFromQuery,
showHidden: (include?: StringBoolean) => include,
},
source,
{
@ -242,6 +246,9 @@ export class Community extends Component<CommunityRouteProps, State> {
this.handleSavePost = this.handleSavePost.bind(this);
this.handlePurgePost = this.handlePurgePost.bind(this);
this.handleFeaturePost = this.handleFeaturePost.bind(this);
this.handleHidePost = this.handleHidePost.bind(this);
this.handleShowHiddenChange = this.handleShowHiddenChange.bind(this);
this.mainContentRef = createRef();
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
@ -274,7 +281,7 @@ export class Community extends Component<CommunityRouteProps, State> {
static async fetchInitialData({
headers,
query: { dataType, pageCursor, sort },
query: { dataType, pageCursor, sort, showHidden },
match: {
params: { name: communityName },
},
@ -303,6 +310,7 @@ export class Community extends Component<CommunityRouteProps, State> {
sort,
type_: "All",
saved_only: false,
show_hidden: showHidden === "true",
};
postsFetch = client.getPosts(getPostsForm);
@ -481,6 +489,7 @@ export class Community extends Component<CommunityRouteProps, State> {
onTransferCommunity={this.handleTransferCommunity}
onFeaturePost={this.handleFeaturePost}
onMarkPostAsRead={async () => {}}
onHidePost={this.handleHidePost}
/>
);
}
@ -560,10 +569,7 @@ export class Community extends Component<CommunityRouteProps, State> {
}
selects(res: GetCommunityResponse) {
// let communityRss = this.state.communityRes.map(r =>
// communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
// );
const { dataType, sort } = this.props;
const { dataType, sort, showHidden } = this.props;
const communityRss = res
? communityRSSUrl(res.community_view.community.actor_id, sort)
: undefined;
@ -576,6 +582,14 @@ export class Community extends Component<CommunityRouteProps, State> {
onChange={this.handleDataTypeChange}
/>
</span>
{dataType === DataType.Post && UserService.Instance.myUserInfo && (
<span className="me-3">
<PostHiddenSelect
showHidden={showHidden}
onShowHiddenChange={this.handleShowHiddenChange}
/>
</span>
)}
<span className="me-2">
<SortSelect sort={sort} onChange={this.handleSortChange} />
</span>
@ -611,19 +625,36 @@ export class Community extends Component<CommunityRouteProps, State> {
this.updateUrl({ dataType, pageCursor: undefined });
}
handleShowHiddenChange(show?: StringBoolean) {
this.updateUrl({
showHidden: show,
pageCursor: undefined,
});
}
handleShowSidebarMobile(i: Community) {
i.setState(({ showSidebarMobile }) => ({
showSidebarMobile: !showSidebarMobile,
}));
}
async updateUrl({ dataType, pageCursor, sort }: Partial<CommunityProps>) {
const { dataType: urlDataType, sort: urlSort } = this.props;
async updateUrl({
dataType,
pageCursor,
sort,
showHidden,
}: Partial<CommunityProps>) {
const {
dataType: urlDataType,
sort: urlSort,
showHidden: urlShowHidden,
} = this.props;
const queryParams: QueryParams<CommunityProps> = {
dataType: getDataTypeString(dataType ?? urlDataType),
pageCursor: pageCursor,
sort: sort ?? urlSort,
showHidden: showHidden ?? urlShowHidden,
};
this.props.history.push(
@ -634,7 +665,7 @@ export class Community extends Component<CommunityRouteProps, State> {
}
async fetchData() {
const { dataType, pageCursor, sort } = this.props;
const { dataType, pageCursor, sort, showHidden } = this.props;
const { name } = this.props.match.params;
if (dataType === DataType.Post) {
@ -647,6 +678,7 @@ export class Community extends Component<CommunityRouteProps, State> {
type_: "All",
community_name: name,
saved_only: false,
show_hidden: showHidden === "true",
}),
});
} else {
@ -824,6 +856,26 @@ export class Community extends Component<CommunityRouteProps, State> {
this.findAndUpdatePost(lockRes);
}
async handleHidePost(form: HidePost) {
const hideRes = await HttpService.client.hidePost(form);
if (hideRes.state === "success") {
this.setState(prev => {
if (prev.postsRes.state === "success") {
for (const post of prev.postsRes.data.posts.filter(p =>
form.post_ids.some(id => id === p.post.id),
)) {
post.hidden = form.hide;
}
}
return prev;
});
toast(I18NextService.i18n.t(form.hide ? "post_hidden" : "post_unhidden"));
}
}
async handleDistinguishComment(form: DistinguishComment) {
const distinguishRes = await HttpService.client.distinguishComment(form);
this.findAndUpdateComment(distinguishRes);

View file

@ -21,7 +21,7 @@ import {
} from "@utils/helpers";
import { scrollMixin } from "../mixins/scroll-mixin";
import { canCreateCommunity } from "@utils/roles";
import type { QueryParams } from "@utils/types";
import type { QueryParams, StringBoolean } from "@utils/types";
import { RouteDataResponse } from "@utils/types";
import { NoOptionI18nKeys } from "i18next";
import { Component, MouseEventHandler, linkEvent } from "inferno";
@ -54,6 +54,7 @@ import {
GetPosts,
GetPostsResponse,
GetSiteResponse,
HidePost,
LemmyHttp,
ListCommunities,
ListCommunitiesResponse,
@ -109,6 +110,7 @@ import {
} from "../common/loading-skeleton";
import { RouteComponentProps } from "inferno-router/dist/Route";
import { IRoutePropsWithFetch } from "../../routes";
import PostHiddenSelect from "../common/post-hidden-select";
import { snapToTop } from "@utils/browser";
interface HomeState {
@ -130,6 +132,7 @@ interface HomeProps {
dataType: DataType;
sort: SortType;
pageCursor?: PaginationCursor;
showHidden?: StringBoolean;
}
type HomeData = RouteDataResponse<{
@ -206,6 +209,7 @@ export function getHomeQueryParams(
listingType: getListingTypeFromQuery,
pageCursor: (cursor?: string) => cursor,
dataType: getDataTypeFromQuery,
showHidden: (include?: StringBoolean) => include,
},
source,
{
@ -287,6 +291,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
this.handleSortChange = this.handleSortChange.bind(this);
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
this.handleShowHiddenChange = this.handleShowHiddenChange.bind(this);
this.handlePageNext = this.handlePageNext.bind(this);
this.handlePagePrev = this.handlePagePrev.bind(this);
@ -317,6 +322,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
this.handleSavePost = this.handleSavePost.bind(this);
this.handlePurgePost = this.handlePurgePost.bind(this);
this.handleFeaturePost = this.handleFeaturePost.bind(this);
this.handleHidePost = this.handleHidePost.bind(this);
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
@ -349,7 +355,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
}
static async fetchInitialData({
query: { listingType, dataType, sort, pageCursor },
query: { listingType, dataType, sort, pageCursor, showHidden },
headers,
}: InitialFetchRequest<HomePathProps, HomeProps>): Promise<HomeData> {
const client = wrapClient(
@ -368,6 +374,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
limit: fetchLimit,
sort,
saved_only: false,
show_hidden: showHidden === "true",
};
postsFetch = client.getPosts(getPostsForm);
@ -658,11 +665,13 @@ export class Home extends Component<HomeRouteProps, HomeState> {
listingType,
pageCursor,
sort,
showHidden,
}: Partial<HomeProps>) {
const {
dataType: urlDataType,
listingType: urlListingType,
sort: urlSort,
showHidden: urlShowHidden,
} = this.props;
const queryParams: QueryParams<HomeProps> = {
@ -670,6 +679,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
listingType: listingType ?? urlListingType,
pageCursor: pageCursor,
sort: sort ?? urlSort,
showHidden: showHidden ?? urlShowHidden,
};
this.props.history.push({
@ -739,6 +749,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
onTransferCommunity={this.handleTransferCommunity}
onFeaturePost={this.handleFeaturePost}
onMarkPostAsRead={async () => {}}
onHidePost={this.handleHidePost}
/>
);
}
@ -786,7 +797,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
}
get selects() {
const { listingType, dataType, sort } = this.props;
const { listingType, dataType, sort, showHidden } = this.props;
return (
<div className="row align-items-center mb-3 g-3">
@ -796,6 +807,14 @@ export class Home extends Component<HomeRouteProps, HomeState> {
onChange={this.handleDataTypeChange}
/>
</div>
{dataType === DataType.Post && UserService.Instance.myUserInfo && (
<div className="col-auto">
<PostHiddenSelect
showHidden={showHidden}
onShowHiddenChange={this.handleShowHiddenChange}
/>
</div>
)}
<div className="col-auto">
<ListingTypeSelect
type_={
@ -833,7 +852,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
}
async fetchData() {
const { dataType, pageCursor, listingType, sort } = this.props;
const { dataType, pageCursor, listingType, sort, showHidden } = this.props;
if (dataType === DataType.Post) {
this.setState({ postsRes: LOADING_REQUEST });
@ -844,6 +863,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
sort,
saved_only: false,
type_: listingType,
show_hidden: showHidden === "true",
}),
});
} else {
@ -899,6 +919,14 @@ export class Home extends Component<HomeRouteProps, HomeState> {
this.updateUrl({ dataType: val, pageCursor: undefined });
}
handleShowHiddenChange(show?: StringBoolean) {
console.log(`Got ${show}`);
this.updateUrl({
showHidden: show,
pageCursor: undefined,
});
}
async handleAddModToCommunity(form: AddModToCommunity) {
// TODO not sure what to do here
await HttpService.client.addModToCommunity(form);
@ -1049,6 +1077,26 @@ export class Home extends Component<HomeRouteProps, HomeState> {
this.updateBan(banRes);
}
async handleHidePost(form: HidePost) {
const hideRes = await HttpService.client.hidePost(form);
if (hideRes.state === "success") {
this.setState(prev => {
if (prev.postsRes.state === "success") {
for (const post of prev.postsRes.data.posts.filter(p =>
form.post_ids.some(id => id === p.post.id),
)) {
post.hidden = form.hide;
}
}
return prev;
});
toast(I18NextService.i18n.t(form.hide ? "post_hidden" : "post_unhidden"));
}
}
updateBanFromCommunity(banRes: RequestState<BanFromCommunityResponse>) {
// Maybe not necessary
if (banRes.state === "success") {

View file

@ -210,6 +210,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
onAddAdmin={this.props.onAddAdmin}
onTransferCommunity={this.props.onTransferCommunity}
onMarkPostAsRead={this.props.onMarkPostAsRead}
onHidePost={async () => {}}
/>
);
}
@ -322,6 +323,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
onAddAdmin={this.props.onAddAdmin}
onTransferCommunity={this.props.onTransferCommunity}
onMarkPostAsRead={this.props.onMarkPostAsRead}
onHidePost={async () => {}}
/>
<hr className="my-3" />
</>

View file

@ -475,6 +475,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
onAddAdmin={async () => {}}
onTransferCommunity={async () => {}}
onMarkPostAsRead={async () => {}}
onHidePost={async () => {}}
/>
</>
)}
@ -688,6 +689,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
onAddAdmin={async () => {}}
onTransferCommunity={async () => {}}
onMarkPostAsRead={async () => {}}
onHidePost={async () => {}}
/>
</>
)

View file

@ -19,6 +19,7 @@ import {
DeletePost,
EditPost,
FeaturePost,
HidePost,
Language,
LockPost,
MarkPostAsRead,
@ -91,6 +92,7 @@ interface PostListingProps {
onAddAdmin(form: AddAdmin): Promise<void>;
onTransferCommunity(form: TransferCommunity): Promise<void>;
onMarkPostAsRead(form: MarkPostAsRead): void;
onHidePost(form: HidePost): Promise<void>;
onScrollIntoCommentsClick?(e: MouseEvent): void;
}
@ -126,6 +128,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this.handleModBanFromSite = this.handleModBanFromSite.bind(this);
this.handlePurgePerson = this.handlePurgePerson.bind(this);
this.handlePurgePost = this.handlePurgePost.bind(this);
this.handleHidePost = this.handleHidePost.bind(this);
}
componentDidMount(): void {
@ -611,6 +614,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
onPurgeUser={this.handlePurgePerson}
onPurgeContent={this.handlePurgePost}
onAppointAdmin={this.handleAppointAdmin}
onHidePost={this.handleHidePost}
/>
)}
</div>
@ -907,6 +911,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
});
}
handleHidePost() {
return this.props.onHidePost({
hide: !this.postView.hidden,
post_ids: [this.postView.post.id],
});
}
handleModBanFromCommunity({
daysUntilExpires,
reason,

View file

@ -12,6 +12,7 @@ import {
DeletePost,
EditPost,
FeaturePost,
HidePost,
Language,
LockPost,
MarkPostAsRead,
@ -53,6 +54,7 @@ interface PostListingsProps {
onAddAdmin(form: AddAdmin): Promise<void>;
onTransferCommunity(form: TransferCommunity): Promise<void>;
onMarkPostAsRead(form: MarkPostAsRead): Promise<void>;
onHidePost(form: HidePost): Promise<void>;
}
export class PostListings extends Component<PostListingsProps, any> {
@ -100,6 +102,7 @@ export class PostListings extends Component<PostListingsProps, any> {
onAddAdmin={this.props.onAddAdmin}
onTransferCommunity={this.props.onTransferCommunity}
onMarkPostAsRead={this.props.onMarkPostAsRead}
onHidePost={this.props.onHidePost}
/>
{idx + 1 !== this.posts.length && <hr className="my-3" />}
</>

View file

@ -94,6 +94,7 @@ export class PostReport extends Component<PostReportProps, PostReportState> {
onAddAdmin={async () => {}}
onTransferCommunity={async () => {}}
onMarkPostAsRead={async () => {}}
onHidePost={async () => {}}
/>
<div>
{I18NextService.i18n.t("reporter")}:{" "}

View file

@ -61,6 +61,7 @@ import {
GetPost,
GetPostResponse,
GetSiteResponse,
HidePost,
LemmyHttp,
LockPost,
MarkCommentReplyAsRead,
@ -195,6 +196,7 @@ export class Post extends Component<PostRouteProps, PostState> {
this.handleSavePost = this.handleSavePost.bind(this);
this.handlePurgePost = this.handlePurgePost.bind(this);
this.handleFeaturePost = this.handleFeaturePost.bind(this);
this.handleHidePost = this.handleHidePost.bind(this);
this.handleScrollIntoCommentsClick =
this.handleScrollIntoCommentsClick.bind(this);
@ -405,6 +407,7 @@ export class Post extends Component<PostRouteProps, PostState> {
onTransferCommunity={this.handleTransferCommunity}
onFeaturePost={this.handleFeaturePost}
onMarkPostAsRead={() => {}}
onHidePost={this.handleHidePost}
onScrollIntoCommentsClick={this.handleScrollIntoCommentsClick}
/>
<div ref={this.state.commentSectionRef} className="mb-2" />
@ -1044,6 +1047,22 @@ export class Post extends Component<PostRouteProps, PostState> {
}
}
async handleHidePost(form: HidePost) {
const hideRes = await HttpService.client.hidePost(form);
if (hideRes.state === "success") {
this.setState(s => {
if (s.postRes.state === "success") {
s.postRes.data.post_view.hidden = form.hide;
}
return s;
});
toast(I18NextService.i18n.t(form.hide ? "post_hidden" : "post_unhidden"));
}
}
updateBanFromCommunity(banRes: RequestState<BanFromCommunityResponse>) {
// Maybe not necessary
if (banRes.state === "success") {

View file

@ -741,6 +741,7 @@ export class Search extends Component<SearchRouteProps, SearchState> {
onAddAdmin={async () => {}}
onTransferCommunity={async () => {}}
onMarkPostAsRead={async () => {}}
onHidePost={async () => {}}
/>
)}
{i.type_ === "comments" && (
@ -892,6 +893,7 @@ export class Search extends Component<SearchRouteProps, SearchState> {
onAddAdmin={async () => {}}
onTransferCommunity={async () => {}}
onMarkPostAsRead={() => {}}
onHidePost={async () => {}}
/>
</div>
</div>

View file

@ -7,6 +7,7 @@ import { RouteDataResponse } from "./route-data-response";
import { ThemeColor } from "./theme-color";
import WithComment from "./with-comment";
import CrossPostParams from "./cross-post-params";
import StringBoolean from "./string-boolean";
export {
Choice,
@ -18,4 +19,5 @@ export {
ThemeColor,
WithComment,
CrossPostParams,
StringBoolean,
};

View file

@ -0,0 +1,3 @@
type StringBoolean = "true" | "false";
export default StringBoolean;