Browse Source

Article list

imwald
Nuša Pukšič 4 days ago
parent
commit
e680969b95
  1. 149
      src/Controller/AuthorController.php

149
src/Controller/AuthorController.php

@ -246,43 +246,91 @@ class AuthorController extends AbstractController @@ -246,43 +246,91 @@ class AuthorController extends AbstractController
$currentUser = $this->getUser();
$isOwnProfile = $currentUser && $currentUser->getUserIdentifier() === $npub;
// Query fresh Article entities (not cached view data)
// This ensures we have proper entities for filtering logic
$articles = $articleSearch->findByPubkey($pubkey, 100, 0);
// Try to get cached view first
$cachedArticles = $viewStore->fetchUserArticles($pubkey);
$viewData = [];
$fromCache = false;
$articles = []; // For message dispatching
// Filter and deduplicate articles at the entity level
$articles = $this->filterAndDeduplicateArticles($articles, $isOwnProfile);
if ($cachedArticles !== null) {
// Cache hit - extract and filter cached data
$fromCache = true;
// Build view objects for template from filtered entities
$viewData = [];
if (!empty($articles)) {
try {
foreach ($articles as $article) {
if ($article instanceof Article) {
$baseObject = $viewFactory->articleBaseObject($article, $author);
$normalized = $viewFactory->normalizeBaseObject($baseObject);
// Extract just the article data and convert to object
// This matches what the template expects (same format as old cache code)
if (isset($normalized['article'])) {
$viewData[] = (object) $normalized['article'];
foreach ($cachedArticles as $baseObject) {
if (isset($baseObject['article'])) {
$articleData = $baseObject['article'];
// Apply filtering to cached data
$kind = $articleData['kind'] ?? null;
$slug = $articleData['slug'] ?? null;
// Skip drafts unless viewing own profile
if (!$isOwnProfile && $kind === KindsEnum::LONGFORM_DRAFT->value) {
continue;
}
if ($slug) {
$viewData[] = (object) $articleData;
}
}
}
// Deduplicate cached data by slug (keep latest)
$viewData = $this->deduplicateViewData($viewData);
} else {
// Cache miss - query entities
$articles = $articleSearch->findByPubkey($pubkey, 100, 0);
// Filter and deduplicate articles at the entity level
$articles = $this->filterAndDeduplicateArticles($articles, $isOwnProfile);
// Build view objects for template from filtered entities
if (!empty($articles)) {
try {
$baseObjects = [];
foreach ($articles as $article) {
if ($article instanceof Article) {
$baseObject = $viewFactory->articleBaseObject($article, $author);
$baseObjects[] = $baseObject;
$normalized = $viewFactory->normalizeBaseObject($baseObject);
// Extract just the article data and convert to object
if (isset($normalized['article'])) {
$viewData[] = (object) $normalized['article'];
}
}
}
// Cache the base objects for next time (unfiltered, filtering happens on read)
if (!empty($baseObjects)) {
$viewStore->storeUserArticles($pubkey, $baseObjects);
}
} catch (\Exception $e) {
$this->logger->error('Failed to build view objects', [
'error' => $e->getMessage()
]);
}
} catch (\Exception $e) {
$this->logger->error('Failed to build view objects', [
'error' => $e->getMessage()
]);
}
}
$fromCache = false;
// Get latest createdAt for dispatching fetch message
if (!empty($articles)) {
// Articles are now guaranteed to be Article entities
$latest = $articles[0]->getCreatedAt()->getTimestamp();
// Dispatch async message to fetch new articles since latest + 1
$messageBus->dispatch(new FetchAuthorArticlesMessage($pubkey, $latest + 1));
if (!empty($viewData)) {
// Extract latest timestamp from view data (works for both cached and fresh)
$latest = 0;
foreach ($viewData as $item) {
$createdAt = $item->createdAt ?? null;
if ($createdAt) {
$timestamp = is_string($createdAt) ? strtotime($createdAt) : $createdAt->getTimestamp();
if ($timestamp > $latest) {
$latest = $timestamp;
}
}
}
if ($latest > 0) {
// Dispatch async message to fetch new articles since latest + 1
$messageBus->dispatch(new FetchAuthorArticlesMessage($pubkey, $latest + 1));
}
} else {
// No articles, fetch all
$messageBus->dispatch(new FetchAuthorArticlesMessage($pubkey, 0));
@ -346,6 +394,51 @@ class AuthorController extends AbstractController @@ -346,6 +394,51 @@ class AuthorController extends AbstractController
return $filtered;
}
/**
* Deduplicate cached view data by slug (keep latest version)
* Handles objects with slug and createdAt properties
*/
private function deduplicateViewData(array $viewData): array
{
$slugMap = [];
foreach ($viewData as $item) {
$slug = $item->slug ?? null;
$createdAt = $item->createdAt ?? null;
if (!$slug) {
continue;
}
// Parse createdAt to comparable format
if (is_string($createdAt)) {
$timestamp = strtotime($createdAt);
} else if ($createdAt instanceof \DateTimeInterface) {
$timestamp = $createdAt->getTimestamp();
} else {
$timestamp = 0;
}
// Keep only the latest version per slug
if (!isset($slugMap[$slug]) || $timestamp > $slugMap[$slug]['timestamp']) {
$slugMap[$slug] = [
'item' => $item,
'timestamp' => $timestamp
];
}
}
// Extract items and sort by timestamp (newest first)
$deduplicated = array_column($slugMap, 'item');
usort($deduplicated, function($a, $b) {
$timeA = is_string($a->createdAt ?? '') ? strtotime($a->createdAt) : 0;
$timeB = is_string($b->createdAt ?? '') ? strtotime($b->createdAt) : 0;
return $timeB <=> $timeA; // Descending order
});
return $deduplicated;
}
/**
* Redirect from /p/{pubkey} to /p/{npub}

Loading…
Cancel
Save