From 3275b8b6c975323a75ef679fd2d94c48c9895d0c Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 12:18:34 +0200 Subject: [PATCH] notifications --- app/globals.css | 32 +++++++++++++++++++++++++++++++ components/notification-badge.tsx | 10 +++++++--- components/safe-html.tsx | 29 ++++++++++++++++++++++++++++ package-lock.json | 1 + package.json | 1 + 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 components/safe-html.tsx diff --git a/app/globals.css b/app/globals.css index db8efa83..6655e0aa 100644 --- a/app/globals.css +++ b/app/globals.css @@ -74,6 +74,38 @@ } } +/* Notification message styles */ +.notification-message { + max-width: 100%; + word-wrap: break-word; + line-height: 1.4; +} + +.notification-message p { + margin: 0.25rem 0; +} + +.notification-message img { + max-width: 16px; + height: 16px; + display: inline-block; + vertical-align: middle; + border-radius: 50%; + margin-right: 4px; +} + +.notification-message a.userMention { + font-weight: 500; + color: #3b82f6; + text-decoration: none; + display: inline-flex; + align-items: center; +} + +.notification-message a:hover { + text-decoration: underline; +} + /* Email display styles */ .email-content-display { max-width: 100%; diff --git a/components/notification-badge.tsx b/components/notification-badge.tsx index 7a180260..6847273a 100644 --- a/components/notification-badge.tsx +++ b/components/notification-badge.tsx @@ -13,6 +13,7 @@ import { DropdownMenuSeparator, } from '@/components/ui/dropdown-menu'; import { formatDistanceToNow } from 'date-fns'; +import { SafeHTML } from '@/components/safe-html'; interface NotificationBadgeProps { className?: string; @@ -168,7 +169,7 @@ export const NotificationBadge = memo(function NotificationBadge({ className }:
-
+

{notification.title} {!notification.isRead && ( @@ -179,7 +180,7 @@ export const NotificationBadge = memo(function NotificationBadge({ className }: {formatDistanceToNow(new Date(notification.timestamp), { addSuffix: true })}

-
+
{!notification.isRead && (
-

{notification.message}

+
))} diff --git a/components/safe-html.tsx b/components/safe-html.tsx new file mode 100644 index 00000000..19020c82 --- /dev/null +++ b/components/safe-html.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import DOMPurify from 'dompurify'; + +interface SafeHTMLProps { + html: string; + className?: string; +} + +export function SafeHTML({ html, className }: SafeHTMLProps) { + const sanitizedHTML = DOMPurify.sanitize(html, { + USE_PROFILES: { html: true }, + ALLOWED_TAGS: [ + 'a', 'p', 'br', 'b', 'i', 'em', 'strong', 'span', 'div', + 'img', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'code', 'pre', 'blockquote' + ], + ALLOWED_ATTR: [ + 'href', 'target', 'class', 'id', 'style', 'src', 'alt', + 'data-tagged-user-id', 'data-mention' + ] + }); + + return ( +
+ ); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 503a8d07..612ac2fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "cookies-next": "^5.1.0", "crypto-js": "^4.2.0", "date-fns": "^3.6.0", + "dompurify": "^3.2.5", "dotenv": "^16.5.0", "embla-carousel-react": "8.5.1", "fullcalendar": "^6.1.15", diff --git a/package.json b/package.json index 137adee2..69423f27 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "cookies-next": "^5.1.0", "crypto-js": "^4.2.0", "date-fns": "^3.6.0", + "dompurify": "^3.2.5", "dotenv": "^16.5.0", "embla-carousel-react": "8.5.1", "fullcalendar": "^6.1.15",