mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-21 11:51:13 +00:00
Fix some submit button issues (#2487)
* Prevent PostForm submit button spam * Keep CreatePost PostForm visible during submission * Keep PostListing PostForm visible during submission * Keep PostForm navigation warning enabled during submission * Remove `finished` from MarkdownTextAreaProps * Handle CommentForm submission failures * Keep CommentForm navigation warning enabled during submission * Handle PrivateMessageForm submission failures * Bypass navigation warning for successful CreatePrivateMessage * Fix absolute import, add eslint rule * Cleaner handleCommentSubmit --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
This commit is contained in:
parent
14ae45fe95
commit
02fcfa26ee
19 changed files with 270 additions and 251 deletions
|
@ -79,6 +79,17 @@ export default [
|
|||
"unicorn/filename-case": 0,
|
||||
"jsx-a11y/media-has-caption": 0,
|
||||
"jsx-a11y/label-has-associated-control": 0,
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: ["assets/*", "client/*", "server/*", "shared/*"],
|
||||
message: "Use relative import instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -2,18 +2,23 @@ import { capitalizeFirstLetter } from "@utils/helpers";
|
|||
import { Component } from "inferno";
|
||||
import { T } from "inferno-i18next-dess";
|
||||
import { Link } from "inferno-router";
|
||||
import { CreateComment, EditComment, Language } from "lemmy-js-client";
|
||||
import {
|
||||
CommentResponse,
|
||||
CreateComment,
|
||||
EditComment,
|
||||
Language,
|
||||
} from "lemmy-js-client";
|
||||
import { CommentNodeI } from "../../interfaces";
|
||||
import { I18NextService, UserService } from "../../services";
|
||||
import { Icon } from "../common/icon";
|
||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||
import { RequestState } from "../../services/HttpService";
|
||||
|
||||
interface CommentFormProps {
|
||||
/**
|
||||
* Can either be the parent, or the editable comment. The right side is a postId.
|
||||
*/
|
||||
node: CommentNodeI | number;
|
||||
finished?: boolean;
|
||||
edit?: boolean;
|
||||
disabled?: boolean;
|
||||
focus?: boolean;
|
||||
|
@ -21,7 +26,9 @@ interface CommentFormProps {
|
|||
allLanguages: Language[];
|
||||
siteLanguages: number[];
|
||||
containerClass?: string;
|
||||
onUpsertComment(form: EditComment | CreateComment): void;
|
||||
onUpsertComment(
|
||||
form: EditComment | CreateComment,
|
||||
): Promise<RequestState<CommentResponse>>;
|
||||
}
|
||||
|
||||
export class CommentForm extends Component<CommentFormProps, any> {
|
||||
|
@ -50,7 +57,6 @@ export class CommentForm extends Component<CommentFormProps, any> {
|
|||
initialContent={initialContent}
|
||||
showLanguage
|
||||
buttonTitle={this.buttonTitle}
|
||||
finished={this.props.finished}
|
||||
replyType={typeof this.props.node !== "number"}
|
||||
focus={this.props.focus}
|
||||
disabled={this.props.disabled}
|
||||
|
@ -83,33 +89,38 @@ export class CommentForm extends Component<CommentFormProps, any> {
|
|||
: capitalizeFirstLetter(I18NextService.i18n.t("reply"));
|
||||
}
|
||||
|
||||
handleCommentSubmit(content: string, language_id?: number) {
|
||||
async handleCommentSubmit(
|
||||
content: string,
|
||||
language_id?: number,
|
||||
): Promise<boolean> {
|
||||
const { node, onUpsertComment, edit } = this.props;
|
||||
let response: RequestState<CommentResponse>;
|
||||
|
||||
if (typeof node === "number") {
|
||||
const post_id = node;
|
||||
onUpsertComment({
|
||||
response = await onUpsertComment({
|
||||
content,
|
||||
post_id,
|
||||
language_id,
|
||||
});
|
||||
} else if (edit) {
|
||||
const comment_id = node.comment_view.comment.id;
|
||||
response = await onUpsertComment({
|
||||
content,
|
||||
comment_id,
|
||||
language_id,
|
||||
});
|
||||
} else {
|
||||
if (edit) {
|
||||
const comment_id = node.comment_view.comment.id;
|
||||
onUpsertComment({
|
||||
content,
|
||||
comment_id,
|
||||
language_id,
|
||||
});
|
||||
} else {
|
||||
const post_id = node.comment_view.post.id;
|
||||
const parent_id = node.comment_view.comment.id;
|
||||
this.props.onUpsertComment({
|
||||
content,
|
||||
parent_id,
|
||||
post_id,
|
||||
language_id,
|
||||
});
|
||||
}
|
||||
const post_id = node.comment_view.post.id;
|
||||
const parent_id = node.comment_view.comment.id;
|
||||
response = await onUpsertComment({
|
||||
content,
|
||||
parent_id,
|
||||
post_id,
|
||||
language_id,
|
||||
});
|
||||
}
|
||||
|
||||
return response.state !== "failed";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { colorList, getCommentParentId } from "@utils/app";
|
|||
import { futureDaysToUnixTime, numToSI } from "@utils/helpers";
|
||||
import classNames from "classnames";
|
||||
import { isBefore, parseISO, subMinutes } from "date-fns";
|
||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||
import { Component, linkEvent } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import {
|
||||
AddAdmin,
|
||||
|
@ -33,7 +33,6 @@ import {
|
|||
SaveComment,
|
||||
TransferCommunity,
|
||||
} from "lemmy-js-client";
|
||||
import deepEqual from "lodash.isequal";
|
||||
import { commentTreeMaxDepth } from "../../config";
|
||||
import {
|
||||
CommentNodeI,
|
||||
|
@ -87,7 +86,6 @@ interface CommentNodeProps {
|
|||
allLanguages: Language[];
|
||||
siteLanguages: number[];
|
||||
hideImages?: boolean;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
onSaveComment(form: SaveComment): Promise<void>;
|
||||
onCommentReplyRead(form: MarkCommentReplyAsRead): void;
|
||||
onPersonMentionRead(form: MarkPersonMentionAsRead): void;
|
||||
|
@ -139,6 +137,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
super(props, context);
|
||||
|
||||
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
||||
this.handleCreateComment = this.handleCreateComment.bind(this);
|
||||
this.handleEditComment = this.handleEditComment.bind(this);
|
||||
this.handleReportComment = this.handleReportComment.bind(this);
|
||||
this.handleRemoveComment = this.handleRemoveComment.bind(this);
|
||||
this.handleReplyClick = this.handleReplyClick.bind(this);
|
||||
|
@ -164,22 +164,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
return this.commentView.comment.id;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(
|
||||
nextProps: Readonly<{ children?: InfernoNode } & CommentNodeProps>,
|
||||
): void {
|
||||
if (!deepEqual(this.props, nextProps)) {
|
||||
this.setState({
|
||||
showEdit: false,
|
||||
showAdvanced: false,
|
||||
createOrEditCommentLoading: false,
|
||||
upvoteLoading: false,
|
||||
downvoteLoading: false,
|
||||
readLoading: false,
|
||||
fetchChildrenLoading: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const node = this.props.node;
|
||||
const cv = this.commentView;
|
||||
|
@ -283,12 +267,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
edit
|
||||
onReplyCancel={this.handleReplyCancel}
|
||||
disabled={this.props.locked}
|
||||
finished={this.props.finished.get(id)}
|
||||
focus
|
||||
allLanguages={this.props.allLanguages}
|
||||
siteLanguages={this.props.siteLanguages}
|
||||
containerClass="comment-comment-container"
|
||||
onUpsertComment={this.props.onEditComment}
|
||||
onUpsertComment={this.handleEditComment}
|
||||
/>
|
||||
)}
|
||||
{!this.state.showEdit && !this.state.collapsed && (
|
||||
|
@ -425,12 +408,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
node={node}
|
||||
onReplyCancel={this.handleReplyCancel}
|
||||
disabled={this.props.locked}
|
||||
finished={this.props.finished.get(id)}
|
||||
focus
|
||||
allLanguages={this.props.allLanguages}
|
||||
siteLanguages={this.props.siteLanguages}
|
||||
containerClass="comment-comment-container"
|
||||
onUpsertComment={this.props.onCreateComment}
|
||||
onUpsertComment={this.handleCreateComment}
|
||||
/>
|
||||
)}
|
||||
{!this.state.collapsed && node.children.length > 0 && (
|
||||
|
@ -447,7 +429,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
hideImages={this.props.hideImages}
|
||||
isChild={!this.props.isTopLevel}
|
||||
depth={this.props.node.depth + 1}
|
||||
finished={this.props.finished}
|
||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||
onPersonMentionRead={this.props.onPersonMentionRead}
|
||||
onCreateComment={this.props.onCreateComment}
|
||||
|
@ -559,6 +540,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.setState({ showReply: false, showEdit: false });
|
||||
}
|
||||
|
||||
async handleCreateComment(
|
||||
form: CreateComment,
|
||||
): Promise<RequestState<CommentResponse>> {
|
||||
const res = await this.props.onCreateComment(form);
|
||||
if (res.state !== "failed") {
|
||||
this.setState({ showReply: false, showEdit: false });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async handleEditComment(
|
||||
form: EditComment,
|
||||
): Promise<RequestState<CommentResponse>> {
|
||||
const res = await this.props.onEditComment(form);
|
||||
if (res.state !== "failed") {
|
||||
this.setState({ showReply: false, showEdit: false });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
isPersonMentionType(item: CommentNodeView): item is PersonMentionView {
|
||||
return item.person_mention?.id !== undefined;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
BanFromCommunity,
|
||||
BanPerson,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentResponse,
|
||||
CommunityModeratorView,
|
||||
CreateComment,
|
||||
|
@ -52,7 +51,6 @@ interface CommentNodesProps {
|
|||
hideImages?: boolean;
|
||||
isChild?: boolean;
|
||||
depth?: number;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
onSaveComment(form: SaveComment): Promise<void>;
|
||||
onCommentReplyRead(form: MarkCommentReplyAsRead): void;
|
||||
onPersonMentionRead(form: MarkPersonMentionAsRead): void;
|
||||
|
@ -124,7 +122,6 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
|||
hideImages={this.props.hideImages}
|
||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||
onPersonMentionRead={this.props.onPersonMentionRead}
|
||||
finished={this.props.finished}
|
||||
onCreateComment={this.props.onCreateComment}
|
||||
onEditComment={this.props.onEditComment}
|
||||
onCommentVote={this.props.onCommentVote}
|
||||
|
|
|
@ -90,7 +90,6 @@ export class CommentReport extends Component<
|
|||
siteLanguages={[]}
|
||||
hideImages
|
||||
// All of these are unused, since its viewonly
|
||||
finished={new Map()}
|
||||
onSaveComment={async () => {}}
|
||||
onBlockPerson={async () => {}}
|
||||
onDeleteComment={async () => {}}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { numToSI, randomStr } from "@utils/helpers";
|
|||
import autosize from "autosize";
|
||||
import classNames from "classnames";
|
||||
import { NoOptionI18nKeys } from "i18next";
|
||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||
import { Component, linkEvent } from "inferno";
|
||||
import { Prompt } from "inferno-router";
|
||||
import { Language } from "lemmy-js-client";
|
||||
import {
|
||||
|
@ -41,7 +41,6 @@ interface MarkdownTextAreaProps {
|
|||
replyType?: boolean;
|
||||
focus?: boolean;
|
||||
disabled?: boolean;
|
||||
finished?: boolean;
|
||||
/**
|
||||
* Whether to show the language selector
|
||||
*/
|
||||
|
@ -49,7 +48,7 @@ interface MarkdownTextAreaProps {
|
|||
hideNavigationWarnings?: boolean;
|
||||
onContentChange?(val: string): void;
|
||||
onReplyCancel?(): void;
|
||||
onSubmit?(content: string, languageId?: number): void;
|
||||
onSubmit?(content: string, languageId?: number): Promise<boolean>;
|
||||
allLanguages: Language[]; // TODO should probably be nullable
|
||||
siteLanguages: number[]; // TODO same
|
||||
}
|
||||
|
@ -115,27 +114,6 @@ export class MarkdownTextArea extends Component<
|
|||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(
|
||||
nextProps: MarkdownTextAreaProps & { children?: InfernoNode },
|
||||
) {
|
||||
if (nextProps.finished) {
|
||||
this.setState({
|
||||
previewMode: false,
|
||||
imageUploadStatus: undefined,
|
||||
loading: false,
|
||||
content: undefined,
|
||||
});
|
||||
if (this.props.replyType) {
|
||||
this.props.onReplyCancel?.();
|
||||
}
|
||||
|
||||
const textarea: any = document.getElementById(this.id);
|
||||
const form: any = document.getElementById(this.formId);
|
||||
form.reset();
|
||||
setTimeout(() => autosize.update(textarea), 10);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const languageId = this.state.languageId;
|
||||
|
||||
|
@ -149,8 +127,8 @@ export class MarkdownTextArea extends Component<
|
|||
message={I18NextService.i18n.t("block_leaving")}
|
||||
when={
|
||||
!this.props.hideNavigationWarnings &&
|
||||
!!this.state.content &&
|
||||
!this.state.submitted
|
||||
((!!this.state.content && !this.state.submitted) ||
|
||||
this.state.loading)
|
||||
}
|
||||
/>
|
||||
<div className="mb-3 row">
|
||||
|
@ -575,11 +553,15 @@ export class MarkdownTextArea extends Component<
|
|||
this.setState({ languageId: val[0] });
|
||||
}
|
||||
|
||||
handleSubmit(i: MarkdownTextArea, event: any) {
|
||||
async handleSubmit(i: MarkdownTextArea, event: any) {
|
||||
event.preventDefault();
|
||||
if (i.state.content) {
|
||||
i.setState({ loading: true, submitted: true });
|
||||
i.props.onSubmit?.(i.state.content, i.state.languageId);
|
||||
const success = await i.props.onSubmit?.(
|
||||
i.state.content,
|
||||
i.state.languageId,
|
||||
);
|
||||
i.setState({ loading: false, submitted: success ?? true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
editWith,
|
||||
enableDownvotes,
|
||||
enableNsfw,
|
||||
getCommentParentId,
|
||||
getDataTypeString,
|
||||
postToCommentSortType,
|
||||
setIsoData,
|
||||
|
@ -42,7 +41,6 @@ import {
|
|||
BanPersonResponse,
|
||||
BlockCommunity,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentReplyResponse,
|
||||
CommentResponse,
|
||||
CommunityResponse,
|
||||
|
@ -136,7 +134,6 @@ interface State {
|
|||
commentsRes: RequestState<GetCommentsResponse>;
|
||||
siteRes: GetSiteResponse;
|
||||
showSidebarMobile: boolean;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
isIsomorphic: boolean;
|
||||
}
|
||||
|
||||
|
@ -201,7 +198,6 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
commentsRes: EMPTY_REQUEST,
|
||||
siteRes: this.isoData.site_res,
|
||||
showSidebarMobile: false,
|
||||
finished: new Map(),
|
||||
isIsomorphic: false,
|
||||
};
|
||||
private readonly mainContentRef: RefObject<HTMLElement>;
|
||||
|
@ -528,7 +524,6 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
<CommentNodes
|
||||
nodes={commentsToFlatNodes(this.state.commentsRes.data.comments)}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.state.finished}
|
||||
isTopLevel
|
||||
showContext
|
||||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
|
@ -820,6 +815,9 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
const createCommentRes = await HttpService.client.createComment(form);
|
||||
this.createAndUpdateComments(createCommentRes);
|
||||
|
||||
if (createCommentRes.state === "failed") {
|
||||
toast(I18NextService.i18n.t(createCommentRes.err.message), "danger");
|
||||
}
|
||||
return createCommentRes;
|
||||
}
|
||||
|
||||
|
@ -827,6 +825,9 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
const editCommentRes = await HttpService.client.editComment(form);
|
||||
this.findAndUpdateCommentEdit(editCommentRes);
|
||||
|
||||
if (editCommentRes.state === "failed") {
|
||||
toast(I18NextService.i18n.t(editCommentRes.err.message), "danger");
|
||||
}
|
||||
return editCommentRes;
|
||||
}
|
||||
|
||||
|
@ -1038,7 +1039,6 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
res.data.comment_view,
|
||||
s.commentsRes.data.comments,
|
||||
);
|
||||
s.finished.set(res.data.comment_view.comment.id, true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
@ -1060,12 +1060,6 @@ export class Community extends Component<CommunityRouteProps, State> {
|
|||
this.setState(s => {
|
||||
if (s.commentsRes.state === "success" && res.state === "success") {
|
||||
s.commentsRes.data.comments.unshift(res.data.comment_view);
|
||||
|
||||
// Set finished for the parent
|
||||
s.finished.set(
|
||||
getCommentParentId(res.data.comment_view.comment) ?? 0,
|
||||
true,
|
||||
);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
editWith,
|
||||
enableDownvotes,
|
||||
enableNsfw,
|
||||
getCommentParentId,
|
||||
getDataTypeString,
|
||||
myAuth,
|
||||
postToCommentSortType,
|
||||
|
@ -37,7 +36,6 @@ import {
|
|||
BanPerson,
|
||||
BanPersonResponse,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentReplyResponse,
|
||||
CommentResponse,
|
||||
CreateComment,
|
||||
|
@ -125,7 +123,6 @@ interface HomeState {
|
|||
subscribedCollapsed: boolean;
|
||||
tagline?: string;
|
||||
siteRes: GetSiteResponse;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
isIsomorphic: boolean;
|
||||
}
|
||||
|
||||
|
@ -274,7 +271,6 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
showTrendingMobile: false,
|
||||
showSidebarMobile: false,
|
||||
subscribedCollapsed: false,
|
||||
finished: new Map(),
|
||||
isIsomorphic: false,
|
||||
};
|
||||
|
||||
|
@ -770,7 +766,6 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
<CommentNodes
|
||||
nodes={commentsToFlatNodes(comments)}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.state.finished}
|
||||
isTopLevel
|
||||
showCommunity
|
||||
showContext
|
||||
|
@ -973,6 +968,9 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
const createCommentRes = await HttpService.client.createComment(form);
|
||||
this.createAndUpdateComments(createCommentRes);
|
||||
|
||||
if (createCommentRes.state === "failed") {
|
||||
toast(I18NextService.i18n.t(createCommentRes.err.message), "danger");
|
||||
}
|
||||
return createCommentRes;
|
||||
}
|
||||
|
||||
|
@ -980,6 +978,9 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
const editCommentRes = await HttpService.client.editComment(form);
|
||||
this.findAndUpdateCommentEdit(editCommentRes);
|
||||
|
||||
if (editCommentRes.state === "failed") {
|
||||
toast(I18NextService.i18n.t(editCommentRes.err.message), "danger");
|
||||
}
|
||||
return editCommentRes;
|
||||
}
|
||||
|
||||
|
@ -1168,7 +1169,6 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
res.data.comment_view,
|
||||
s.commentsRes.data.comments,
|
||||
);
|
||||
s.finished.set(res.data.comment_view.comment.id, true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
@ -1190,12 +1190,6 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
|||
this.setState(s => {
|
||||
if (s.commentsRes.state === "success" && res.state === "success") {
|
||||
s.commentsRes.data.comments.unshift(res.data.comment_view);
|
||||
|
||||
// Set finished for the parent
|
||||
s.finished.set(
|
||||
getCommentParentId(res.data.comment_view.comment) ?? 0,
|
||||
true,
|
||||
);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
editPrivateMessage,
|
||||
editWith,
|
||||
enableDownvotes,
|
||||
getCommentParentId,
|
||||
myAuth,
|
||||
setIsoData,
|
||||
updatePersonBlock,
|
||||
|
@ -28,7 +27,6 @@ import {
|
|||
BanPerson,
|
||||
BanPersonResponse,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentReplyResponse,
|
||||
CommentReplyView,
|
||||
CommentReportResponse,
|
||||
|
@ -132,7 +130,6 @@ interface InboxState {
|
|||
sort: CommentSortType;
|
||||
page: number;
|
||||
siteRes: GetSiteResponse;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
isIsomorphic: boolean;
|
||||
}
|
||||
|
||||
|
@ -157,7 +154,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
mentionsRes: EMPTY_REQUEST,
|
||||
messagesRes: EMPTY_REQUEST,
|
||||
markAllAsReadRes: EMPTY_REQUEST,
|
||||
finished: new Map(),
|
||||
isIsomorphic: false,
|
||||
};
|
||||
|
||||
|
@ -512,7 +508,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
{ comment_view: i.view as CommentView, children: [], depth: 0 },
|
||||
]}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.state.finished}
|
||||
markable
|
||||
showCommunity
|
||||
showContext
|
||||
|
@ -551,7 +546,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
depth: 0,
|
||||
},
|
||||
]}
|
||||
finished={this.state.finished}
|
||||
viewType={CommentViewType.Flat}
|
||||
markable
|
||||
showCommunity
|
||||
|
@ -623,7 +617,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
<CommentNodes
|
||||
nodes={commentsToFlatNodes(replies)}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.state.finished}
|
||||
markable
|
||||
showCommunity
|
||||
showContext
|
||||
|
@ -670,7 +663,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
key={umv.person_mention.id}
|
||||
nodes={[{ comment_view: umv, children: [], depth: 0 }]}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.state.finished}
|
||||
markable
|
||||
showCommunity
|
||||
showContext
|
||||
|
@ -996,9 +988,13 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
this.findAndUpdateMessage(res);
|
||||
}
|
||||
|
||||
async handleEditMessage(form: EditPrivateMessage) {
|
||||
async handleEditMessage(form: EditPrivateMessage): Promise<boolean> {
|
||||
const res = await HttpService.client.editPrivateMessage(form);
|
||||
this.findAndUpdateMessage(res);
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
return res.state !== "failed";
|
||||
}
|
||||
|
||||
async handleMarkMessageAsRead(form: MarkPrivateMessageAsRead) {
|
||||
|
@ -1015,7 +1011,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
this.reportToast(res);
|
||||
}
|
||||
|
||||
async handleCreateMessage(form: CreatePrivateMessage) {
|
||||
async handleCreateMessage(form: CreatePrivateMessage): Promise<boolean> {
|
||||
const res = await HttpService.client.createPrivateMessage(form);
|
||||
this.setState(s => {
|
||||
if (s.messagesRes.state === "success" && res.state === "success") {
|
||||
|
@ -1026,6 +1022,10 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
|
||||
return s;
|
||||
});
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
return res.state !== "failed";
|
||||
}
|
||||
|
||||
findAndUpdateMessage(res: RequestState<PrivateMessageResponse>) {
|
||||
|
@ -1094,6 +1094,8 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
) {
|
||||
if (res.state === "success") {
|
||||
toast(I18NextService.i18n.t("report_created"));
|
||||
} else if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1113,11 +1115,6 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
|||
s.mentionsRes.data.mentions,
|
||||
);
|
||||
}
|
||||
// Set finished for the parent
|
||||
s.finished.set(
|
||||
getCommentParentId(res.data.comment_view.comment) ?? 0,
|
||||
true,
|
||||
);
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
BanFromCommunity,
|
||||
BanPerson,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentResponse,
|
||||
CommentView,
|
||||
CreateComment,
|
||||
|
@ -49,7 +48,6 @@ import { RequestState } from "../../services/HttpService";
|
|||
|
||||
interface PersonDetailsProps {
|
||||
personRes: GetPersonDetailsResponse;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
admins: PersonView[];
|
||||
allLanguages: Language[];
|
||||
siteLanguages: number[];
|
||||
|
@ -153,7 +151,6 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
|||
key={i.id}
|
||||
nodes={[{ comment_view: c, children: [], depth: 0 }]}
|
||||
viewType={CommentViewType.Flat}
|
||||
finished={this.props.finished}
|
||||
admins={this.props.admins}
|
||||
noBorder
|
||||
showCommunity
|
||||
|
@ -266,7 +263,6 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
|||
nodes={commentsToFlatNodes(this.props.personRes.comments)}
|
||||
viewType={CommentViewType.Flat}
|
||||
admins={this.props.admins}
|
||||
finished={this.props.finished}
|
||||
isTopLevel
|
||||
showCommunity
|
||||
showContext
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
editWith,
|
||||
enableDownvotes,
|
||||
enableNsfw,
|
||||
getCommentParentId,
|
||||
setIsoData,
|
||||
updatePersonBlock,
|
||||
voteDisplayMode,
|
||||
|
@ -38,7 +37,6 @@ import {
|
|||
BanPerson,
|
||||
BanPersonResponse,
|
||||
BlockPerson,
|
||||
CommentId,
|
||||
CommentReplyResponse,
|
||||
CommentResponse,
|
||||
Community,
|
||||
|
@ -120,7 +118,6 @@ interface ProfileState {
|
|||
showBanDialog: boolean;
|
||||
removeData: boolean;
|
||||
siteRes: GetSiteResponse;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
isIsomorphic: boolean;
|
||||
}
|
||||
|
||||
|
@ -206,7 +203,6 @@ export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
|||
siteRes: this.isoData.site_res,
|
||||
showBanDialog: false,
|
||||
removeData: false,
|
||||
finished: new Map(),
|
||||
isIsomorphic: false,
|
||||
};
|
||||
|
||||
|
@ -491,7 +487,6 @@ export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
|||
sort={sort}
|
||||
page={page}
|
||||
limit={fetchLimit}
|
||||
finished={this.state.finished}
|
||||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||
enableNsfw={enableNsfw(siteRes)}
|
||||
|
@ -1178,7 +1173,6 @@ export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
|||
res.data.comment_view,
|
||||
s.personRes.data.comments,
|
||||
);
|
||||
s.finished.set(res.data.comment_view.comment.id, true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
@ -1200,11 +1194,6 @@ export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
|||
this.setState(s => {
|
||||
if (s.personRes.state === "success" && res.state === "success") {
|
||||
s.personRes.data.comments.unshift(res.data.comment_view);
|
||||
// Set finished for the parent
|
||||
s.finished.set(
|
||||
getCommentParentId(res.data.comment_view.comment) ?? 0,
|
||||
true,
|
||||
);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
|
|
@ -27,7 +27,6 @@ import {
|
|||
wrapClient,
|
||||
} from "../../services/HttpService";
|
||||
import { HtmlTags } from "../common/html-tags";
|
||||
import { Spinner } from "../common/icon";
|
||||
import { PostForm } from "./post-form";
|
||||
import { getHttpBaseInternal } from "../../utils/env";
|
||||
import { IRoutePropsWithFetch } from "../../routes";
|
||||
|
@ -178,39 +177,28 @@ export class CreatePost extends Component<
|
|||
title={this.documentTitle}
|
||||
path={this.context.router.route.match.url}
|
||||
/>
|
||||
{this.state.loading ? (
|
||||
<h5>
|
||||
<Spinner large />
|
||||
</h5>
|
||||
) : (
|
||||
<div className="row">
|
||||
<div
|
||||
id="createPostForm"
|
||||
className="col-12 col-lg-6 offset-lg-3 mb-4"
|
||||
>
|
||||
<h1 className="h4 mb-4">
|
||||
{I18NextService.i18n.t("create_post")}
|
||||
</h1>
|
||||
<PostForm
|
||||
onCreate={this.handlePostCreate}
|
||||
params={locationState}
|
||||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||
enableNsfw={enableNsfw(siteRes)}
|
||||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
selectedCommunityChoice={selectedCommunityChoice}
|
||||
onSelectCommunity={this.handleSelectedCommunityChange}
|
||||
initialCommunities={
|
||||
this.state.initialCommunitiesRes.state === "success"
|
||||
? this.state.initialCommunitiesRes.data.communities
|
||||
: []
|
||||
}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div id="createPostForm" className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h1 className="h4 mb-4">{I18NextService.i18n.t("create_post")}</h1>
|
||||
<PostForm
|
||||
onCreate={this.handlePostCreate}
|
||||
params={locationState}
|
||||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||
enableNsfw={enableNsfw(siteRes)}
|
||||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
selectedCommunityChoice={selectedCommunityChoice}
|
||||
onSelectCommunity={this.handleSelectedCommunityChange}
|
||||
initialCommunities={
|
||||
this.state.initialCommunitiesRes.state === "success"
|
||||
? this.state.initialCommunitiesRes.data.communities
|
||||
: []
|
||||
}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -242,11 +230,13 @@ export class CreatePost extends Component<
|
|||
});
|
||||
}
|
||||
|
||||
async handlePostCreate(form: CreatePostI) {
|
||||
async handlePostCreate(form: CreatePostI, bypassNavWarning: () => void) {
|
||||
this.setState({ loading: true });
|
||||
const res = await HttpService.client.createPost(form);
|
||||
|
||||
if (res.state === "success") {
|
||||
const postId = res.data.post_view.post.id;
|
||||
bypassNavWarning();
|
||||
this.props.history.replace(`/post/${postId}`);
|
||||
} else if (res.state === "failed") {
|
||||
this.setState({
|
||||
|
|
|
@ -54,8 +54,8 @@ interface PostFormProps {
|
|||
siteLanguages: number[];
|
||||
params?: PostFormParams;
|
||||
onCancel?(): void;
|
||||
onCreate?(form: CreatePost): void;
|
||||
onEdit?(form: EditPost): void;
|
||||
onCreate?(form: CreatePost, bypassNavWarning: () => void): void;
|
||||
onEdit?(form: EditPost, bypassNavWarning: () => void): void;
|
||||
enableNsfw?: boolean;
|
||||
enableDownvotes?: boolean;
|
||||
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||
|
@ -85,6 +85,7 @@ interface PostFormState {
|
|||
communitySearchOptions: Choice[];
|
||||
previewMode: boolean;
|
||||
submitted: boolean;
|
||||
bypassNavWarning: boolean;
|
||||
}
|
||||
|
||||
function handlePostSubmit(i: PostForm, event: any) {
|
||||
|
@ -93,34 +94,46 @@ function handlePostSubmit(i: PostForm, event: any) {
|
|||
if ((i.state.form.url ?? "") === "") {
|
||||
i.setState(s => ((s.form.url = undefined), s));
|
||||
}
|
||||
// This forces `props.loading` to become true, then false, to enable the
|
||||
// submit button again.
|
||||
i.setState({ submitted: true });
|
||||
|
||||
const pForm = i.state.form;
|
||||
const pv = i.props.post_view;
|
||||
|
||||
if (pv) {
|
||||
i.props.onEdit?.({
|
||||
post_id: pv.post.id,
|
||||
name: pForm.name,
|
||||
url: pForm.url,
|
||||
body: pForm.body,
|
||||
nsfw: pForm.nsfw,
|
||||
language_id: pForm.language_id,
|
||||
custom_thumbnail: pForm.custom_thumbnail,
|
||||
alt_text: pForm.alt_text,
|
||||
});
|
||||
i.props.onEdit?.(
|
||||
{
|
||||
post_id: pv.post.id,
|
||||
name: pForm.name,
|
||||
url: pForm.url,
|
||||
body: pForm.body,
|
||||
nsfw: pForm.nsfw,
|
||||
language_id: pForm.language_id,
|
||||
custom_thumbnail: pForm.custom_thumbnail,
|
||||
alt_text: pForm.alt_text,
|
||||
},
|
||||
() => {
|
||||
i.setState({ bypassNavWarning: true });
|
||||
},
|
||||
);
|
||||
} else if (pForm.name && pForm.community_id) {
|
||||
i.props.onCreate?.({
|
||||
name: pForm.name,
|
||||
community_id: pForm.community_id,
|
||||
url: pForm.url,
|
||||
body: pForm.body,
|
||||
nsfw: pForm.nsfw,
|
||||
language_id: pForm.language_id,
|
||||
honeypot: pForm.honeypot,
|
||||
custom_thumbnail: pForm.custom_thumbnail,
|
||||
alt_text: pForm.alt_text,
|
||||
});
|
||||
i.props.onCreate?.(
|
||||
{
|
||||
name: pForm.name,
|
||||
community_id: pForm.community_id,
|
||||
url: pForm.url,
|
||||
body: pForm.body,
|
||||
nsfw: pForm.nsfw,
|
||||
language_id: pForm.language_id,
|
||||
honeypot: pForm.honeypot,
|
||||
custom_thumbnail: pForm.custom_thumbnail,
|
||||
alt_text: pForm.alt_text,
|
||||
},
|
||||
() => {
|
||||
i.setState({ bypassNavWarning: true });
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,6 +260,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
previewMode: false,
|
||||
communitySearchOptions: [],
|
||||
submitted: false,
|
||||
bypassNavWarning: false,
|
||||
};
|
||||
|
||||
postTitleRef = createRef<HTMLTextAreaElement>();
|
||||
|
@ -347,6 +361,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
nextProps.initialCommunities?.map(communityToChoice) ?? [],
|
||||
});
|
||||
}
|
||||
if (this.props.loading && !nextProps.loading) {
|
||||
this.setState({ submitted: false, bypassNavWarning: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -364,7 +381,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
this.state.form.name ||
|
||||
this.state.form.url ||
|
||||
this.state.form.body
|
||||
) && !this.state.submitted
|
||||
) && !this.state.bypassNavWarning
|
||||
}
|
||||
/>
|
||||
<div className="mb-3 row">
|
||||
|
@ -618,7 +635,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
<div className="mb-3 row">
|
||||
<div className="col-sm-10">
|
||||
<button
|
||||
disabled={!this.state.form.community_id || this.props.loading}
|
||||
disabled={
|
||||
!this.state.form.community_id ||
|
||||
this.props.loading ||
|
||||
this.state.submitted
|
||||
}
|
||||
type="submit"
|
||||
className="btn btn-secondary me-2"
|
||||
>
|
||||
|
|
|
@ -842,13 +842,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
|
||||
// The actual editing is done in the receive for post
|
||||
async handleEditPost(form: EditPost) {
|
||||
this.setState({ showEdit: false, loading: true });
|
||||
this.setState({ loading: true });
|
||||
const res = await this.props.onPostEdit(form);
|
||||
|
||||
if (res.state === "success") {
|
||||
toast(I18NextService.i18n.t("edited_post"));
|
||||
this.setState({ loading: false, showEdit: false });
|
||||
} else if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,8 +120,8 @@ interface PostState {
|
|||
siteRes: GetSiteResponse;
|
||||
showSidebarMobile: boolean;
|
||||
maxCommentsShown: number;
|
||||
finished: Map<CommentId, boolean | undefined>;
|
||||
isIsomorphic: boolean;
|
||||
lastCreatedCommentId?: CommentId;
|
||||
}
|
||||
|
||||
const defaultCommentSort: CommentSortType = "Hot";
|
||||
|
@ -219,7 +219,6 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
siteRes: this.isoData.site_res,
|
||||
showSidebarMobile: false,
|
||||
maxCommentsShown: commentsShownInterval,
|
||||
finished: new Map(),
|
||||
isIsomorphic: false,
|
||||
};
|
||||
|
||||
|
@ -236,6 +235,8 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
this.handleFollow = this.handleFollow.bind(this);
|
||||
this.handleModRemoveCommunity = this.handleModRemoveCommunity.bind(this);
|
||||
this.handleCreateComment = this.handleCreateComment.bind(this);
|
||||
this.handleCreateToplevelComment =
|
||||
this.handleCreateToplevelComment.bind(this);
|
||||
this.handleEditComment = this.handleEditComment.bind(this);
|
||||
this.handleSaveComment = this.handleSaveComment.bind(this);
|
||||
this.handleBlockCommunity = this.handleBlockCommunity.bind(this);
|
||||
|
@ -585,7 +586,8 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
) && (
|
||||
<CommentForm
|
||||
key={
|
||||
this.context.router.history.location.key
|
||||
this.context.router.history.location.key +
|
||||
this.state.lastCreatedCommentId
|
||||
// reset on new location, otherwise <Prompt /> stops working
|
||||
}
|
||||
node={res.post_view.post.id}
|
||||
|
@ -593,8 +595,7 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
containerClass="post-comment-container"
|
||||
onUpsertComment={this.handleCreateComment}
|
||||
finished={this.state.finished.get(0)}
|
||||
onUpsertComment={this.handleCreateToplevelComment}
|
||||
/>
|
||||
)}
|
||||
<div className="d-block d-md-none">
|
||||
|
@ -774,7 +775,6 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||
showContext
|
||||
finished={this.state.finished}
|
||||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
onSaveComment={this.handleSaveComment}
|
||||
|
@ -885,7 +885,6 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
admins={siteRes.admins}
|
||||
enableDownvotes={enableDownvotes(siteRes)}
|
||||
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||
finished={this.state.finished}
|
||||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
onSaveComment={this.handleSaveComment}
|
||||
|
@ -1063,6 +1062,14 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
return res;
|
||||
}
|
||||
|
||||
async handleCreateToplevelComment(form: CreateComment) {
|
||||
const res = await this.handleCreateComment(form);
|
||||
if (res.state === "success") {
|
||||
this.setState({ lastCreatedCommentId: res.data.comment_view.comment.id });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async handleCreateComment(form: CreateComment) {
|
||||
const createCommentRes = await HttpService.client.createComment(form);
|
||||
this.createAndUpdateComments(createCommentRes);
|
||||
|
@ -1380,12 +1387,12 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
);
|
||||
|
||||
comments.splice(foundCommentParentIndex + 1, 0, newComment);
|
||||
|
||||
// Set finished for the parent
|
||||
s.finished.set(newCommentParentId ?? 0, true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
findAndUpdateCommentEdit(res: RequestState<CommentResponse>) {
|
||||
|
@ -1395,13 +1402,14 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
res.data.comment_view,
|
||||
s.commentsRes.data.comments,
|
||||
);
|
||||
s.finished.set(res.data.comment_view.comment.id, true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
// No need to set finished on a comment vote, save, etc
|
||||
findAndUpdateComment(res: RequestState<CommentResponse>) {
|
||||
this.setState(s => {
|
||||
if (s.commentsRes.state === "success" && res.state === "success") {
|
||||
|
@ -1412,6 +1420,9 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
}
|
||||
return s;
|
||||
});
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
findAndUpdateCommentReply(res: RequestState<CommentReplyResponse>) {
|
||||
|
@ -1424,6 +1435,9 @@ export class Post extends Component<PostRouteProps, PostState> {
|
|||
}
|
||||
return s;
|
||||
});
|
||||
if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
updateModerators(res: RequestState<AddModToCommunityResponse>) {
|
||||
|
|
|
@ -168,14 +168,22 @@ export class CreatePrivateMessage extends Component<
|
|||
);
|
||||
}
|
||||
|
||||
async handlePrivateMessageCreate(form: CreatePrivateMessageI) {
|
||||
async handlePrivateMessageCreate(
|
||||
form: CreatePrivateMessageI,
|
||||
bypassNavWarning: () => void,
|
||||
): Promise<boolean> {
|
||||
const res = await HttpService.client.createPrivateMessage(form);
|
||||
|
||||
if (res.state === "success") {
|
||||
toast(I18NextService.i18n.t("message_sent"));
|
||||
|
||||
bypassNavWarning();
|
||||
// Navigate to the front
|
||||
this.context.router.history.push("/");
|
||||
} else if (res.state === "failed") {
|
||||
toast(I18NextService.i18n.t(res.err.message), "danger");
|
||||
}
|
||||
|
||||
return res.state !== "failed";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { capitalizeFirstLetter } from "@utils/helpers";
|
||||
import { Component, InfernoNode } from "inferno";
|
||||
import { Component } from "inferno";
|
||||
import { T } from "inferno-i18next-dess";
|
||||
import { Prompt } from "inferno-router";
|
||||
import {
|
||||
|
@ -19,8 +19,14 @@ interface PrivateMessageFormProps {
|
|||
privateMessageView?: PrivateMessageView; // If a pm is given, that means this is an edit
|
||||
replyType?: boolean;
|
||||
onCancel?(): any;
|
||||
onCreate?(form: CreatePrivateMessage): void;
|
||||
onEdit?(form: EditPrivateMessage): void;
|
||||
onCreate?(
|
||||
form: CreatePrivateMessage,
|
||||
bypassNavWarning: () => void,
|
||||
): Promise<boolean>;
|
||||
onEdit?(
|
||||
form: EditPrivateMessage,
|
||||
bypassNavWarning: () => void,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
interface PrivateMessageFormState {
|
||||
|
@ -28,6 +34,7 @@ interface PrivateMessageFormState {
|
|||
loading: boolean;
|
||||
previewMode: boolean;
|
||||
submitted: boolean;
|
||||
bypassNavWarning?: boolean;
|
||||
}
|
||||
|
||||
export class PrivateMessageForm extends Component<
|
||||
|
@ -51,21 +58,15 @@ export class PrivateMessageForm extends Component<
|
|||
this.handlePrivateMessageSubmit.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(
|
||||
nextProps: Readonly<{ children?: InfernoNode } & PrivateMessageFormProps>,
|
||||
): void {
|
||||
if (this.props !== nextProps) {
|
||||
this.setState({ loading: false, content: undefined, previewMode: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form className="private-message-form">
|
||||
<Prompt
|
||||
message={I18NextService.i18n.t("block_leaving")}
|
||||
when={
|
||||
!this.state.loading && !!this.state.content && !this.state.submitted
|
||||
!this.state.bypassNavWarning &&
|
||||
((!!this.state.content && !this.state.submitted) ||
|
||||
this.state.loading)
|
||||
}
|
||||
/>
|
||||
{!this.props.privateMessageView && (
|
||||
|
@ -140,21 +141,34 @@ export class PrivateMessageForm extends Component<
|
|||
);
|
||||
}
|
||||
|
||||
handlePrivateMessageSubmit() {
|
||||
async handlePrivateMessageSubmit(): Promise<boolean> {
|
||||
this.setState({ loading: true, submitted: true });
|
||||
const pm = this.props.privateMessageView;
|
||||
const content = this.state.content ?? "";
|
||||
let success: boolean | undefined;
|
||||
if (pm) {
|
||||
this.props.onEdit?.({
|
||||
private_message_id: pm.private_message.id,
|
||||
content,
|
||||
});
|
||||
success = await this.props.onEdit?.(
|
||||
{
|
||||
private_message_id: pm.private_message.id,
|
||||
content,
|
||||
},
|
||||
() => {
|
||||
this.setState({ bypassNavWarning: true });
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this.props.onCreate?.({
|
||||
content,
|
||||
recipient_id: this.props.recipient.id,
|
||||
});
|
||||
success = await this.props.onCreate?.(
|
||||
{
|
||||
content,
|
||||
recipient_id: this.props.recipient.id,
|
||||
},
|
||||
() => {
|
||||
this.setState({ bypassNavWarning: true });
|
||||
},
|
||||
);
|
||||
}
|
||||
this.setState({ loading: false, submitted: success ?? true });
|
||||
return success ?? true;
|
||||
}
|
||||
|
||||
handleContentChange(val: string) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||
import { Component, linkEvent } from "inferno";
|
||||
import {
|
||||
CreatePrivateMessage,
|
||||
CreatePrivateMessageReport,
|
||||
|
@ -32,8 +32,8 @@ interface PrivateMessageProps {
|
|||
onDelete(form: DeletePrivateMessage): void;
|
||||
onMarkRead(form: MarkPrivateMessageAsRead): void;
|
||||
onReport(form: CreatePrivateMessageReport): void;
|
||||
onCreate(form: CreatePrivateMessage): void;
|
||||
onEdit(form: EditPrivateMessage): void;
|
||||
onCreate(form: CreatePrivateMessage): Promise<boolean>;
|
||||
onEdit(form: EditPrivateMessage): Promise<boolean>;
|
||||
}
|
||||
|
||||
@tippyMixin
|
||||
|
@ -56,6 +56,8 @@ export class PrivateMessage extends Component<
|
|||
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
||||
this.handleReportSubmit = this.handleReportSubmit.bind(this);
|
||||
this.hideReportDialog = this.hideReportDialog.bind(this);
|
||||
this.handleCreate = this.handleCreate.bind(this);
|
||||
this.handleEdit = this.handleEdit.bind(this);
|
||||
}
|
||||
|
||||
get mine(): boolean {
|
||||
|
@ -65,22 +67,6 @@ export class PrivateMessage extends Component<
|
|||
);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(
|
||||
nextProps: Readonly<{ children?: InfernoNode } & PrivateMessageProps>,
|
||||
): void {
|
||||
if (this.props !== nextProps) {
|
||||
this.setState({
|
||||
showReply: false,
|
||||
showEdit: false,
|
||||
collapsed: false,
|
||||
viewSource: false,
|
||||
showReportDialog: false,
|
||||
deleteLoading: false,
|
||||
readLoading: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const message_view = this.props.private_message_view;
|
||||
const otherPerson: Person = this.mine
|
||||
|
@ -126,7 +112,7 @@ export class PrivateMessage extends Component<
|
|||
<PrivateMessageForm
|
||||
recipient={otherPerson}
|
||||
privateMessageView={message_view}
|
||||
onEdit={this.props.onEdit}
|
||||
onEdit={this.handleEdit}
|
||||
onCancel={this.handleReplyCancel}
|
||||
/>
|
||||
)}
|
||||
|
@ -265,7 +251,7 @@ export class PrivateMessage extends Component<
|
|||
<PrivateMessageForm
|
||||
replyType={true}
|
||||
recipient={otherPerson}
|
||||
onCreate={this.props.onCreate}
|
||||
onCreate={this.handleCreate}
|
||||
onCancel={this.handleReplyCancel}
|
||||
/>
|
||||
</div>
|
||||
|
@ -304,7 +290,6 @@ export class PrivateMessage extends Component<
|
|||
|
||||
handleEditClick(i: PrivateMessage) {
|
||||
i.setState({ showEdit: true });
|
||||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleDeleteClick(i: PrivateMessage) {
|
||||
|
@ -319,6 +304,22 @@ export class PrivateMessage extends Component<
|
|||
this.setState({ showReply: false, showEdit: false });
|
||||
}
|
||||
|
||||
async handleCreate(form: CreatePrivateMessage): Promise<boolean> {
|
||||
const success = await this.props.onCreate(form);
|
||||
if (success) {
|
||||
this.setState({ showReply: false });
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
async handleEdit(form: EditPrivateMessage): Promise<boolean> {
|
||||
const success = await this.props.onEdit(form);
|
||||
if (success) {
|
||||
this.setState({ showEdit: false });
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
handleMarkRead(i: PrivateMessage) {
|
||||
i.setState({ readLoading: true });
|
||||
i.props.onMarkRead({
|
||||
|
|
|
@ -824,7 +824,6 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
|||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
// All of these are unused, since its viewonly
|
||||
finished={new Map()}
|
||||
onSaveComment={async () => {}}
|
||||
onBlockPerson={async () => {}}
|
||||
onDeleteComment={async () => {}}
|
||||
|
@ -886,7 +885,6 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
|||
allLanguages={siteRes.all_languages}
|
||||
siteLanguages={siteRes.discussion_languages}
|
||||
// All of these are unused, since its viewonly
|
||||
finished={new Map()}
|
||||
onSaveComment={async () => {}}
|
||||
onBlockPerson={async () => {}}
|
||||
onDeleteComment={async () => {}}
|
||||
|
|
Loading…
Reference in a new issue