Refonte du système de notifications : passage aux notifications dérivées
Remplace le système de notifications stockées par un système de providers qui dérivent les notifications des données existantes (commentaires, réponses, demandes de projet, demandes de rendez-vous, validations de brief). - Ajout du NotificationCollector et de l'interface NotificationProvider - Création de 5 providers : Comment, Reply, ProjectRequest, AppointmentRequest, Content - Métadonnées de notifications stockées directement sur les entités source - Nouvelles routes mark-as-read et mark-all-read - Mise à jour du frontend pour le nouveau système - Route de migration pour les données existantes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c68b51f639
commit
a7d315942a
26 changed files with 1406 additions and 137 deletions
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace adrienpayet\notifications\providers;
|
||||
|
||||
use adrienpayet\notifications\NotificationProvider;
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\User;
|
||||
use Kirby\Data\Yaml;
|
||||
|
||||
/**
|
||||
* Provider pour les notifications de type "comment".
|
||||
* Collecte les commentaires depuis les fichiers des étapes du projet.
|
||||
*/
|
||||
class CommentProvider implements NotificationProvider
|
||||
{
|
||||
public function getType(): string
|
||||
{
|
||||
return 'comment';
|
||||
}
|
||||
|
||||
public function collect(Page $project, User $user): array
|
||||
{
|
||||
$notifications = [];
|
||||
$userUuid = (string) $user->uuid();
|
||||
|
||||
// Parcourir toutes les étapes du projet
|
||||
foreach ($project->children() as $step) {
|
||||
// Parcourir tous les fichiers de chaque étape
|
||||
foreach ($step->files() as $file) {
|
||||
if ($file->comments()->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$comments = Yaml::decode($file->comments()->value());
|
||||
if (!is_array($comments)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
// Ignorer les commentaires de type reply (gérés par ReplyProvider)
|
||||
if (($comment['type'] ?? 'comment') === 'comment-reply') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ne pas notifier l'auteur de son propre commentaire
|
||||
$authorUuid = $comment['author']['uuid'] ?? '';
|
||||
if ($authorUuid === $userUuid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$readby = $comment['readby'] ?? [];
|
||||
|
||||
$location = $comment['location'] ?? [];
|
||||
// Assurer que location.project existe toujours
|
||||
if (!isset($location['project'])) {
|
||||
$location['project'] = [
|
||||
'uri' => $project->uri(),
|
||||
'title' => (string) $project->title(),
|
||||
];
|
||||
}
|
||||
|
||||
$notifications[] = [
|
||||
'id' => $comment['id'],
|
||||
'type' => 'comment',
|
||||
'text' => $comment['text'] ?? '',
|
||||
'author' => $comment['author'] ?? [],
|
||||
'date' => $comment['date'] ?? '',
|
||||
'location' => $location,
|
||||
'position' => $comment['position'] ?? [],
|
||||
'readby' => $readby,
|
||||
'isRead' => in_array($userUuid, $readby),
|
||||
// Métadonnées pour markAsRead
|
||||
'_file' => (string) $file->uuid(),
|
||||
'_stepUri' => $step->uri(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Parcourir aussi les sous-pages (ex: tracks dans virtual-sample)
|
||||
foreach ($step->children() as $subPage) {
|
||||
foreach ($subPage->files() as $file) {
|
||||
if ($file->comments()->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$comments = Yaml::decode($file->comments()->value());
|
||||
if (!is_array($comments)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
if (($comment['type'] ?? 'comment') === 'comment-reply') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$authorUuid = $comment['author']['uuid'] ?? '';
|
||||
if ($authorUuid === $userUuid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$readby = $comment['readby'] ?? [];
|
||||
|
||||
$location = $comment['location'] ?? [];
|
||||
// Assurer que location.project existe toujours
|
||||
if (!isset($location['project'])) {
|
||||
$location['project'] = [
|
||||
'uri' => $project->uri(),
|
||||
'title' => (string) $project->title(),
|
||||
];
|
||||
}
|
||||
|
||||
$notifications[] = [
|
||||
'id' => $comment['id'],
|
||||
'type' => 'comment',
|
||||
'text' => $comment['text'] ?? '',
|
||||
'author' => $comment['author'] ?? [],
|
||||
'date' => $comment['date'] ?? '',
|
||||
'location' => $location,
|
||||
'position' => $comment['position'] ?? [],
|
||||
'readby' => $readby,
|
||||
'isRead' => in_array($userUuid, $readby),
|
||||
'_file' => (string) $file->uuid(),
|
||||
'_stepUri' => $subPage->uri(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $notifications;
|
||||
}
|
||||
|
||||
public function markAsRead(string $id, array $location, User $user): bool
|
||||
{
|
||||
$fileUuid = $location['_file'] ?? null;
|
||||
if (!$fileUuid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trouver le fichier par UUID (peut être avec ou sans préfixe file://)
|
||||
$fileUri = str_starts_with($fileUuid, 'file://') ? $fileUuid : 'file://' . $fileUuid;
|
||||
$file = kirby()->file($fileUri);
|
||||
if (!$file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$comments = Yaml::decode($file->comments()->value());
|
||||
if (!is_array($comments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userUuid = (string) $user->uuid();
|
||||
$updated = false;
|
||||
|
||||
foreach ($comments as &$comment) {
|
||||
if ($comment['id'] === $id) {
|
||||
$comment['readby'] = $comment['readby'] ?? [];
|
||||
if (!in_array($userUuid, $comment['readby'])) {
|
||||
$comment['readby'][] = $userUuid;
|
||||
$updated = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($updated) {
|
||||
$file->update(['comments' => $comments]);
|
||||
}
|
||||
|
||||
return $updated;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue