@@ -196,11 +242,11 @@ export function PushPreferencesSection() {
)}
- {/* v0.10.5 F552: Weekly digest opt-in */}
+ {/* v0.10.5 F552: Weekly digest — new tracks from followed artists (ORIGIN §8.1) */}
Digest hebdomadaire
- Recevoir un récapitulatif par email des notifications non lues chaque dimanche
+ Recevoir chaque dimanche un récapitulatif des nouvelles sorties des artistes que vous suivez
diff --git a/veza-backend-api/internal/services/notification_digest_worker.go b/veza-backend-api/internal/services/notification_digest_worker.go
index b678e02fa..599a8081a 100644
--- a/veza-backend-api/internal/services/notification_digest_worker.go
+++ b/veza-backend-api/internal/services/notification_digest_worker.go
@@ -1,4 +1,4 @@
-// Package services - v0.10.5 F552: Weekly notification digest worker
+// Package services - v0.10.5 F552: Weekly digest of new tracks from followed users (ORIGIN §8.1)
package services
import (
@@ -12,6 +12,14 @@ import (
"gorm.io/gorm"
)
+// digestTrackItem represents a track for the weekly digest (new releases from followed artists)
+type digestTrackItem struct {
+ TrackID string
+ Title string
+ Artist string
+ Link string
+}
+
// NotificationDigestWorker sends weekly digest emails to users with weekly_digest_enabled
type NotificationDigestWorker struct {
db *gorm.DB
@@ -81,55 +89,70 @@ func (w *NotificationDigestWorker) runDigest(ctx context.Context) error {
if baseURL == "" {
baseURL = "http://localhost:5173"
}
- notificationsURL := baseURL + "/notifications"
for _, u := range users {
- var notifs []struct {
+ // ORIGIN §8.1: Weekly digest = new tracks from followed users (not unread notifications)
+ var tracks []struct {
+ TrackID string
Title string
- Content string
- Link string
- Type string
+ Artist string
}
err := w.db.WithContext(ctx).Raw(`
- SELECT title, content, COALESCE(link, '') as link, type
- FROM notifications
- WHERE user_id = ? AND read = FALSE AND created_at > NOW() - INTERVAL '7 days'
- ORDER BY created_at DESC
+ SELECT t.id::text as track_id, t.title,
+ COALESCE(NULLIF(TRIM(t.artist), ''), up.display_name, u.username) as artist
+ FROM tracks t
+ INNER JOIN follows f ON f.followed_id = t.creator_id AND f.follower_id = ?
+ LEFT JOIN users u ON u.id = t.creator_id
+ LEFT JOIN user_profiles up ON up.user_id = t.creator_id
+ WHERE t.status = 'completed' AND t.is_public = true
+ AND t.created_at > NOW() - INTERVAL '7 days'
+ ORDER BY t.created_at DESC
LIMIT 50
- `, u.UserID).Scan(¬ifs).Error
+ `, u.UserID).Scan(&tracks).Error
if err != nil {
- w.logger.Warn("Failed to get notifications for digest", zap.String("user_id", u.UserID.String()), zap.Error(err))
+ w.logger.Warn("Failed to get feed tracks for digest", zap.String("user_id", u.UserID.String()), zap.Error(err))
continue
}
- if len(notifs) == 0 {
+ if len(tracks) == 0 {
continue
}
- notifList := make([]map[string]string, len(notifs))
- for i, n := range notifs {
- notifList[i] = map[string]string{
- "Title": n.Title,
- "Content": n.Content,
- "Link": n.Link,
- "Type": n.Type,
+ trackList := make([]digestTrackItem, len(tracks))
+ for i, t := range tracks {
+ trackList[i] = digestTrackItem{
+ TrackID: t.TrackID,
+ Title: t.Title,
+ Artist: t.Artist,
+ Link: "/tracks/" + t.TrackID,
+ }
+ }
+ // Template expects []map[string]string for range
+ trackMaps := make([]map[string]string, len(trackList))
+ for i, t := range trackList {
+ trackMaps[i] = map[string]string{
+ "TrackID": t.TrackID,
+ "Title": t.Title,
+ "Artist": t.Artist,
+ "Link": t.Link,
}
}
templateData := map[string]interface{}{
- "Username": u.Username,
- "Notifications": notifList,
- "BaseURL": baseURL,
- "NotificationsURL": notificationsURL,
+ "Username": u.Username,
+ "Tracks": trackMaps,
+ "BaseURL": baseURL,
+ "FeedURL": baseURL + "/feed",
+ "TrackCount": len(tracks),
}
w.jobEnqueuer.EnqueueEmailJobWithTemplate(
u.Email,
- "Your Veza weekly digest",
+ "New releases from artists you follow — Veza",
"notification_digest",
templateData,
)
- w.logger.Info("Digest queued", zap.String("user_id", u.UserID.String()), zap.Int("count", len(notifs)))
+ w.logger.Info("Digest queued", zap.String("user_id", u.UserID.String()), zap.Int("count", len(tracks)))
}
return nil
diff --git a/veza-backend-api/internal/services/notification_service.go b/veza-backend-api/internal/services/notification_service.go
index 5fe351047..9ec63d531 100644
--- a/veza-backend-api/internal/services/notification_service.go
+++ b/veza-backend-api/internal/services/notification_service.go
@@ -418,7 +418,8 @@ type NotificationPrefs struct {
func (ns *NotificationService) GetPreferences(userID uuid.UUID) (*NotificationPrefs, error) {
ctx := context.Background()
- prefs := &NotificationPrefs{PushFollow: true, PushLike: true, PushComment: true, PushMessage: true, PushMention: true}
+ // v0.10.5: default push_message and push_follow only; others off (anti-FOMO)
+ prefs := &NotificationPrefs{PushFollow: true, PushMessage: true, PushLike: false, PushComment: false, PushMention: false}
var startNullable, endNullable sql.NullString
err := ns.db.QueryRowContext(ctx, `
@@ -456,10 +457,11 @@ func (ns *NotificationService) UpdatePreferences(userID uuid.UUID, pushFollow, p
qhStart := nullIfEmpty(quietHoursStart)
qhEnd := nullIfEmpty(quietHoursEnd)
+ // v0.10.5: defaults — push_follow, push_message true; push_like, push_comment, push_mention false
_, err := ns.db.ExecContext(ctx, `
INSERT INTO notification_preferences (user_id, push_follow, push_like, push_comment, push_message, push_mention,
quiet_hours_enabled, quiet_hours_start, quiet_hours_end, weekly_digest_enabled, updated_at)
- VALUES ($1, COALESCE($2, true), COALESCE($3, true), COALESCE($4, true), COALESCE($5, true), COALESCE($6, true),
+ VALUES ($1, COALESCE($2, true), COALESCE($3, false), COALESCE($4, false), COALESCE($5, true), COALESCE($6, false),
COALESCE($7, false), $8::time, $9::time, COALESCE($10, false), NOW())
ON CONFLICT (user_id) DO UPDATE SET
push_follow = CASE WHEN $2 IS NOT NULL THEN $2 ELSE notification_preferences.push_follow END,
diff --git a/veza-backend-api/migrations/941_notification_prefs_defaults_v0105.sql b/veza-backend-api/migrations/941_notification_prefs_defaults_v0105.sql
new file mode 100644
index 000000000..57931e2fd
--- /dev/null
+++ b/veza-backend-api/migrations/941_notification_prefs_defaults_v0105.sql
@@ -0,0 +1,15 @@
+-- Migration 941: v0.10.5 — Notification preferences defaults
+-- Par défaut : push_message et push_follow activés ; likes/commentaires/mentions désactivés (anti-FOMO)
+
+ALTER TABLE notification_preferences
+ ALTER COLUMN push_follow SET DEFAULT true,
+ ALTER COLUMN push_message SET DEFAULT true,
+ ALTER COLUMN push_like SET DEFAULT false,
+ ALTER COLUMN push_comment SET DEFAULT false,
+ ALTER COLUMN push_mention SET DEFAULT false;
+
+COMMENT ON COLUMN notification_preferences.push_follow IS 'v0.10.5: default true — new followers';
+COMMENT ON COLUMN notification_preferences.push_message IS 'v0.10.5: default true — direct messages';
+COMMENT ON COLUMN notification_preferences.push_like IS 'v0.10.5: default false — engagement (opt-in)';
+COMMENT ON COLUMN notification_preferences.push_comment IS 'v0.10.5: default false — engagement (opt-in)';
+COMMENT ON COLUMN notification_preferences.push_mention IS 'v0.10.5: default false — mentions (opt-in)';
diff --git a/veza-backend-api/migrations/941_notification_prefs_defaults_v0105_down.sql b/veza-backend-api/migrations/941_notification_prefs_defaults_v0105_down.sql
new file mode 100644
index 000000000..4eb5b2972
--- /dev/null
+++ b/veza-backend-api/migrations/941_notification_prefs_defaults_v0105_down.sql
@@ -0,0 +1,7 @@
+-- Rollback migration 941: restore original push defaults
+ALTER TABLE notification_preferences
+ ALTER COLUMN push_follow SET DEFAULT true,
+ ALTER COLUMN push_message SET DEFAULT true,
+ ALTER COLUMN push_like SET DEFAULT true,
+ ALTER COLUMN push_comment SET DEFAULT true,
+ ALTER COLUMN push_mention SET DEFAULT true;
diff --git a/veza-backend-api/templates/email/notification_digest.html b/veza-backend-api/templates/email/notification_digest.html
index 755ac00ed..c06640431 100644
--- a/veza-backend-api/templates/email/notification_digest.html
+++ b/veza-backend-api/templates/email/notification_digest.html
@@ -3,27 +3,24 @@
- Your Veza weekly digest
+ New releases from artists you follow — Veza
-
Your weekly digest
+
New releases from artists you follow
Hello {{.Username}},
-
Here are your unread notifications from the past week:
+
Here are the new tracks from artists you follow this week: