Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Comments and Replies

This plugin lets visitors comment on and reply to Halo content, with optional image uploads.

## Language

**Comment or reply submission**: One attempt to send a visitor's comment or reply and obtain its outcome. It may include images.

**Upload submission ticket**: A record linking a comment or reply submission to its temporary image uploads so the outcome and image ownership can be confirmed or recovered.
111 changes: 12 additions & 99 deletions packages/comment-widget/src/comment-form.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import type { Comment, CommentRequest, User } from '@halo-dev/api-client';
import type { CommentRequest, User } from '@halo-dev/api-client';
import { consume } from '@lit/context';
import { html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import { createRef, type Ref, ref } from 'lit/directives/ref.js';
import './base-form';
import { msg } from '@lit/localize';
import { FetchError, type FetchResponse } from 'ofetch';
import type { BaseForm } from './base-form';
import {
allowAnonymousCommentsContext,
Expand All @@ -18,19 +16,7 @@ import {
versionContext,
} from './context';
import type { ToastManager } from './lit-toast';
import type { ProblemDetail } from './types';
import {
type CaptchaRequiredResponse,
getAltchaHeader,
getCaptchaCodeHeader,
getCaptchaMessage,
isRequireCaptcha,
} from './utils/captcha';
import {
isPendingReview,
type SubmissionEvent,
submissionErrorMessage,
} from './utils/submission';
import { type SubmissionEvent, submitCommentOrReply } from './utils/submission';

export class CommentForm extends LitElement {
@consume({ context: baseUrlContext })
Expand Down Expand Up @@ -82,16 +68,9 @@ export class CommentForm extends LitElement {
></base-form>`;
}

async onSubmit(e: SubmissionEvent) {
e.preventDefault();

this.submitting = true;

onSubmit(e: SubmissionEvent) {
const data = e.detail;
const baseForm = this.baseFormRef.value;
const submittedDraft = baseForm?.getDraftSnapshot();

const { displayName, email, website, content, hidden } = data || {};
const { content, hidden } = data || {};

const commentRequest: CommentRequest = {
raw: content,
Expand All @@ -107,80 +86,14 @@ export class CommentForm extends LitElement {
},
};

if (!this.currentUser && !this.allowAnonymousComments) {
this.toastManager?.warn(msg('Please login first'));
this.submitting = false;
return;
}

if (!this.currentUser && this.allowAnonymousComments) {
if (!displayName || !email) {
this.toastManager?.warn(
msg('Please log in or complete the information first')
);
this.submitting = false;
return;
} else {
commentRequest.owner = {
displayName: displayName,
email: email,
website: website,
};
}
}

try {
const newComment = await data.uploadSession.submit<Comment>(
`${this.baseUrl}/apis/api.halo.run/v1alpha1/comments`,
commentRequest,
data.uploadIds,
{
...getCaptchaCodeHeader(data.captchaCode ?? '', data.turnstileToken),
...getAltchaHeader(data.altchaPayload),
},
this.baseUrl
);

this.baseFormRef.value?.handleFetchCaptcha();

if (!isPendingReview(newComment)) {
this.toastManager?.success(msg('Comment submitted successfully'));
} else {
this.toastManager?.success(
msg('Comment submitted successfully, pending review')
);
}

baseForm?.resetForm(submittedDraft);
window.dispatchEvent(new CustomEvent('halo:comment:created'));
} catch (error) {
this.reportSubmissionError(error);
} finally {
this.baseFormRef.value?.resetVerification();
this.submitting = false;
}
}
private reportSubmissionError(error: unknown) {
if (error instanceof FetchError) {
if (
isRequireCaptcha(
error.response as FetchResponse<CaptchaRequiredResponse>
)
) {
const response = error.data as CaptchaRequiredResponse;
this.captcha = response.captcha ?? '';
this.toastManager?.warn(getCaptchaMessage(response));
return;
}

const problemDetail = error.data as unknown as ProblemDetail;
this.toastManager?.error(
[problemDetail?.title, problemDetail?.detail].join(' - ') ||
msg('Comment failed, please try again later')
);
return;
}
this.toastManager?.error(submissionErrorMessage(error));
return submitCommentOrReply(e, this, {
url: `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments`,
request: commentRequest,
onSuccess: (baseForm, submittedDraft) => {
baseForm?.resetForm(submittedDraft);
window.dispatchEvent(new CustomEvent('halo:comment:created'));
},
});
}
}

Expand Down
123 changes: 17 additions & 106 deletions packages/comment-widget/src/reply-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {
CommentVo,
Reply,
ReplyRequest,
ReplyVo,
User,
Expand All @@ -10,8 +9,6 @@ import { html, LitElement } from 'lit';
import { property, state } from 'lit/decorators.js';
import { createRef, type Ref, ref } from 'lit/directives/ref.js';
import './base-form';
import { msg } from '@lit/localize';
import { FetchError, type FetchResponse } from 'ofetch';
import type { BaseForm } from './base-form';
import {
allowAnonymousCommentsContext,
Expand All @@ -20,19 +17,7 @@ import {
toastContext,
} from './context';
import type { ToastManager } from './lit-toast';
import type { ProblemDetail } from './types';
import {
type CaptchaRequiredResponse,
getAltchaHeader,
getCaptchaCodeHeader,
getCaptchaMessage,
isRequireCaptcha,
} from './utils/captcha';
import {
isPendingReview,
type SubmissionEvent,
submissionErrorMessage,
} from './utils/submission';
import { type SubmissionEvent, submitCommentOrReply } from './utils/submission';

export class ReplyForm extends LitElement {
@consume({ context: baseUrlContext })
Expand Down Expand Up @@ -92,16 +77,9 @@ export class ReplyForm extends LitElement {
></base-form>`;
}

async onSubmit(e: SubmissionEvent) {
e.preventDefault();

this.submitting = true;

onSubmit(e: SubmissionEvent) {
const data = e.detail;
const baseForm = this.baseFormRef.value;
const submittedDraft = baseForm?.getDraftSnapshot();

const { displayName, email, website, content } = data || {};
const { content } = data || {};

const replyRequest: ReplyRequest = {
raw: content,
Expand All @@ -114,88 +92,21 @@ export class ReplyForm extends LitElement {
replyRequest.quoteReply = this.quoteReply.metadata.name;
}

if (!this.currentUser && !this.allowAnonymousComments) {
this.toastManager?.warn(msg('Please login first'));
this.submitting = false;
return;
}

if (!this.currentUser && this.allowAnonymousComments) {
if (!displayName || !email) {
this.toastManager?.warn(
msg('Please log in or complete the information first')
return submitCommentOrReply(e, this, {
url: `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${this.comment?.metadata.name}/reply`,
request: replyRequest,
onSuccess: (baseForm, submittedDraft) => {
this.dispatchEvent(
new CustomEvent('reload', {
detail: {
resetForm: (form: BaseForm) => form.resetForm(submittedDraft),
},
})
);
this.submitting = false;
return;
} else {
replyRequest.owner = {
displayName: displayName,
email: email,
website: website,
};
}
}

try {
const newReply = await data.uploadSession.submit<Reply>(
`${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${this.comment?.metadata.name}/reply`,
replyRequest,
data.uploadIds,
{
...getCaptchaCodeHeader(data.captchaCode ?? '', data.turnstileToken),
...getAltchaHeader(data.altchaPayload),
},
this.baseUrl
);

this.baseFormRef.value?.handleFetchCaptcha();

if (!isPendingReview(newReply)) {
this.toastManager?.success(msg('Comment submitted successfully'));
} else {
this.toastManager?.success(
msg('Comment submitted successfully, pending review')
);
}

this.dispatchEvent(
new CustomEvent('reload', {
detail: {
resetForm: (form: BaseForm) => form.resetForm(submittedDraft),
},
})
);
baseForm?.resetForm(submittedDraft);
window.dispatchEvent(new CustomEvent('halo:comment-reply:created'));
} catch (error) {
this.reportSubmissionError(error);
} finally {
this.baseFormRef.value?.resetVerification();
this.submitting = false;
}
}
private reportSubmissionError(error: unknown) {
if (error instanceof FetchError) {
if (
isRequireCaptcha(
error.response as FetchResponse<CaptchaRequiredResponse>
)
) {
const response = error.data as CaptchaRequiredResponse;
this.captcha = response.captcha ?? '';
this.toastManager?.warn(getCaptchaMessage(response));
return;
}

const problemDetail = error.data as unknown as ProblemDetail;
this.toastManager?.error(
[problemDetail?.title, problemDetail?.detail].join(' - ') ||
msg('Comment failed, please try again later')
);
return;
}

this.toastManager?.error(submissionErrorMessage(error));
baseForm?.resetForm(submittedDraft);
window.dispatchEvent(new CustomEvent('halo:comment-reply:created'));
},
});
}
}

Expand Down
Loading
Loading