feat: implement email notification system for comments

- Added MailModule to handle email notifications for comment events.
- Introduced CommentCreatedEvent to encapsulate comment creation details.
- Implemented CommentNotificationListener to send notifications to relevant users when a comment is created.
- Integrated Resend service for sending emails, with templates for comment notifications.
- Updated various modules to support the new email notification feature.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-12-03 20:57:26 +08:00
parent 1aac293020
commit ced9e747aa
26 changed files with 455 additions and 36 deletions

View File

@@ -1,3 +1,5 @@
import './index.css'
import { clsxm } from '@afilmory/utils'
import * as ScrollAreaBase from '@radix-ui/react-scroll-area'
import clsx from 'clsx'
@@ -72,7 +74,7 @@ Scrollbar.displayName = 'ScrollArea.Scrollbar'
const Viewport = ({
ref: forwardedRef,
className,
mask = false,
focusable = true,
...rest
}: React.ComponentPropsWithoutRef<typeof ScrollAreaBase.Viewport> & {
@@ -89,11 +91,7 @@ const Viewport = ({
{...rest}
ref={ref}
tabIndex={focusable ? -1 : void 0}
className={clsxm(
'block size-full',
className,
)}
className={clsxm('block size-full', mask && 'mask-scroller', className)}
/>
)
}

View File

@@ -0,0 +1,44 @@
.mask-scroller {
mask:
linear-gradient(white, transparent) 50% 0 / 100% 0 no-repeat,
linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
linear-gradient(transparent, white) 50% 100% / 100% 30px no-repeat;
mask-composite: exclude;
mask-size:
100% calc((var(--scroll-progress-top) / 100) * 30px),
100% 100%,
100% calc((100 - (100 * (var(--scroll-progress-bottom) / 100))) * 1px);
}
@supports (animation-timeline: scroll()) {
.mask-scroller {
mask:
linear-gradient(white, transparent) 50% 0 / 100% 0 no-repeat,
linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
linear-gradient(transparent, white) 50% 100% / 100% 30px no-repeat;
mask-composite: exclude;
animation:
mask-up both linear,
mask-down both linear;
animation-timeline: scroll(self);
animation-range:
0 50px,
calc(100% - 50px) 100%;
}
}
@keyframes mask-up {
100% {
mask-size:
100% 30px,
100% 100%,
100% 30px;
}
}
@keyframes mask-down {
100% {
mask-size:
100% 30px,
100% 100%,
100% 0;
}
}

View File

@@ -1,7 +1,7 @@
import type { PhotoManifestItem } from '@afilmory/builder'
const GENERATOR_NAME = 'Afilmory Feed Generator'
const EXIF_NAMESPACE = 'https://afilmory.com/rss/exif'
const EXIF_NAMESPACE = 'https://afilmory.art/rss/exif'
const PROTOCOL_VERSION = '1.1'
const PROTOCOL_ID = 'afilmory-rss-exif'