diff --git a/app/views/history.php b/app/views/history.php index a1783f65..13771048 100644 --- a/app/views/history.php +++ b/app/views/history.php @@ -5,14 +5,18 @@
$item): ?>
-
picture( - $item['coverImg'], - 'jpg', - ['width' => '110px', 'height' => '156px'], - ['width' => '110px', 'height' => '156px'] - ) ?>
+
+ + picture( + $item['coverImg'], + 'jpg', + ['width' => '110px', 'height' => '156px'], + ['width' => '110px', 'height' => '156px'] + ) ?> + +
- + a($item['url'], $item['title']) ?>

diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php index 77fd83a4..4a96dad9 100644 --- a/src/AnimeClient/API/Kitsu/Model.php +++ b/src/AnimeClient/API/Kitsu/Model.php @@ -189,10 +189,7 @@ final class Model { $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); - $transformer = new AnimeHistoryTransformer(); - $transformer->setContainer($this->getContainer()); - - return $transformer->transform($organized); + return (new AnimeHistoryTransformer())->transform($organized); } /** @@ -209,10 +206,7 @@ final class Model { $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); - $transformer = new MangaHistoryTransformer(); - $transformer->setContainer($this->getContainer()); - - return $transformer->transform($organized); + return (new MangaHistoryTransformer())->transform($organized); } /** diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeHistoryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeHistoryTransformer.php index 86fb3af8..11228432 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeHistoryTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeHistoryTransformer.php @@ -17,202 +17,9 @@ namespace Aviat\AnimeClient\API\Kitsu\Transformer; use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus; -use Aviat\AnimeClient\Types\HistoryItem; -use Aviat\Ion\Di\ContainerAware; -use DateTimeImmutable; -use DateTimeInterface; -use DateTimeZone; -class AnimeHistoryTransformer { - use ContainerAware; +class AnimeHistoryTransformer extends HistoryTransformer { + protected string $type = 'anime'; - protected array $skipList = []; - - /** - * Convert raw history - * - * @param array $data - * @return array - */ - public function transform(array $data): array - { - $output = []; - - foreach ($data as $id => $entry) - { - if ( ! isset($entry['relationships']['anime'])) - { - continue; - } - - if (in_array($id, $this->skipList, FALSE)) - { - continue; - } - - $kind = $entry['attributes']['kind']; - - if ($kind === 'progressed' && ! empty($entry['attributes']['changedData']['progress'])) - { - $output[] = $this->transformProgress($entry); - } - else if ($kind === 'updated') - { - $output[] = $this->transformUpdated($entry); - } - } - - return $this->aggregate($output); - } - - /** - * Combine consecutive 'progressed' events - * - * @param array $singles - * @return array - */ - protected function aggregate (array $singles): array - { - $output = []; - - $count = count($singles); - for ($i = 0; $i < $count; $i++) - { - $entries = []; - $entry = $singles[$i]; - $prevTitle = $entry['title']; - $nextId = $i; - $next = $singles[$nextId]; - while ( - $next['kind'] === 'progressed' && - $next['title'] === $prevTitle - ) { - $entries[] = $next; - $prevTitle = $next['title']; - - if ($nextId + 1 < $count) - { - $nextId++; - $next = $singles[$nextId]; - continue; - } - - break; - } - - if (count($entries) > 1) - { - $episodes = []; - $updated = []; - - foreach ($entries as $e) - { - $episodes[] = max($e['original']['attributes']['changedData']['progress']); - $updated[] = $e['updated']; - } - $firstEpisode = min($episodes); - $lastEpisode = max($episodes); - $firstUpdate = min($updated); - $lastUpdate = max($updated); - - $title = $entries[0]['title']; - - $action = (count($entries) > 3) - ? "Marathoned episodes {$firstEpisode}-{$lastEpisode}" - : "Watched episodes {$firstEpisode}-{$lastEpisode}"; - - $output[] = HistoryItem::from([ - 'action' => $action, - 'coverImg' => $entries[0]['coverImg'], - 'dateRange' => [$firstUpdate, $lastUpdate], - 'isAggregate' => true, - 'title' => $title, - 'updated' => $entries[0]['updated'], - ]); - - // Skip the rest of the aggregate in the main loop - $i += count($entries) - 1; - continue; - } - - $output[] = $entry; - } - - return $output; - } - - protected function transformProgress ($entry): HistoryItem - { - $animeId = array_keys($entry['relationships']['anime'])[0]; - $animeData = $entry['relationships']['anime'][$animeId]['attributes']; - $title = $this->linkTitle($animeData); - $imgUrl = 'images/anime/' . $animeId . '.webp'; - $episode = max($entry['attributes']['changedData']['progress']); - - return HistoryItem::from([ - 'action' => "Watched episode {$episode}", - 'coverImg' => $imgUrl, - 'kind' => 'progressed', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - protected function transformUpdated($entry): HistoryItem - { - $animeId = array_keys($entry['relationships']['anime'])[0]; - $animeData = $entry['relationships']['anime'][$animeId]['attributes']; - $title = $this->linkTitle($animeData); - $imgUrl = 'images/anime/' . $animeId . '.webp'; - - $kind = array_key_first($entry['attributes']['changedData']); - - if ($kind === 'status') - { - $status = array_pop($entry['attributes']['changedData']['status']); - $statusName = AnimeWatchingStatus::KITSU_TO_TITLE[$status]; - - if ($statusName === 'Completed') - { - return HistoryItem::from([ - 'action' => 'Completed', - 'coverImg' => $imgUrl, - 'kind' => 'updated', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - return HistoryItem::from([ - 'action' => "Set status to {$statusName}", - 'coverImg' => $imgUrl, - 'kind' => 'updated', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - return $entry; - } - - protected function linkTitle (array $animeData): string - { - $url = '/anime/details/' . $animeData['slug']; - - $helper = $this->getContainer()->get('html-helper'); - return $helper->a($url, $animeData['canonicalTitle'], ['id' => $animeData['slug']]); - } - - protected function parseDate (string $date): DateTimeImmutable - { - $dateTime = DateTimeImmutable::createFromFormat( - DateTimeInterface::RFC3339_EXTENDED, - $date - ); - - return $dateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - } + protected array $statusMap = AnimeWatchingStatus::KITSU_TO_TITLE; } \ No newline at end of file diff --git a/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php new file mode 100644 index 00000000..d7ace80e --- /dev/null +++ b/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php @@ -0,0 +1,214 @@ + + * @copyright 2015 - 2020 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 5 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\API\Kitsu\Transformer; + +use Aviat\AnimeClient\Types\HistoryItem; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; + +abstract class HistoryTransformer { + /** + * @var string The media type + */ + protected string $type; + + /** + * @var array The mapping of api status to display status + */ + protected array $statusMap; + + /** + * Convert raw history + * + * @param array $data + * @return array + */ + public function transform(array $data): array + { + $output = []; + + foreach ($data as $entry) + { + if ( ! isset($entry['relationships'][$this->type])) + { + continue; + } + + $kind = $entry['attributes']['kind']; + + if ($kind === 'progressed' && ! empty($entry['attributes']['changedData']['progress'])) + { + $output[] = $this->transformProgress($entry); + } + else if ($kind === 'updated') + { + $output[] = $this->transformUpdated($entry); + } + } + + return $this->aggregate($output); + } + + /** + * Combine consecutive 'progressed' events + * + * @param array $singles + * @return array + */ + protected function aggregate (array $singles): array + { + $output = []; + + $count = count($singles); + for ($i = 0; $i < $count; $i++) + { + $entries = []; + $entry = $singles[$i]; + $prevTitle = $entry['title']; + $nextId = $i; + $next = $singles[$nextId]; + while ( + $next['kind'] === 'progressed' && + $next['title'] === $prevTitle + ) { + $entries[] = $next; + $prevTitle = $next['title']; + + if ($nextId + 1 < $count) + { + $nextId++; + $next = $singles[$nextId]; + continue; + } + + break; + } + + if (count($entries) > 1) + { + $episodes = []; + $updated = []; + + foreach ($entries as $e) + { + $episodes[] = max($e['original']['attributes']['changedData']['progress']); + $updated[] = $e['updated']; + } + $firstEpisode = min($episodes); + $lastEpisode = max($episodes); + $firstUpdate = min($updated); + $lastUpdate = max($updated); + + $title = $entries[0]['title']; + + $action = (count($entries) > 3) + ? "Marathoned episodes {$firstEpisode}-{$lastEpisode}" + : "Watched episodes {$firstEpisode}-{$lastEpisode}"; + + $output[] = HistoryItem::from([ + 'action' => $action, + 'coverImg' => $entries[0]['coverImg'], + 'dateRange' => [$firstUpdate, $lastUpdate], + 'isAggregate' => true, + 'title' => $title, + 'updated' => $entries[0]['updated'], + 'url' => $entries[0]['url'], + ]); + + // Skip the rest of the aggregate in the main loop + $i += count($entries) - 1; + continue; + } + + $output[] = $entry; + } + + return $output; + } + + protected function transformProgress (array $entry): HistoryItem + { + $id = array_keys($entry['relationships'][$this->type])[0]; + $data = $entry['relationships'][$this->type][$id]['attributes']; + $title = $this->linkTitle($data); + $imgUrl = "images/{$this->type}/{$id}.webp"; + $episode = max($entry['attributes']['changedData']['progress']); + + $action = ($this->type === 'anime') + ? "Watched episode {$episode}" + : "Read chapter {$episode}"; + + return HistoryItem::from([ + 'action' => $action, + 'coverImg' => $imgUrl, + 'kind' => 'progressed', + 'original' => $entry, + 'title' => $title, + 'updated' => $this->parseDate($entry['attributes']['updatedAt']), + 'url' => $this->getUrl($data), + ]); + } + + protected function transformUpdated($entry): HistoryItem + { + $id = array_keys($entry['relationships'][$this->type])[0]; + $data = $entry['relationships'][$this->type][$id]['attributes']; + $title = $this->linkTitle($data); + $imgUrl = "images/{$this->type}/{$id}.webp"; + + $kind = array_key_first($entry['attributes']['changedData']); + + if ($kind === 'status') + { + $status = array_pop($entry['attributes']['changedData']['status']); + $statusName = $this->statusMap[$status]; + + return HistoryItem::from([ + 'action' => $statusName, + 'coverImg' => $imgUrl, + 'kind' => 'updated', + 'original' => $entry, + 'title' => $title, + 'updated' => $this->parseDate($entry['attributes']['updatedAt']), + 'url' => $this->getUrl($data), + ]); + } + + return $entry; + } + + protected function linkTitle (array $data): string + { + return $data['canonicalTitle']; + } + + protected function parseDate (string $date): DateTimeImmutable + { + $dateTime = DateTimeImmutable::createFromFormat( + DateTimeInterface::RFC3339_EXTENDED, + $date + ); + + return $dateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); + } + + protected function getUrl (array $data): string + { + return "/{$this->type}/details/{$data['slug']}"; + } +} \ No newline at end of file diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaHistoryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaHistoryTransformer.php index 9735091c..b86afbef 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaHistoryTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaHistoryTransformer.php @@ -18,52 +18,11 @@ namespace Aviat\AnimeClient\API\Kitsu\Transformer; use Aviat\AnimeClient\API\Mapping\MangaReadingStatus; use Aviat\AnimeClient\Types\HistoryItem; -use Aviat\Ion\Di\ContainerAware; -use DateTimeImmutable; -use DateTimeInterface; -use DateTimeZone; -class MangaHistoryTransformer { - use ContainerAware; +class MangaHistoryTransformer extends HistoryTransformer { + protected string $type = 'manga'; - protected array $skipList = []; - - /** - * Convert raw history - * - * @param array $data - * @return array - */ - public function transform(array $data): array - { - $output = []; - - foreach ($data as $id => $entry) - { - if ( ! isset($entry['relationships']['manga'])) - { - continue; - } - - if (in_array($id, $this->skipList, FALSE)) - { - continue; - } - - $kind = $entry['attributes']['kind']; - - if ($kind === 'progressed' && ! empty($entry['attributes']['changedData']['progress'])) - { - $output[] = $this->transformProgress($entry); - } - else if ($kind === 'updated') - { - $output[] = $this->transformUpdated($entry); - } - } - - return $this->aggregate($output); - } + protected array $statusMap = MangaReadingStatus::KITSU_TO_TITLE; /** * Combine consecutive 'progressed' events @@ -128,6 +87,7 @@ class MangaHistoryTransformer { 'isAggregate' => true, 'title' => $title, 'updated' => $entries[0]['updated'], + 'url' => $entries[0]['url'], ]); // Skip the rest of the aggregate in the main loop @@ -140,79 +100,4 @@ class MangaHistoryTransformer { return $output; } - - protected function transformProgress ($entry): HistoryItem - { - $mangaId = array_keys($entry['relationships']['manga'])[0]; - $mangaData = $entry['relationships']['manga'][$mangaId]['attributes']; - $title = $this->linkTitle($mangaData); - $imgUrl = 'images/manga/' . $mangaId . '.webp'; - $chapter = max($entry['attributes']['changedData']['progress']); - - return HistoryItem::from([ - 'action' => "Watched chapter {$chapter}", - 'coverImg' => $imgUrl, - 'kind' => 'progressed', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - protected function transformUpdated($entry): HistoryItem - { - $mangaId = array_keys($entry['relationships']['manga'])[0]; - $mangaData = $entry['relationships']['manga'][$mangaId]['attributes']; - $title = $this->linkTitle($mangaData); - $imgUrl = 'images/manga/' . $mangaId . '.webp'; - - $kind = array_key_first($entry['attributes']['changedData']); - - if ($kind === 'status') - { - $status = array_pop($entry['attributes']['changedData']['status']); - $statusName = MangaReadingStatus::KITSU_TO_TITLE[$status]; - - if ($statusName === 'Completed') - { - return HistoryItem::from([ - 'action' => 'Completed', - 'coverImg' => $imgUrl, - 'kind' => 'updated', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - return HistoryItem::from([ - 'action' => "Set status to {$statusName}", - 'coverImg' => $imgUrl, - 'kind' => 'updated', - 'original' => $entry, - 'title' => $title, - 'updated' => $this->parseDate($entry['attributes']['updatedAt']), - ]); - } - - return $entry; - } - - protected function linkTitle (array $mangaData): string - { - $url = '/manga/details/' . $mangaData['slug']; - - $helper = $this->getContainer()->get('html-helper'); - return $helper->a($url, $mangaData['canonicalTitle'], ['id' => $mangaData['slug']]); - } - - protected function parseDate (string $date): DateTimeImmutable - { - $dateTime = DateTimeImmutable::createFromFormat( - DateTimeInterface::RFC3339_EXTENDED, - $date - ); - - return $dateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - } } \ No newline at end of file diff --git a/src/AnimeClient/Types/HistoryItem.php b/src/AnimeClient/Types/HistoryItem.php index 486dfb19..ee9ab1f3 100644 --- a/src/AnimeClient/Types/HistoryItem.php +++ b/src/AnimeClient/Types/HistoryItem.php @@ -54,6 +54,11 @@ class HistoryItem extends AbstractType { */ public array $dateRange = []; + /** + * @var string Url to details page + */ + public string $url = ''; + /** * @var array The item before transformation */