219 lines
5.7 KiB
Go
219 lines
5.7 KiB
Go
// backend/notification_preferences.go
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
type notificationPreferencesResponse struct {
|
|
SoundEnabled bool `json:"soundEnabled"`
|
|
SoundName string `json:"soundName"`
|
|
EmailEnabled bool `json:"emailEnabled"`
|
|
DeviceUpdates bool `json:"deviceUpdates"`
|
|
DeviceJournals bool `json:"deviceJournals"`
|
|
ChatMessages bool `json:"chatMessages"`
|
|
ChannelMessages bool `json:"channelMessages"`
|
|
DesktopEnabled bool `json:"desktopEnabled"`
|
|
PushChatMessages bool `json:"pushChatMessages"`
|
|
PushChannelMessages bool `json:"pushChannelMessages"`
|
|
}
|
|
|
|
func defaultNotificationPreferences() notificationPreferencesResponse {
|
|
return notificationPreferencesResponse{
|
|
SoundEnabled: false,
|
|
SoundName: "default",
|
|
EmailEnabled: false,
|
|
DeviceUpdates: true,
|
|
DeviceJournals: true,
|
|
ChatMessages: true,
|
|
ChannelMessages: true,
|
|
DesktopEnabled: false,
|
|
PushChatMessages: true,
|
|
PushChannelMessages: true,
|
|
}
|
|
}
|
|
|
|
// shouldPushNotification entscheidet anhand der granularen Push-Einstellungen,
|
|
// ob für den jeweiligen Benachrichtigungstyp ein Web-Push gesendet werden soll.
|
|
// Es deckt aktuell die Nachrichtentypen ab, für die der Web-Push-Payload korrekt
|
|
// aufgebaut werden kann (Chat- und Channel-Nachrichten).
|
|
func shouldPushNotification(
|
|
preferences notificationPreferencesResponse,
|
|
notificationType string,
|
|
) bool {
|
|
switch notificationType {
|
|
case "channel.message":
|
|
return preferences.PushChannelMessages
|
|
case "chat.message":
|
|
return preferences.PushChatMessages
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func normalizeNotificationSoundName(value string) string {
|
|
value = strings.TrimSpace(strings.ToLower(value))
|
|
|
|
switch value {
|
|
case "default", "chime", "soft", "alert", "success":
|
|
return value
|
|
default:
|
|
return "default"
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleGetNotificationPreferences(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := userFromContext(r.Context())
|
|
if !ok {
|
|
writeError(w, http.StatusUnauthorized, "Unauthorized")
|
|
return
|
|
}
|
|
|
|
settings, err := s.getNotificationPreferences(r.Context(), user.ID)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"settings": defaultNotificationPreferences(),
|
|
})
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen konnten nicht geladen werden")
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"settings": settings,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := userFromContext(r.Context())
|
|
if !ok {
|
|
writeError(w, http.StatusUnauthorized, "Unauthorized")
|
|
return
|
|
}
|
|
|
|
var input notificationPreferencesResponse
|
|
|
|
if err := readJSON(r, &input); err != nil {
|
|
writeError(w, http.StatusBadRequest, "Invalid JSON")
|
|
return
|
|
}
|
|
|
|
input.SoundName = normalizeNotificationSoundName(input.SoundName)
|
|
|
|
_, err := s.db.Exec(
|
|
r.Context(),
|
|
`
|
|
INSERT INTO notification_preferences (
|
|
user_id,
|
|
sound_enabled,
|
|
sound_name,
|
|
email_enabled,
|
|
device_updates,
|
|
device_journals,
|
|
chat_messages,
|
|
channel_messages,
|
|
desktop_enabled,
|
|
push_chat_messages,
|
|
push_channel_messages
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
ON CONFLICT (user_id)
|
|
DO UPDATE SET
|
|
sound_enabled = EXCLUDED.sound_enabled,
|
|
sound_name = EXCLUDED.sound_name,
|
|
email_enabled = EXCLUDED.email_enabled,
|
|
device_updates = EXCLUDED.device_updates,
|
|
device_journals = EXCLUDED.device_journals,
|
|
chat_messages = EXCLUDED.chat_messages,
|
|
channel_messages = EXCLUDED.channel_messages,
|
|
desktop_enabled = EXCLUDED.desktop_enabled,
|
|
push_chat_messages = EXCLUDED.push_chat_messages,
|
|
push_channel_messages = EXCLUDED.push_channel_messages,
|
|
updated_at = now()
|
|
`,
|
|
user.ID,
|
|
input.SoundEnabled,
|
|
input.SoundName,
|
|
input.EmailEnabled,
|
|
input.DeviceUpdates,
|
|
input.DeviceJournals,
|
|
input.ChatMessages,
|
|
input.ChannelMessages,
|
|
input.DesktopEnabled,
|
|
input.PushChatMessages,
|
|
input.PushChannelMessages,
|
|
)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen konnten nicht gespeichert werden")
|
|
return
|
|
}
|
|
|
|
settings, err := s.getNotificationPreferences(r.Context(), user.ID)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen wurden gespeichert, konnten aber nicht neu geladen werden")
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"settings": settings,
|
|
})
|
|
}
|
|
|
|
func (s *Server) getNotificationPreferences(ctx context.Context, userID string) (notificationPreferencesResponse, error) {
|
|
settings := defaultNotificationPreferences()
|
|
|
|
err := s.db.QueryRow(
|
|
ctx,
|
|
`
|
|
SELECT
|
|
sound_enabled,
|
|
COALESCE(sound_name, 'default'),
|
|
email_enabled,
|
|
device_updates,
|
|
device_journals,
|
|
chat_messages,
|
|
channel_messages,
|
|
desktop_enabled,
|
|
push_chat_messages,
|
|
push_channel_messages
|
|
FROM notification_preferences
|
|
WHERE user_id = $1
|
|
LIMIT 1
|
|
`,
|
|
userID,
|
|
).Scan(
|
|
&settings.SoundEnabled,
|
|
&settings.SoundName,
|
|
&settings.EmailEnabled,
|
|
&settings.DeviceUpdates,
|
|
&settings.DeviceJournals,
|
|
&settings.ChatMessages,
|
|
&settings.ChannelMessages,
|
|
&settings.DesktopEnabled,
|
|
&settings.PushChatMessages,
|
|
&settings.PushChannelMessages,
|
|
)
|
|
|
|
settings.SoundName = normalizeNotificationSoundName(settings.SoundName)
|
|
|
|
return settings, err
|
|
}
|
|
|
|
func (s *Server) getNotificationPreferencesOrDefault(ctx context.Context, userID string) (notificationPreferencesResponse, error) {
|
|
settings, err := s.getNotificationPreferences(ctx, userID)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return defaultNotificationPreferences(), nil
|
|
}
|
|
|
|
return settings, err
|
|
}
|