Version 5.1 - All the GraphQL #32

Closed
timw4mail wants to merge 1160 commits from develop into master
19 changed files with 267 additions and 101 deletions
Showing only changes of commit 30ee7b5601 - Show all commits

View File

@ -1,3 +1,4 @@
<?php use function Aviat\AnimeClient\col_not_empty; ?>
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
@ -13,6 +14,9 @@
<?php if (empty($items)): ?>
<h3>There's nothing here!</h3>
<?php else: ?>
<?php
$hasNotes = col_not_empty($items, 'notes');
?>
<table class='media-wrap'>
<thead>
<tr>
@ -25,8 +29,8 @@
<th>Type</th>
<th>Progress</th>
<th>Rated</th>
<th colspan="2">Attributes</th>
<th>Notes</th>
<th>Attributes</th>
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
<th>Genres</th>
</tr>
</thead>
@ -47,9 +51,8 @@
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
<?= $item['anime']['title'] ?>
</a>
<?php foreach ($item['anime']['titles'] as $title): ?>
<br/><?= $title ?>
<?php endforeach ?>
<br />
<?= implode('<br />', $item['anime']['titles']) ?>
</td>
<td><?= $item['airing']['status'] ?></td>
<td><?= $item['user_rating'] ?> / 10 </td>
@ -60,41 +63,36 @@
</td>
<td><?= $item['anime']['age_rating'] ?></td>
<td>
<ul>
<?php if ($item['rewatched'] > 0): ?>
<li>Rewatched <?= $item['rewatched'] ?> time(s)</li>
<?php endif ?>
<?php foreach(['private','rewatching'] as $attr): ?>
<?php if($item[$attr]): ?>
<li><?= ucfirst($attr); ?></li>
<?php endif ?>
<?php endforeach ?>
</ul>
</td>
<td>
<?php foreach($item['anime']['streaming_links'] as $link): ?>
<?php if ($link['meta']['link'] !== FALSE): ?>
<a href="<?= $link['link'] ?>" title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
<?= $helper->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
'class' => 'small-streaming-logo',
'width' => 25,
'height' => 25,
'alt' => "{$link['meta']['name']} logo",
]) ?>
</a>
<?php else: ?>
<?= $helper->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
'class' => 'small-streaming-logo',
'width' => 25,
'height' => 25,
'alt' => "{$link['meta']['name']} logo",
]) ?>
<?php endif ?>
<?php endforeach ?>
<br />
<ul>
<?php if ($item['rewatched'] > 0): ?>li>Rewatched <?= $item['rewatched'] ?> time(s)</li><?php endif ?>
<?php foreach(['private','rewatching'] as $attr): ?>
<?php if($item[$attr]): ?><li><?= ucfirst($attr); ?></li><?php endif ?>
<?php endforeach ?>
</ul>
</td>
<td>
<p><?= $escape->html($item['notes']) ?></p>
</td>
<?php if ($hasNotes): ?><td><p><?= $escape->html($item['notes']) ?></p></td><?php endif ?>
<td class="align-left">
<?php sort($item['anime']->genres) ?>
<?= implode(', ', $item['anime']->genres) ?>

View File

@ -19,7 +19,7 @@
<tr>
<td class="align-right"><label for="media_id">Media</label></td>
<td class='align-left'>
<?php include '_media-list.php' ?>
<?php include '_media-select-list.php' ?>
</td>
</tr>
<tr>

View File

@ -18,7 +18,9 @@
</div>
<?php endif ?>
<div class="row">
<?php if ($item['episode_count'] > 1): ?>
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
<?php endif ?>
<div class="media_type"><?= $item['show_type'] ?></div>
<div class="age-rating"><?= $item['age_rating'] ?></div>
</div>

View File

@ -11,7 +11,7 @@
<div class="tabs">
<?php $i = 0; ?>
<?php foreach ($sections as $name => $items): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<section class="media-wrap">
@ -23,17 +23,14 @@
<?php $i++; ?>
<?php endforeach ?>
<!-- All Tab -->
<input type='radio' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class='content full-height'>
<?php foreach ($sections as $name => $items): ?>
<h3><?= $name ?></h3>
<section class="media-wrap">
<?php foreach ($items as $item): ?>
<?php foreach ($all as $item): ?>
<?php include __DIR__ . '/cover-item.php'; ?>
<?php endforeach ?>
</section>
<?php endforeach ?>
</div>
</div>
<?php endif ?>

View File

@ -24,7 +24,7 @@
<tr>
<td class="align-right"><label for="media_id">Media</label></td>
<td class="align-left">
<?php include '_media-list.php' ?>
<?php include '_media-select-list.php' ?>
</td>
</tr>
<tr>

View File

@ -0,0 +1,44 @@
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class="content full-height">
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?><td>&nbsp;</td><?php endif ?>
<th>Title</th>
<th>Media</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<th>Notes</th>
<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach ($all as $item): ?>
<?php $editLink = $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]); ?>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>
<a class="bracketed" href="<?= $editLink ?>">Edit</a>
</td>
<?php endif ?>
<td class="align-left">
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
<?= $item['title'] ?>
</a>
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
</td>
<td><?= implode(', ', $item['media']) ?></td>
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
<td><?= $item['episode_length'] ?></td>
<td><?= $item['show_type'] ?></td>
<td><?= $item['age_rating'] ?></td>
<td class="align-left"><?= $item['notes'] ?></td>
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -12,9 +12,9 @@
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
</td>
<td><?= $item['episode_count'] ?></td>
<td><?= $item['episode_length'] ?></td>
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
<td><?= $item['show_type'] ?></td>
<td><?= $item['age_rating'] ?></td>
<?php if ($hasNotes): ?><td class="align-left"><?= $item['notes'] ?></td><?php endif ?>
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
<td class="align-left"><?= $item['notes'] ?></td>
</tr>

View File

@ -1,3 +1,4 @@
<?php use function Aviat\AnimeClient\col_not_empty; ?>
<main>
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
@ -11,28 +12,26 @@
<?php $i = 0; ?>
<div class="tabs">
<?php foreach ($sections as $name => $items): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>"
name="collection-tabs"/>
<?php $hasNotes = col_not_empty($items, 'notes') ?>
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>Actions</td>
<?php endif ?>
<?php if ($auth->isAuthenticated()): ?><td>&nbsp;</td><?php endif ?>
<th>Title</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<?php if ($hasNotes): ?><th>Notes</th><?php endif ?>
<th>Genres</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/list-item.php' ?>
<?php include 'list-item.php' ?>
<?php endforeach ?>
</tbody>
</table>
@ -40,34 +39,7 @@
<?php $i++ ?>
<?php endforeach ?>
<!-- All -->
<input type='radio' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class="content full-height">
<?php foreach ($sections as $name => $items): ?>
<h3><?= $name ?></h3>
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>Actions</td>
<?php endif ?>
<th>Title</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<th>Genres</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/list-item.php' ?>
<?php endforeach ?>
</tbody>
</table>
<?php endforeach; ?>
</div>
<?php include 'list-all.php' ?>
</div>
<?php endif ?>
</main>

View File

@ -87,10 +87,6 @@ tbody > tr:nth-child(odd) {
background: #ddd;
}
select[multiple] {
width: 100%;
}
a:hover, a:active {
color: var(--link-hover-color)
}
@ -877,6 +873,12 @@ aside picture, aside img {
vertical-align: middle;
}
.small-streaming-logo {
width: 25px;
height: 25px;
vertical-align: middle;
}
.cover-streaming-link {
display: none;
}

View File

@ -0,0 +1,26 @@
<?php
use Phinx\Migration\AbstractMigration;
class AnimeCollectionCleanup extends AbstractMigration
{
public function up()
{
if ($this->hasTable('genre_anime_set_link'))
{
$this->table('genre_anime_set_link')
->rename('anime_set_genre_link')
->update();
}
}
public function down()
{
if ($this->hasTable('anime_set_genre_link'))
{
$this->table('anime_set_genre_link')
->rename('genre_anime_set_link')
->update();
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -333,3 +333,16 @@ function createPlaceholderImage ($path, ?int $width, ?int $height, $text = 'Imag
imagedestroy($pngImage);
}
/**
* Check that there is a value for at least one item in a collection with the specified key
*
* @param array $search
* @param string $key
* @return bool
*/
function col_not_empty(array $search, string $key): bool
{
$items = array_filter(array_column($search, $key), fn ($x) => ( ! empty($x)));
return count($items) > 0;
}

View File

@ -101,12 +101,10 @@ final class AnimeCollection extends BaseController {
'list' => 'list'
];
$data = $this->animeCollectionModel->getCollection();
$this->outputHTML('collection/' . $viewMap[$view], [
'title' => $this->config->get('whose_list') . "'s Anime Collection",
'sections' => $data,
'genres' => $this->animeCollectionModel->getGenreList()
'sections' => $this->animeCollectionModel->getCollection(),
'all' => $this->animeCollectionModel->getFlatCollection(),
]);
}
@ -233,6 +231,13 @@ final class AnimeCollection extends BaseController {
$this->redirect('/anime-collection/view', 303);
}
/**
* Update a collection item
*
* @param $data
* @throws ContainerException
* @throws NotFoundException
*/
protected function update($data): void
{
if (array_key_exists('hummingbird_id', $data))

View File

@ -69,6 +69,47 @@ final class AnimeCollection extends Collection {
return $collection;
}
/**
* Get the collection from the database
*
* @return array
*/
public function getFlatCollection(): array
{
if ( ! $this->validDatabase)
{
return [];
}
$query = $this->db->select('a.hummingbird_id, slug, title, alternate_title, show_type,
age_rating, episode_count, episode_length, cover_image, notes')
->from('anime_set a')
->orderBy('title')
->get();
// Add genres associated with each item
$rows = $query->fetchAll(PDO::FETCH_ASSOC);
$genres = $this->getGenreList();
$media = $this->getMediaList();
foreach($rows as &$row)
{
$id = $row['hummingbird_id'];
$row['genres'] = array_key_exists($id, $genres)
? $genres[$id]
: [];
$row['media'] = array_key_exists($id, $media)
? $media[$id]
: [];
sort($row['genres']);
}
return $rows;
}
/**
* Get list of media types
*
@ -262,7 +303,7 @@ final class AnimeCollection extends Collection {
$this->db->beginTransaction();
$this->db->where('hummingbird_id', $data['hummingbird_id'])
->delete('genre_anime_set_link');
->delete('anime_set_genre_link');
$this->db->where('hummingbird_id', $data['hummingbird_id'])
->delete('anime_set_media_link');
@ -361,7 +402,7 @@ final class AnimeCollection extends Collection {
try
{
$this->db->select('hummingbird_id, genre')
->from('genre_anime_set_link gl')
->from('anime_set_genre_link gl')
->join('genres g', 'g.id=gl.genre_id', 'left');
@ -402,6 +443,68 @@ final class AnimeCollection extends Collection {
return $output;
}
/**
* Get media for anime collection items
*
* @param array $filter
* @return array
*/
public function getMediaList(array $filter = []): array
{
if ($this->validDatabase === FALSE)
{
return [];
}
$output = [];
// Catch the missing table PDOException
// so that the collection does not show an
// error by default
try
{
$this->db->select('m.type as media, hummingbird_id')
->from('anime_set_media_link ml')
->join('media m', 'm.id=ml.media_id', 'left');
if ( ! empty($filter))
{
$this->db->whereIn('hummingbird_id', $filter);
}
$query = $this->db->orderBy('hummingbird_id')
->orderBy('media')
->get();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row)
{
$id = $row['hummingbird_id'];
$media = $row['media'];
// Empty genre names aren't useful
if (empty($media))
{
continue;
}
if (array_key_exists($id, $output))
{
$output[$id][] = $media;
}
else
{
$output[$id] = [$media];
}
}
}
catch (PDOException $e) {}
$this->db->resetQuery();
return $output;
}
private function updateMediaLink(string $animeId, array $media): void
{
$this->db->beginTransaction();
@ -469,7 +572,11 @@ final class AnimeCollection extends Collection {
if ( ! empty($linksToInsert))
{
$this->db->insertBatch('genre_anime_set_link', $linksToInsert);
try
{
$this->db->insertBatch('anime_set_genre_link', $linksToInsert);
}
catch (PDOException $e) {}
}
}
@ -554,7 +661,7 @@ final class AnimeCollection extends Collection {
$links = [];
$query = $this->db->select('hummingbird_id, genre_id')
->from('genre_anime_set_link')
->from('anime_set_genre_link')
->get();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $link)

View File

@ -98,12 +98,12 @@ class Config extends AbstractType {
/**
* @var bool
*/
public bool $show_anime_collection = FALSE;
public $show_anime_collection = FALSE;
/**
* @var bool
*/
public bool $show_manga_collection = FALSE;
public $show_manga_collection = FALSE;
/**
* CSS theme: light, dark, or auto-switching

View File

@ -21,7 +21,7 @@ namespace Aviat\AnimeClient\Types;
*/
class FormItem extends AbstractType {
/**
* @var string
* @var string|int
*/
public $id;
@ -31,9 +31,9 @@ class FormItem extends AbstractType {
public ?string $anilist_item_id;
/**
* @var string
* @var string|int
*/
public ?string $mal_id;
public $mal_id;
/**
* @var FormItemData