Update profile page to use GraphQL, see #27

This commit is contained in:
Timothy Warren 2020-08-24 15:20:07 -04:00
parent 778cda6efc
commit e944ddc75c
11 changed files with 215 additions and 106 deletions

View File

@ -50,6 +50,18 @@ use function Aviat\AnimeClient\getLocalImg;
</td> </td>
</tr> </tr>
<?php endif ?> <?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr> <tr>
<td>Genres</td> <td>Genres</td>
<td> <td>
@ -58,11 +70,13 @@ use function Aviat\AnimeClient\getLocalImg;
</tr> </tr>
</table> </table>
<br /> <br />
</aside> </aside>
<article class="text"> <article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2> <h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?> <?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3> <h3><?= $title ?></h3>
<?php endforeach ?> <?php endforeach ?>

View File

@ -34,6 +34,18 @@
</td> </td>
</tr> </tr>
<?php endif ?> <?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr> <tr>
<td>Genres</td> <td>Genres</td>
<td> <td>
@ -45,7 +57,7 @@
<br /> <br />
</aside> </aside>
<article class="text"> <article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2> <h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?> <?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3> <h3><?= $title ?></h3>
<?php endforeach ?> <?php endforeach ?>

View File

@ -36,7 +36,7 @@ use Aviat\AnimeClient\API\Kitsu;
$character = $data['waifu']['character']; $character = $data['waifu']['character'];
echo $helper->a( echo $helper->a(
$url->generate('character', ['slug' => $character['slug']]), $url->generate('character', ['slug' => $character['slug']]),
$character['canonicalName'] $character['names']['canonical']
); );
?> ?>
</td> </td>
@ -59,29 +59,32 @@ use Aviat\AnimeClient\API\Kitsu;
<h3>Favorites</h3> <h3>Favorites</h3>
<?= $component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($component, $helper, $url) { <?= $component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($component, $helper, $url) {
$rendered = []; $rendered = [];
if ($type === 'characters') if ($type === 'character')
{ {
uasort($items, fn ($a, $b) => $a['canonicalName'] <=> $b['canonicalName']); uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
} }
else else
{ {
uasort($items, fn ($a, $b) => Kitsu::filterTitles($a)[0] <=> Kitsu::filterTitles($b)[0]); uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
} }
foreach ($items as $id => $item) foreach ($items as $id => $item)
{ {
if ($type === 'characters') if ($type === 'character')
{ {
$rendered[] = $component->character( $rendered[] = $component->character(
$item['canonicalName'], $item['names']['canonical'],
$url->generate('character', ['slug', $item['slug']]), $url->generate('character', ['slug' => $item['slug']]),
$helper->picture("images/characters/{$item['id']}.webp") $helper->picture("images/characters/{$item['id']}.webp")
); );
} }
else else
{ {
$rendered[] = $component->media( $rendered[] = $component->media(
Kitsu::filterTitles($item), array_merge(
[$item['titles']['canonical']],
Kitsu::getFilteredTitles($item['titles']),
),
$url->generate("{$type}.details", ['id' => $item['slug']]), $url->generate("{$type}.details", ['id' => $item['slug']]),
$helper->picture("images/{$type}/{$item['id']}.webp"), $helper->picture("images/{$type}/{$item['id']}.webp"),
); );

View File

@ -91,6 +91,48 @@ final class Kitsu {
return MangaPublishingStatus::NOT_YET_PUBLISHED; return MangaPublishingStatus::NOT_YET_PUBLISHED;
} }
public static function mappingsToUrls(array $mappings, string $kitsuLink = ''): array
{
$output = [];
foreach ($mappings as $mapping)
{
switch ($mapping['externalSite'])
{
case 'ANIDB':
$output['AniDB'] = "https://anidb.net/anime/{$mapping['externalId']}";
break;
case 'ANILIST_ANIME':
$output['Anilist'] = "https://anilist.co/anime/{$mapping['externalId']}/";
break;
case 'ANILIST_MANGA':
$output['Anilist'] = "https://anilist.co/manga/{$mapping['externalId']}/";
break;
case 'MYANIMELIST_ANIME':
$output['MyAnimeList'] = "https://myanimelist.net/anime/{$mapping['externalId']}";
break;
case 'MYANIMELIST_MANGA':
$output['MyAnimeList'] = "https://myanimelist.net/manga/{$mapping['externalId']}";
break;
default:
continue 2;
}
}
if ($kitsuLink !== '')
{
$output['Kitsu'] = $kitsuLink;
}
ksort($output);
return $output;
}
/** /**
* Reorganize streaming links * Reorganize streaming links
* *

View File

@ -231,18 +231,8 @@ final class Model {
*/ */
public function getUserData(string $username): array public function getUserData(string $username): array
{ {
return $this->requestBuilder->getRequest('users', [ return $this->requestBuilder->runQuery('UserDetails', [
'query' => [ 'slug' => $username,
'filter' => [
'name' => $username,
],
'fields' => [
'anime' => 'slug,canonicalTitle,posterImage',
'manga' => 'slug,canonicalTitle,posterImage',
'characters' => 'slug,canonicalName,image',
],
'include' => 'waifu,favorites.item,stats'
]
]); ]);
} }

View File

@ -19,6 +19,7 @@ query ($slug: String!) {
} }
birthday birthday
id id
location
name name
proMessage proMessage
proTier proTier
@ -29,6 +30,89 @@ query ($slug: String!) {
url url
} }
} }
favorites {
nodes {
id
item {
__typename,
...on Anime {
id
slug
posterImage {
original {
url
height
width
}
views {
url
height
width
}
}
titles {
canonical
localized
}
}
...on Manga {
id
slug
posterImage {
original {
url
height
width
}
views {
url
height
width
}
}
titles {
canonical
localized
}
}
...on Person {
id
slug
image {
original {
url
}
views {
url
height
width
}
}
names {
alternatives
canonical
canonicalLocale
localized
},
}
...on Character {
id
slug
image {
original {
url
}
}
names {
alternatives
canonical
canonicalLocale
localized
},
}
}
}
}
stats { stats {
animeAmountConsumed { animeAmountConsumed {
completed completed

View File

@ -38,6 +38,7 @@ final class AnimeTransformer extends AbstractTransformer {
? $item['data']['findAnimeBySlug'] ? $item['data']['findAnimeBySlug']
: $item['data']['findAnimeById']; : $item['data']['findAnimeById'];
$characters = []; $characters = [];
$links = [];
$staff = []; $staff = [];
$genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']); $genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']);
@ -108,6 +109,11 @@ final class AnimeTransformer extends AbstractTransformer {
ksort($staff); ksort($staff);
} }
if (count($base['mappings']['nodes']) > 0)
{
$links = Kitsu::mappingsToUrls($base['mappings']['nodes'], "https://kitsu.io/anime/{$base['slug']}");
}
return AnimePage::from([ return AnimePage::from([
'age_rating' => $base['ageRating'], 'age_rating' => $base['ageRating'],
'age_rating_guide' => $base['ageRatingGuide'], 'age_rating_guide' => $base['ageRatingGuide'],
@ -116,12 +122,13 @@ final class AnimeTransformer extends AbstractTransformer {
'episode_count' => $base['episodeCount'], 'episode_count' => $base['episodeCount'],
'episode_length' => $base['episodeLength'], 'episode_length' => $base['episodeLength'],
'genres' => $genres, 'genres' => $genres,
'links' => $links,
'id' => $base['id'], 'id' => $base['id'],
'slug' => $base['slug'], 'slug' => $base['slug'],
'staff' => $staff, 'staff' => $staff,
'show_type' => $base['subtype'], 'show_type' => $base['subtype'],
'status' => Kitsu::getAiringStatus($base['startDate'], $base['endDate']), 'status' => Kitsu::getAiringStatus($base['startDate'], $base['endDate']),
'streaming_links' => Kitsu::parseStreamingLinks($base['streamingLinks']['nodes']), 'streaming_links' => Kitsu::parseStreamingLinks($base['streamingLinks']['nodes'] ?? []),
'synopsis' => $base['description']['en'], 'synopsis' => $base['description']['en'],
'title' => $title, 'title' => $title,
'titles' => $titles, 'titles' => $titles,

View File

@ -39,6 +39,7 @@ final class MangaTransformer extends AbstractTransformer {
: $item['data']['findMangaById']; : $item['data']['findMangaById'];
$characters = []; $characters = [];
$links = [];
$staff = []; $staff = [];
$genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']); $genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']);
sort($genres); sort($genres);
@ -108,6 +109,11 @@ final class MangaTransformer extends AbstractTransformer {
ksort($staff); ksort($staff);
} }
if (count($base['mappings']['nodes']) > 0)
{
$links = Kitsu::mappingsToUrls($base['mappings']['nodes'], "https://kitsu.io/manga/{$base['slug']}");
}
$data = [ $data = [
'age_rating' => $base['ageRating'], 'age_rating' => $base['ageRating'],
'age_rating_guide' => $base['ageRatingGuide'], 'age_rating_guide' => $base['ageRatingGuide'],
@ -116,6 +122,7 @@ final class MangaTransformer extends AbstractTransformer {
'volume_count' => $base['volumeCount'], 'volume_count' => $base['volumeCount'],
'cover_image' => $base['posterImage']['views'][1]['url'], 'cover_image' => $base['posterImage']['views'][1]['url'],
'genres' => $genres, 'genres' => $genres,
'links' => $links,
'manga_type' => $base['subtype'], 'manga_type' => $base['subtype'],
'id' => $base['id'], 'id' => $base['id'],
'staff' => $staff, 'staff' => $staff,

View File

@ -16,9 +16,9 @@
namespace Aviat\AnimeClient\API\Kitsu\Transformer; namespace Aviat\AnimeClient\API\Kitsu\Transformer;
use Aviat\AnimeClient\API\Kitsu;
use function Aviat\AnimeClient\getLocalImg; use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\JsonAPI;
use Aviat\AnimeClient\Types\User; use Aviat\AnimeClient\Types\User;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
@ -31,36 +31,24 @@ use Aviat\Ion\Transformer\AbstractTransformer;
final class UserTransformer extends AbstractTransformer { final class UserTransformer extends AbstractTransformer {
public function transform($profileData): User public function transform($profileData): User
{ {
$orgData = JsonAPI::organizeData($profileData)[0]; $base = $profileData['data']['findProfileBySlug'] ?? [];
$attributes = $orgData['attributes']; $favorites = $base['favorites']['nodes'] ?? [];
$stats = $base['stats'] ?? [];
$rels = $orgData['relationships'] ?? []; $waifu = (array_key_exists('waifu', $base)) ? [
$favorites = array_key_exists('favorites', $rels) ? $rels['favorites'] : []; 'label' => $base['waifuOrHusbando'],
'character' => $base['waifu'],
$stats = [];
foreach ($rels['stats'] as $sid => &$item)
{
$key = $item['attributes']['kind'];
$stats[$key] = $item['attributes']['statsData'];
unset($item);
}
unset($item);
$waifu = (array_key_exists('waifu', $rels)) ? [
'label' => $attributes['waifuOrHusbando'],
'character' => $rels['waifu']['attributes'],
] : []; ] : [];
return User::from([ return User::from([
'about' => $attributes['about'], 'about' => $base['about'],
'avatar' => getLocalImg($attributes['avatar']['original'], FALSE), 'avatar' => getLocalImg($base['avatarImage']['original']['url'], FALSE),
'favorites' => $this->organizeFavorites($favorites), 'favorites' => $this->organizeFavorites($favorites),
'location' => $attributes['location'], 'location' => $base['location'],
'name' => $attributes['name'], 'name' => $base['name'],
'slug' => $attributes['slug'], 'slug' => $base['slug'],
'stats' => $this->organizeStats($stats, $attributes), 'stats' => $this->organizeStats($stats),
'waifu' => $waifu, 'waifu' => $waifu,
'website' => $attributes['website'], 'website' => $base['siteLinks']['nodes'][0]['url'],
]); ]);
} }
@ -74,58 +62,10 @@ final class UserTransformer extends AbstractTransformer {
{ {
$output = []; $output = [];
unset($rawFavorites['data']);
foreach ($rawFavorites as $item) foreach ($rawFavorites as $item)
{ {
$rank = $item['attributes']['favRank']; $type = strtolower($item['item']['__typename']);
foreach ($item['relationships']['item'] as $key => $fav) $output[$type][$item['id']] = $item['item'];
{
$output[$key] = $output[$key] ?? [];
foreach ($fav as $id => $data)
{
$output[$key][$rank] = array_merge(['id' => $id], $data['attributes']);
}
ksort($output[$key]);
}
}
return $output;
}
/**
* Format the time spent on anime in a more readable format
*
* @param int $seconds
* @return string
*/
private function formatAnimeTime(int $seconds): string
{
// All the seconds left
$remSeconds = $seconds % 60;
$minutes = ($seconds - $remSeconds) / 60;
$minutesPerDay = 1440;
$minutesPerYear = $minutesPerDay * 365;
// Minutes short of a year
$years = (int)floor($minutes / $minutesPerYear);
$minutes %= $minutesPerYear;
// Minutes short of a day
$extraMinutes = $minutes % $minutesPerDay;
$days = ($minutes - $extraMinutes) / $minutesPerDay;
// Minutes short of an hour
$remMinutes = $extraMinutes % 60;
$hours = ($extraMinutes - $remMinutes) / 60;
$output = "{$days} days, {$hours} hours, {$remMinutes} minutes, and {$remSeconds} seconds.";
if ($years > 0)
{
$output = "{$years} year(s),{$output}";
} }
return $output; return $output;
@ -137,20 +77,20 @@ final class UserTransformer extends AbstractTransformer {
$mangaStats = []; $mangaStats = [];
$otherStats = []; $otherStats = [];
if (array_key_exists('anime-amount-consumed', $stats)) if (array_key_exists('animeAmountConsumed', $stats))
{ {
$animeStats = [ $animeStats = [
'Time spent watching anime:' => $this->formatAnimeTime($stats['anime-amount-consumed']['time']), 'Time spent watching anime:' => Kitsu::friendlyTime($stats['animeAmountConsumed']['time']),
'Anime series watched:' => number_format($stats['anime-amount-consumed']['media']), 'Anime series watched:' => number_format($stats['animeAmountConsumed']['media']),
'Anime episodes watched:' => number_format($stats['anime-amount-consumed']['units']), 'Anime episodes watched:' => number_format($stats['animeAmountConsumed']['units']),
]; ];
} }
if (array_key_exists('manga-amount-consumed', $stats)) if (array_key_exists('mangaAmountConsumed', $stats))
{ {
$mangaStats = [ $mangaStats = [
'Manga series read:' => number_format($stats['manga-amount-consumed']['media']), 'Manga series read:' => number_format($stats['mangaAmountConsumed']['media']),
'Manga chapters read:' => number_format($stats['manga-amount-consumed']['units']), 'Manga chapters read:' => number_format($stats['mangaAmountConsumed']['units']),
]; ];
} }

View File

@ -25,6 +25,11 @@ final class AnimePage extends Anime {
*/ */
public array $characters = []; public array $characters = [];
/**
* @var array
*/
public array $links = [];
/** /**
* @var array * @var array
*/ */

View File

@ -52,6 +52,11 @@ final class MangaPage extends AbstractType {
*/ */
public array $genres; public array $genres;
/**
* @var array
*/
public array $links;
/** /**
* @var string * @var string
*/ */