feat(notifications) : add profile pictures and guide previews
This commit is contained in:
@@ -55,19 +55,20 @@ describe("comment notifications", () => {
|
||||
expect(state.send).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([false, true])("still sends new-comment notifications (reply: %s)", async (reply) => {
|
||||
it.each([false, true])("still sends new-comment notifications with the author's profile picture (reply: %s)", async (reply) => {
|
||||
const recipient = { id: reply ? "reader" : "admin", admin: !reply };
|
||||
state.rows = [
|
||||
[{ id, version: 1, hidden: false, deletedAt: null, authorId: "author", replyToId: reply ? "parent-id" : null, rootId: reply ? "root-id" : null }],
|
||||
...(reply ? [[{ authorId: recipient.id, hidden: false, deletedAt: null }], [{ id: recipient.id, role: "user", banned: false }]] : [[{ id: recipient.id }]]),
|
||||
[{ name: "Author" }], [{ text: "Original comment" }], [subscription],
|
||||
[{ name: "Author", image: "https://profile.example/author.png" }], [{ text: "Original comment" }], [subscription],
|
||||
];
|
||||
await sendCommentPush(id, 1);
|
||||
expect(state.notifications).toHaveBeenCalledWith([expect.objectContaining({ userId: recipient.id, adminOnly: !reply })], expect.objectContaining({
|
||||
kind: reply ? "comment_reply" : "comment_new", eventKey: `comment:${id}`, body: "Original comment",
|
||||
}));
|
||||
expect(state.send).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(state.send.mock.calls[0][1])).toMatchObject({ body: "Original comment" });
|
||||
expect(JSON.parse(state.send.mock.calls[0][1])).toMatchObject({ body: "Original comment", icon: "https://profile.example/author.png" });
|
||||
expect(state.notifications.mock.calls[0][1].title).toBe(reply ? "Author ตอบกลับความคิดเห็นของคุณ" : "Author แสดงความคิดเห็นใหม่ในไกด์ Test Guide");
|
||||
});
|
||||
|
||||
it("still delivers a delayed initial notification when the comment has since been edited", async () => {
|
||||
@@ -78,6 +79,17 @@ describe("comment notifications", () => {
|
||||
await sendCommentPush(id, 1);
|
||||
expect(state.notifications).toHaveBeenCalledWith(expect.any(Array), expect.objectContaining({ kind: "comment_new", body: "Original revision" }));
|
||||
expect(state.send).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(state.send.mock.calls[0][1]).icon).toBe("/icon/nav/Comment.webp");
|
||||
});
|
||||
|
||||
it("keeps Stygian notification titles separate from guide titles", async () => {
|
||||
state.authorize.mockResolvedValue({ destination: { target: "stygian:123", href: "/stygian/comment", name: "Test round", writable: true } });
|
||||
state.rows = [
|
||||
[{ id, version: 1, hidden: false, deletedAt: null, authorId: "author", replyToId: null }],
|
||||
[{ id: "admin" }], [{ name: "Author" }], [{ text: "Original comment" }], [subscription],
|
||||
];
|
||||
await sendCommentPush(id, 1);
|
||||
expect(state.notifications.mock.calls[0][1].title).toBe("Author แสดงความคิดเห็นใหม่ใน Test round");
|
||||
});
|
||||
|
||||
it("saves edits through PATCH without scheduling a notification", async () => {
|
||||
|
||||
@@ -40,9 +40,9 @@ export async function sendCommentPush(id: string, version?: number) {
|
||||
} catch { return; }
|
||||
if (!destination.writable) return;
|
||||
url = `/admin/comments?target=${encodeURIComponent(destination.target)}&comment=${id}`;
|
||||
title = `แสดงความคิดเห็นใหม่ใน ${destination.name}`;
|
||||
title = destination.target.startsWith("guide:") ? `แสดงความคิดเห็นใหม่ในไกด์ ${destination.name}` : `แสดงความคิดเห็นใหม่ใน ${destination.name}`;
|
||||
}
|
||||
const [author] = await getDb().select({ name: users.name }).from(users).where(eq(users.id, reply.authorId)).limit(1);
|
||||
const [author] = await getDb().select({ name: users.name, image: users.image }).from(users).where(eq(users.id, reply.authorId)).limit(1);
|
||||
const [revision] = await getDb().select({ text: commentRevisions.text }).from(commentRevisions)
|
||||
.where(and(eq(commentRevisions.commentId, id), eq(commentRevisions.version, notificationVersion))).limit(1);
|
||||
if (!revision) return;
|
||||
@@ -56,7 +56,7 @@ export async function sendCommentPush(id: string, version?: number) {
|
||||
if (!subscriptions.length) return;
|
||||
webPush.setVapidDetails(process.env.WEB_PUSH_SUBJECT!, process.env.WEB_PUSH_PUBLIC_KEY!, process.env.WEB_PUSH_PRIVATE_KEY!);
|
||||
const payload = JSON.stringify({ id: `comment-${id}`, title: notificationTitle,
|
||||
body, icon: "/icon/nav/Comment.webp",
|
||||
body, icon: author?.image || "/icon/nav/Comment.webp",
|
||||
url });
|
||||
for (let offset = 0; offset < subscriptions.length; offset += 10) {
|
||||
await Promise.all(subscriptions.slice(offset, offset + 10).map(async (subscription) => {
|
||||
|
||||
Reference in New Issue
Block a user