Get Person detail pages via GraphQL, resolves #27
This commit is contained in:
parent
1b74df5269
commit
e2f29c6731
@ -186,9 +186,8 @@ $routes = [
|
||||
]
|
||||
],
|
||||
'person' => [
|
||||
'path' => '/people/{id}{/slug}',
|
||||
'path' => '/people/{slug}',
|
||||
'tokens' => [
|
||||
'id' => SLUG_PATTERN,
|
||||
'slug' => SLUG_PATTERN,
|
||||
]
|
||||
],
|
||||
|
5
app/templates/single-tab.php
Normal file
5
app/templates/single-tab.php
Normal file
@ -0,0 +1,5 @@
|
||||
<section class="<?= $className ?>">
|
||||
<?php foreach ($data as $tabName => $tabData): ?>
|
||||
<?= $callback($tabData, $tabName) ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
@ -186,7 +186,7 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
}
|
||||
$rendered[] = $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['id' => $person['id'], 'slug' => $person['slug']]),
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture(getLocalImg($person['image']['original'] ?? NULL)),
|
||||
'character small-person',
|
||||
);
|
||||
|
@ -120,10 +120,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
foreach ($casting as $id => $c):
|
||||
$person = $component->character(
|
||||
$c['person']['name'],
|
||||
$url->generate('person', [
|
||||
'id' => $c['person']['id'],
|
||||
'slug' => $c['person']['slug']
|
||||
]),
|
||||
$url->generate('person', ['slug' => $c['person']['slug']]),
|
||||
$helper->picture(getLocalImg($c['person']['image']))
|
||||
);
|
||||
$medias = array_map(fn ($series) => $component->media(
|
||||
|
@ -95,7 +95,7 @@
|
||||
fn($people) => implode('', array_map(
|
||||
fn ($person) => $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['id' => $person['id'], 'slug' => $person['slug']]),
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture("images/people/{$person['id']}.webp")
|
||||
),
|
||||
$people
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
@ -9,6 +8,14 @@ use Aviat\AnimeClient\Kitsu;
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||
<?php foreach ($data['names'] as $name): ?>
|
||||
<h3><?= $name ?></h3>
|
||||
<?php endforeach ?>
|
||||
<br />
|
||||
<hr />
|
||||
<div class="description">
|
||||
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -24,7 +31,6 @@ use Aviat\AnimeClient\Kitsu;
|
||||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
||||
<?php foreach ($entries as $type => $casting): ?>
|
||||
<?php if ($type === 'characters') continue; ?>
|
||||
<?php if (isset($entries['manga'], $entries['anime'])): ?>
|
||||
<h4><?= ucfirst($type) ?></h4>
|
||||
<?php endif ?>
|
||||
@ -32,7 +38,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
<?php foreach ($casting as $sid => $series): ?>
|
||||
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
|
||||
<?= $component->media(
|
||||
Kitsu::filterTitles($series),
|
||||
$series['titles'],
|
||||
$url->generate("{$mediaType}.details", ['id' => $series['slug']]),
|
||||
$helper->picture("images/{$type}/{$sid}.webp")
|
||||
) ?>
|
||||
@ -46,7 +52,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
|
||||
<?php if ( ! empty($data['characters'])): ?>
|
||||
<section>
|
||||
<h3>Voice Acting Roles</h3>
|
||||
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $url) {
|
||||
@ -61,7 +67,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
foreach ($item['media'] as $sid => $series)
|
||||
{
|
||||
$medias[] = $component->media(
|
||||
Kitsu::filterTitles($series),
|
||||
$series['titles'],
|
||||
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||
$helper->picture("images/anime/{$sid}.webp")
|
||||
);
|
||||
|
@ -163,7 +163,7 @@ CSS Tabs
|
||||
/* text-align: center; */
|
||||
}
|
||||
|
||||
.tabs .content {
|
||||
.tabs .content, .single-tab {
|
||||
display: none;
|
||||
max-height: 950px;
|
||||
border: 1px solid #e5e5e5;
|
||||
@ -175,7 +175,14 @@ CSS Tabs
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tabs .content.full-height {
|
||||
.single-tab {
|
||||
display: block;
|
||||
border: 1px solid #e5e5e5;
|
||||
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.tabs .content.full-height, .single-tab.full-height {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,8 @@ button:active {
|
||||
.tabs > [type="radio"]:checked + label,
|
||||
.tabs > [type="radio"]:checked + label + .content,
|
||||
.vertical-tabs [type="radio"]:checked + label,
|
||||
.vertical-tabs [type="radio"]:checked ~ .content {
|
||||
.vertical-tabs [type="radio"]:checked ~ .content,
|
||||
.single-tab {
|
||||
/* border-color: #333; */
|
||||
border: 0;
|
||||
background: #666;
|
||||
|
2
public/css/auto.min.css
vendored
2
public/css/auto.min.css
vendored
File diff suppressed because one or more lines are too long
2
public/css/dark.min.css
vendored
2
public/css/dark.min.css
vendored
File diff suppressed because one or more lines are too long
2
public/css/light.min.css
vendored
2
public/css/light.min.css
vendored
File diff suppressed because one or more lines are too long
@ -199,27 +199,14 @@ final class Model {
|
||||
/**
|
||||
* Get information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $slug
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getPerson(string $id): array
|
||||
public function getPerson(string $slug): array
|
||||
{
|
||||
return $this->getCached("kitsu-person-{$id}", fn () => $this->requestBuilder->getRequest("people/{$id}", [
|
||||
'query' => [
|
||||
'filter' => [
|
||||
'id' => $id,
|
||||
],
|
||||
'fields' => [
|
||||
'characters' => 'canonicalName,slug,image',
|
||||
'characterVoices' => 'mediaCharacter',
|
||||
'anime' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
||||
'manga' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
||||
'mediaCharacters' => 'role,media,character',
|
||||
'mediaStaff' => 'role,media,person',
|
||||
],
|
||||
'include' => 'voices.mediaCharacter.media,voices.mediaCharacter.character,staff.media',
|
||||
],
|
||||
return $this->getCached("kitsu-person-{$slug}", fn () => $this->requestBuilder->runQuery('PersonDetails', [
|
||||
'slug' => $slug
|
||||
]));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
query ($id: ID!) {
|
||||
findPersonById(id: $id) {
|
||||
query ($slug: String!) {
|
||||
findPersonBySlug(slug: $slug) {
|
||||
id
|
||||
description
|
||||
birthday
|
||||
@ -22,6 +22,36 @@ query ($id: ID!) {
|
||||
canonical
|
||||
localized
|
||||
}
|
||||
mediaStaff {
|
||||
nodes {
|
||||
id
|
||||
role
|
||||
media {
|
||||
id
|
||||
slug
|
||||
type
|
||||
posterImage {
|
||||
original {
|
||||
height
|
||||
name
|
||||
url
|
||||
width
|
||||
}
|
||||
views {
|
||||
height
|
||||
name
|
||||
url
|
||||
width
|
||||
}
|
||||
}
|
||||
titles {
|
||||
alternatives
|
||||
canonical
|
||||
localized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
voices {
|
||||
nodes {
|
||||
locale
|
||||
@ -29,6 +59,7 @@ query ($id: ID!) {
|
||||
role
|
||||
character {
|
||||
id
|
||||
slug
|
||||
image {
|
||||
original {
|
||||
height
|
||||
@ -43,6 +74,7 @@ query ($id: ID!) {
|
||||
}
|
||||
media {
|
||||
id
|
||||
slug
|
||||
posterImage {
|
||||
original {
|
||||
height
|
||||
|
@ -50,7 +50,7 @@ final class CharacterTransformer extends AbstractTransformer {
|
||||
|
||||
if (isset($data['media']['nodes']))
|
||||
{
|
||||
[$media, $castings] = $this->organizeMediaAndVoices($data['media']['nodes']);
|
||||
[$media, $castings] = $this->organizeMediaAndVoices($data['media']['nodes'] ?? []);
|
||||
}
|
||||
|
||||
return Character::from([
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use Aviat\AnimeClient\Types\Person;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
@ -31,14 +31,17 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
*/
|
||||
public function transform($personData): Person
|
||||
{
|
||||
$data = JsonAPI::organizeData($personData);
|
||||
$included = JsonAPI::organizeIncludes($personData['included']);
|
||||
$data = $personData['data']['findPersonBySlug'] ?? [];
|
||||
$canonicalName = $data['names']['localized'][$data['names']['canonical']]
|
||||
?? array_shift($data['names']['localized']);
|
||||
|
||||
$orgData = $this->organizeData($included);
|
||||
$orgData = $this->organizeData($data);
|
||||
|
||||
return Person::from([
|
||||
'id' => $data['id'],
|
||||
'name' => $data['attributes']['name'],
|
||||
'name' => $canonicalName,
|
||||
'names' => array_diff($data['names']['localized'], [$canonicalName]),
|
||||
'description' => $data['description']['en'] ?? '',
|
||||
'characters' => $orgData['characters'],
|
||||
'staff' => $orgData['staff'],
|
||||
]);
|
||||
@ -47,88 +50,98 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
protected function organizeData(array $data): array
|
||||
{
|
||||
$output = [
|
||||
'characters' => [
|
||||
'main' => [],
|
||||
'supporting' => [],
|
||||
],
|
||||
'characters' => [],
|
||||
'staff' => [],
|
||||
];
|
||||
|
||||
if (array_key_exists('characterVoices', $data))
|
||||
{
|
||||
foreach ($data['characterVoices'] as $cv)
|
||||
{
|
||||
$mcId = $cv['relationships']['mediaCharacter']['data']['id'];
|
||||
$characters = [];
|
||||
$staff = [];
|
||||
|
||||
if ( ! array_key_exists($mcId, $data['mediaCharacters']))
|
||||
if (count($data['mediaStaff']['nodes']) > 0)
|
||||
{
|
||||
continue;
|
||||
$roles = array_unique(array_column($data['mediaStaff']['nodes'], 'role'));
|
||||
foreach ($roles as $role)
|
||||
{
|
||||
$staff[$role] = [];
|
||||
}
|
||||
ksort($staff);
|
||||
|
||||
$mc = $data['mediaCharacters'][$mcId];
|
||||
foreach ($data['mediaStaff']['nodes'] as $staffing)
|
||||
{
|
||||
$media = $staffing['media'];
|
||||
$role = $staffing['role'];
|
||||
$title = $media['titles']['canonical'];
|
||||
$type = strtolower($media['type']);
|
||||
|
||||
$role = $mc['role'];
|
||||
|
||||
$charId = $mc['relationships']['character']['data']['id'];
|
||||
$mediaId = $mc['relationships']['media']['data']['id'];
|
||||
|
||||
$existingMedia = array_key_exists($charId, $output['characters'][$role])
|
||||
? $output['characters'][$role][$charId]['media']
|
||||
: [];
|
||||
|
||||
$relatedMedia = [
|
||||
$mediaId => $data['anime'][$mediaId],
|
||||
$staff[$role][$type][$media['id']] = [
|
||||
'id' => $media['id'],
|
||||
'title' => $title,
|
||||
'titles' => array_merge([$title], Kitsu::getFilteredTitles($media['titles'])),
|
||||
'image' => [
|
||||
'original' => $media['posterImage']['views'][1]['url'],
|
||||
],
|
||||
'slug' => $media['slug'],
|
||||
];
|
||||
|
||||
$includedMedia = array_replace_recursive($existingMedia, $relatedMedia);
|
||||
uasort($staff[$role][$type], fn ($a, $b) => $a['title'] <=> $b['title']);
|
||||
}
|
||||
|
||||
uasort($includedMedia, static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
$output['staff'] = $staff;
|
||||
}
|
||||
|
||||
$character = $data['characters'][$charId];
|
||||
if (count($data['voices']['nodes']) > 0)
|
||||
{
|
||||
foreach ($data['voices']['nodes'] as $voicing)
|
||||
{
|
||||
$character = $voicing['mediaCharacter']['character'];
|
||||
$charId = $character['id'];
|
||||
$rawMedia = $voicing['mediaCharacter']['media'];
|
||||
$role = strtolower($voicing['mediaCharacter']['role']);
|
||||
|
||||
$output['characters'][$role][$charId] = [
|
||||
'character' => $character,
|
||||
'media' => $includedMedia,
|
||||
$media = [
|
||||
'id' => $rawMedia['id'],
|
||||
'slug' => $rawMedia['slug'],
|
||||
'titles' => array_merge(
|
||||
[$rawMedia['titles']['canonical']],
|
||||
Kitsu::getFilteredTitles($rawMedia['titles']),
|
||||
),
|
||||
];
|
||||
|
||||
if ( ! isset($characters[$role][$charId]))
|
||||
{
|
||||
if ( ! array_key_exists($role, $characters))
|
||||
{
|
||||
$characters[$role] = [];
|
||||
}
|
||||
|
||||
$characters[$role][$charId] = [
|
||||
'character' => [
|
||||
'id' => $character['id'],
|
||||
'slug' => $character['slug'],
|
||||
'image' => [
|
||||
'original' => $character['image']['original']['url'],
|
||||
],
|
||||
'canonicalName' => $character['names']['canonical'],
|
||||
],
|
||||
'media' => [
|
||||
$media['id'] => $media
|
||||
],
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
$characters[$role][$charId]['media'][$media['id']] = $media;
|
||||
}
|
||||
|
||||
if (array_key_exists('mediaStaff', $data))
|
||||
{
|
||||
foreach ($data['mediaStaff'] as $rid => $role)
|
||||
{
|
||||
$roleName = $role['role'];
|
||||
$mediaType = $role['relationships']['media']['data']['type'];
|
||||
$mediaId = $role['relationships']['media']['data']['id'];
|
||||
$media = $data[$mediaType][$mediaId];
|
||||
$output['staff'][$roleName][$mediaType][$mediaId] = $media;
|
||||
}
|
||||
uasort(
|
||||
$characters[$role][$charId]['media'],
|
||||
fn ($a, $b) => $a['titles'][0] <=> $b['titles'][0]
|
||||
);
|
||||
}
|
||||
|
||||
uasort($output['characters']['main'], static function ($a, $b) {
|
||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||
});
|
||||
uasort($output['characters']['supporting'], static function ($a, $b) {
|
||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||
});
|
||||
ksort($output['staff']);
|
||||
foreach ($output['staff'] as $role => &$media)
|
||||
{
|
||||
if (array_key_exists('anime', $media))
|
||||
{
|
||||
uasort($media['anime'], static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
krsort($characters);
|
||||
|
||||
if (array_key_exists('manga', $media))
|
||||
{
|
||||
uasort($media['manga'], static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
$output['characters'] = $characters;
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
@ -402,10 +402,9 @@ type Anime implements Episodic & Media & WithTimestamps {
|
||||
youtubeTrailerVideoId: String
|
||||
}
|
||||
|
||||
type AnimeAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
type AnimeAmountConsumed implements AmountConsumed {
|
||||
"Total media completed atleast once."
|
||||
completed: Int!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"Total amount of media."
|
||||
media: Int!
|
||||
@ -417,13 +416,11 @@ type AnimeAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
time: Int!
|
||||
"Total progress of library including reconsuming."
|
||||
units: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type AnimeCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
type AnimeCategoryBreakdown implements CategoryBreakdown {
|
||||
"A Map of category_id -> count for all categories present on the library entries"
|
||||
categories: Map!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"The profile related to the user for this stat."
|
||||
profile: Profile!
|
||||
@ -431,7 +428,6 @@ type AnimeCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
recalculatedAt: ISO8601Date!
|
||||
"The total amount of library entries."
|
||||
total: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"The connection type for Anime."
|
||||
@ -468,13 +464,12 @@ type AnimeEdge {
|
||||
node: Anime
|
||||
}
|
||||
|
||||
type AnimeMutation implements WithTimestamps {
|
||||
type AnimeMutation {
|
||||
"Create an Anime."
|
||||
create(
|
||||
"Create an Anime."
|
||||
input: AnimeCreateInput!
|
||||
): AnimeCreatePayload
|
||||
createdAt: ISO8601DateTime!
|
||||
"Delete an Anime."
|
||||
delete(
|
||||
"Delete an Anime."
|
||||
@ -485,7 +480,6 @@ type AnimeMutation implements WithTimestamps {
|
||||
"Update an Anime."
|
||||
input: AnimeUpdateInput!
|
||||
): AnimeUpdatePayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of AnimeUpdate"
|
||||
@ -739,6 +733,20 @@ type EpisodeConnection {
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeCreate"
|
||||
type EpisodeCreatePayload {
|
||||
episode: Episode
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeDelete"
|
||||
type EpisodeDeletePayload {
|
||||
episode: GenericDelete
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"An edge in a connection."
|
||||
type EpisodeEdge {
|
||||
"A cursor for use in pagination."
|
||||
@ -747,6 +755,31 @@ type EpisodeEdge {
|
||||
node: Episode
|
||||
}
|
||||
|
||||
type EpisodeMutation {
|
||||
"Create an Episode."
|
||||
create(
|
||||
"Create an Episode"
|
||||
input: EpisodeCreateInput!
|
||||
): EpisodeCreatePayload
|
||||
"Delete an Episode."
|
||||
delete(
|
||||
"Delete an Episode"
|
||||
input: GenericDeleteInput!
|
||||
): EpisodeDeletePayload
|
||||
"Update an Episode."
|
||||
update(
|
||||
"Update an Episode"
|
||||
input: EpisodeUpdateInput!
|
||||
): EpisodeUpdatePayload
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeUpdate"
|
||||
type EpisodeUpdatePayload {
|
||||
episode: Episode
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"Favorite media, characters, and people for a user"
|
||||
type Favorite implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
@ -778,30 +811,24 @@ type FavoriteEdge {
|
||||
node: Favorite
|
||||
}
|
||||
|
||||
type Generic implements Base & WithTimestamps {
|
||||
type Generic implements Base {
|
||||
"The error code."
|
||||
code: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"A description of the error"
|
||||
message: String!
|
||||
"Which input value this error came from"
|
||||
path: [String!]
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type GenericDelete implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
type GenericDelete {
|
||||
id: ID!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type Image implements WithTimestamps {
|
||||
type Image {
|
||||
"A blurhash-encoded version of this image"
|
||||
blurhash: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"The original image"
|
||||
original: ImageView!
|
||||
updatedAt: ISO8601DateTime!
|
||||
"The various generated views of this image"
|
||||
views(names: [String!]): [ImageView!]!
|
||||
}
|
||||
@ -820,7 +847,7 @@ type ImageView implements WithTimestamps {
|
||||
}
|
||||
|
||||
"The user library filterable by media_type and status"
|
||||
type Library implements WithTimestamps {
|
||||
type Library {
|
||||
"All Library Entries for a specific Media"
|
||||
all(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
@ -846,7 +873,6 @@ type Library implements WithTimestamps {
|
||||
last: Int,
|
||||
mediaType: media_type!
|
||||
): LibraryEntryConnection!
|
||||
createdAt: ISO8601DateTime!
|
||||
"Library Entries for a specific Media filtered by the current status"
|
||||
current(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
@ -895,7 +921,6 @@ type Library implements WithTimestamps {
|
||||
last: Int,
|
||||
mediaType: media_type!
|
||||
): LibraryEntryConnection!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Information about a specific media entry for a user"
|
||||
@ -984,13 +1009,12 @@ type LibraryEntryEdge {
|
||||
node: LibraryEntry
|
||||
}
|
||||
|
||||
type LibraryEntryMutation implements WithTimestamps {
|
||||
type LibraryEntryMutation {
|
||||
"Create a library entry"
|
||||
create(
|
||||
"Create a Library Entry"
|
||||
input: LibraryEntryCreateInput!
|
||||
): LibraryEntryCreatePayload
|
||||
createdAt: ISO8601DateTime!
|
||||
"Delete a library entry"
|
||||
delete(
|
||||
"Delete Library Entry"
|
||||
@ -1001,17 +1025,36 @@ type LibraryEntryMutation implements WithTimestamps {
|
||||
"Update Library Entry"
|
||||
input: LibraryEntryUpdateInput!
|
||||
): LibraryEntryUpdatePayload
|
||||
"Update a library entry status by id"
|
||||
"Update library entry progress by id"
|
||||
updateProgressById(
|
||||
"Update library entry progress by id"
|
||||
input: UpdateProgressByIdInput!
|
||||
): LibraryEntryUpdateProgressByIdPayload
|
||||
"Update library entry progress by media"
|
||||
updateProgressByMedia(
|
||||
"Update library entry progress by media"
|
||||
input: UpdateProgressByMediaInput!
|
||||
): LibraryEntryUpdateProgressByMediaPayload
|
||||
"Update library entry rating by id"
|
||||
updateRatingById(
|
||||
"Update library entry rating by id"
|
||||
input: UpdateRatingByIdInput!
|
||||
): LibraryEntryUpdateRatingByIdPayload
|
||||
"Update library entry rating by media"
|
||||
updateRatingByMedia(
|
||||
"Update library entry rating by media"
|
||||
input: UpdateRatingByMediaInput!
|
||||
): LibraryEntryUpdateRatingByMediaPayload
|
||||
"Update library entry status by id"
|
||||
updateStatusById(
|
||||
"Update a library entry status by id"
|
||||
"Update library entry status by id"
|
||||
input: UpdateStatusByIdInput!
|
||||
): LibraryEntryUpdateStatusByIdPayload
|
||||
"Update a library entry status by media"
|
||||
"Update library entry status by media"
|
||||
updateStatusByMedia(
|
||||
"Update a library entry status by media"
|
||||
"Update library entry status by media"
|
||||
input: UpdateStatusByMediaInput!
|
||||
): LibraryEntryUpdateStatusByMediaPayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdate"
|
||||
@ -1021,6 +1064,34 @@ type LibraryEntryUpdatePayload {
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateProgressById"
|
||||
type LibraryEntryUpdateProgressByIdPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateProgressByMedia"
|
||||
type LibraryEntryUpdateProgressByMediaPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateRatingById"
|
||||
type LibraryEntryUpdateRatingByIdPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateRatingByMedia"
|
||||
type LibraryEntryUpdateRatingByMediaPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateStatusById"
|
||||
type LibraryEntryUpdateStatusByIdPayload {
|
||||
"Graphql Errors"
|
||||
@ -1210,10 +1281,9 @@ type Manga implements Media & WithTimestamps {
|
||||
volumeCount: Int
|
||||
}
|
||||
|
||||
type MangaAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
type MangaAmountConsumed implements AmountConsumed {
|
||||
"Total media completed atleast once."
|
||||
completed: Int!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"Total amount of media."
|
||||
media: Int!
|
||||
@ -1223,13 +1293,11 @@ type MangaAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
recalculatedAt: ISO8601Date!
|
||||
"Total progress of library including reconsuming."
|
||||
units: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type MangaCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
type MangaCategoryBreakdown implements CategoryBreakdown {
|
||||
"A Map of category_id -> count for all categories present on the library entries"
|
||||
categories: Map!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"The profile related to the user for this stat."
|
||||
profile: Profile!
|
||||
@ -1237,7 +1305,6 @@ type MangaCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
recalculatedAt: ISO8601Date!
|
||||
"The total amount of library entries."
|
||||
total: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"The connection type for Manga."
|
||||
@ -1470,12 +1537,11 @@ type MediaStaffEdge {
|
||||
node: MediaStaff
|
||||
}
|
||||
|
||||
type Mutation implements WithTimestamps {
|
||||
type Mutation {
|
||||
anime: AnimeMutation
|
||||
createdAt: ISO8601DateTime!
|
||||
episode: EpisodeMutation
|
||||
libraryEntry: LibraryEntryMutation
|
||||
pro: ProMutation!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Information about pagination in a connection."
|
||||
@ -1490,11 +1556,7 @@ type PageInfo {
|
||||
startCursor: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
A Voice Actor, Director, Animator, or other person who works in the creation and\
|
||||
localization of media
|
||||
"""
|
||||
"A Voice Actor, Director, Animator, or other person who works in the creation and localization of media"
|
||||
type Person implements WithTimestamps {
|
||||
"The day when this person was born"
|
||||
birthday: Date
|
||||
@ -1504,6 +1566,17 @@ type Person implements WithTimestamps {
|
||||
id: ID!
|
||||
"An image of the person"
|
||||
image: Image
|
||||
"Information about the person working on specific media"
|
||||
mediaStaff(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
after: String,
|
||||
"Returns the elements in the list that come before the specified cursor."
|
||||
before: String,
|
||||
"Returns the first _n_ elements from the list."
|
||||
first: Int,
|
||||
"Returns the last _n_ elements from the list."
|
||||
last: Int
|
||||
): MediaStaffConnection
|
||||
"The primary name of this person."
|
||||
name: String!
|
||||
"The name of this person in various languages"
|
||||
@ -1596,8 +1669,7 @@ type PostEdge {
|
||||
node: Post
|
||||
}
|
||||
|
||||
type ProMutation implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
type ProMutation {
|
||||
"Set the user's discord tag"
|
||||
setDiscord(
|
||||
"Your discord tag (Name#1234)"
|
||||
@ -1610,7 +1682,6 @@ type ProMutation implements WithTimestamps {
|
||||
): SetMessagePayload
|
||||
"End the user's pro subscription"
|
||||
unsubscribe: UnsubscribePayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"A subscription to Kitsu PRO"
|
||||
@ -1719,11 +1790,7 @@ type Profile implements WithTimestamps {
|
||||
"Returns the last _n_ elements from the list."
|
||||
last: Int
|
||||
): MediaReactionConnection!
|
||||
"""
|
||||
|
||||
A non-unique publicly visible name for the profile.
|
||||
Minimum of 3 characters and any valid Unicode character
|
||||
"""
|
||||
"A non-unique publicly visible name for the profile. Minimum of 3 characters and any valid Unicode character"
|
||||
name: String!
|
||||
"Post pinned to the user profile"
|
||||
pinnedPost: Post
|
||||
@ -1787,17 +1854,15 @@ type ProfileEdge {
|
||||
}
|
||||
|
||||
"The different types of user stats that we calculate."
|
||||
type ProfileStats implements WithTimestamps {
|
||||
type ProfileStats {
|
||||
"The total amount of anime you have watched over your whole life."
|
||||
animeAmountConsumed: AnimeAmountConsumed!
|
||||
"The breakdown of the different categories related to the anime you have completed"
|
||||
animeCategoryBreakdown: AnimeCategoryBreakdown!
|
||||
createdAt: ISO8601DateTime!
|
||||
"The total amount of manga you ahve read over your whole life."
|
||||
mangaAmountConsumed: MangaAmountConsumed!
|
||||
"The breakdown of the different categories related to the manga you have completed"
|
||||
mangaCategoryBreakdown: MangaCategoryBreakdown!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type Query {
|
||||
@ -2017,13 +2082,11 @@ type QuoteLineEdge {
|
||||
}
|
||||
|
||||
"Information about a user session"
|
||||
type Session implements WithTimestamps {
|
||||
type Session {
|
||||
"The account associated with this session"
|
||||
account: Account
|
||||
createdAt: ISO8601DateTime!
|
||||
"The profile associated with this session"
|
||||
profile: Profile
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of SetDiscord"
|
||||
@ -2141,17 +2204,15 @@ type StreamingLinkEdge {
|
||||
node: StreamingLink
|
||||
}
|
||||
|
||||
type TitlesList implements WithTimestamps {
|
||||
type TitlesList {
|
||||
"A list of additional, alternative, abbreviated, or unofficial titles"
|
||||
alternatives: [String!]
|
||||
"The official or de facto international title"
|
||||
canonical: String
|
||||
"The locale code that identifies which title is used as the canonical title"
|
||||
canonicalLocale: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"The list of localized titles keyed by locale"
|
||||
localized(locales: [String!]): Map!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of Unsubscribe"
|
||||
@ -2424,6 +2485,27 @@ input AnimeUpdateInput {
|
||||
youtubeTrailerVideoId: String
|
||||
}
|
||||
|
||||
input EpisodeCreateInput {
|
||||
description: Map
|
||||
length: Int
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
number: Int!
|
||||
releasedAt: Date
|
||||
thumbnailImage: Upload
|
||||
titles: TitlesListInput!
|
||||
}
|
||||
|
||||
input EpisodeUpdateInput {
|
||||
description: Map
|
||||
id: ID!
|
||||
length: Int
|
||||
number: Int
|
||||
releasedAt: Date
|
||||
thumbnailImage: Upload
|
||||
titles: TitlesListInput
|
||||
}
|
||||
|
||||
input GenericDeleteInput {
|
||||
id: ID!
|
||||
}
|
||||
@ -2464,6 +2546,30 @@ input TitlesListInput {
|
||||
localized: Map
|
||||
}
|
||||
|
||||
input UpdateProgressByIdInput {
|
||||
id: ID!
|
||||
progress: Int!
|
||||
}
|
||||
|
||||
input UpdateProgressByMediaInput {
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
progress: Int!
|
||||
}
|
||||
|
||||
input UpdateRatingByIdInput {
|
||||
id: ID!
|
||||
"A number between 2 - 20"
|
||||
rating: Int!
|
||||
}
|
||||
|
||||
input UpdateRatingByMediaInput {
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
"A number between 2 - 20"
|
||||
rating: Int!
|
||||
}
|
||||
|
||||
input UpdateStatusByIdInput {
|
||||
id: ID!
|
||||
status: LibraryEntryStatus!
|
||||
|
@ -38,6 +38,17 @@ final class Tabs {
|
||||
bool $hasSectionWrapper = false
|
||||
): string
|
||||
{
|
||||
if (count($tabData) < 2)
|
||||
{
|
||||
return $this->render('single-tab.php', [
|
||||
'name' => $name,
|
||||
'data' => $tabData,
|
||||
'callback' => $cb,
|
||||
'className' => $className . ' single-tab',
|
||||
'hasSectionWrapper' => $hasSectionWrapper,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('tabs.php', [
|
||||
'name' => $name,
|
||||
'data' => $tabData,
|
||||
|
@ -50,15 +50,14 @@ final class People extends BaseController {
|
||||
/**
|
||||
* Show information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|null $slug
|
||||
* @param string $slug
|
||||
* @return void
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function index(string $id, ?string $slug = NULL): void
|
||||
public function index(string $slug): void
|
||||
{
|
||||
$rawData = $this->model->getPerson($id, $slug);
|
||||
$rawData = $this->model->getPerson($slug);
|
||||
$data = (new PersonTransformer())->transform($rawData)->toArray();
|
||||
|
||||
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||
|
@ -20,28 +20,16 @@ namespace Aviat\AnimeClient\Types;
|
||||
* Type representing a person for display
|
||||
*/
|
||||
final class Person extends AbstractType {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public ?string $name;
|
||||
|
||||
/**
|
||||
* @var Characters
|
||||
*/
|
||||
public ?Characters $characters;
|
||||
public array $names = [];
|
||||
|
||||
public ?string $description;
|
||||
|
||||
public array $characters = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public array $staff = [];
|
||||
|
||||
public function setCharacters($characters): void
|
||||
{
|
||||
$this->characters = Characters::from($characters);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user