diff --git a/backend/.env b/backend/.env index 7f27cac..8165ec3 100644 --- a/backend/.env +++ b/backend/.env @@ -1,7 +1,7 @@ PORT=8080 DATABASE_URL=postgres://postgres:Timmy0104199%3F@localhost:5432/teg?sslmode=disable JWT_SECRET=tegvideo7010! -FRONTEND_ORIGIN=http://10.0.1.25:5173 +FRONTEND_ORIGIN=http://localhost:5173 ADDRESS_SEARCH_PROVIDER=photon ADDRESS_SEARCH_URL=https://photon.komoot.io/api ADDRESS_SEARCH_LANGUAGE=de @@ -11,7 +11,7 @@ ADDRESS_SEARCH_USER_AGENT=TEG-App/1.0 ADMIN_USERNAME=admin ADMIN_DISPLAY_NAME=Administrator -ADMIN_EMAIL=admin@example.com +ADMIN_EMAIL=admin@tegdssd.de ADMIN_UNIT=Administration ADMIN_GROUP=admin ADMIN_RIGHTS=admin,users:read,users:write diff --git a/backend/chat_websocket.go b/backend/chat_websocket.go index 4f4a1da..fe84b4b 100644 --- a/backend/chat_websocket.go +++ b/backend/chat_websocket.go @@ -352,10 +352,15 @@ func (s *Server) publishChatMessage( } for _, recipientID := range recipientIDs { + notificationType := "chat.message" + if conversationType == "channel" { + notificationType = "channel.message" + } + _ = s.createAndPublishNotification( ctx, recipientID, - "chat.message", + notificationType, title, preview, "chat", diff --git a/backend/notification_preferences.go b/backend/notification_preferences.go index 48915b7..890a428 100644 --- a/backend/notification_preferences.go +++ b/backend/notification_preferences.go @@ -22,6 +22,7 @@ type notificationPreferencesResponse struct { DeviceJournals bool `json:"deviceJournals"` SystemMessages bool `json:"systemMessages"` ChatMessages bool `json:"chatMessages"` + ChannelMessages bool `json:"channelMessages"` DesktopEnabled bool `json:"desktopEnabled"` } @@ -37,6 +38,7 @@ func defaultNotificationPreferences() notificationPreferencesResponse { DeviceJournals: true, SystemMessages: true, ChatMessages: true, + ChannelMessages: true, DesktopEnabled: false, } } @@ -108,9 +110,10 @@ func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *h device_journals, system_messages, chat_messages, + channel_messages, desktop_enabled ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ON CONFLICT (user_id) DO UPDATE SET in_app_enabled = EXCLUDED.in_app_enabled, @@ -123,6 +126,7 @@ func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *h device_journals = EXCLUDED.device_journals, system_messages = EXCLUDED.system_messages, chat_messages = EXCLUDED.chat_messages, + channel_messages = EXCLUDED.channel_messages, desktop_enabled = EXCLUDED.desktop_enabled, updated_at = now() `, @@ -137,6 +141,7 @@ func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *h input.DeviceJournals, input.SystemMessages, input.ChatMessages, + input.ChannelMessages, input.DesktopEnabled, ) if err != nil { @@ -172,6 +177,7 @@ func (s *Server) getNotificationPreferences(ctx context.Context, userID string) device_journals, system_messages, chat_messages, + channel_messages, desktop_enabled FROM notification_preferences WHERE user_id = $1 @@ -189,6 +195,7 @@ func (s *Server) getNotificationPreferences(ctx context.Context, userID string) &settings.DeviceJournals, &settings.SystemMessages, &settings.ChatMessages, + &settings.ChannelMessages, &settings.DesktopEnabled, ) diff --git a/backend/notifications.go b/backend/notifications.go index 416bb28..c680961 100644 --- a/backend/notifications.go +++ b/backend/notifications.go @@ -98,6 +98,9 @@ func shouldDeliverNotification( entityType string, ) bool { switch { + case notificationType == "channel.message": + return preferences.ChannelMessages + case strings.HasPrefix(notificationType, "chat.") || entityType == "chat": return preferences.ChatMessages @@ -228,7 +231,11 @@ func (s *Server) createAndPublishNotification( notification.Desktop = preferences.DesktopEnabled notification.InApp = preferences.InAppEnabled if notification.EntityType == "chat" { - notification.InApp = preferences.ChatMessages + if notification.Type == "channel.message" { + notification.InApp = preferences.ChannelMessages + } else { + notification.InApp = preferences.ChatMessages + } } } diff --git a/backend/setup/main.go b/backend/setup/main.go index 1aad2f3..5305ee2 100644 --- a/backend/setup/main.go +++ b/backend/setup/main.go @@ -623,6 +623,7 @@ func createSchema(ctx context.Context, db *pgxpool.Pool) error { device_journals BOOLEAN NOT NULL DEFAULT true, system_messages BOOLEAN NOT NULL DEFAULT true, chat_messages BOOLEAN NOT NULL DEFAULT true, + channel_messages BOOLEAN NOT NULL DEFAULT true, desktop_enabled BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), @@ -631,6 +632,7 @@ func createSchema(ctx context.Context, db *pgxpool.Pool) error { `, `ALTER TABLE IF EXISTS notification_preferences ADD COLUMN IF NOT EXISTS chat_messages BOOLEAN NOT NULL DEFAULT true`, + `ALTER TABLE IF EXISTS notification_preferences ADD COLUMN IF NOT EXISTS channel_messages BOOLEAN NOT NULL DEFAULT true`, `ALTER TABLE IF EXISTS notification_preferences ADD COLUMN IF NOT EXISTS desktop_enabled BOOLEAN NOT NULL DEFAULT false`, ` diff --git a/frontend/src/pages/chat/ChatPage.tsx b/frontend/src/pages/chat/ChatPage.tsx index c0904a2..a77d88c 100644 --- a/frontend/src/pages/chat/ChatPage.tsx +++ b/frontend/src/pages/chat/ChatPage.tsx @@ -24,6 +24,7 @@ import { } from '@heroicons/react/24/outline' import { MapPinIcon as MapPinSolidIcon } from '@heroicons/react/24/solid' import { + Fragment, useCallback, useEffect, useMemo, @@ -291,6 +292,49 @@ function formatTime(value: string) { }).format(new Date(value)) } +function isSameMessageDay(first: string, second: string) { + const firstDate = new Date(first) + const secondDate = new Date(second) + + if ( + Number.isNaN(firstDate.getTime()) || + Number.isNaN(secondDate.getTime()) + ) { + return first === second + } + + return ( + firstDate.getFullYear() === secondDate.getFullYear() && + firstDate.getMonth() === secondDate.getMonth() && + firstDate.getDate() === secondDate.getDate() + ) +} + +function formatMessageDate(value: string) { + const date = new Date(value) + if (Number.isNaN(date.getTime())) { + return '' + } + + const today = new Date() + const yesterday = new Date(today) + yesterday.setDate(today.getDate() - 1) + + if (isSameMessageDay(value, today.toISOString())) { + return 'Heute' + } + if (isSameMessageDay(value, yesterday.toISOString())) { + return 'Gestern' + } + + return new Intl.DateTimeFormat('de-DE', { + weekday: 'long', + day: '2-digit', + month: 'long', + year: date.getFullYear() === today.getFullYear() ? undefined : 'numeric', + }).format(date) +} + function formatConversationTime(value?: string | null) { if (!value) { return '' @@ -1879,17 +1923,41 @@ export default function ChatPage({ currentUser }: ChatPageProps) { ) : (