NeahStable/NOTIFICATIONS_FLOW_ANALYSIS.md
2026-01-11 22:22:53 +01:00

10 KiB

🔔 Analyse du Flow de Notifications

📋 Vue d'ensemble

Le système de notifications est un système multi-sources qui agrège les notifications de plusieurs services externes (Leantime, Nextcloud, etc.) et les affiche dans un badge clignotant rouge dans la navbar.


🎯 Déclenchement du Badge Rouge Clignotant

Condition d'affichage

Le badge rouge apparaît lorsque :

hasUnread = notificationCount.unread > 0

Fichier : components/notification-badge.tsx:26

const hasUnread = notificationCount.unread > 0;
{hasUnread && (
  <Badge variant="notification" size="notification" className="absolute -top-2 -right-2 z-50">
    {notificationCount.unread > 99 ? '99+' : notificationCount.unread}
  </Badge>
)}

Style du badge

Fichier : components/ui/badge.tsx:18-19

notification: "border-transparent bg-red-500 text-white hover:bg-red-600 absolute -top-1 -right-1 px-1.5 py-0.5 min-w-[1.25rem] h-5 flex items-center justify-center"

Le badge est rouge (bg-red-500) et positionné en haut à droite de l'icône cloche.


🔄 Flow Complet de Notifications

1. Initialisation (Au chargement de la page)

MainNav (navbar)
  └─> NotificationBadge (composant)
      └─> useNotifications() (hook)
          └─> useEffect() [status === 'authenticated']
              ├─> fetchNotificationCount(true)  // Force refresh
              └─> fetchNotifications(1, 20)     // Charge les 20 premières

Fichiers :

  • components/main-nav.tsx - Affiche le badge
  • components/notification-badge.tsx:86-91 - Fetch initial
  • hooks/use-notifications.ts:265-277 - Initialisation

2. Rafraîchissement Automatique (Polling)

Le système utilise un système de rafraîchissement unifié qui poll les notifications toutes les 30 secondes.

useUnifiedRefresh({
  resource: 'notifications-count',
  interval: 30000,  // 30 secondes
  priority: 'high',
  onRefresh: fetchNotificationCount(true)
})

Fichiers :

  • hooks/use-notifications.ts:253-262 - Configuration du refresh
  • lib/constants/refresh-intervals.ts:12 - Interval défini
  • lib/services/refresh-manager.ts - Gestionnaire centralisé

Interval de rafraîchissement :

  • Notifications Count : 30 secondes (priorité haute)
  • Notifications List : 30 secondes (priorité haute)

3. Récupération des Notifications (API Calls)

A. Fetch du Count (Badge)

GET /api/notifications/count
  └─> NotificationService.getInstance()
      └─> getNotificationCount(userId)
          ├─> Check Redis Cache (TTL: 30s)
          └─> Si pas en cache:
              ├─> LeantimeAdapter.getNotificationCount()
              │   └─> API Leantime: getAllNotifications(limit: 1000)
              │       └─> Compte les notifications avec read=0
              └─> Autres adapters (futurs)
          └─> Cache dans Redis (30s)

Fichiers :

  • app/api/notifications/count/route.ts
  • lib/services/notifications/notification-service.ts:182-310
  • lib/services/notifications/leantime-adapter.ts:150-280

B. Fetch de la Liste

GET /api/notifications?page=1&limit=20
  └─> NotificationService.getInstance()
      └─> getNotifications(userId, page, limit)
          ├─> Check Redis Cache (TTL: 30s)
          └─> Si pas en cache:
              ├─> LeantimeAdapter.getNotifications()
              │   └─> API Leantime: getAllNotifications()
              │       └─> Transforme en Notification[]
              └─> Autres adapters (futurs)
          └─> Trie par timestamp (newest first)
          └─> Cache dans Redis (30s)

Fichiers :

  • app/api/notifications/route.ts
  • lib/services/notifications/notification-service.ts:61-177
  • lib/services/notifications/leantime-adapter.ts:57-148

4. Sources de Notifications (Adapters)

Actuellement, un seul adapter est actif :

LeantimeAdapter

Source : Leantime (Agilité - agilite.slm-lab.net)

Méthode API :

{
  "jsonrpc": "2.0",
  "method": "leantime.rpc.Notifications.Notifications.getAllNotifications",
  "params": {
    "userId": <leantime_user_id>,
    "showNewOnly": 0,
    "limitStart": 0,
    "limitEnd": 1000
  }
}

Types de notifications Leantime :

  • Tâches assignées
  • Commentaires
  • Mentions
  • Changements de statut
  • Dates d'échéance

Fichier : lib/services/notifications/leantime-adapter.ts

Futurs adapters (non implémentés) :

  • NextcloudAdapter
  • GiteaAdapter
  • DolibarrAdapter
  • MoodleAdapter

5. Cache Redis (Performance)

Le système utilise Redis pour mettre en cache les notifications et éviter les appels API répétés.

Clés de cache :

  • notifications:count:{userId} - TTL: 30 secondes
  • notifications:list:{userId}:{page}:{limit} - TTL: 30 secondes

Stratégie :

  • Cache-first avec fallback API
  • Background refresh si TTL < 50%
  • Invalidation automatique après 30s

Fichier : lib/services/notifications/notification-service.ts:11-18


6. Déduplication des Requêtes

Le système utilise un request deduplicator pour éviter les appels API en double.

Window de déduplication : 2000ms (2 secondes)

Fichier : hooks/use-notifications.ts:39-59

const requestKey = `notifications-count-${session.user.id}`;
const data = await requestDeduplicator.execute(
  requestKey,
  async () => { /* fetch */ },
  2000 // 2 secondes
);

🎨 Affichage du Badge

Composant NotificationBadge

Localisation : components/notification-badge.tsx

Structure :

<DropdownMenu>
  <DropdownMenuTrigger>
    <Button>
      <Bell /> {/* Icône cloche */}
      {hasUnread && (
        <Badge variant="notification">
          {count > 99 ? '99+' : count}
        </Badge>
      )}
    </Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    {/* Liste des notifications */}
  </DropdownMenuContent>
</DropdownMenu>

États du Badge

État Condition Affichage
Visible notificationCount.unread > 0 Badge rouge avec nombre
Caché notificationCount.unread === 0 Pas de badge
99+ notificationCount.unread > 99 Affiche "99+"

🔍 Déclencheurs du Badge Rouge

1. Notifications Leantime

Les notifications sont créées dans Leantime lorsque :

  • Une tâche vous est assignée
  • Quelqu'un commente sur une tâche
  • Vous êtes mentionné
  • Une date d'échéance approche
  • Un statut change

Flow :

Action dans Leantime
  └─> Leantime crée notification (read=0)
      └─> Polling toutes les 30s
          └─> LeantimeAdapter récupère
              └─> NotificationService agrège
                  └─> API retourne count
                      └─> Badge apparaît si unread > 0

2. Rafraîchissement Automatique

Le badge se met à jour automatiquement via :

  • Polling : Toutes les 30 secondes
  • Ouverture du dropdown : Fetch immédiat
  • Mount du composant : Fetch initial

Fichier : hooks/use-notifications.ts:253-262

3. Marquer comme lu

Quand l'utilisateur marque une notification comme lue :

Clic sur "Mark as read"
  └─> POST /api/notifications/{id}/read
      └─> LeantimeAdapter.markAsRead()
          └─> API Leantime: markNotificationRead()
              └─> Update local state (optimistic)
                  └─> Refresh count (polling)
                      └─> Badge disparaît si unread === 0

Fichier : hooks/use-notifications.ts:123-189


📊 Structure des Données

NotificationCount

interface NotificationCount {
  total: number;        // Total de notifications
  unread: number;       // Nombre de non lues (TRIGGER DU BADGE)
  sources: {
    leantime: {
      total: number;
      unread: number;
    }
  }
}

Notification

interface Notification {
  id: string;
  source: 'leantime' | 'nextcloud' | ...;
  sourceId: string;
  type: string;
  title: string;
  message: string;
  link?: string;
  isRead: boolean;      // false = non lue
  timestamp: Date;
  priority: 'low' | 'normal' | 'high';
  user: { id: string; name?: string; };
  metadata?: Record<string, any>;
}

🚀 Points d'Entrée (Triggers)

1. Au chargement de l'app

  • NotificationBadge monte
  • useNotifications s'initialise
  • Fetch immédiat du count et de la liste

2. Polling automatique

  • Toutes les 30 secondes
  • Via useUnifiedRefresh
  • Priorité haute

3. Ouverture du dropdown

  • Fetch immédiat des notifications
  • Rafraîchissement du count

4. Actions utilisateur

  • Marquer comme lu → Update count
  • Marquer tout comme lu → unread = 0

🔧 Configuration

Intervalles de rafraîchissement

Fichier : lib/constants/refresh-intervals.ts

NOTIFICATIONS_COUNT: 30000  // 30 secondes
NOTIFICATIONS: 30000        // 30 secondes

Cache TTL

Fichier : lib/services/notifications/notification-service.ts:15-16

COUNT_CACHE_TTL = 30;  // 30 secondes
LIST_CACHE_TTL = 30;   // 30 secondes

🐛 Debugging

Logs disponibles

Le système logge abondamment :

  • [NOTIFICATION_BADGE] - Actions du composant
  • [useNotifications] - Actions du hook
  • [NOTIFICATION_SERVICE] - Service backend
  • [LEANTIME_ADAPTER] - Appels API Leantime

Endpoints de debug

  • GET /api/debug/notifications - État du système
  • GET /api/debug/leantime-methods - Méthodes Leantime disponibles

📝 Résumé : Ce qui déclenche le badge rouge

  1. Condition : notificationCount.unread > 0
  2. Source principale : Leantime (notifications non lues)
  3. Rafraîchissement : Toutes les 30 secondes automatiquement
  4. Cache : Redis (30s TTL) pour performance
  5. Déduplication : 2 secondes pour éviter les doublons
  6. Affichage : Badge rouge avec nombre (ou "99+")

Le badge apparaît dès qu'il y a au moins une notification non lue dans Leantime (ou autres sources futures).