Create post query params (#2515)

* Allow create post page form inputs to be populated from query params

* Fix issue with cross post params.

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>
This commit is contained in:
SleeplessOne1917 2024-06-06 22:56:17 -04:00 committed by GitHub
parent 06b5925e85
commit 7003b564a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 184 additions and 33 deletions

View file

@ -77,8 +77,6 @@ export class ImageUploadForm extends Component<
i.setState({ loading: true });
HttpService.client.uploadImage({ image }).then(res => {
console.log("pictrs upload:");
console.log(res);
if (res.state === "success") {
if (res.data.msg === "ok") {
i.props.onUpload(res.data.url as string);

View file

@ -47,6 +47,7 @@ interface MarkdownTextAreaProps {
showLanguage?: boolean;
hideNavigationWarnings?: boolean;
onContentChange?(val: string): void;
onContentBlur?(val: string): void;
onReplyCancel?(): void;
onSubmit?(content: string, languageId?: number): Promise<boolean>;
allLanguages: Language[]; // TODO should probably be nullable
@ -215,6 +216,7 @@ export class MarkdownTextArea extends Component<
)}
value={this.state.content}
onInput={linkEvent(this, this.handleContentChange)}
onBlur={linkEvent(this, this.handleContentBlur)}
onPaste={linkEvent(this, this.handlePaste)}
onKeyDown={linkEvent(this, this.handleKeyBinds)}
required
@ -457,8 +459,6 @@ export class MarkdownTextArea extends Component<
async uploadSingleImage(i: MarkdownTextArea, image: File) {
const res = await HttpService.client.uploadImage({ image });
console.log("pictrs upload:");
console.log(res);
if (res.state === "success") {
if (res.data.msg === "ok") {
const imageMarkdown = `![](${res.data.url})`;
@ -496,6 +496,10 @@ export class MarkdownTextArea extends Component<
i.contentChange();
}
handleContentBlur(i: MarkdownTextArea, event: any) {
i.props.onContentBlur?.(event.target.value);
}
// Keybind handler
// Keybinds inspired by github comment area
handleKeyBinds(i: MarkdownTextArea, event: KeyboardEvent) {

View file

@ -497,8 +497,6 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
}));
HttpService.client.uploadImage({ image: file }).then(res => {
console.log("pictrs upload:");
console.log(res);
if (res.state === "success") {
if (res.data.msg === "ok") {
pictrsDeleteToast(file.name, res.data.delete_url as string);

View file

@ -930,7 +930,6 @@ export class Home extends Component<HomeRouteProps, HomeState> {
}
handleShowHiddenChange(show?: StringBoolean) {
console.log(`Got ${show}`);
this.updateUrl({
showHidden: show,
pageCursor: undefined,

View file

@ -5,8 +5,18 @@ import {
setIsoData,
voteDisplayMode,
} from "@utils/app";
import { getIdFromString, getQueryParams } from "@utils/helpers";
import { Choice, RouteDataResponse } from "@utils/types";
import {
getIdFromString,
getQueryParams,
getQueryString,
} from "@utils/helpers";
import {
Choice,
CrossPostParams,
QueryParams,
RouteDataResponse,
StringBoolean,
} from "@utils/types";
import { Component } from "inferno";
import { RouteComponentProps } from "inferno-router/dist/Route";
import {
@ -36,6 +46,13 @@ import { isBrowser } from "@utils/browser";
export interface CreatePostProps {
communityId?: number;
url?: string;
title?: string;
body?: string;
languageId?: number;
nsfw?: StringBoolean;
customThumbnailUrl?: string;
altText?: string;
}
type CreatePostData = RouteDataResponse<{
@ -47,6 +64,13 @@ export function getCreatePostQueryParams(source?: string): CreatePostProps {
return getQueryParams<CreatePostProps>(
{
communityId: getIdFromString,
url: (url?: string) => url,
body: (body?: string) => body,
languageId: getIdFromString,
nsfw: (nsfw?: StringBoolean) => nsfw,
customThumbnailUrl: (customThumbnailUrl?: string) => customThumbnailUrl,
title: (title?: string) => title,
altText: (altText?: string) => altText,
},
source,
);
@ -92,8 +116,15 @@ export class CreatePost extends Component<
this.handlePostCreate = this.handlePostCreate.bind(this);
this.handleSelectedCommunityChange =
this.handleSelectedCommunityChange.bind(this);
this.handleTitleBlur = this.handleTitleBlur.bind(this);
this.handleUrlBlur = this.handleUrlBlur.bind(this);
this.handleBodyBlur = this.handleBodyBlur.bind(this);
this.handleLanguageChange = this.handleLanguageChange.bind(this);
this.handleNsfwChange = this.handleNsfwChange.bind(this);
this.handleThumbnailUrlBlur = this.handleThumbnailUrlBlur.bind(this);
this.handleAltTextBlur = this.handleAltTextBlur.bind(this);
// Only fetch the data if coming from another route
// Only fetch the data if coming from another routeupdate
if (FirstLoadService.isFirstLoad) {
const { communityResponse: communityRes, initialCommunitiesRes } =
this.isoData.routeData;
@ -166,11 +197,31 @@ export class CreatePost extends Component<
render() {
const { selectedCommunityChoice, siteRes, loading } = this.state;
const {
body,
communityId,
customThumbnailUrl,
languageId,
title,
nsfw,
url,
} = this.props;
// Only use the name, url, and body from this
const locationState = this.props.history.location.state as
| PostFormParams
| CrossPostParams
| undefined;
const params: PostFormParams = {
name: title || locationState?.name,
url: url || locationState?.url,
body: body || locationState?.body,
community_id: communityId,
custom_thumbnail: customThumbnailUrl,
language_id: languageId,
nsfw: nsfw === "true",
};
return (
<div className="create-post container-lg">
<HtmlTags
@ -182,7 +233,7 @@ export class CreatePost extends Component<
<h1 className="h4 mb-4">{I18NextService.i18n.t("create_post")}</h1>
<PostForm
onCreate={this.handlePostCreate}
params={locationState}
params={params}
enableDownvotes={enableDownvotes(siteRes)}
voteDisplayMode={voteDisplayMode(siteRes)}
enableNsfw={enableNsfw(siteRes)}
@ -196,6 +247,12 @@ export class CreatePost extends Component<
: []
}
loading={loading}
onBodyBlur={this.handleBodyBlur}
onLanguageChange={this.handleLanguageChange}
onTitleBlur={this.handleTitleBlur}
onUrlBlur={this.handleUrlBlur}
onThumbnailUrlBlur={this.handleThumbnailUrlBlur}
onNsfwChange={this.handleNsfwChange}
/>
</div>
</div>
@ -203,23 +260,36 @@ export class CreatePost extends Component<
);
}
async updateUrl({ communityId }: Partial<CreatePostProps>) {
const locationState = this.props.history.location.state as
| PostFormParams
| undefined;
async updateUrl(props: Partial<CreatePostProps>) {
const {
body,
communityId,
customThumbnailUrl,
languageId,
nsfw,
url,
title,
altText,
} = {
...this.props,
...props,
};
const url = new URL(location.href);
const createPostQueryParams: QueryParams<CreatePostProps> = {
body,
communityId: communityId?.toString(),
customThumbnailUrl,
languageId: languageId?.toString(),
title,
nsfw,
url,
altText,
};
const newId = communityId?.toString();
if (newId !== undefined) {
url.searchParams.set("communityId", newId);
} else {
url.searchParams.delete("communityId");
}
// This bypasses the router and doesn't update the query props.
window.history.replaceState(locationState, "", url);
this.props.history.replace({
pathname: "/create_post",
search: getQueryString(createPostQueryParams),
});
await this.fetchCommunity({ communityId });
}
@ -230,6 +300,34 @@ export class CreatePost extends Component<
});
}
handleTitleBlur(title: string) {
this.updateUrl({ title });
}
handleUrlBlur(url: string) {
this.updateUrl({ url });
}
handleBodyBlur(body: string) {
this.updateUrl({ body });
}
handleLanguageChange(languageId: number) {
this.updateUrl({ languageId });
}
handleNsfwChange(nsfw: StringBoolean) {
this.updateUrl({ nsfw });
}
handleThumbnailUrlBlur(customThumbnailUrl: string) {
this.updateUrl({ customThumbnailUrl });
}
handleAltTextBlur(altText: string) {
this.updateUrl({ altText });
}
async handlePostCreate(form: CreatePostI, bypassNavWarning: () => void) {
this.setState({ loading: true });
const res = await HttpService.client.createPost(form);

View file

@ -8,7 +8,7 @@ import {
validURL,
} from "@utils/helpers";
import { isImage } from "@utils/media";
import { Choice } from "@utils/types";
import { Choice, StringBoolean } from "@utils/types";
import autosize from "autosize";
import { Component, InfernoNode, createRef, linkEvent } from "inferno";
import { Prompt } from "inferno-router";
@ -63,6 +63,13 @@ interface PostFormProps {
onSelectCommunity?: (choice: Choice) => void;
initialCommunities?: CommunityView[];
loading: boolean;
onTitleBlur?: (title: string) => void;
onUrlBlur?: (url: string) => void;
onBodyBlur?: (body: string) => void;
onLanguageChange?: (languageId?: number) => void;
onNsfwChange?: (nsfw: StringBoolean) => void;
onThumbnailUrlBlur?: (thumbnailUrl: string) => void;
onAltTextBlur?: (altText: string) => void;
}
interface PostFormState {
@ -167,8 +174,18 @@ function handlePostUrlChange(i: PostForm, event: any) {
i.fetchPageTitle();
}
function handlePostUrlBlur(i: PostForm, event: any) {
i.setState({ bypassNavWarning: true });
i.props.onUrlBlur?.(event.target.value);
i.setState({ bypassNavWarning: false });
}
function handlePostNsfwChange(i: PostForm, event: any) {
i.setState(s => ((s.form.nsfw = event.target.checked), s));
i.setState({ bypassNavWarning: true });
i.props.onNsfwChange?.(event.target.checked ? "true" : "false");
i.setState({ bypassNavWarning: false });
}
function handleHoneyPotChange(i: PostForm, event: any) {
@ -179,10 +196,22 @@ function handleAltTextChange(i: PostForm, event: any) {
i.setState(s => ((s.form.alt_text = event.target.value), s));
}
function handleAltTextBlur(i: PostForm, event: any) {
i.setState({ bypassNavWarning: true });
i.props.onAltTextBlur?.(event.target.value);
i.setState({ bypassNavWarning: false });
}
function handleCustomThumbnailChange(i: PostForm, event: any) {
i.setState(s => ((s.form.custom_thumbnail = event.target.value), s));
}
function handleCustomThumbnailBlur(i: PostForm, event: any) {
i.setState({ bypassNavWarning: true });
i.props.onThumbnailUrlBlur?.(event.target.value);
i.setState({ bypassNavWarning: false });
}
function handleCancel(i: PostForm) {
i.props.onCancel?.();
}
@ -206,8 +235,6 @@ function handleImageUpload(i: PostForm, event: any) {
i.setState({ imageLoading: true });
HttpService.client.uploadImage({ image: file }).then(res => {
console.log("pictrs upload:");
console.log(res);
if (res.state === "success") {
if (res.data.msg === "ok") {
i.state.form.url = res.data.url;
@ -233,6 +260,12 @@ function handlePostNameChange(i: PostForm, event: any) {
i.fetchSimilarPosts();
}
function handlePostNameBlur(i: PostForm, event: any) {
i.setState({ bypassNavWarning: true });
i.props.onTitleBlur?.(event.target.value);
i.setState({ bypassNavWarning: false });
}
function handleImageDelete(i: PostForm) {
const { imageDeleteUrl } = i.state;
@ -270,6 +303,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
this.fetchSimilarPosts = debounce(this.fetchSimilarPosts.bind(this));
this.fetchPageTitle = debounce(this.fetchPageTitle.bind(this));
this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
this.handlePostBodyBlur = this.handlePostBodyBlur.bind(this);
this.handleLanguageChange = this.handleLanguageChange.bind(this);
this.handleCommunitySelect = this.handleCommunitySelect.bind(this);
@ -393,6 +427,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
value={this.state.form.name}
id="post-title"
onInput={linkEvent(this, handlePostNameChange)}
onBlur={linkEvent(this, handlePostNameBlur)}
className={`form-control ${
!validTitle(this.state.form.name) && "is-invalid"
}`}
@ -423,6 +458,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
className="form-control mb-3"
value={url}
onInput={linkEvent(this, handlePostUrlChange)}
onBlur={linkEvent(this, handlePostUrlBlur)}
onPaste={linkEvent(this, handleImageUploadPaste)}
/>
{this.renderSuggestedTitleCopy()}
@ -538,6 +574,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
className="form-control mb-3"
value={this.state.form.custom_thumbnail}
onInput={linkEvent(this, handleCustomThumbnailChange)}
onBlur={linkEvent(this, handleCustomThumbnailBlur)}
/>
</div>
</div>
@ -552,6 +589,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
initialContent={this.state.form.body}
placeholder={I18NextService.i18n.t("optional")}
onContentChange={this.handlePostBodyChange}
onContentBlur={this.handlePostBodyBlur}
allLanguages={this.props.allLanguages}
siteLanguages={this.props.siteLanguages}
hideNavigationWarnings
@ -581,6 +619,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
id="post-alt-text"
value={this.state.form.alt_text}
onInput={linkEvent(this, handleAltTextChange)}
onBlur={linkEvent(this, handleAltTextBlur)}
/>
</div>
</div>
@ -776,8 +815,18 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
this.setState(s => ((s.form.body = val), s));
}
handlePostBodyBlur(val: string) {
this.setState({ bypassNavWarning: true });
this.props.onBodyBlur?.(val);
this.setState({ bypassNavWarning: false });
}
handleLanguageChange(val: number[]) {
this.setState(s => ((s.form.language_id = val.at(0)), s));
this.setState({ bypassNavWarning: true });
this.props.onLanguageChange?.(val.at(0));
this.setState({ bypassNavWarning: false });
}
handleCommunitySearch = debounce(async (text: string) => {
@ -804,8 +853,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
});
handleCommunitySelect(choice: Choice) {
if (this.props.onSelectCommunity) {
this.props.onSelectCommunity(choice);
}
this.setState({ bypassNavWarning: true });
this.props.onSelectCommunity?.(choice);
this.setState({ bypassNavWarning: false });
}
}

View file

@ -46,6 +46,11 @@ export interface PostFormParams {
name?: string;
url?: string;
body?: string;
nsfw?: boolean;
language_id?: number;
community_id?: number;
custom_thumbnail?: string;
alt_text?: string;
}
export enum CommentViewType {