-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #228 from mobeigi/comment-notify-on-reply-feature
Email comment author when somebody replies to them
- Loading branch information
Showing
13 changed files
with
333 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,4 +30,5 @@ PAYLOAD_SECRET="YOUR_SECRET_HERE" | |
PAYLOAD_SYSTEM_USER_API_KEY="example" | ||
|
||
PAYLOAD_FROM_EMAIL_ADDRESS="[email protected]" | ||
PAYLOAD_FROM_NAME="MoBeigi.com" | ||
PAYLOAD_TO_EMAIL_ADDRESS="[email protected]" |
62 changes: 62 additions & 0 deletions
62
app/src/app/(frontend)/api/custom/comments/[commentId]/unsubscribe/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { NextResponse } from 'next/server'; | ||
import { getPayload } from 'payload'; | ||
import config from '@payload-config'; | ||
import { createPayloadHmac256 } from '@/utils/crypto/hmac256'; | ||
import { headers } from 'next/headers'; | ||
|
||
export const GET = async (request: Request, { params: paramsPromise }: { params: Promise<{ commentId: string }> }) => { | ||
try { | ||
const params = await paramsPromise; | ||
|
||
const { commentId } = params; | ||
|
||
const url = new URL(request.url); | ||
const token = url.searchParams.get('token'); | ||
|
||
if (!token) { | ||
return NextResponse.json({ error: 'Token is required.' }, { status: 400 }); | ||
} | ||
|
||
const payload = await getPayload({ | ||
config, | ||
}); | ||
|
||
const comment = await payload.findByID({ | ||
collection: 'comments', | ||
id: commentId, | ||
disableErrors: true, | ||
}); | ||
|
||
if (!comment) { | ||
return NextResponse.json({ error: `Comment with id '${commentId}' does not exist.` }, { status: 404 }); | ||
} | ||
|
||
// Validate token | ||
const commentIdHmac256 = createPayloadHmac256({ data: commentId }); | ||
if (commentIdHmac256 !== token) { | ||
return NextResponse.json({ error: 'Token is invalid.' }, { status: 401 }); | ||
} | ||
|
||
if (!comment.notifyOnReply) { | ||
return NextResponse.json({ error: 'You have already unsubscribed from this comment.' }, { status: 400 }); | ||
} | ||
|
||
const headerList = await headers(); | ||
const result = await payload.auth({ headers: headerList }); | ||
|
||
await payload.update({ | ||
collection: 'comments', | ||
id: commentId, | ||
data: { | ||
notifyOnReply: false, | ||
}, | ||
overrideAccess: true, | ||
user: result.user, | ||
}); | ||
|
||
return NextResponse.json({ message: 'You have successfully unsubscribed from this comment.' }, { status: 200 }); | ||
} catch (error) { | ||
console.error('Failed to unsubscribe from comment.', error); | ||
return NextResponse.json({ error: 'Failed to unsubscribe from comment.' }, { status: 500 }); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Html, Body, Heading, Text, Link, Section } from '@react-email/components'; | ||
import { render } from '@react-email/render'; | ||
import { HeaderSection } from './common/HeaderSection'; | ||
import { format as formatDate } from 'date-fns'; | ||
|
||
interface NewReplyToCommentEmailProps { | ||
postTitle: string; | ||
commentUrl: string; | ||
unsubscribeUrl: string; | ||
replyCommentDisplayName: string; | ||
replyCommentUrl: string; | ||
replyCommentCreatedAt: Date; | ||
replyCommentTextContent: string; | ||
} | ||
|
||
export const NewReplyToCommentEmail = ({ | ||
postTitle, | ||
commentUrl, | ||
unsubscribeUrl, | ||
replyCommentDisplayName, | ||
replyCommentUrl, | ||
replyCommentCreatedAt, | ||
replyCommentTextContent, | ||
}: NewReplyToCommentEmailProps) => { | ||
// TODO: Use timezone library to include server timezone | ||
const replyCommentCreatedAtDateString = formatDate( | ||
replyCommentCreatedAt, | ||
"d MMMM yyyy 'at' hh:mm a '(Sydney, Australia time)'", | ||
); | ||
|
||
return ( | ||
<Html> | ||
<Body> | ||
<HeaderSection /> | ||
<Section> | ||
<Heading as="h2">New reply to your comment on:</Heading> | ||
<Text> | ||
<Link href={commentUrl}>{postTitle}</Link> | ||
</Text> | ||
</Section> | ||
<Section> | ||
<Heading as="h2">Comment Reply Details</Heading> | ||
<Text> | ||
<span> | ||
<strong>Display Name:</strong> {replyCommentDisplayName} | ||
</span> | ||
<br /> | ||
<span> | ||
<strong>Comment URL:</strong> {replyCommentUrl} | ||
</span> | ||
<br /> | ||
<span> | ||
<strong>Created at:</strong> {replyCommentCreatedAtDateString} | ||
</span> | ||
</Text> | ||
</Section> | ||
<Section> | ||
<Text>{replyCommentTextContent}</Text> | ||
</Section> | ||
<Section> | ||
<Text | ||
style={{ | ||
color: '#6c757d', | ||
fontSize: '12px', | ||
textAlign: 'center', | ||
}} | ||
> | ||
<span> | ||
<Link | ||
href={unsubscribeUrl} | ||
style={{ | ||
color: '#6c757d', | ||
textDecoration: 'underline', | ||
}} | ||
> | ||
Unsubscribe here | ||
</Link>{' '} | ||
to stop receiving emails for replies to this comment. | ||
</span> | ||
</Text> | ||
</Section> | ||
</Body> | ||
</Html> | ||
); | ||
}; | ||
|
||
export const newReplyToCommentEmailHtml = async (props: NewReplyToCommentEmailProps): Promise<string> => { | ||
const component = <NewReplyToCommentEmail {...props} />; | ||
return render(component); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 0 additions & 47 deletions
47
app/src/payload/collections/Comments/hooks/emailAfterNewCommentHook.ts
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.