updated chat
This commit is contained in:
parent
b3c07355a8
commit
04ab4523a6
@ -1,7 +1,7 @@
|
|||||||
PORT=8080
|
PORT=8080
|
||||||
DATABASE_URL=postgres://postgres:Timmy0104199%3F@localhost:5432/teg?sslmode=disable
|
DATABASE_URL=postgres://postgres:Timmy0104199%3F@localhost:5432/teg?sslmode=disable
|
||||||
JWT_SECRET=tegvideo7010!
|
JWT_SECRET=tegvideo7010!
|
||||||
FRONTEND_ORIGIN=http://10.0.1.25:5173
|
FRONTEND_ORIGIN=http://localhost:5173
|
||||||
ADDRESS_SEARCH_PROVIDER=photon
|
ADDRESS_SEARCH_PROVIDER=photon
|
||||||
ADDRESS_SEARCH_URL=https://photon.komoot.io/api
|
ADDRESS_SEARCH_URL=https://photon.komoot.io/api
|
||||||
ADDRESS_SEARCH_LANGUAGE=de
|
ADDRESS_SEARCH_LANGUAGE=de
|
||||||
@ -11,7 +11,7 @@ ADDRESS_SEARCH_USER_AGENT=TEG-App/1.0
|
|||||||
|
|
||||||
ADMIN_USERNAME=admin
|
ADMIN_USERNAME=admin
|
||||||
ADMIN_DISPLAY_NAME=Administrator
|
ADMIN_DISPLAY_NAME=Administrator
|
||||||
ADMIN_EMAIL=admin@example.com
|
ADMIN_EMAIL=admin@tegdssd.de
|
||||||
ADMIN_UNIT=Administration
|
ADMIN_UNIT=Administration
|
||||||
ADMIN_GROUP=admin
|
ADMIN_GROUP=admin
|
||||||
ADMIN_RIGHTS=admin,users:read,users:write
|
ADMIN_RIGHTS=admin,users:read,users:write
|
||||||
|
|||||||
@ -352,10 +352,15 @@ func (s *Server) publishChatMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, recipientID := range recipientIDs {
|
for _, recipientID := range recipientIDs {
|
||||||
|
notificationType := "chat.message"
|
||||||
|
if conversationType == "channel" {
|
||||||
|
notificationType = "channel.message"
|
||||||
|
}
|
||||||
|
|
||||||
_ = s.createAndPublishNotification(
|
_ = s.createAndPublishNotification(
|
||||||
ctx,
|
ctx,
|
||||||
recipientID,
|
recipientID,
|
||||||
"chat.message",
|
notificationType,
|
||||||
title,
|
title,
|
||||||
preview,
|
preview,
|
||||||
"chat",
|
"chat",
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type notificationPreferencesResponse struct {
|
|||||||
DeviceJournals bool `json:"deviceJournals"`
|
DeviceJournals bool `json:"deviceJournals"`
|
||||||
SystemMessages bool `json:"systemMessages"`
|
SystemMessages bool `json:"systemMessages"`
|
||||||
ChatMessages bool `json:"chatMessages"`
|
ChatMessages bool `json:"chatMessages"`
|
||||||
|
ChannelMessages bool `json:"channelMessages"`
|
||||||
DesktopEnabled bool `json:"desktopEnabled"`
|
DesktopEnabled bool `json:"desktopEnabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ func defaultNotificationPreferences() notificationPreferencesResponse {
|
|||||||
DeviceJournals: true,
|
DeviceJournals: true,
|
||||||
SystemMessages: true,
|
SystemMessages: true,
|
||||||
ChatMessages: true,
|
ChatMessages: true,
|
||||||
|
ChannelMessages: true,
|
||||||
DesktopEnabled: false,
|
DesktopEnabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,9 +110,10 @@ func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *h
|
|||||||
device_journals,
|
device_journals,
|
||||||
system_messages,
|
system_messages,
|
||||||
chat_messages,
|
chat_messages,
|
||||||
|
channel_messages,
|
||||||
desktop_enabled
|
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)
|
ON CONFLICT (user_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
in_app_enabled = EXCLUDED.in_app_enabled,
|
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,
|
device_journals = EXCLUDED.device_journals,
|
||||||
system_messages = EXCLUDED.system_messages,
|
system_messages = EXCLUDED.system_messages,
|
||||||
chat_messages = EXCLUDED.chat_messages,
|
chat_messages = EXCLUDED.chat_messages,
|
||||||
|
channel_messages = EXCLUDED.channel_messages,
|
||||||
desktop_enabled = EXCLUDED.desktop_enabled,
|
desktop_enabled = EXCLUDED.desktop_enabled,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
`,
|
`,
|
||||||
@ -137,6 +141,7 @@ func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *h
|
|||||||
input.DeviceJournals,
|
input.DeviceJournals,
|
||||||
input.SystemMessages,
|
input.SystemMessages,
|
||||||
input.ChatMessages,
|
input.ChatMessages,
|
||||||
|
input.ChannelMessages,
|
||||||
input.DesktopEnabled,
|
input.DesktopEnabled,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,6 +177,7 @@ func (s *Server) getNotificationPreferences(ctx context.Context, userID string)
|
|||||||
device_journals,
|
device_journals,
|
||||||
system_messages,
|
system_messages,
|
||||||
chat_messages,
|
chat_messages,
|
||||||
|
channel_messages,
|
||||||
desktop_enabled
|
desktop_enabled
|
||||||
FROM notification_preferences
|
FROM notification_preferences
|
||||||
WHERE user_id = $1
|
WHERE user_id = $1
|
||||||
@ -189,6 +195,7 @@ func (s *Server) getNotificationPreferences(ctx context.Context, userID string)
|
|||||||
&settings.DeviceJournals,
|
&settings.DeviceJournals,
|
||||||
&settings.SystemMessages,
|
&settings.SystemMessages,
|
||||||
&settings.ChatMessages,
|
&settings.ChatMessages,
|
||||||
|
&settings.ChannelMessages,
|
||||||
&settings.DesktopEnabled,
|
&settings.DesktopEnabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -98,6 +98,9 @@ func shouldDeliverNotification(
|
|||||||
entityType string,
|
entityType string,
|
||||||
) bool {
|
) bool {
|
||||||
switch {
|
switch {
|
||||||
|
case notificationType == "channel.message":
|
||||||
|
return preferences.ChannelMessages
|
||||||
|
|
||||||
case strings.HasPrefix(notificationType, "chat.") || entityType == "chat":
|
case strings.HasPrefix(notificationType, "chat.") || entityType == "chat":
|
||||||
return preferences.ChatMessages
|
return preferences.ChatMessages
|
||||||
|
|
||||||
@ -228,7 +231,11 @@ func (s *Server) createAndPublishNotification(
|
|||||||
notification.Desktop = preferences.DesktopEnabled
|
notification.Desktop = preferences.DesktopEnabled
|
||||||
notification.InApp = preferences.InAppEnabled
|
notification.InApp = preferences.InAppEnabled
|
||||||
if notification.EntityType == "chat" {
|
if notification.EntityType == "chat" {
|
||||||
notification.InApp = preferences.ChatMessages
|
if notification.Type == "channel.message" {
|
||||||
|
notification.InApp = preferences.ChannelMessages
|
||||||
|
} else {
|
||||||
|
notification.InApp = preferences.ChatMessages
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -623,6 +623,7 @@ func createSchema(ctx context.Context, db *pgxpool.Pool) error {
|
|||||||
device_journals BOOLEAN NOT NULL DEFAULT true,
|
device_journals BOOLEAN NOT NULL DEFAULT true,
|
||||||
system_messages BOOLEAN NOT NULL DEFAULT true,
|
system_messages BOOLEAN NOT NULL DEFAULT true,
|
||||||
chat_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,
|
desktop_enabled BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
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 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`,
|
`ALTER TABLE IF EXISTS notification_preferences ADD COLUMN IF NOT EXISTS desktop_enabled BOOLEAN NOT NULL DEFAULT false`,
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
import { MapPinIcon as MapPinSolidIcon } from '@heroicons/react/24/solid'
|
import { MapPinIcon as MapPinSolidIcon } from '@heroicons/react/24/solid'
|
||||||
import {
|
import {
|
||||||
|
Fragment,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
@ -291,6 +292,49 @@ function formatTime(value: string) {
|
|||||||
}).format(new Date(value))
|
}).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) {
|
function formatConversationTime(value?: string | null) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return ''
|
return ''
|
||||||
@ -1879,17 +1923,41 @@ export default function ChatPage({ currentUser }: ChatPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mx-auto max-w-3xl space-y-4">
|
<div className="mx-auto max-w-3xl space-y-4">
|
||||||
{messages.map((message) => {
|
{messages.map((message, index) => {
|
||||||
const ownMessage = message.sender.id === currentUser.id
|
const ownMessage = message.sender.id === currentUser.id
|
||||||
|
const previousMessage = messages[index - 1]
|
||||||
|
const showDateSeparator =
|
||||||
|
!previousMessage ||
|
||||||
|
!isSameMessageDay(
|
||||||
|
previousMessage.createdAt,
|
||||||
|
message.createdAt,
|
||||||
|
)
|
||||||
|
const dateLabel = showDateSeparator
|
||||||
|
? formatMessageDate(message.createdAt)
|
||||||
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Fragment key={message.id}>
|
||||||
key={message.id}
|
{showDateSeparator && (
|
||||||
className={classNames(
|
<div
|
||||||
ownMessage ? 'justify-end' : 'justify-start',
|
role="separator"
|
||||||
'flex items-end gap-2',
|
aria-label={dateLabel}
|
||||||
|
className="flex items-center gap-3 py-1"
|
||||||
|
>
|
||||||
|
<span className="h-px flex-1 bg-gray-200 dark:bg-white/10" />
|
||||||
|
<span className="rounded-full bg-white px-3 py-1 text-[0.6875rem] font-semibold text-gray-500 shadow-sm ring-1 ring-gray-200 dark:bg-gray-900 dark:text-gray-400 dark:ring-white/10">
|
||||||
|
{dateLabel}
|
||||||
|
</span>
|
||||||
|
<span className="h-px flex-1 bg-gray-200 dark:bg-white/10" />
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
>
|
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
ownMessage ? 'justify-end' : 'justify-start',
|
||||||
|
'flex items-end gap-2',
|
||||||
|
)}
|
||||||
|
>
|
||||||
{!ownMessage && (
|
{!ownMessage && (
|
||||||
<Avatar
|
<Avatar
|
||||||
src={message.sender.avatar}
|
src={message.sender.avatar}
|
||||||
@ -2064,7 +2132,8 @@ export default function ChatPage({ currentUser }: ChatPageProps) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Fragment>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{showDirectTypingIndicator && (
|
{showDirectTypingIndicator && (
|
||||||
|
|||||||
@ -33,6 +33,7 @@ type NotificationSettings = {
|
|||||||
deviceJournals: boolean
|
deviceJournals: boolean
|
||||||
systemMessages: boolean
|
systemMessages: boolean
|
||||||
chatMessages: boolean
|
chatMessages: boolean
|
||||||
|
channelMessages: boolean
|
||||||
desktopEnabled: boolean
|
desktopEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ const defaultSettings: NotificationSettings = {
|
|||||||
deviceJournals: true,
|
deviceJournals: true,
|
||||||
systemMessages: true,
|
systemMessages: true,
|
||||||
chatMessages: true,
|
chatMessages: true,
|
||||||
|
channelMessages: true,
|
||||||
desktopEnabled: false,
|
desktopEnabled: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +295,7 @@ export default function Benachrichtigungen() {
|
|||||||
|
|
||||||
updateSetting('desktopEnabled', true)
|
updateSetting('desktopEnabled', true)
|
||||||
new Notification('TEG-Benachrichtigungen aktiviert', {
|
new Notification('TEG-Benachrichtigungen aktiviert', {
|
||||||
body: 'Neue Chatnachrichten können jetzt auf Windows-Ebene angezeigt werden.',
|
body: 'Neue Chat- und Channel-Nachrichten können jetzt auf Windows-Ebene angezeigt werden.',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +408,7 @@ export default function Benachrichtigungen() {
|
|||||||
<NotificationCard
|
<NotificationCard
|
||||||
icon={<EnvelopeIcon aria-hidden="true" className="size-5" />}
|
icon={<EnvelopeIcon aria-hidden="true" className="size-5" />}
|
||||||
title="Windows-Benachrichtigungen"
|
title="Windows-Benachrichtigungen"
|
||||||
description="Zeigt neue Chatnachrichten außerhalb des Browserfensters an."
|
description="Zeigt neue Chat- und Channel-Nachrichten außerhalb des Browserfensters an."
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
checked={settings.desktopEnabled}
|
checked={settings.desktopEnabled}
|
||||||
@ -449,6 +451,15 @@ export default function Benachrichtigungen() {
|
|||||||
description="Zeigt eine Browser-Schnellantwort bei neuen Team- und Direktnachrichten."
|
description="Zeigt eine Browser-Schnellantwort bei neuen Team- und Direktnachrichten."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
checked={settings.channelMessages}
|
||||||
|
onChange={(event) =>
|
||||||
|
updateSetting('channelMessages', event.target.checked)
|
||||||
|
}
|
||||||
|
label="Channel-Nachrichten"
|
||||||
|
description="Zeigt eine Browser-Benachrichtigung bei neuen Meldungen aus Channels."
|
||||||
|
/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
checked={settings.operationUpdates}
|
checked={settings.operationUpdates}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user