0.1.2
This commit is contained in:
@@ -3,10 +3,11 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Helper\ZhConversion;
|
use App\Helper\ZhConversion;
|
||||||
|
use App\Jobs\ImageUpsert;
|
||||||
|
use App\Jobs\RemotePrefetch;
|
||||||
use App\Models\Author;
|
use App\Models\Author;
|
||||||
use App\Models\Chapter;
|
use App\Models\Chapter;
|
||||||
use App\Models\Comic;
|
use App\Models\Comic;
|
||||||
use App\Models\Image;
|
|
||||||
use App\Remote\CopyManga;
|
use App\Remote\CopyManga;
|
||||||
use App\Remote\ImageFetcher;
|
use App\Remote\ImageFetcher;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
@@ -22,13 +23,26 @@ use Illuminate\Support\Facades\Cache;
|
|||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comic Controller
|
||||||
|
*/
|
||||||
class ComicController extends Controller
|
class ComicController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CopyManga $copyManga
|
||||||
|
* @param ZhConversion $zhConversion
|
||||||
|
*/
|
||||||
public function __construct(private readonly CopyManga $copyManga, private readonly ZhConversion $zhConversion)
|
public function __construct(private readonly CopyManga $copyManga, private readonly ZhConversion $zhConversion)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert SC to ZH
|
||||||
|
*
|
||||||
|
* @param mixed $string
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
protected function scToZh(mixed $string): mixed
|
protected function scToZh(mixed $string): mixed
|
||||||
{
|
{
|
||||||
if (gettype($string) !== 'string') {
|
if (gettype($string) !== 'string') {
|
||||||
@@ -323,24 +337,8 @@ class ComicController extends Controller
|
|||||||
$chapterObj = Chapter::where('chapter_uuid', $chapter['chapter']['uuid'])->first();
|
$chapterObj = Chapter::where('chapter_uuid', $chapter['chapter']['uuid'])->first();
|
||||||
$comicObj = Comic::where('pathword', $pathword)->first();
|
$comicObj = Comic::where('pathword', $pathword)->first();
|
||||||
|
|
||||||
$arrayForUpsert = [];
|
// Image Upsert
|
||||||
|
ImageUpsert::dispatch($comicObj->id, $chapterObj->id, $chapter);
|
||||||
foreach ($chapter['sorted'] as $k => $image) {
|
|
||||||
$metadata = $chapter;
|
|
||||||
unset($metadata['sorted']);
|
|
||||||
unset($metadata['chapter']['contents'], $metadata['chapter']['words']);
|
|
||||||
|
|
||||||
$arrayForUpsert[] = [
|
|
||||||
'comic_id' => $comicObj->id,
|
|
||||||
'chapter_id' => $chapterObj->id,
|
|
||||||
'order' => $k,
|
|
||||||
'url' => $image['url'],
|
|
||||||
'metadata' => json_encode($metadata),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do an upsert
|
|
||||||
Image::upsert($arrayForUpsert, uniqueBy: 'url');
|
|
||||||
|
|
||||||
// Update history
|
// Update history
|
||||||
$request->user()->readingHistories()->attach($chapterObj->id, ['comic_id' => $comicObj->id]);
|
$request->user()->readingHistories()->attach($chapterObj->id, ['comic_id' => $comicObj->id]);
|
||||||
@@ -348,6 +346,15 @@ class ComicController extends Controller
|
|||||||
// Get chapters from DB
|
// Get chapters from DB
|
||||||
$chapters = $comicObj->chapters()->where('metadata->group_path_word', $chapter['chapter']['group_path_word'])->orderBy('order')->get(['name', 'chapter_uuid']);
|
$chapters = $comicObj->chapters()->where('metadata->group_path_word', $chapter['chapter']['group_path_word'])->orderBy('order')->get(['name', 'chapter_uuid']);
|
||||||
|
|
||||||
|
// Do remote prefetch if needed
|
||||||
|
if ($chapter['chapter']['next'] !== null) {
|
||||||
|
try {
|
||||||
|
Chapter::where('chapter_uuid', $chapter['chapter']['next'])->firstOrFail()->images()->firstOrFail();
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
RemotePrefetch::dispatch('chapter', ['pathword' => $pathword, 'uuid' => $chapter['chapter']['next']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Inertia::render('Comic/Read', [
|
return Inertia::render('Comic/Read', [
|
||||||
'comic' => $this->scToZh($comic),
|
'comic' => $this->scToZh($comic),
|
||||||
'chapter' => $this->scToZh($chapter),
|
'chapter' => $this->scToZh($chapter),
|
||||||
|
|||||||
51
app/Jobs/ImageUpsert.php
Normal file
51
app/Jobs/ImageUpsert.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Image;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Queue\Queueable;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class ImageUpsert implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public int $comicId,
|
||||||
|
public int $chapterId,
|
||||||
|
public array $chapter
|
||||||
|
){}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
Log::info("JOB ImageUpsert START, comicId: {$this->comicId}, chapterId: {$this->chapterId}");
|
||||||
|
|
||||||
|
$arrayForUpsert = [];
|
||||||
|
|
||||||
|
foreach ($this->chapter['sorted'] as $k => $image) {
|
||||||
|
$metadata = $this->chapter;
|
||||||
|
unset($metadata['sorted']);
|
||||||
|
unset($metadata['chapter']['contents'], $metadata['chapter']['words']);
|
||||||
|
|
||||||
|
$arrayForUpsert[] = [
|
||||||
|
'comic_id' => $this->comicId,
|
||||||
|
'chapter_id' => $this->chapterId,
|
||||||
|
'order' => $k,
|
||||||
|
'url' => $image['url'],
|
||||||
|
'metadata' => json_encode($metadata),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do an upsert
|
||||||
|
Image::upsert($arrayForUpsert, uniqueBy: 'url');
|
||||||
|
|
||||||
|
Log::info('JOB ImageUpsert END');
|
||||||
|
}
|
||||||
|
}
|
||||||
51
app/Jobs/RemotePrefetch.php
Normal file
51
app/Jobs/RemotePrefetch.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Remote\CopyManga;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Queue\Queueable;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class RemotePrefetch implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*/
|
||||||
|
public function __construct(public string $action, public array $parameters = [])
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$copyManga = new CopyManga();
|
||||||
|
|
||||||
|
switch ($this->action) {
|
||||||
|
case 'chapter':
|
||||||
|
Log::info("JOB RemotePrefetch START, action '{$this->action}', Pathword: {$this->parameters['pathword']}, UUID: {$this->parameters['uuid']}");
|
||||||
|
|
||||||
|
$copyManga->chapter($this->parameters['pathword'], $this->parameters['uuid']);
|
||||||
|
|
||||||
|
Log::info("JOB RemotePrefetch END, action '{$this->action}'");
|
||||||
|
break;
|
||||||
|
case 'chapters':
|
||||||
|
// TODO:
|
||||||
|
break;
|
||||||
|
case 'index':
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
case 'tags':
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log::info("JOB RemotePrefetch Unknown action '{$this->action}'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ class CopyManga
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array Caching options
|
* @var array Caching options
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
protected array $options = [
|
protected array $options = [
|
||||||
'caching' => true,
|
'caching' => true,
|
||||||
@@ -101,17 +102,17 @@ class CopyManga
|
|||||||
*
|
*
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $value
|
* @param array $value
|
||||||
|
* @param int $ttl
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function writeToCache(string $url, array $value): void
|
protected function writeToCache(string $url, array $value, int $ttl = 0): void
|
||||||
{
|
{
|
||||||
if ($this->options['caching']) {
|
if ($this->options['caching']) {
|
||||||
Cache::add("URL_{$url}", array_merge($value, ['CACHE' => [
|
Cache::add("URL_{$url}", array_merge($value, ['CACHE' => [
|
||||||
'CACHE' => true,
|
'CACHE' => true,
|
||||||
'CACHED_AT' => Date::now(),
|
'CACHED_AT' => Date::now(),
|
||||||
'EXPIRE_AT' => Date::now()->addSeconds($this->options['cachingTimeout'])]
|
'EXPIRE_AT' => Date::now()->addSeconds(($ttl !== 0) ? $ttl : $this->options['cachingTimeout'])]
|
||||||
]),
|
]), $this->options['cachingTimeout']);
|
||||||
$this->options['cachingTimeout']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,10 +122,11 @@ class CopyManga
|
|||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string $method
|
* @param string $method
|
||||||
* @param string $userAgent
|
* @param string $userAgent
|
||||||
|
* @param int $ttl
|
||||||
* @return mixed|string
|
* @return mixed|string
|
||||||
* @throws GuzzleException
|
* @throws GuzzleException
|
||||||
*/
|
*/
|
||||||
protected function execute(string $url, string $method = 'GET', string $userAgent = ""): mixed
|
protected function execute(string $url, string $method = 'GET', string $userAgent = "", int $ttl = 0): mixed
|
||||||
{
|
{
|
||||||
if ($this->options['caching']) {
|
if ($this->options['caching']) {
|
||||||
// Check cache exist
|
// Check cache exist
|
||||||
@@ -156,7 +158,7 @@ class CopyManga
|
|||||||
if ($userAgent !== "") {
|
if ($userAgent !== "") {
|
||||||
// Directly send html to method to process
|
// Directly send html to method to process
|
||||||
$html = $response->getBody()->getContents();
|
$html = $response->getBody()->getContents();
|
||||||
$this->writeToCache($url, ['response' => $html, 'type' => 'HTML']);
|
$this->writeToCache($url, ['response' => $html, 'type' => 'HTML'], $ttl);
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,7 @@ class CopyManga
|
|||||||
|
|
||||||
if (json_last_error() === JSON_ERROR_NONE) {
|
if (json_last_error() === JSON_ERROR_NONE) {
|
||||||
// Save to cache if needed
|
// Save to cache if needed
|
||||||
$this->writeToCache($url, $json['results']);
|
$this->writeToCache($url, $json['results'], $ttl);
|
||||||
return $json['results'];
|
return $json['results'];
|
||||||
} else {
|
} else {
|
||||||
throw new Exception($json['code']);
|
throw new Exception($json['code']);
|
||||||
@@ -209,11 +211,8 @@ class CopyManga
|
|||||||
$parameters['limit'] = $limit;
|
$parameters['limit'] = $limit;
|
||||||
$parameters['offset'] = $offset;
|
$parameters['offset'] = $offset;
|
||||||
$parameters['top'] = $top;
|
$parameters['top'] = $top;
|
||||||
//OPTIONS
|
|
||||||
// https://api.mangacopy.com/api/v3/comics?format=json&platform=1&q=x&limit=30&offset=0&top=all"
|
|
||||||
// https://api.mangacopy.com/api/v3/search/comic?platform=1&q=x&limit=20&offset=0&q_type=&_update=true
|
|
||||||
|
|
||||||
return $this->execute($this->buildUrl("comics", $parameters));
|
return $this->execute($this->buildUrl("comics", $parameters), ttl: 15 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -233,7 +232,7 @@ class CopyManga
|
|||||||
$parameters['limit'] = $limit;
|
$parameters['limit'] = $limit;
|
||||||
$parameters['offset'] = $offset;
|
$parameters['offset'] = $offset;
|
||||||
|
|
||||||
return $this->execute($this->buildUrl("search/comic", $parameters));
|
return $this->execute($this->buildUrl("search/comic", $parameters), ttl: 15 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,7 +245,7 @@ class CopyManga
|
|||||||
*/
|
*/
|
||||||
public function comic(string $comic, array $parameters = []): mixed
|
public function comic(string $comic, array $parameters = []): mixed
|
||||||
{
|
{
|
||||||
return $this->execute($this->buildUrl("comic2/{$comic}", $parameters));
|
return $this->execute($this->buildUrl("comic2/{$comic}", $parameters), ttl: 24 * 60 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,7 +265,7 @@ class CopyManga
|
|||||||
$parameters['offset'] = $offset;
|
$parameters['offset'] = $offset;
|
||||||
$options = $this->execute($this->buildUrl("comic/{$comic}/group/{$group}/chapters", $parameters, false), 'OPTIONS');
|
$options = $this->execute($this->buildUrl("comic/{$comic}/group/{$group}/chapters", $parameters, false), 'OPTIONS');
|
||||||
|
|
||||||
return $this->execute($this->buildUrl("comic/{$comic}/group/{$group}/chapters", $parameters));
|
return $this->execute($this->buildUrl("comic/{$comic}/group/{$group}/chapters", $parameters), ttl: 24 * 60 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,7 +296,7 @@ class CopyManga
|
|||||||
public function legacyChapter(string $comic, string $chapter): array
|
public function legacyChapter(string $comic, string $chapter): array
|
||||||
{
|
{
|
||||||
$userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
|
$userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
|
||||||
$responses = $this->execute($this->legacyBuildUrl("comic/{$comic}/chapter/{$chapter}"), "GET", $userAgent);
|
$responses = $this->execute($this->legacyBuildUrl("comic/{$comic}/chapter/{$chapter}"), "GET", $userAgent, ttl: 24 * 60 * 60);
|
||||||
|
|
||||||
// Get Content Key
|
// Get Content Key
|
||||||
$dom = new DOMDocument();
|
$dom = new DOMDocument();
|
||||||
@@ -347,11 +346,12 @@ class CopyManga
|
|||||||
*/
|
*/
|
||||||
public function chapter(string $comic, string $chapter, array $parameters = []): array
|
public function chapter(string $comic, string $chapter, array $parameters = []): array
|
||||||
{
|
{
|
||||||
$responses = $this->execute($this->buildUrl("comic/{$comic}/chapter2/{$chapter}", $parameters));
|
$responses = $this->execute($this->buildUrl("comic/{$comic}/chapter2/{$chapter}", $parameters), ttl: 24 * 60 * 60);
|
||||||
$responses['sorted'] = $this->sort($responses['chapter']['contents'], $responses['chapter']['words']);
|
|
||||||
|
|
||||||
if ($this->legacyImagesFetch) {
|
if ($this->legacyImagesFetch) {
|
||||||
$responses['sorted'] = $this->legacyChapter($comic, $chapter);
|
$responses['sorted'] = $this->legacyChapter($comic, $chapter);
|
||||||
|
} else {
|
||||||
|
$responses['sorted'] = $this->sort($responses['chapter']['contents'], $responses['chapter']['words']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $responses;
|
return $responses;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"laravel/framework": "^11.31",
|
"laravel/framework": "^11.31",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.0",
|
||||||
"laravel/tinker": "^2.9",
|
"laravel/tinker": "^2.9",
|
||||||
|
"plesk/ext-laravel-integration": "^7.0",
|
||||||
"predis/predis": "^2.0",
|
"predis/predis": "^2.0",
|
||||||
"sentry/sentry-laravel": "^4.10",
|
"sentry/sentry-laravel": "^4.10",
|
||||||
"tightenco/ziggy": "^2.0"
|
"tightenco/ziggy": "^2.0"
|
||||||
|
|||||||
53
composer.lock
generated
53
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "b4f2ee211728714c2fa05fbad3914d2e",
|
"content-hash": "641b7b21cd3231c89ab305cd615eeb6f",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -2310,12 +2310,12 @@
|
|||||||
"version": "3.8.4",
|
"version": "3.8.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
"url": "https://github.com/CarbonPHP/carbon.git",
|
||||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58"
|
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58",
|
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58",
|
||||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58",
|
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
@@ -2857,6 +2857,53 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-07-20T21:41:07+00:00"
|
"time": "2024-07-20T21:41:07+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "plesk/ext-laravel-integration",
|
||||||
|
"version": "7.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/plesk/ext-laravel-integration.git",
|
||||||
|
"reference": "73dc4ea3f99e033396497f147aa033eb0602f943"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/plesk/ext-laravel-integration/zipball/73dc4ea3f99e033396497f147aa033eb0602f943",
|
||||||
|
"reference": "73dc4ea3f99e033396497f147aa033eb0602f943",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"laravel/framework": ">=7.0.0",
|
||||||
|
"php": ">=7.4"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"PleskExtLaravel\\Providers\\ConsoleServiceProvider"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"component": "package"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PleskExtLaravel\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"description": "Plesk Laravel Toolkit integration with Laravel applications.",
|
||||||
|
"keywords": [
|
||||||
|
"laravel",
|
||||||
|
"plesk"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/plesk/ext-laravel-integration/issues",
|
||||||
|
"source": "https://github.com/plesk/ext-laravel-integration/tree/7.0.0"
|
||||||
|
},
|
||||||
|
"time": "2023-01-20T09:40:57+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "predis/predis",
|
"name": "predis/predis",
|
||||||
"version": "v2.3.0",
|
"version": "v2.3.0",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.0",
|
||||||
"@inertiajs/react": "^2.0.0",
|
"@inertiajs/react": "^2.0.0",
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"vite": "^6.0.7"
|
"vite": "^6.0.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.9.1",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.2",
|
"@radix-ui/react-avatar": "^1.1.2",
|
||||||
"@radix-ui/react-checkbox": "^1.1.3",
|
"@radix-ui/react-checkbox": "^1.1.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.2",
|
"@radix-ui/react-collapsible": "^1.1.2",
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
"@radix-ui/react-tabs": "^1.1.2",
|
"@radix-ui/react-tabs": "^1.1.2",
|
||||||
"@radix-ui/react-toast": "^1.2.4",
|
"@radix-ui/react-toast": "^1.2.4",
|
||||||
"@radix-ui/react-tooltip": "^1.1.6",
|
"@radix-ui/react-tooltip": "^1.1.6",
|
||||||
"@sentry/react": "^8.47.0",
|
"@sentry/react": "^8.48.0",
|
||||||
"@sentry/vite-plugin": "^2.22.7",
|
"@sentry/vite-plugin": "^2.23.0",
|
||||||
"@tanstack/react-table": "^8.20.6",
|
"@tanstack/react-table": "^8.20.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ export default function AppLayout({ auth, header, children, toolbar }) {
|
|||||||
const [theme, setTheme] = useState(getTheme());
|
const [theme, setTheme] = useState(getTheme());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getTheme();
|
setTheme(getTheme());
|
||||||
setTheme(theme);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
|||||||
{ chapters.list.sort((a, b) => ascending ? (a.index - b.index) : (b.index - a.index)).map(c => (
|
{ chapters.list.sort((a, b) => ascending ? (a.index - b.index) : (b.index - a.index)).map(c => (
|
||||||
<ComicChapterLink key={ c.uuid } { ...c } />
|
<ComicChapterLink key={ c.uuid } { ...c } />
|
||||||
) ) }
|
) ) }
|
||||||
{ (chapters.total > chapters.limit && chapters.offset === 0) && (
|
{ (chapters.total > chapters.limit && chapters.total > chapters.offset) && (
|
||||||
<Button size="sm" variant="outline" asChild>
|
<Button size="sm" variant="outline" asChild>
|
||||||
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) + chapters.limit }}>
|
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) + chapters.limit }}>
|
||||||
Next <ChevronsRight />
|
Next <ChevronsRight />
|
||||||
|
|||||||
@@ -11,6 +11,18 @@ export default function Updates({ auth }) {
|
|||||||
<title>Updates</title>
|
<title>Updates</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div className="p-3 pt-1">
|
<div className="p-3 pt-1">
|
||||||
|
<Card className="w-[90%] m-3 mx-auto">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>0.1.2</CardTitle>
|
||||||
|
<CardDescription>Release: 11 Jan 2025</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul>
|
||||||
|
<li>Updated cache TTL</li>
|
||||||
|
<li>Beta: Next chapter prefetch</li>
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
<Card className="w-[90%] m-3 mx-auto">
|
<Card className="w-[90%] m-3 mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>0.1.1</CardTitle>
|
<CardTitle>0.1.1</CardTitle>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function AppSidebar({ auth }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-0.5 leading-none">
|
<div className="flex flex-col gap-0.5 leading-none">
|
||||||
<span className="font-semibold">Comic</span>
|
<span className="font-semibold">Comic</span>
|
||||||
<span>0.1.1</span>
|
<span>0.1.2</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user