Compare commits
14 Commits
f5c706b587
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f8022b3f18 | |||
| 57d1ec34c3 | |||
| 81639aaabf | |||
| 5dc1bff60c | |||
| 2f3f0edc30 | |||
| a71b2976af | |||
| 2c016274ab | |||
| 5ba0a55316 | |||
| a6fe34a0d1 | |||
| bb53e06c69 | |||
| 5cae5dc658 | |||
| 356d07365f | |||
| 3574d20fae | |||
| 9fc9e8ed10 |
@@ -16,7 +16,7 @@ class GenerateSitemap extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'sitemap:generate';
|
protected $signature = 'app:generate-sitemap';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Console\Command;
|
|||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class SyncSubscriptionKeys extends Command
|
class SyncSubscriptionKeys extends Command
|
||||||
{
|
{
|
||||||
@@ -83,7 +84,10 @@ class SyncSubscriptionKeys extends Command
|
|||||||
->whereNotIn('subscription_key', $activeKeys)
|
->whereNotIn('subscription_key', $activeKeys)
|
||||||
->chunk(100, function ($users) {
|
->chunk(100, function ($users) {
|
||||||
foreach($users as $user) {
|
foreach($users as $user) {
|
||||||
$user->removeRole(UserRole::SUPPORTER);
|
if ($user->hasRole(UserRole::SUPPORTER)) {
|
||||||
|
Log::info("Removed Supporter Role from {$user->name}");
|
||||||
|
$user->removeRole(UserRole::SUPPORTER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -95,7 +99,10 @@ class SyncSubscriptionKeys extends Command
|
|||||||
->whereIn('subscription_key', $activeKeys)
|
->whereIn('subscription_key', $activeKeys)
|
||||||
->chunk(100, function ($users) {
|
->chunk(100, function ($users) {
|
||||||
foreach($users as $user) {
|
foreach($users as $user) {
|
||||||
$user->addRole(UserRole::SUPPORTER);
|
if (!$user->hasRole(UserRole::SUPPORTER)) {
|
||||||
|
Log::info("Added Supporter Role for {$user->name}");
|
||||||
|
$user->addRole(UserRole::SUPPORTER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class CacheHelper
|
|||||||
public static function getLatestComments()
|
public static function getLatestComments()
|
||||||
{
|
{
|
||||||
return Cache::remember('latest_comments', now()->addMinutes(60), function () {
|
return Cache::remember('latest_comments', now()->addMinutes(60), function () {
|
||||||
return Comment::latest()->take(10)->get();
|
return Comment::with('user')->latest()->take(10)->get();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Jobs\DiscordReleaseNotification;
|
use App\Jobs\DiscordReleaseNotification;
|
||||||
use App\Models\Episode;
|
use App\Models\Episode;
|
||||||
@@ -63,6 +64,17 @@ class EpisodeController extends Controller
|
|||||||
public function update(Request $request): RedirectResponse
|
public function update(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$episode = Episode::with('hentai')->where('id', $request->input('episode_id'))->firstOrFail();
|
$episode = Episode::with('hentai')->where('id', $request->input('episode_id'))->firstOrFail();
|
||||||
|
|
||||||
|
if ($request->user()->hasRole(UserRole::MODERATOR)) {
|
||||||
|
$this->episodeService->updateEpisodeModerator($request, $episode->id);
|
||||||
|
|
||||||
|
cache()->flush();
|
||||||
|
|
||||||
|
return to_route('hentai.index', [
|
||||||
|
'title' => $episode->slug,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$studio = $this->episodeService->getOrCreateStudio(json_decode($request->input('studio'))[0]->value);
|
$studio = $this->episodeService->getOrCreateStudio(json_decode($request->input('studio'))[0]->value);
|
||||||
|
|
||||||
$oldinterpolated = $episode->interpolated;
|
$oldinterpolated = $episode->interpolated;
|
||||||
|
|||||||
@@ -23,11 +23,21 @@ class DownloadButton extends Component
|
|||||||
|
|
||||||
public $fileExtension = 'HEVC';
|
public $fileExtension = 'HEVC';
|
||||||
|
|
||||||
|
public $version = '';
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (str_contains($this->downloadUrl, 'AV1')) {
|
if (str_contains($this->downloadUrl, 'AV1')) {
|
||||||
$this->fileExtension = 'AV1';
|
$this->fileExtension = 'AV1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str_contains($this->downloadUrl, 'v2')) {
|
||||||
|
$this->version = 'v2';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_contains($this->downloadUrl, 'v3')) {
|
||||||
|
$this->version = 'v3';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clicked($downloadId)
|
public function clicked($downloadId)
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire;
|
|
||||||
|
|
||||||
use App\Models\Episode;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class ViewCount extends Component
|
|
||||||
{
|
|
||||||
public $episodeId = 0;
|
|
||||||
|
|
||||||
public $viewCount = 0;
|
|
||||||
|
|
||||||
public function mount(Episode $episode)
|
|
||||||
{
|
|
||||||
$this->episodeId = $episode->id;
|
|
||||||
$this->viewCount = $episode->view_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update()
|
|
||||||
{
|
|
||||||
$this->viewCount = Episode::where('id', $this->episodeId)->firstOrFail()->view_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.view-count');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ namespace App\Services;
|
|||||||
use App\Models\Episode;
|
use App\Models\Episode;
|
||||||
use App\Models\Hentai;
|
use App\Models\Hentai;
|
||||||
use App\Models\Studios;
|
use App\Models\Studios;
|
||||||
|
use App\Models\ModLog;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@@ -62,6 +63,68 @@ class EpisodeService
|
|||||||
return $episode;
|
return $episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function applyTags(Request $request, Episode $episode): void
|
||||||
|
{
|
||||||
|
$tags = json_decode($request->input('tags'));
|
||||||
|
$newtags = [];
|
||||||
|
foreach ($tags as $t) {
|
||||||
|
$newtags[] = $t->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTagsTemp = $newtags;
|
||||||
|
$oldTagsTemp = $episode->tagNames();
|
||||||
|
|
||||||
|
sort($newTagsTemp);
|
||||||
|
sort($oldTagsTemp);
|
||||||
|
|
||||||
|
if ($newTagsTemp !== $oldTagsTemp) {
|
||||||
|
ModLog::create([
|
||||||
|
'moderator' => $request->user()->name,
|
||||||
|
'data' => sprintf(
|
||||||
|
'Updated Episode tags from %s to %s',
|
||||||
|
implode(', ', $oldTagsTemp),
|
||||||
|
implode(', ', $newTagsTemp),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$episode->retag($newtags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateTitle(Request $request, Episode $episode): void
|
||||||
|
{
|
||||||
|
$updates = [];
|
||||||
|
|
||||||
|
if ($episode->title !== $request->input('title')) {
|
||||||
|
$updates['title'] = $request->input('title');
|
||||||
|
$updates['title_search'] = preg_replace(
|
||||||
|
'/[^A-Za-z0-9 ]/',
|
||||||
|
'',
|
||||||
|
$request->input('title')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Log to ModLog
|
||||||
|
ModLog::create([
|
||||||
|
'moderator' => $request->user()->name,
|
||||||
|
'data' => "Updating Hentai Title from {$episode->title} to {$request->input('title')}",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($episode->title_jpn !== $request->input('title_jpn')) {
|
||||||
|
$updates['title_jpn'] = $request->input('title_jpn');
|
||||||
|
|
||||||
|
// Log to ModLog
|
||||||
|
ModLog::create([
|
||||||
|
'moderator' => $request->user()->name,
|
||||||
|
'data' => "Updating Hentai Title from {$episode->title_jpn} to {$request->input('title_jpn')}",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($updates)) {
|
||||||
|
$episode->hentai->episodes()->update($updates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function updateEpisode(Request $request, Studios $studio, int $episodeId): Episode
|
public function updateEpisode(Request $request, Studios $studio, int $episodeId): Episode
|
||||||
{
|
{
|
||||||
$episode = Episode::where('id', $episodeId)->firstOrFail();
|
$episode = Episode::where('id', $episodeId)->firstOrFail();
|
||||||
@@ -75,17 +138,31 @@ class EpisodeService
|
|||||||
$episode->dmca_takedown = $request->input('dmca_takedown') == 'true';
|
$episode->dmca_takedown = $request->input('dmca_takedown') == 'true';
|
||||||
$episode->save();
|
$episode->save();
|
||||||
|
|
||||||
// Tagging
|
$this->applyTags($request, $episode);
|
||||||
$tags = json_decode($request->input('tags'));
|
$this->updateTitle($request, $episode);
|
||||||
$newtags = [];
|
|
||||||
foreach ($tags as $t) {
|
|
||||||
$newtags[] = $t->value;
|
|
||||||
}
|
|
||||||
$episode->retag($newtags);
|
|
||||||
|
|
||||||
return $episode;
|
return $episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateEpisodeModerator(Request $request, int $episodeId): void
|
||||||
|
{
|
||||||
|
$episode = Episode::where('id', $episodeId)->firstOrFail();
|
||||||
|
$oldDescription = $episode->description;
|
||||||
|
$episode->description = $request->input('description');
|
||||||
|
$episode->save();
|
||||||
|
|
||||||
|
if ($episode->description !== $oldDescription) {
|
||||||
|
// Log to ModLog
|
||||||
|
ModLog::create([
|
||||||
|
'moderator' => $request->user()->name,
|
||||||
|
'data' => "Updated Episode description from {$oldDescription} to {$episode->description}",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->applyTags($request, $episode);
|
||||||
|
$this->updateTitle($request, $episode);
|
||||||
|
}
|
||||||
|
|
||||||
public function getOrCreateStudio(string $studioName): Studios
|
public function getOrCreateStudio(string $studioName): Studios
|
||||||
{
|
{
|
||||||
return Studios::firstOrCreate(
|
return Studios::firstOrCreate(
|
||||||
|
|||||||
Generated
+430
-427
File diff suppressed because it is too large
Load Diff
Generated
+382
-319
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
|||||||
function updateGrid(grid) {
|
|
||||||
// Skip hidden grids
|
|
||||||
if (grid.offsetParent === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const items = [...grid.querySelectorAll('.episode-item')];
|
|
||||||
|
|
||||||
if (!items.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset visibility first
|
|
||||||
items.forEach(item => {
|
|
||||||
item.style.display = '';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Determine actual column count
|
|
||||||
const firstTop = items[0].offsetTop;
|
|
||||||
|
|
||||||
let columns = 0;
|
|
||||||
|
|
||||||
for (const item of items) {
|
|
||||||
if (item.offsetTop !== firstTop) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
columns++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = parseInt(grid.dataset.rows || '2', 10);
|
|
||||||
|
|
||||||
const visibleItems = columns * rows;
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
item.style.display = index < visibleItems
|
|
||||||
? ''
|
|
||||||
: 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAllEpisodeGrids() {
|
|
||||||
document.querySelectorAll('.episode-grid').forEach(updateGrid);
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(entries => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
updateGrid(entry.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll('.episode-grid').forEach(grid => {
|
|
||||||
observer.observe(grid);
|
|
||||||
});
|
|
||||||
|
|
||||||
let resizeTimeout;
|
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
clearTimeout(resizeTimeout);
|
|
||||||
|
|
||||||
resizeTimeout = setTimeout(() => {
|
|
||||||
updateAllEpisodeGrids();
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('load', updateAllEpisodeGrids);
|
|
||||||
@@ -1,21 +1,41 @@
|
|||||||
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalEditEpisode" tabindex="-1" aria-labelledby="Upload" aria-modal="true" role="dialog">
|
<div
|
||||||
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[50%]">
|
data-te-modal-init
|
||||||
<div class="flex relative flex-col w-full text-current bg-clip-padding bg-white rounded-md border-none shadow-lg outline-none pointer-events-auto dark:bg-neutral-800">
|
id="modalEditEpisode"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-modal="true"
|
||||||
|
role="dialog"
|
||||||
|
class="fixed inset-0 z-[1055] hidden overflow-y-auto bg-black/60 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
<div data-te-modal-dialog-ref class="flex min-h-screen items-center justify-center p-4">
|
||||||
|
<div class="relative w-full max-w-7xl overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-2xl dark:border-neutral-700 dark:bg-neutral-900">
|
||||||
<x-modal-header :title="__('Edit Episode')"/>
|
<x-modal-header :title="__('Edit Episode')"/>
|
||||||
|
|
||||||
<!--Modal body-->
|
<!--Modal body-->
|
||||||
<div class="relative p-4 pt-0">
|
<div class="relative p-4 pt-0">
|
||||||
<form method="POST" action="{{ route('admin.edit') }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('admin.episode.edit') }}" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="grid grid-cols-3">
|
<div class="flex flex-col gap-2 p-2">
|
||||||
<div class="col-span-2">
|
<div>
|
||||||
<!-- Tags -->
|
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="title">Title:</label>
|
||||||
<div class="row-span-2 p-0">
|
<x-text-input id="title" value="{{ $episode->title }}" class="block w-full" type="text" name="title" required autofocus/>
|
||||||
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="tags">Tags:</label>
|
|
||||||
<x-text-input id="tags" class="block w-full" type="text" name="tags" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="title_jpn">Title JPN:</label>
|
||||||
|
<x-text-input id="title_jpn" value="{{ $episode->title_jpn }}" class="block w-full" type="text" name="title_jpn" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 p-2">
|
||||||
|
<div class="col-span-2">
|
||||||
|
<!-- Tags -->
|
||||||
|
<div class="row-span-2 p-0">
|
||||||
|
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="tags">Tags:</label>
|
||||||
|
<x-text-input id="tags" class="block w-full" type="text" name="tags" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<div class="grid grid-rows-2">
|
<div class="grid grid-rows-2">
|
||||||
<!-- Studio -->
|
<!-- Studio -->
|
||||||
<div class="p-2 pt-0">
|
<div class="p-2 pt-0">
|
||||||
@@ -47,13 +67,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<!-- Stream URL -->
|
<!-- Stream URL -->
|
||||||
<div class="p-2 pt-0">
|
<div class="p-2 pt-0">
|
||||||
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="baseurl">Stream:</label>
|
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="baseurl">Stream:</label>
|
||||||
<x-text-input id="baseurl" class="block w-full" type="text" name="baseurl" value="{{ $episode->url }}" required />
|
<x-text-input id="baseurl" class="block w-full" type="text" name="baseurl" value="{{ $episode->url }}" required />
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<input name="episode_id" id="episode_id" type="hidden" value="{{ $episode->id }}" />
|
<input name="episode_id" id="episode_id" type="hidden" value="{{ $episode->id }}" />
|
||||||
|
|
||||||
@@ -62,6 +85,7 @@
|
|||||||
<textarea rows="4" cols="50" id="description" name="description" class="block mt-1 w-full rounded-md border-gray-300 shadow-sm dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600" required>{{ $episode->description }}</textarea>
|
<textarea rows="4" cols="50" id="description" name="description" class="block mt-1 w-full rounded-md border-gray-300 shadow-sm dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600" required>{{ $episode->description }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<!-- Episodes -->
|
<!-- Episodes -->
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2">
|
||||||
<!-- Cover -->
|
<!-- Cover -->
|
||||||
@@ -95,8 +119,10 @@
|
|||||||
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="downloadUHDi1">Download 4k Interpolated:</label>
|
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="downloadUHDi1">Download 4k Interpolated:</label>
|
||||||
<x-text-input id="downloadUHDi1" class="block w-full" type="text" name="downloadUHDi1" value="{{ $episode->getDownloadByType('UHDi')->url ?? '' }}" />
|
<x-text-input id="downloadUHDi1" class="block w-full" type="text" name="downloadUHDi1" value="{{ $episode->getDownloadByType('UHDi')->url ?? '' }}" />
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="flex flex-wrap flex-shrink-0 justify-end items-center p-4 rounded-b-md">
|
<div class="sticky bottom-0 flex items-center justify-end gap-3 border-t border-neutral-200 bg-white/90 px-6 py-4 backdrop-blur dark:border-neutral-700 dark:bg-neutral-900/90">
|
||||||
|
@if(auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<div class="inline-block mr-2">
|
<div class="inline-block mr-2">
|
||||||
<input class="w-4 h-4 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
|
<input class="w-4 h-4 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
|
||||||
type="checkbox" value="true" id="v2" name="v2" />
|
type="checkbox" value="true" id="v2" name="v2" />
|
||||||
@@ -111,10 +137,17 @@
|
|||||||
DMCA Takedown
|
DMCA Takedown
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="inline-block px-6 pt-2.5 pb-2 text-xs font-medium leading-normal uppercase rounded transition duration-150 ease-in-out bg-primary-100 text-primary-700 hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
|
@endif
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-te-modal-dismiss
|
||||||
|
class="rounded-xl border border-neutral-300 px-5 py-2.5 text-sm font-medium text-neutral-700 transition hover:bg-neutral-100 dark:border-neutral-600 dark:text-neutral-200 dark:hover:bg-neutral-800">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="inline-block px-6 pt-2.5 pb-2 ml-1 text-xs font-medium leading-normal text-white uppercase bg-rose-600 rounded transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-te-ripple-init
|
||||||
|
class="rounded-xl bg-rose-600 px-5 py-2.5 text-sm font-semibold text-white shadow-lg shadow-rose-600/20 transition hover:bg-rose-700">
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
@auth
|
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR))
|
|
||||||
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
|
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
|
||||||
<div class="float-left">
|
|
||||||
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
|
||||||
<i class="fa-solid fa-plus pr-[6px]"></i> Add Episode
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<div class="float-right">
|
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
|
||||||
<a data-te-toggle="modal" data-te-target="#modalAddSubtitles" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
|
||||||
<i class="fa-solid fa-plus pr-[6px]"></i> Add Subtitles
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
<a data-te-toggle="modal" data-te-target="#modalEditEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
|
||||||
<i class="fa-solid fa-pen pr-[6px]"></i> Edit Episode
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
@endauth
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<div class="group w-full p-1">
|
<div class="group w-full p-1">
|
||||||
<a
|
<a
|
||||||
href="{{ route('hentai.index', ['title' => $episode->slug]) }}"
|
href="{{ route('hentai.index', ['title' => $episode->slug]) }}"
|
||||||
class="block overflow-hidden rounded-2xl border dark:border-neutral-800 border-neutral-300 dark:bg-neutral-900 transition-all duration-300 hover:-translate-y-1 dark:hover:border-neutral-700 hover:border-neutral-400 hover:shadow-2xl hover:shadow-black/30"
|
class="block overflow-hidden rounded-2xl border border-neutral-200 bg-white transition-all duration-300 hover:-translate-y-1 hover:border-neutral-400 hover:shadow-xl dark:border-neutral-800 dark:bg-neutral-900 dark:hover:border-neutral-700"
|
||||||
>
|
>
|
||||||
<div class="relative overflow-hidden">
|
<div class="relative overflow-hidden">
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
alt="{{ $episode->title }} - {{ $episode->episode }}"
|
alt="{{ $episode->title }} - {{ $episode->episode }}"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
width="400"
|
width="400"
|
||||||
class="aspect-[11/16] w-full object-cover object-center transform-gpu transition-transform duration-500 group-hover:scale-[1.02]"
|
class="aspect-[11/16] w-full object-cover object-center transition-transform duration-500 group-hover:scale-[1.03]"
|
||||||
>
|
>
|
||||||
@elseif ($view === 'thumbnail')
|
@elseif ($view === 'thumbnail')
|
||||||
@php
|
@php
|
||||||
@@ -50,49 +50,56 @@
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
width="1000"
|
width="1000"
|
||||||
data-gallery='@json($galleryImages)'
|
data-gallery='@json($galleryImages)'
|
||||||
class="preview-gallery aspect-video w-full object-cover object-center transform-gpu transition-transform duration-500 group-hover:scale-[1.02]"
|
class="preview-gallery aspect-video w-full object-cover object-center transition-transform duration-500 group-hover:scale-[1.03]"
|
||||||
>
|
>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{{-- Overlay Gradient --}}
|
{{-- Dark Overlay --}}
|
||||||
<div class="pointer-events-none absolute inset-0 bg-gradient-to-t from-black/80 via-black/10 to-transparent"></div>
|
<div class="pointer-events-none absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-transparent"></div>
|
||||||
|
|
||||||
{{-- Top Row --}}
|
{{-- Top Meta --}}
|
||||||
<div class="absolute inset-x-0 top-0 z-20 flex items-start justify-between p-3">
|
<div class="pointer-events-none absolute inset-x-0 top-0 z-20 flex items-start justify-between p-3">
|
||||||
|
|
||||||
{{-- Problematic Tags --}}
|
{{-- Problematic Tags --}}
|
||||||
@if (!empty($problematic))
|
@if (!empty($problematic))
|
||||||
<div class="rounded-xl border border-red-500/30 bg-red-900/70 px-2.5 py-1 text-xs font-semibold text-white">
|
<div class="rounded-full bg-red-700/40 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-white ring-1 ring-red-700/70">
|
||||||
<i class="fa-solid fa-triangle-exclamation mr-1"></i>
|
<i class="fa-solid fa-triangle-exclamation mr-1"></i>
|
||||||
{{ $problematic }}
|
{{ $problematic }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{{-- Resolution --}}
|
{{-- Resolution --}}
|
||||||
<div class="ml-auto rounded-xl bg-black/70 px-2.5 py-1 text-xs font-semibold tracking-wide text-neutral-100 ring-1 ring-white/10">
|
<div class="ml-auto rounded-full bg-black/70 px-2 py-1 text-[11px] font-semibold tracking-wide text-white ring-1 ring-white/10">
|
||||||
{{ $episode->getResolution() }}
|
{{ $episode->getResolution() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Bottom Stats --}}
|
{{-- Bottom Content --}}
|
||||||
<div class="absolute inset-x-0 bottom-0 z-20 p-3">
|
<div class="pointer-events-none absolute inset-x-0 bottom-0 z-20 p-4">
|
||||||
<div class="flex items-end justify-between gap-3">
|
|
||||||
|
{{-- Title --}}
|
||||||
|
<h3 class=" text-sm font-semibold leading-snug text-white md:text-base">
|
||||||
|
{{ $title }}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{{-- Bottom Row --}}
|
||||||
|
<div class="mt-3 flex items-center justify-between gap-3">
|
||||||
|
|
||||||
{{-- Stats --}}
|
{{-- Stats --}}
|
||||||
<div class="flex flex-1 flex-wrap items-center gap-x-3 gap-y-1 text-sm font-bold text-neutral-200">
|
<div class="flex flex-wrap items-center gap-3 text-sm font-bold text-neutral-200">
|
||||||
|
|
||||||
<span class="flex items-center gap-1">
|
<span class="flex items-center gap-1">
|
||||||
<i class="fa-regular fa-eye text-neutral-400"></i>
|
<i class="fa-regular fa-eye text-neutral-200 font-bold"></i>
|
||||||
{{ $episode->viewCountFormatted() }}
|
{{ $episode->viewCountFormatted() }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="flex items-center gap-1">
|
<span class="flex items-center gap-1">
|
||||||
<i class="fa-regular fa-heart text-neutral-400"></i>
|
<i class="fa-regular fa-heart text-neutral-200 font-bold"></i>
|
||||||
{{ $episode->likeCount() }}
|
{{ $episode->likeCount() }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="flex items-center gap-1">
|
<span class="flex items-center gap-1">
|
||||||
<i class="fa-regular fa-comment text-neutral-400"></i>
|
<i class="fa-regular fa-comment text-neutral-200 font-bold"></i>
|
||||||
{{ $episode->commentCount() }}
|
{{ $episode->commentCount() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,9 +109,9 @@
|
|||||||
@if ($isWatched)
|
@if ($isWatched)
|
||||||
<div class="shrink-0 rounded-full bg-emerald-800/40 px-2.5 py-1 text-xs font-semibold text-emerald-300 ring-1 ring-emerald-500/30">
|
<div class="shrink-0 rounded-full bg-emerald-800/40 px-2.5 py-1 text-xs font-semibold text-emerald-300 ring-1 ring-emerald-500/30">
|
||||||
@if ($view === 'thumbnail')
|
@if ($view === 'thumbnail')
|
||||||
<i class="fa-solid fa-check mr-1"></i> Watched
|
<i class="fa-solid fa-eye mr-1"></i> Watched
|
||||||
@else
|
@else
|
||||||
<i class="fa-solid fa-check"></i>
|
<i class="fa-solid fa-eye"></i>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
@@ -116,13 +123,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- Content --}}
|
|
||||||
<div class="relative isolate border-t dark:border-neutral-800 dark:bg-neutral-900 bg-white border-neutral-100 p-4">
|
|
||||||
<h3 class="text-sm font-semibold leading-relaxed dark:text-neutral-100 text-neutral-900 transition-colors duration-200 dark:group-hover:text-white">
|
|
||||||
{{ $title }}
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,6 +28,4 @@
|
|||||||
<div class="mx-auto pt-6 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%] pb-2">
|
<div class="mx-auto pt-6 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%] pb-2">
|
||||||
@include('home.partials.comments')
|
@include('home.partials.comments')
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@vite(['resources/js/responsive.js'])
|
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
@@ -55,10 +55,9 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
{{-- Resolution Badge --}}
|
{{-- Resolution Badge --}}
|
||||||
<span
|
<div class="absolute right-2 top-2 rounded-lg bg-black/70 px-2 py-1 text-[11px] font-semibold tracking-wide text-white ring-1 ring-white/10">
|
||||||
class="absolute right-0 top-0 rounded-bl-lg bg-rose-700/70 px-3 py-1 text-xs font-semibold text-white shadow-lg backdrop-blur">
|
|
||||||
{{ $resolution }}
|
{{ $resolution }}
|
||||||
</span>
|
</div>
|
||||||
|
|
||||||
{{-- Gradient Overlay --}}
|
{{-- Gradient Overlay --}}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
@php
|
@php
|
||||||
$random = \cache()->remember('random_home', 300, function () {
|
$random = \cache()->remember('random_home', 300, function () {
|
||||||
return \App\Models\Episode::inRandomOrder()->limit(8)->get(); ;
|
return \App\Models\Episode::inRandomOrder()->limit(16)->get(); ;
|
||||||
});
|
});
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
@include('home.partials.tab.template', ['episodes' => $random, 'showThumbnails' => false])
|
@include('home.partials.tab.template', ['episodes' => $random, 'isThumbnail' => false])
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,34 @@
|
|||||||
|
@props(['isThumbnail'])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$isThumbnail = $showThumbnails;
|
|
||||||
|
|
||||||
$gridClasses = $isThumbnail
|
|
||||||
? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6'
|
|
||||||
: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-8';
|
|
||||||
|
|
||||||
// Render enough items for largest possible layout
|
// Render enough items for largest possible layout
|
||||||
$limit = 24;
|
$limit = 16;
|
||||||
|
|
||||||
$view = $isThumbnail ? 'thumbnail' : 'poster';
|
$view = $isThumbnail ? 'thumbnail' : 'poster';
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
|
@if ($isThumbnail)
|
||||||
<div
|
<div
|
||||||
class="episode-grid grid {{ $gridClasses }}"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5
|
||||||
data-rows="2"
|
[&>.episode-item]:hidden
|
||||||
|
[&>.episode-item:nth-child(-n+8)]:block
|
||||||
|
md:[&>.episode-item:nth-child(-n+8)]:block
|
||||||
|
lg:[&>.episode-item:nth-child(-n+9)]:block
|
||||||
|
xl:[&>.episode-item:nth-child(-n+9)]:block
|
||||||
|
2xl:[&>.episode-item:nth-child(-n+12)]:block
|
||||||
|
3xl:[&>.episode-item:nth-child(-n+15)]:block"
|
||||||
>
|
>
|
||||||
|
@else
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-8
|
||||||
|
[&>.episode-item]:hidden
|
||||||
|
[&>.episode-item:nth-child(-n+12)]:block
|
||||||
|
md:[&>.episode-item:nth-child(-n+12)]:block
|
||||||
|
xl:[&>.episode-item:nth-child(-n+12)]:block
|
||||||
|
2xl:[&>.episode-item:nth-child(-n+15)]:block
|
||||||
|
3xl:[&>.episode-item:nth-child(-n+16)]:block"
|
||||||
|
>
|
||||||
|
@endif
|
||||||
@foreach ($episodes->take($limit) as $ep)
|
@foreach ($episodes->take($limit) as $ep)
|
||||||
@php
|
@php
|
||||||
$episode = isset($popularView)
|
$episode = isset($popularView)
|
||||||
@@ -22,7 +36,7 @@
|
|||||||
: $ep;
|
: $ep;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="episode-item">
|
<div class="episode-item p-1">
|
||||||
<x-episode-cover
|
<x-episode-cover
|
||||||
:episode="$episode"
|
:episode="$episode"
|
||||||
:view="$view"
|
:view="$view"
|
||||||
|
|||||||
@@ -34,11 +34,11 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@php $showThumbnails = true; @endphp
|
@php $isThumbnail = true; @endphp
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@if (!Auth::user()->home_middle_design)
|
@if (!Auth::user()->home_middle_design)
|
||||||
@php $showThumbnails = false; @endphp
|
@php $isThumbnail = false; @endphp
|
||||||
@endif
|
@endif
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
id="tabs-most-views" role="tabpanel" aria-labelledby="tabs-most-views-tab" data-te-tab-active>
|
id="tabs-most-views" role="tabpanel" aria-labelledby="tabs-most-views-tab" data-te-tab-active>
|
||||||
@include('home.partials.tab.template', [
|
@include('home.partials.tab.template', [
|
||||||
'episodes' => $popularAllTime,
|
'episodes' => $popularAllTime,
|
||||||
'showThumbnails' => $showThumbnails,
|
'isThumbnail' => $isThumbnail,
|
||||||
])
|
])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
|
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
id="tabs-most-likes" role="tabpanel" aria-labelledby="tabs-most-likes-tab">
|
id="tabs-most-likes" role="tabpanel" aria-labelledby="tabs-most-likes-tab">
|
||||||
@include('home.partials.tab.template', [
|
@include('home.partials.tab.template', [
|
||||||
'episodes' => $mostLikes,
|
'episodes' => $mostLikes,
|
||||||
'showThumbnails' => $showThumbnails,
|
'isThumbnail' => $isThumbnail,
|
||||||
])
|
])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
|
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
id="tabs-popular-weekly" role="tabpanel" aria-labelledby="tabs-popular-weekly-tab">
|
id="tabs-popular-weekly" role="tabpanel" aria-labelledby="tabs-popular-weekly-tab">
|
||||||
@include('home.partials.tab.template', [
|
@include('home.partials.tab.template', [
|
||||||
'episodes' => $popularWeekly,
|
'episodes' => $popularWeekly,
|
||||||
'showThumbnails' => $showThumbnails,
|
'isThumbnail' => $isThumbnail,
|
||||||
'popularView' => true,
|
'popularView' => true,
|
||||||
])
|
])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
id="tabs-popular-monthly" role="tabpanel" aria-labelledby="tabs-popular-monthly-tab">
|
id="tabs-popular-monthly" role="tabpanel" aria-labelledby="tabs-popular-monthly-tab">
|
||||||
@include('home.partials.tab.template', [
|
@include('home.partials.tab.template', [
|
||||||
'episodes' => $popularMonthly,
|
'episodes' => $popularMonthly,
|
||||||
'showThumbnails' => $showThumbnails,
|
'isThumbnail' => $isThumbnail,
|
||||||
'popularView' => true,
|
'popularView' => true,
|
||||||
])
|
])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
|
|||||||
@@ -21,18 +21,18 @@
|
|||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@php $showThumbnails = false; @endphp
|
@php $isThumbnail = false; @endphp
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->home_top_design)
|
@if(Auth::user()->home_top_design)
|
||||||
@php $showThumbnails = true; @endphp
|
@php $isThumbnail = true; @endphp
|
||||||
@endif
|
@endif
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
<!--Tabs content-->
|
<!--Tabs content-->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<div class="hidden opacity-100 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-uploaded" role="tabpanel" aria-labelledby="tabs-recently-uploaded-tab" data-te-tab-active>
|
<div class="hidden opacity-100 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-uploaded" role="tabpanel" aria-labelledby="tabs-recently-uploaded-tab" data-te-tab-active>
|
||||||
@include('home.partials.tab.template', ['episodes' => $recentlyUploaded, 'showThumbnails' => $showThumbnails])
|
@include('home.partials.tab.template', ['episodes' => $recentlyUploaded, 'isThumbnail' => $isThumbnail])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded']) }}"
|
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded']) }}"
|
||||||
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-released" role="tabpanel"aria-labelledby="tabs-recently-released-tab">
|
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-released" role="tabpanel"aria-labelledby="tabs-recently-released-tab">
|
||||||
@include('home.partials.tab.template', ['episodes' => $recentlyReleased, 'showThumbnails' => $showThumbnails])
|
@include('home.partials.tab.template', ['episodes' => $recentlyReleased, 'isThumbnail' => $isThumbnail])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
|
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
|
||||||
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-trending" role="tabpanel"aria-labelledby="tabs-trending-tab">
|
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-trending" role="tabpanel"aria-labelledby="tabs-trending-tab">
|
||||||
@include('home.partials.tab.template', ['episodes' => $popularDaily, 'showThumbnails' => $showThumbnails, 'popularView' => true])
|
@include('home.partials.tab.template', ['episodes' => $popularDaily, 'isThumbnail' => $isThumbnail, 'popularView' => true])
|
||||||
<div class="grid text-center pt-5 ">
|
<div class="grid text-center pt-5 ">
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
|
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
|
||||||
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
||||||
|
|||||||
@@ -1,153 +1,253 @@
|
|||||||
<div>
|
<div id="comment-{{ $comment->id }}">
|
||||||
<div class="flex" id="comment-{{ $comment->id }}">
|
|
||||||
<div class="flex-shrink-0 mr-4">
|
<div class="group flex gap-4">
|
||||||
|
|
||||||
|
{{-- Avatar --}}
|
||||||
|
<div class="shrink-0">
|
||||||
@if($comment->isDeletedByModerator())
|
@if($comment->isDeletedByModerator())
|
||||||
<img class="h-10 w-10 rounded-full" src="{{ asset('images/default-avatar.webp') }}" alt="Deleted comment">
|
<img
|
||||||
|
class="h-10 w-10 rounded-full object-cover opacity-60"
|
||||||
|
src="{{ asset('images/default-avatar.webp') }}"
|
||||||
|
alt="Deleted comment"
|
||||||
|
>
|
||||||
@else
|
@else
|
||||||
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
<img
|
||||||
|
class="h-10 w-10 rounded-full object-cover ring-2 ring-white dark:ring-neutral-800"
|
||||||
|
src="{{ $comment->user->getAvatar() }}"
|
||||||
|
alt="{{ $comment->user->name }}"
|
||||||
|
>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow">
|
|
||||||
<div class="flex gap-2">
|
|
||||||
@if($comment->isDeletedByModerator())
|
|
||||||
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
|
||||||
<p class="font-medium text-gray-900 dark:text-gray-100">Deleted ({{ $comment->user->name }})</p>
|
|
||||||
@else
|
|
||||||
<p class="font-medium text-gray-900 dark:text-gray-100">Deleted</p>
|
|
||||||
@endif
|
|
||||||
@else
|
|
||||||
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
|
||||||
@endif
|
|
||||||
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
|
||||||
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
|
||||||
@endif
|
|
||||||
@if($comment->user->hasRole(\App\Enums\UserRole::MODERATOR))
|
|
||||||
<a data-te-toggle="tooltip" title="Admin" class="text-rose-600">Moderator</a>
|
|
||||||
@endif
|
|
||||||
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
|
||||||
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 flex-grow w-full">
|
|
||||||
@if($comment->isDeletedByModerator())
|
|
||||||
<div class="text-gray-700 dark:text-gray-200">Deleted by moderation.</div>
|
|
||||||
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
|
||||||
<div class="text-gray-700 dark:text-gray-300 pt-1">Original comment: {!! $comment->presenter()->markdownBody() !!}</div>
|
|
||||||
@endif
|
|
||||||
@else
|
|
||||||
@if ($isEditing)
|
|
||||||
<form wire:submit.prevent="editComment">
|
|
||||||
<div>
|
|
||||||
<label for="comment" class="sr-only">Comment body</label>
|
|
||||||
<textarea id="comment" name="comment" rows="3"
|
|
||||||
class="bg-white dark:bg-neutral-700 shadow-sm block w-full focus:ring-rose-500 focus:border-rose-500 border-gray-300 dark:border-gray-400/40 text-gray-900 dark:text-gray-200 placeholder:text-gray-400 rounded-md
|
|
||||||
@error('editState.body') border-red-500 @enderror"
|
|
||||||
placeholder="Write something" wire:model.defer="editState.body"></textarea>
|
|
||||||
@error('editState.body')
|
|
||||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
<div class="mt-3 flex items-center justify-between">
|
|
||||||
<button type="submit"
|
|
||||||
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@else
|
|
||||||
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
|
||||||
@endif
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="mt-2 space-x-2 flex flex-row">
|
|
||||||
<span class="text-gray-500 dark:text-gray-300">
|
|
||||||
{{ $comment->presenter()->relativeCreatedAt() }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
@guest
|
{{-- Content --}}
|
||||||
<span data-te-toggle="tooltip" title="Please login to like the episode" class="text-gray-800 cursor-pointer dark:text-gray-200">
|
<div class="min-w-0 flex-1">
|
||||||
<i class="fa-regular fa-heart"></i> {{ $comment->likeCount() }}
|
|
||||||
</span>
|
|
||||||
@endguest
|
|
||||||
|
|
||||||
@auth
|
<div class="rounded-2xl border border-neutral-200 bg-white px-5 py-4 shadow-sm transition group-hover:border-neutral-300 dark:border-neutral-800 dark:bg-neutral-800 dark:group-hover:border-neutral-700">
|
||||||
<!-- Like Button -->
|
|
||||||
<button class="text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:click="like">
|
{{-- Header --}}
|
||||||
@if ($liked)
|
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||||
<i class="fa-solid fa-heart text-rose-600"></i> {{ $likeCount }}
|
|
||||||
@else
|
@if($comment->isDeletedByModerator())
|
||||||
<i class="fa-solid fa-heart"></i> {{ $likeCount }}
|
|
||||||
|
<span class="font-semibold text-neutral-900 dark:text-neutral-100">
|
||||||
|
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
||||||
|
Deleted ({{ $comment->user->name }})
|
||||||
|
@else
|
||||||
|
Deleted
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
<span class="font-semibold text-neutral-900 dark:text-neutral-100">
|
||||||
|
{{ $comment->user->name }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Badges --}}
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
|
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-700 dark:bg-yellow-500/10 dark:text-yellow-400">
|
||||||
|
<i class="fa-solid fa-crown mr-1"></i>
|
||||||
|
Admin
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::MODERATOR))
|
||||||
|
<span class="inline-flex items-center rounded-full bg-rose-100 px-2 py-0.5 text-xs font-medium text-rose-700 dark:bg-rose-500/10 dark:text-rose-400">
|
||||||
|
Moderator
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
|
<span class="inline-flex items-center rounded-full bg-pink-100 px-2 py-0.5 text-xs font-medium text-pink-700 dark:bg-pink-500/10 dark:text-pink-400">
|
||||||
|
<i class="fa-solid fa-heart mr-1"></i>
|
||||||
|
Supporter
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
<div class="prose prose-sm max-w-none dark:prose-invert">
|
||||||
|
|
||||||
|
@if($comment->isDeletedByModerator())
|
||||||
|
|
||||||
|
<p class="italic text-neutral-500 dark:text-neutral-400">
|
||||||
|
Deleted by moderation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
||||||
|
<div class="mt-3 rounded-xl bg-neutral-100 p-3 text-sm dark:bg-neutral-800">
|
||||||
|
{!! $comment->presenter()->markdownBody() !!}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
@if ($isEditing)
|
||||||
|
|
||||||
|
<form wire:submit.prevent="editComment" class="space-y-4">
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
rows="4"
|
||||||
|
wire:model.defer="editState.body"
|
||||||
|
class="w-full rounded-2xl border border-neutral-300 bg-white px-4 py-3 text-sm text-neutral-900 shadow-sm transition focus:border-rose-500 focus:outline-none focus:ring-4 focus:ring-rose-500/10 dark:border-neutral-700 dark:bg-neutral-950 dark:text-neutral-100 @error('editState.body') border-red-500 @enderror"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
@error('editState.body')
|
||||||
|
<p class="text-sm text-red-500">
|
||||||
|
{{ $message }}
|
||||||
|
</p>
|
||||||
|
@enderror
|
||||||
|
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="rounded-xl bg-rose-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-rose-700"
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@else
|
||||||
|
<div class="text-gray-700 dark:text-gray-200">
|
||||||
|
{!! $comment->presenter()->markdownBody() !!}
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</button>
|
|
||||||
@endauth
|
|
||||||
|
|
||||||
@auth
|
|
||||||
@if ($comment->depth() < 2)
|
|
||||||
<button wire:click="$toggle('isReplying')" type="button" class="text-gray-900 dark:text-gray-100 font-medium">
|
|
||||||
Reply
|
|
||||||
</button>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@can ('update', $comment)
|
</div>
|
||||||
<button wire:click="$toggle('isEditing')" type="button" class="text-gray-900 dark:text-gray-100 font-medium">
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
@endcan
|
|
||||||
|
|
||||||
@can ('destroy', $comment)
|
{{-- Footer --}}
|
||||||
<button x-data="{
|
<div class="mt-4 flex flex-wrap items-center gap-4 text-sm">
|
||||||
confirmCommentDeletion () {
|
|
||||||
if (window.confirm('Are you sure you want to delete this comment?')) {
|
<span class="text-neutral-500 dark:text-neutral-400">
|
||||||
@this.call('deleteComment');
|
{{ $comment->presenter()->relativeCreatedAt() }}
|
||||||
}
|
</span>
|
||||||
}
|
|
||||||
}"
|
{{-- Like --}}
|
||||||
@click="confirmCommentDeletion"
|
@guest
|
||||||
type="button"
|
<span class="flex items-center gap-1 text-neutral-500 dark:text-neutral-400">
|
||||||
class="text-gray-900 dark:text-gray-100 font-medium"
|
<i class="fa-regular fa-heart"></i>
|
||||||
>
|
{{ $comment->likeCount() }}
|
||||||
Delete
|
</span>
|
||||||
</button>
|
@endguest
|
||||||
@endcan
|
|
||||||
|
@auth
|
||||||
@can ('restore', $comment)
|
|
||||||
<button
|
<button
|
||||||
wire:click="restoreComment"
|
wire:click="like"
|
||||||
type="button"
|
class="flex items-center gap-1 text-neutral-500 transition hover:text-rose-600 dark:text-neutral-400 dark:hover:text-rose-400"
|
||||||
class="text-gray-900 dark:text-gray-100 font-medium"
|
|
||||||
>
|
>
|
||||||
Restore
|
@if ($liked)
|
||||||
|
<i class="fa-solid fa-heart text-rose-600"></i>
|
||||||
|
@else
|
||||||
|
<i class="fa-regular fa-heart"></i>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{ $likeCount }}
|
||||||
</button>
|
</button>
|
||||||
@endcan
|
@endauth
|
||||||
@endauth
|
|
||||||
|
{{-- Actions --}}
|
||||||
|
@auth
|
||||||
|
|
||||||
|
@if ($comment->depth() < 2)
|
||||||
|
<button
|
||||||
|
wire:click="$toggle('isReplying')"
|
||||||
|
class="font-medium text-neutral-600 transition hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100"
|
||||||
|
>
|
||||||
|
Reply
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@can ('update', $comment)
|
||||||
|
<button
|
||||||
|
wire:click="$toggle('isEditing')"
|
||||||
|
class="font-medium text-neutral-600 transition hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@can ('destroy', $comment)
|
||||||
|
<button
|
||||||
|
x-data="{
|
||||||
|
confirmCommentDeletion () {
|
||||||
|
if (window.confirm('Delete this comment?')) {
|
||||||
|
@this.call('deleteComment');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
@click="confirmCommentDeletion"
|
||||||
|
class="font-medium text-red-500 transition hover:text-red-600"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@can ('restore', $comment)
|
||||||
|
<button
|
||||||
|
wire:click="restoreComment"
|
||||||
|
class="font-medium text-emerald-600 transition hover:text-emerald-700"
|
||||||
|
>
|
||||||
|
Restore
|
||||||
|
</button>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@endauth
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Reply Form --}}
|
||||||
|
@if ($isReplying)
|
||||||
|
|
||||||
|
<div class="mt-4 ml-2">
|
||||||
|
<form wire:submit.prevent="postReply" class="space-y-4">
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
rows="3"
|
||||||
|
wire:model.defer="replyState.body"
|
||||||
|
placeholder="Write a reply..."
|
||||||
|
class="w-full rounded-2xl border border-neutral-300 bg-white px-4 py-3 text-sm text-neutral-900 shadow-sm transition focus:border-rose-500 focus:outline-none focus:ring-4 focus:ring-rose-500/10 dark:border-neutral-700 dark:bg-neutral-950 dark:text-neutral-100 @error('replyState.body') border-red-500 @enderror"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
@error('replyState.body')
|
||||||
|
<p class="text-sm text-red-500">
|
||||||
|
{{ $message }}
|
||||||
|
</p>
|
||||||
|
@enderror
|
||||||
|
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="rounded-xl bg-rose-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-rose-700"
|
||||||
|
>
|
||||||
|
Reply
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Replies --}}
|
||||||
|
@if ($comment->children->count())
|
||||||
|
<div class="mt-2 space-y-2 border-l-2 border-neutral-200 pl-6 dark:border-neutral-700">
|
||||||
|
@foreach ($comment->children as $child)
|
||||||
|
<livewire:comment :comment="$child" :key="$child->id"/>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ml-14 mt-6">
|
|
||||||
@if ($isReplying)
|
|
||||||
<form wire:submit.prevent="postReply" class="my-4">
|
|
||||||
<div>
|
|
||||||
<label for="comment" class="sr-only">Reply body</label>
|
|
||||||
<textarea id="comment" name="comment" rows="3"
|
|
||||||
class="bg-white dark:bg-neutral-700 shadow-sm block w-full focus:ring-rose-500 focus:border-rose-500 border-gray-300 dark:border-gray-400/40 text-gray-900 dark:text-gray-200 placeholder:text-gray-400 rounded-md
|
|
||||||
@error('replyState.body') border-red-500 @enderror"
|
|
||||||
placeholder="Write something" wire:model.defer="replyState.body"></textarea>
|
|
||||||
@error('replyState.body')
|
|
||||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
<div class="mt-3 flex items-center justify-between">
|
|
||||||
<button type="submit"
|
|
||||||
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
|
||||||
Comment
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@foreach ($comment->children as $child)
|
|
||||||
<livewire:comment :comment="$child" :key="$child->id"/>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
@@ -1,57 +1,93 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="bg-white dark:bg-neutral-800 shadow sm:rounded-xl sm:overflow-hidden">
|
<div id="comments" class="overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-sm dark:border-neutral-800 dark:bg-neutral-900">
|
||||||
<div class="px-4 py-5 sm:px-6">
|
|
||||||
<h2 class="leading-normal font-bold text-lg text-gray-900 dark:text-gray-200">Comments</h2>
|
{{-- Header --}}
|
||||||
|
<div class="border-b border-neutral-200 px-6 py-5 dark:border-neutral-800">
|
||||||
|
<h2 class="text-xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-100">
|
||||||
|
Comments
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<!-- Comment Input -->
|
{{-- Comment Form --}}
|
||||||
<div class="bg-gray-50 dark:bg-neutral-800 px-4 py-6 sm:px-6 border-t border-b dark:border-neutral-950 border-neutral-200">
|
<div class="border-b border-neutral-200 bg-neutral-50/80 px-6 py-6 dark:border-neutral-800 dark:bg-neutral-950/40">
|
||||||
@auth
|
@auth
|
||||||
<div class="flex">
|
<div class="flex gap-4">
|
||||||
<div class="flex-shrink-0 mr-4">
|
<img
|
||||||
<img class="h-10 w-10 rounded-full" src="{{ auth()->user()->getAvatar() }}" alt="{{ auth()->user()->name }}">
|
class="h-11 w-11 rounded-full object-cover ring-2 ring-white dark:ring-neutral-800"
|
||||||
</div>
|
src="{{ auth()->user()->getAvatar() }}"
|
||||||
<div class="min-w-0 flex-1">
|
alt="{{ auth()->user()->name }}"
|
||||||
<form wire:submit.prevent="postComment">
|
>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<form wire:submit.prevent="postComment" class="space-y-4">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="comment" class="sr-only">Comment body</label>
|
<label for="comment" class="sr-only">
|
||||||
<textarea id="comment" name="comment" rows="3"
|
Comment body
|
||||||
class="peer block min-h-[auto] w-full border-1 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear dark:placeholder:text-neutral-200 border-gray-300 dark:border-neutral-950 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm
|
</label>
|
||||||
@error('newCommentState.body') border-red-500 @enderror"
|
|
||||||
placeholder="Write something" wire:model.defer="newCommentState.body"></textarea>
|
<textarea
|
||||||
|
id="comment"
|
||||||
|
rows="4"
|
||||||
|
wire:model.defer="newCommentState.body"
|
||||||
|
placeholder="Write a comment..."
|
||||||
|
class="w-full rounded-2xl border border-neutral-300 bg-white px-4 py-3 text-sm text-neutral-900 placeholder:text-neutral-400 shadow-sm transition focus:border-rose-500 focus:outline-none focus:ring-4 focus:ring-rose-500/10 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100 dark:placeholder:text-neutral-500 dark:focus:border-rose-500 @error('newCommentState.body') border-red-500 @enderror"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
@error('newCommentState.body')
|
@error('newCommentState.body')
|
||||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
<p class="mt-2 text-sm text-red-500">
|
||||||
|
{{ $message }}
|
||||||
|
</p>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 flex items-center justify-between">
|
|
||||||
<button type="submit"
|
<div class="flex justify-end">
|
||||||
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
<button
|
||||||
Comment
|
type="submit"
|
||||||
|
class="inline-flex items-center rounded-xl bg-rose-600 px-5 py-2.5 text-sm font-medium text-white transition hover:bg-rose-700 focus:outline-none focus:ring-4 focus:ring-rose-500/30"
|
||||||
|
>
|
||||||
|
Post Comment
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
@guest
|
@guest
|
||||||
<p class="text-gray-900 dark:text-gray-200">Log in to comment.</p>
|
<div class="rounded-xl border border-dashed border-neutral-300 p-6 text-center dark:border-neutral-700">
|
||||||
@endguest
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
</div>
|
Log in to join the discussion.
|
||||||
|
</p>
|
||||||
<!-- Comments -->
|
|
||||||
<div class="px-4 py-6 sm:px-6">
|
|
||||||
<div class="space-y-8">
|
|
||||||
@if ($comments->isNotEmpty())
|
|
||||||
@foreach($comments as $comment)
|
|
||||||
<livewire:comment :comment="$comment" :key="$comment->id"/>
|
|
||||||
@endforeach
|
|
||||||
{{ $comments->links('pagination::tailwind') }}
|
|
||||||
@else
|
|
||||||
<p class="text-gray-900 dark:text-gray-200">No comments yet.</p>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endguest
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Comments --}}
|
||||||
|
<div class="px-6 py-6">
|
||||||
|
@if ($comments->isNotEmpty())
|
||||||
|
|
||||||
|
<div class="space-y-6">
|
||||||
|
@foreach($comments as $comment)
|
||||||
|
<livewire:comment :comment="$comment" :key="$comment->id"/>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8">
|
||||||
|
{{ $comments->links('pagination::tailwind') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
<div class="rounded-2xl border border-dashed border-neutral-300 py-12 text-center dark:border-neutral-700">
|
||||||
|
<p class="text-neutral-500 dark:text-neutral-400">
|
||||||
|
No comments yet.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -8,9 +8,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col text-center w-full">
|
<div class="flex flex-col text-center w-full">
|
||||||
@if($fillNumbers)
|
@if($fillNumbers)
|
||||||
<p class="text-lg">Episode {{ str_pad($episodeNumber, 2, '0', STR_PAD_LEFT) }}</p>
|
@if($version)
|
||||||
|
<p class="text-lg">Episode {{ str_pad($episodeNumber, 2, '0', STR_PAD_LEFT) }} ({{ $version }})</p>
|
||||||
|
@else
|
||||||
|
<p class="text-lg">Episode {{ str_pad($episodeNumber, 2, '0', STR_PAD_LEFT) }}</p>
|
||||||
|
@endif
|
||||||
@else
|
@else
|
||||||
<p class="text-lg">Episode {{ $episodeNumber }}</p>
|
@if($version)
|
||||||
|
<p class="text-lg">Episode {{ $episodeNumber }} ({{ $version }})</p>
|
||||||
|
@else
|
||||||
|
<p class="text-lg">Episode {{ $episodeNumber }}</p>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<p class="text-xs">{{ $fileExtension }} MKV {{ $fileSize ?? '' }}</p>
|
<p class="text-xs">{{ $fileExtension }} MKV {{ $fileSize ?? '' }}</p>
|
||||||
<p class="text-xs" id="count-{{ $downloadId }}">Downloaded {{ $downloadCount }} times</p>
|
<p class="text-xs" id="count-{{ $downloadId }}">Downloaded {{ $downloadCount }} times</p>
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
<div>
|
<div>
|
||||||
@if (Auth::check())
|
<button class="inline-flex font-bold items-center gap-2 rounded-xl bg-gray-100 px-4 py-2 text-gray-700 dark:bg-white/5 dark:text-gray-200" wire:click="like" wire:poll.90000ms="update">
|
||||||
<div class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:click="like" wire:poll.90000ms="update">
|
|
||||||
@else
|
|
||||||
<div data-te-toggle="tooltip" title="Please login to like the episode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:poll.60000ms="update">
|
|
||||||
@endif
|
|
||||||
@if ($liked)
|
@if ($liked)
|
||||||
<i class="fa-solid fa-heart pr-[4px] text-rose-600"></i> {{ $likeCount }}
|
<i class="fa-solid fa-heart pr-[4px] text-rose-600"></i> {{ $likeCount }}
|
||||||
@else
|
@else
|
||||||
<i class="fa-regular fa-heart pr-[4px]"></i> {{ $likeCount }}
|
<i class="fa-regular fa-heart pr-[4px]"></i> {{ $likeCount }}
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<div>
|
|
||||||
<a class="text-xl text-gray-800 dark:text-gray-200 leading-tight whitespace-nowrap" wire:poll.90000ms="update">
|
|
||||||
<i class="fa-regular fa-eye pr-0.5"></i> {{ $viewCount }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4">
|
||||||
@foreach ($episodes as $episode)
|
@foreach ($episodes as $episode)
|
||||||
<div class="mt-2 mb-6 ml-4">
|
<div class="mt-2 mb-6 ml-4">
|
||||||
<x-episode-cover :episode="$episode->episode" view="thumbnail" />
|
<x-episode-cover :episode="$episode->episode" view="thumbnail" />
|
||||||
@@ -25,4 +25,4 @@
|
|||||||
</ol>
|
</ol>
|
||||||
{{ $watched->links('pagination::tailwind') }}
|
{{ $watched->links('pagination::tailwind') }}
|
||||||
</div>
|
</div>
|
||||||
</div
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
@php
|
@php
|
||||||
$download = $hdl->getDownloadByType('FHDi');
|
$download = $hdl->getDownloadByType('FHDi');
|
||||||
$downloadURL = $dldomains[array_rand($dldomains)].'/'.$download->url;
|
$downloadURL = $dldomains[array_rand($dldomains)].'/'.$download->url;
|
||||||
|
$version = str_contains($download->url, 'v2') ? 'v2' : '';
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<livewire:download-button
|
<livewire:download-button
|
||||||
@@ -14,5 +15,6 @@
|
|||||||
:download-count="$download->count"
|
:download-count="$download->count"
|
||||||
:episode-number="$hdl->episode"
|
:episode-number="$hdl->episode"
|
||||||
:fill-numbers="$fillNumbers"
|
:fill-numbers="$fillNumbers"
|
||||||
:file-size="$download->getFileSize()">
|
:file-size="$download->getFileSize()"
|
||||||
|
:version="$version">
|
||||||
@endif
|
@endif
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
$now = \Illuminate\Support\Carbon::now();
|
$now = \Illuminate\Support\Carbon::now();
|
||||||
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
|
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
|
||||||
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
|
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
|
||||||
|
$version = str_contains($download->url, 'v2') ? 'v2' : '';
|
||||||
|
|
||||||
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
|
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
|
||||||
@endphp
|
@endphp
|
||||||
@@ -20,5 +21,6 @@
|
|||||||
:download-count="$download->count"
|
:download-count="$download->count"
|
||||||
:episode-number="$hdl->episode"
|
:episode-number="$hdl->episode"
|
||||||
:fill-numbers="$fillNumbers"
|
:fill-numbers="$fillNumbers"
|
||||||
:file-size="$download->getFileSize()">
|
:file-size="$download->getFileSize()"
|
||||||
|
:version="$version">
|
||||||
@endif
|
@endif
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
$now = \Illuminate\Support\Carbon::now();
|
$now = \Illuminate\Support\Carbon::now();
|
||||||
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
|
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
|
||||||
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
|
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
|
||||||
|
$version = str_contains($download->url, 'v2') ? 'v2' : '';
|
||||||
|
|
||||||
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
|
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
|
||||||
@endphp
|
@endphp
|
||||||
@@ -20,5 +21,6 @@
|
|||||||
:download-count="$download->count"
|
:download-count="$download->count"
|
||||||
:episode-number="$hdl->episode"
|
:episode-number="$hdl->episode"
|
||||||
:fill-numbers="$fillNumbers"
|
:fill-numbers="$fillNumbers"
|
||||||
:file-size="$download->getFileSize()">
|
:file-size="$download->getFileSize()"
|
||||||
|
:version="$version">
|
||||||
@endif
|
@endif
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<x-app-layout>
|
<x-app-layout>
|
||||||
@include('partials.background')
|
@include('partials.background')
|
||||||
<div
|
<div class="relative max-w-[120rem] mx-auto sm:px-6 lg:px-8 space-y-6 pt-10 flex flex-row justify-center md:justify-normal">
|
||||||
class="relative max-w-[120rem] mx-auto sm:px-6 lg:px-8 space-y-6 pt-20 mt-[65px] flex flex-row justify-center md:justify-normal">
|
|
||||||
<div class="grid md:grid-flow-col gap-4 xl:w-5/6 flex-row">
|
<div class="grid md:grid-flow-col gap-4 xl:w-5/6 flex-row">
|
||||||
@include('profile.partials.sidebar')
|
@include('profile.partials.sidebar')
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="pt-6">
|
<div class="pt-6">
|
||||||
<div class="flex flex-col lg:flex-row justify-center">
|
<div class="flex flex-col lg:flex-row justify-center">
|
||||||
<div class="pt-2 sm:px-2 lg:px-4 space-y-6 max-w-[100%] xl:max-w-[70%] 2xl:max-w-[60%] z-10">
|
<div class="pt-2 sm:px-2 lg:px-4 space-y-6 max-w-[100%] xl:max-w-[70%] 2xl:max-w-[60%] z-10">
|
||||||
@include('series.partials.info')
|
@include('stream.partials.info', ['streamPage' => false])
|
||||||
|
|
||||||
@include('series.partials.episodes')
|
@include('series.partials.episodes')
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<div class="bg-transparent rounded-lg overflow-hidden bg-white dark:bg-neutral-800 p-5">
|
<div class="overflow-hidden rounded-2xl border border-gray-200/70 bg-white/90 shadow-sm backdrop-blur-sm transition-colors dark:border-white/10 dark:bg-neutral-900/80">
|
||||||
<a class="leading-normal font-bold text-lg text-rose-600">
|
<div class="p-5 md:p-7">
|
||||||
{{ __('home.episodes') }} ({{ $hentai->episodes->count() }})
|
<a class="text-lg font-bold text-gray-900 dark:text-white">
|
||||||
</a>
|
{{ __('home.episodes') }} ({{ $hentai->episodes->count() }})
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- Episode List -->
|
<!-- Episode List -->
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-2">
|
<div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-2 mt-4">
|
||||||
@foreach ($hentai->episodes as $episode)
|
@foreach ($hentai->episodes as $episode)
|
||||||
<x-episode-cover :episode="$episode" view="thumbnail" />
|
<x-episode-cover :episode="$episode" view="thumbnail" />
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
<div class="bg-transparent rounded-lg overflow-hidden bg-white dark:bg-neutral-800 p-5">
|
|
||||||
<!-- Cover -->
|
|
||||||
<div class="w-[100px] md:w-[150px] mr-4 float-left">
|
|
||||||
<img alt="{{ $episode->title }}" loading="lazy" width="150"
|
|
||||||
class="block relative rounded-lg object-cover object-center aspect-[11/16] z-20"
|
|
||||||
src="{{ $episode->cover_url }}"></img>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Info -->
|
|
||||||
<div class="relative">
|
|
||||||
<!-- Title -->
|
|
||||||
<h1 class="text-3xl font-bold text-rose-600">
|
|
||||||
{{ $episode->title }} ({{ $episode->title_jpn }})
|
|
||||||
</h1>
|
|
||||||
<div>
|
|
||||||
<a data-te-toggle="tooltip"
|
|
||||||
title="Released {{ \Carbon\Carbon::parse($episode->release_date)->diffForHumans(['parts' => 2]) }}"
|
|
||||||
class="text-l text-gray-800 dark:text-white leading-tight pl-1">
|
|
||||||
<i class="fa-regular fa-calendar"></i> {{ $episode->release_date }}
|
|
||||||
|
|
|
||||||
</a>
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'studios[0]' => $episode->studio->slug]) }}"
|
|
||||||
class="text-l text-gray-800 dark:text-white leading-tight hover:underline hover:underline-offset-4">
|
|
||||||
{{ $episode->studio->name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<hr class="border-gray-400/40 mt-2 mb-2">
|
|
||||||
<p class="leading-normal font-bold text-lg text-rose-600">
|
|
||||||
{{ __('stream.description') }}
|
|
||||||
</p>
|
|
||||||
<p class="text-gray-800 dark:text-gray-200 leading-tight min-h-[50%]">
|
|
||||||
{{ $hentai->description }}
|
|
||||||
</p>
|
|
||||||
<hr class="border-gray-400/40 mt-2 mb-1">
|
|
||||||
<ul class="list-none text-center" style="overflow: hidden;">
|
|
||||||
<a class="text-gray-400">
|
|
||||||
|
|
|
||||||
</a>
|
|
||||||
@foreach ($episode->tags->sortBy('slug') as $tag)
|
|
||||||
<li class="inline-block p-1">
|
|
||||||
@if ($tag->slug == 'uncensored' || $tag->slug == 'vanilla' || $tag->slug == '4k')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="relative block items-center px-2 py-2 mt-1 dark:focus:ring-offset-gray-800 border border-transparent rounded-md font-semibold text-xs text-green-500 dark:hover:text-white hover:text-white uppercase tracking-widest hover:bg-green-700 focus:bg-green-700 active:bg-green-900 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@elseif($tag->slug == 'censored')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="relative block items-center px-2 py-2 mt-1 dark:focus:ring-offset-gray-800 border border-transparent rounded-md font-semibold text-xs text-yellow-600 dark:hover:text-white hover:text-white uppercase tracking-widest hover:bg-yellow-700 focus:bg-yellow-700 active:bg-yellow-900 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@elseif($tag->slug == 'gore' || $tag->slug == 'horror' || $tag->slug == 'scat' || $tag->slug == 'ntr' || $tag->slug == 'rape')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="relative block items-center px-2 py-2 mt-1 dark:focus:ring-offset-gray-800 border border-transparent rounded-md font-semibold text-xs text-red-600 dark:hover:text-white hover:text-white uppercase tracking-widest hover:bg-red-700 focus:bg-red-700 active:bg-red-900 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
|
||||||
<i class="fa-solid fa-triangle-exclamation"></i> {{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="relative block items-center px-2 py-2 mt-1 dark:focus:ring-offset-gray-800 border border-transparent rounded-md font-semibold text-xs dark:text-white hover:text-white uppercase tracking-widest hover:bg-rose-700 focus:bg-rose-700 active:bg-rose-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</li>
|
|
||||||
<a class="text-gray-400">
|
|
||||||
|
|
|
||||||
</a>
|
|
||||||
@endforeach
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@include('admin.stream')
|
|
||||||
<!-- Infos -->
|
<!-- Infos -->
|
||||||
@include('stream.partials.info')
|
@include('stream.partials.info')
|
||||||
<!-- Comments -->
|
<!-- Comments -->
|
||||||
@@ -38,18 +37,18 @@
|
|||||||
@include('modals.add-to-playlist')
|
@include('modals.add-to-playlist')
|
||||||
@include('modals.share')
|
@include('modals.share')
|
||||||
|
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
|
@if (auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR))
|
|
||||||
@include('admin.modals.edit-episode')
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
|
||||||
@include('admin.modals.upload-episode')
|
@include('admin.modals.upload-episode')
|
||||||
@include('admin.modals.add-subtitles')
|
@include('admin.modals.add-subtitles')
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if (auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || auth()->user()->hasRole(\App\Enums\UserRole::MODERATOR))
|
||||||
|
@include('admin.modals.edit-episode')
|
||||||
|
@endif
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
<!-- Player Script -->
|
<!-- Player Script -->
|
||||||
@vite(['resources/js/player.js'])
|
@vite(['resources/js/player.js'])
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -1,58 +1,66 @@
|
|||||||
<div class="bg-transparent rounded-lg">
|
<div>
|
||||||
<div class="px-1 sm:px-2">
|
|
||||||
<p class="leading-normal font-bold text-lg text-gray-900 dark:text-gray-200">
|
<div class="mb-5 flex items-center justify-between">
|
||||||
|
<h2 class="text-xl font-bold text-gray-900 dark:text-white">
|
||||||
{{ __('stream.gallery') }}
|
{{ __('stream.gallery') }}
|
||||||
</p>
|
</h2>
|
||||||
|
|
||||||
|
@if ($gallery->count() > 5)
|
||||||
|
<button
|
||||||
|
id="galleryToggle"
|
||||||
|
class="text-sm font-semibold text-gray-900 dark:text-white transition">
|
||||||
|
{{ __('home.show-more') }}
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if ($gallery->count() > 5)
|
|
||||||
<div class="grid grid-rows-1 w-30 text-left">
|
<div
|
||||||
<ul data-te-lightbox-init class="list-none text-center" style="overflow: hidden;">
|
data-te-lightbox-init
|
||||||
@php $counter = 0; @endphp
|
class="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
|
||||||
@foreach($gallery as $image)
|
|
||||||
@php $counter++; @endphp
|
@foreach($gallery as $index => $image)
|
||||||
<li class="inline-block m-1 w-[45%] sm:w-[45%] md:w-[20%] xl:w-[18%]">
|
|
||||||
@if ($counter > 5)
|
<div class="{{ $index >= 5 ? 'hidden extra-gallery-item' : '' }}">
|
||||||
<div class="!visible hidden" id="collapseGallery" data-te-collapse-item>
|
|
||||||
@else
|
<div
|
||||||
<div>
|
class="group overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm transition hover:-translate-y-1 hover:shadow-xl dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
@endif
|
|
||||||
<div class="py-2 mt-2">
|
<img
|
||||||
<img onClick="(function(){player.play(); player.pause(); })();" src="{{ $image->thumbnail_url }}" data-te-img="{{ $image->image_url }}" alt="{{ $episode->title }} - {{ $episode->episode }} - Screenshot {{ $counter }}" class="relative block items-center h-full w-full rounded-lg tracking-widest transition ease-in-out duration-150 cursor-zoom-in shadow-sm data-[te-lightbox-disabled]:cursor-auto" />
|
onClick="(function(){player.play(); player.pause(); })();"
|
||||||
</div>
|
src="{{ $image->thumbnail_url }}"
|
||||||
</div>
|
data-te-img="{{ $image->image_url }}"
|
||||||
</li>
|
alt="{{ $episode->title }} - Screenshot {{ $index + 1 }}"
|
||||||
@endforeach
|
class="aspect-video w-full cursor-zoom-in object-cover transition duration-300 group-hover:scale-105"
|
||||||
</ul>
|
>
|
||||||
</div>
|
|
||||||
<div class="grid grid-rows-1 w-30 text-center">
|
|
||||||
<a id="galleryShowMore" data-te-collapse-init data-te-ripple-init data-te-ripple-color="light" href="#collapseGallery" role="button" aria-expanded="false" aria-controls="collapseGallery" class="text-sm float-right cursor-pointer text-rose-600">{{ __('home.show-more') }}</a>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
var state = 0;
|
|
||||||
function toggleGallery() {
|
|
||||||
if (state == 0) {
|
|
||||||
document.getElementById('galleryShowMore').innerText = "{{ __('stream.show-less') }}";
|
|
||||||
state = 1;
|
|
||||||
} else {
|
|
||||||
document.getElementById('galleryShowMore').innerText = "{{ __('home.show-more') }}";
|
|
||||||
state = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById('galleryShowMore').addEventListener('click', toggleGallery);
|
|
||||||
</script>
|
|
||||||
@else
|
|
||||||
<div class="grid grid-rows-1 w-30 text-left">
|
|
||||||
<ul data-te-lightbox-init class="list-none text-center" style="overflow: hidden;">
|
|
||||||
@php $counter = 0; @endphp
|
|
||||||
@foreach($gallery as $image)
|
|
||||||
@php $counter++; @endphp
|
|
||||||
<li class="inline-block m-1 w-[45%] sm:w-[45%] md:w-[20%] xl:w-[18%]">
|
|
||||||
<div class="py-2 mt-2">
|
|
||||||
<img onClick="(function(){player.play(); player.pause(); })();" src="{{ $image->thumbnail_url }}" data-te-img="{{ $image->image_url }}" alt="{{ $episode->title }} - {{ $episode->episode }} - Screenshot {{ $counter }}" class="relative block items-center h-full w-full rounded-lg tracking-widest transition ease-in-out duration-150 cursor-zoom-in shadow-sm data-[te-lightbox-disabled]:cursor-auto" />
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
|
||||||
@endforeach
|
</div>
|
||||||
</ul>
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if ($gallery->count() > 5)
|
||||||
|
<script>
|
||||||
|
const toggleBtn = document.getElementById('galleryToggle');
|
||||||
|
const hiddenItems = document.querySelectorAll('.extra-gallery-item');
|
||||||
|
|
||||||
|
let expanded = false;
|
||||||
|
|
||||||
|
toggleBtn.addEventListener('click', () => {
|
||||||
|
|
||||||
|
expanded = !expanded;
|
||||||
|
|
||||||
|
hiddenItems.forEach(item => {
|
||||||
|
item.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
toggleBtn.innerText = expanded
|
||||||
|
? "{{ __('stream.show-less') }}"
|
||||||
|
: "{{ __('home.show-more') }}";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
@@ -1,167 +1,246 @@
|
|||||||
<div class="overflow-hidden p-5 bg-transparent bg-white rounded-xl dark:bg-neutral-800">
|
@props([
|
||||||
<!-- Cover -->
|
'streamPage' => true,
|
||||||
<div class="w-[100px] md:w-[150px] mr-4 float-left hidden md:block">
|
])
|
||||||
<img alt="{{ $episode->title }}" loading="lazy" width="150"
|
|
||||||
class="block relative rounded-xl object-cover object-center aspect-[11/16] z-20"
|
|
||||||
src="{{ $episode->cover_url }}"></img>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Info -->
|
<div
|
||||||
<div class="relative">
|
class="overflow-hidden rounded-2xl border border-gray-200/70 bg-white/90 shadow-sm backdrop-blur-sm transition-colors dark:border-white/10 dark:bg-neutral-900/80">
|
||||||
|
|
||||||
|
<div class="p-5 md:p-7">
|
||||||
|
@if($streamPage)
|
||||||
<input id="e_id" type="hidden" value="{{ $episode->id }}" />
|
<input id="e_id" type="hidden" value="{{ $episode->id }}" />
|
||||||
<div class="flex flex-col justify-between xl:flex-row">
|
@endif
|
||||||
<div>
|
|
||||||
<!-- Title -->
|
|
||||||
<h1 class="text-3xl font-bold text-rose-600">
|
|
||||||
<a class="text-rose-600 break-words hover:underline hover:underline-offset-4"
|
|
||||||
href="{{ route('hentai.index', ['title' => $episode->hentai->slug]) }}">{{ $episode->title }}</a>
|
|
||||||
- {{ $episode->episode }}
|
|
||||||
</h1>
|
|
||||||
<div>
|
|
||||||
<h2 class="inline leading-tight text-gray-800 dark:text-white">{{ $episode->title_jpn }}</h2>
|
|
||||||
|
|
||||||
<a data-te-toggle="tooltip"
|
<div class="flex flex-col gap-6 lg:flex-row">
|
||||||
title="Uploaded {{ $episode->created_at->diffForHumans(['parts' => 2]) }}"
|
<!-- Cover -->
|
||||||
class="pl-1 leading-tight text-gray-800 text-l dark:text-white">
|
<div class="hidden shrink-0 md:block">
|
||||||
<p class="inline">
|
<img
|
||||||
| <i class="fa-solid fa-upload"></i> {{ $episode->created_at->format('Y-m-d') }} |
|
alt="{{ $episode->title }}"
|
||||||
</p>
|
loading="lazy"
|
||||||
</a>
|
width="180"
|
||||||
<a data-te-toggle="tooltip"
|
src="{{ $episode->cover_url }}"
|
||||||
title="Released {{ \Carbon\Carbon::parse($episode->release_date)->diffForHumans(['parts' => 2]) }}"
|
class="aspect-[11/16] w-[140px] rounded-2xl object-cover shadow-lg ring-1 ring-black/5 dark:ring-white/10" />
|
||||||
class="pl-1 leading-tight text-gray-800 text-l dark:text-white">
|
|
||||||
<p class="inline">
|
|
||||||
<i class="fa-regular fa-calendar"></i> {{ $episode->release_date }}
|
|
||||||
|
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'studios[0]' => $episode->studio->slug]) }}"
|
|
||||||
class="leading-tight text-gray-800 text-l dark:text-white hover:underline hover:underline-offset-4">
|
|
||||||
{{ $episode->studio->name }}
|
|
||||||
</a>
|
|
||||||
<a id="av1-unsupported" data-te-toggle="tooltip"
|
|
||||||
title="For 1080p and 4k streams we are using the new AV1 codec. Edge users on Windows have to install the AV1 extension pack from the Microsoft Store."
|
|
||||||
class="hidden leading-tight text-red-800 cursor-pointer text-l dark:text-red-500">
|
|
||||||
AV1 Unsupported
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<!-- Main Content -->
|
||||||
<!-- View Count and Misc Buttons -->
|
<div class="flex-1">
|
||||||
<div class="float-right">
|
<div class="flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between">
|
||||||
<div class="grid">
|
|
||||||
<div class="flex gap-x-4">
|
<!-- Title + Metadata -->
|
||||||
|
<div class="min-w-0">
|
||||||
|
<h1
|
||||||
|
class="break-words text-2xl font-black tracking-tight text-gray-900 dark:text-white md:text-4xl">
|
||||||
|
@if ($streamPage)
|
||||||
|
<a
|
||||||
|
href="{{ route('hentai.index', ['title' => $episode->hentai->slug]) }}"
|
||||||
|
class="bg-gradient-to-r from-rose-500 to-pink-500 bg-clip-text text-transparent transition hover:opacity-80">
|
||||||
|
{{ "$episode->title - $episode->episode" }}
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<span
|
||||||
|
class="bg-gradient-to-r from-rose-500 to-pink-500 bg-clip-text text-transparent transition">
|
||||||
|
{{ $episode->title }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400 md:text-base">
|
||||||
|
{{ $episode->title_jpn }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Meta Pills -->
|
||||||
|
<div class="mt-4 flex flex-wrap items-center gap-2 text-sm">
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="inline-flex items-center gap-2 rounded-full bg-gray-100 px-3 py-1 text-gray-700 dark:bg-white/5 dark:text-gray-300">
|
||||||
|
<i class="fa-solid fa-upload text-xs"></i>
|
||||||
|
{{ $episode->created_at->format('Y-m-d') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="inline-flex items-center gap-2 rounded-full bg-gray-100 px-3 py-1 text-gray-700 dark:bg-white/5 dark:text-gray-300">
|
||||||
|
<i class="fa-regular fa-calendar text-xs"></i>
|
||||||
|
{{ $episode->release_date }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'studios[0]' => $episode->studio->slug]) }}"
|
||||||
|
class="inline-flex items-center rounded-full bg-rose-100 px-3 py-1 font-medium text-rose-700 transition hover:bg-rose-200 dark:bg-rose-500/10 dark:text-rose-300 dark:hover:bg-rose-500/20">
|
||||||
|
{{ $episode->studio->name }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
id="av1-unsupported"
|
||||||
|
data-te-toggle="tooltip"
|
||||||
|
title="For 1080p and 4k streams we are using the new AV1 codec."
|
||||||
|
class="hidden rounded-full bg-red-100 px-3 py-1 text-sm font-medium text-red-700 dark:bg-red-500/10 dark:text-red-400">
|
||||||
|
AV1 Unsupported
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($streamPage)
|
||||||
|
<!-- Stats + Actions -->
|
||||||
|
<div class="flex flex-col gap-3 xl:items-end min-w-[330px]">
|
||||||
|
|
||||||
|
<!-- Stats -->
|
||||||
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="inline-flex font-bold items-center gap-2 rounded-xl bg-gray-100 px-4 py-2 text-gray-700 dark:bg-white/5 dark:text-gray-200">
|
||||||
|
<i class="fa-regular fa-eye"></i>
|
||||||
|
{{ $episode->viewCountFormatted() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@livewire('view-count', ['episode' => $episode])
|
|
||||||
@livewire('like-button', ['episode' => $episode])
|
@livewire('like-button', ['episode' => $episode])
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
@guest
|
@guest
|
||||||
<div>
|
<div
|
||||||
<a class="text-xl leading-tight text-gray-800 whitespace-nowrap dark:text-gray-200">
|
data-te-toggle="tooltip"
|
||||||
<i class="pr-0.5 fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }}
|
title="Please login to like the episode"
|
||||||
</a>
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl bg-gray-100 px-4 py-2 text-gray-700 dark:bg-white/5 dark:text-gray-200">
|
||||||
</div>
|
<i class="fa-regular fa-heart"></i>
|
||||||
<div data-te-toggle="tooltip" title="Please login to like the episode"
|
{{ $episode->likeCount() }}
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap cursor-pointer dark:text-gray-200">
|
|
||||||
<i class="fa-regular fa-heart pr-[4px]"></i> {{ $episode->likeCount() }}
|
|
||||||
</div>
|
</div>
|
||||||
@endguest
|
@endguest
|
||||||
|
|
||||||
@php $commentcount = $episode->commentCount(); @endphp
|
@php $commentcount = $episode->commentCount(); @endphp
|
||||||
@if ($commentcount > 0)
|
|
||||||
<a href="#comments"
|
<a
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap dark:text-gray-200">
|
href="#comments"
|
||||||
<i class="pr-0.5 fa-regular fa-comment"></i> {{ $commentcount }}
|
class="inline-flex items-center gap-2 rounded-xl bg-gray-100 px-4 py-2 text-gray-700 transition hover:bg-gray-200 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
</a>
|
<i class="fa-regular fa-comment"></i>
|
||||||
@else
|
{{ $commentcount }}
|
||||||
<a href="#comments" data-te-toggle="tooltip" title="Be the first one to comment!"
|
</a>
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap dark:text-gray-200">
|
|
||||||
<i class="pr-0.5 fa-regular fa-comment"></i> {{ $commentcount }}
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<!-- Action Buttons -->
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
|
||||||
@if(!$episode->dmca_takedown)
|
@if(!$episode->dmca_takedown)
|
||||||
<a data-te-toggle="modal" data-te-target="#modalDownload" id="reloadCaptchaModal"
|
<a
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap cursor-pointer dark:text-gray-200">
|
data-te-toggle="modal"
|
||||||
<i class="fa-solid fa-download pr-[4px]"></i> {{ __('stream.download') }}
|
data-te-target="#modalDownload"
|
||||||
|
id="reloadCaptchaModal"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl bg-rose-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-rose-700">
|
||||||
|
<i class="fa-solid fa-download"></i>
|
||||||
|
{{ __('stream.download') }}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<a
|
||||||
|
data-te-toggle="modal"
|
||||||
|
data-te-target="#modalShare"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-100 dark:border-white/10 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
|
<i class="fa-solid fa-share"></i>
|
||||||
|
{{ __('stream.share') }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
@auth
|
||||||
|
<a
|
||||||
|
data-te-toggle="modal"
|
||||||
|
data-te-target="#modalAddToPlaylist"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-100 dark:border-white/10 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
|
<i class="fa-solid fa-square-plus"></i>
|
||||||
|
{{ __('playlist.playlist') }}
|
||||||
|
</a>
|
||||||
|
@endauth
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(auth()->check() && (auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || auth()->user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
||||||
|
<div class="flex flex-wrap flex-row-reverse gap-2">
|
||||||
|
@if(auth()->user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
|
<a
|
||||||
|
data-te-toggle="modal"
|
||||||
|
data-te-target="#modalAddSubtitles"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-100 dark:border-white/10 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
|
<i class="fa-solid fa-plus"></i>
|
||||||
|
Add Subtitles
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
data-te-toggle="modal"
|
||||||
|
data-te-target="#modalUploadEpisode"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-100 dark:border-white/10 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
|
<i class="fa-solid fa-plus"></i>
|
||||||
|
Add Episode
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<a data-te-toggle="modal" data-te-target="#modalShare"
|
<a
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap cursor-pointer dark:text-gray-200">
|
data-te-toggle="modal"
|
||||||
<i class="fa-solid fa-share pr-[4px]"></i> {{ __('stream.share') }}
|
data-te-target="#modalEditEpisode"
|
||||||
|
class="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-100 dark:border-white/10 dark:bg-white/5 dark:text-gray-200 dark:hover:bg-white/10">
|
||||||
|
<i class="fa-solid fa-pen"></i>
|
||||||
|
Edit Episode
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
@auth
|
<!-- Description -->
|
||||||
<div>
|
<div class="mt-8 border-t border-gray-200 pt-6 dark:border-white/10">
|
||||||
<a data-te-toggle="modal" data-te-target="#modalAddToPlaylist"
|
<h2 class="mb-3 text-lg font-bold text-gray-900 dark:text-white">
|
||||||
class="text-xl leading-tight text-gray-800 whitespace-nowrap cursor-pointer dark:text-gray-200">
|
{{ __('stream.description') }}
|
||||||
<i class="fa-solid fa-square-plus pr-[6px]"></i> {{ __('playlist.playlist') }}
|
</h2>
|
||||||
</a>
|
|
||||||
</div>
|
<p class="leading-relaxed text-gray-700 dark:text-gray-300">
|
||||||
@endauth
|
{{ $episode->description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Genres -->
|
||||||
|
<div class="mt-8 border-t border-gray-200 pt-6 dark:border-white/10">
|
||||||
|
<h2 class="mb-4 text-lg font-bold text-gray-900 dark:text-white">
|
||||||
|
{{ __('stream.genres') }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
|
||||||
|
@foreach ($episode->tags->sortBy('slug') as $tag)
|
||||||
|
|
||||||
|
@php
|
||||||
|
$classes = 'bg-gray-100 text-gray-700 hover:bg-rose-600 hover:text-white dark:bg-white/5 dark:text-gray-300 dark:hover:bg-rose-600';
|
||||||
|
|
||||||
|
if (in_array($tag->slug, ['uncensored', 'vanilla', '4k'])) {
|
||||||
|
$classes = 'bg-green-100 text-green-700 hover:bg-green-600 hover:text-white dark:hover:text-white dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-600';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tag->slug === 'censored') {
|
||||||
|
$classes = 'bg-yellow-100 text-yellow-700 hover:bg-yellow-500 hover:text-white dark:hover:text-white dark:bg-yellow-500/10 dark:text-yellow-400 dark:hover:bg-yellow-500';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($tag->slug, ['gore', 'horror', 'scat', 'ntr', 'rape'])) {
|
||||||
|
$classes = 'bg-red-100 text-red-700 hover:bg-red-600 hover:text-white dark:hover:text-white dark:bg-red-500/10 dark:text-red-400 dark:hover:bg-red-600';
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
||||||
|
class="inline-flex items-center gap-2 rounded-full px-4 py-2 text-xs font-bold uppercase tracking-wide transition {{ $classes }}">
|
||||||
|
|
||||||
|
@if(in_array($tag->slug, ['gore', 'horror', 'scat', 'ntr', 'rape']))
|
||||||
|
<i class="fa-solid fa-triangle-exclamation text-[10px]"></i>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{ $tag->name }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Gallery -->
|
||||||
|
@if($streamPage)
|
||||||
|
<div class="mt-8 border-t border-gray-200 pt-6 dark:border-white/10">
|
||||||
|
@include('stream.partials.gallery')
|
||||||
|
</div>
|
||||||
|
@endisset
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mt-2 mb-2 border-gray-400/40">
|
|
||||||
<p class="text-lg font-bold leading-normal text-rose-600">
|
|
||||||
{{ __('stream.description') }}
|
|
||||||
</p>
|
|
||||||
<p class="text-gray-800 dark:text-gray-200 leading-tight min-h-[50%]">
|
|
||||||
{{ $episode->description }}
|
|
||||||
</p>
|
|
||||||
<hr class="mt-2 mb-1 border-gray-400/40">
|
|
||||||
<p class="text-lg font-bold leading-normal text-rose-600">
|
|
||||||
{{ __('stream.genres') }}
|
|
||||||
</p>
|
|
||||||
<ul class="list-none text-center" style="overflow: hidden;">
|
|
||||||
<a class="text-gray-400">
|
|
||||||
|
|
|
||||||
</a>
|
|
||||||
@foreach ($episode->tags->sortBy('slug') as $tag)
|
|
||||||
<li class="inline-block p-1">
|
|
||||||
@if ($tag->slug == 'uncensored' || $tag->slug == 'vanilla' || $tag->slug == '4k')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="block relative items-center px-2 py-2 mt-1 text-xs font-semibold tracking-widest text-green-500 uppercase rounded-md border border-transparent transition duration-150 ease-in-out dark:focus:ring-offset-gray-800 dark:hover:text-white hover:text-white hover:bg-green-700 focus:bg-green-700 active:bg-green-900 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@elseif($tag->slug == 'censored')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="block relative items-center px-2 py-2 mt-1 text-xs font-semibold tracking-widest text-yellow-600 uppercase rounded-md border border-transparent transition duration-150 ease-in-out dark:focus:ring-offset-gray-800 dark:hover:text-white hover:text-white hover:bg-yellow-700 focus:bg-yellow-700 active:bg-yellow-900 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@elseif(
|
|
||||||
$tag->slug == 'gore' ||
|
|
||||||
$tag->slug == 'horror' ||
|
|
||||||
$tag->slug == 'scat' ||
|
|
||||||
$tag->slug == 'ntr' ||
|
|
||||||
$tag->slug == 'rape')
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="block relative items-center px-2 py-2 mt-1 text-xs font-semibold tracking-widest text-red-600 uppercase rounded-md border border-transparent transition duration-150 ease-in-out dark:focus:ring-offset-gray-800 dark:hover:text-white hover:text-white hover:bg-red-700 focus:bg-red-700 active:bg-red-900 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
|
|
||||||
<i class="fa-solid fa-triangle-exclamation"></i> {{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $tag->slug]) }}"
|
|
||||||
class="block relative items-center px-2 py-2 mt-1 text-xs font-semibold tracking-widest uppercase rounded-md border border-transparent transition duration-150 ease-in-out dark:focus:ring-offset-gray-800 dark:text-white hover:text-white hover:bg-rose-700 focus:bg-rose-700 active:bg-rose-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2">
|
|
||||||
{{ $tag->name }}
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</li>
|
|
||||||
<a class="text-gray-400">
|
|
||||||
|
|
|
||||||
</a>
|
|
||||||
@endforeach
|
|
||||||
</ul>
|
|
||||||
<hr class="mt-2 mb-1 border-gray-400/40">
|
|
||||||
<div class="inline-block pt-5">
|
|
||||||
@include('stream.partials.gallery')
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
+4
-1
@@ -45,7 +45,7 @@ Route::group(['middleware' => ['auth', 'auth.admin']], function () {
|
|||||||
|
|
||||||
// Episode
|
// Episode
|
||||||
Route::post('/admin/episode/upload', [EpisodeController::class, 'store'])->name('admin.upload.episode');
|
Route::post('/admin/episode/upload', [EpisodeController::class, 'store'])->name('admin.upload.episode');
|
||||||
Route::post('/admin/episode/edit', [EpisodeController::class, 'update'])->name('admin.edit');
|
|
||||||
|
|
||||||
// Get Tags used for Upload Form
|
// Get Tags used for Upload Form
|
||||||
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
||||||
@@ -66,4 +66,7 @@ Route::group(['middleware' => ['auth', 'auth.moderator']], function () {
|
|||||||
// Get Tags for editing Episode
|
// Get Tags for editing Episode
|
||||||
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
||||||
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
||||||
|
|
||||||
|
// Edit Episode
|
||||||
|
Route::post('/admin/episode/edit', [EpisodeController::class, 'update'])->name('admin.episode.edit');
|
||||||
});
|
});
|
||||||
+5
-3
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use Illuminate\Foundation\Inspiring;
|
use Illuminate\Foundation\Inspiring;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Schedule;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -14,6 +15,7 @@ use Illuminate\Support\Facades\Artisan;
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Artisan::command('inspire', function () {
|
Schedule::command('app:auto-stats')->hourly();
|
||||||
$this->comment(Inspiring::quote());
|
Schedule::command('app:reset-user-downloads')->daily();
|
||||||
})->purpose('Display an inspiring quote');
|
Schedule::command('app:generate-sitemap')->daily();
|
||||||
|
Schedule::command('app:sync-subscription-keys')->daily();
|
||||||
|
|||||||
@@ -18,6 +18,25 @@ export default {
|
|||||||
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
screens: {
|
||||||
|
'sm': '640px',
|
||||||
|
// => @media (min-width: 640px) { ... }
|
||||||
|
|
||||||
|
'md': '768px',
|
||||||
|
// => @media (min-width: 768px) { ... }
|
||||||
|
|
||||||
|
'lg': '1024px',
|
||||||
|
// => @media (min-width: 1024px) { ... }
|
||||||
|
|
||||||
|
'xl': '1280px',
|
||||||
|
// => @media (min-width: 1280px) { ... }
|
||||||
|
|
||||||
|
'2xl': '1536px',
|
||||||
|
// => @media (min-width: 1536px) { ... }
|
||||||
|
|
||||||
|
'3xl': '1900px',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [forms, require("tw-elements/dist/plugin")],
|
plugins: [forms, require("tw-elements/dist/plugin")],
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export default defineConfig({
|
|||||||
'resources/js/admin-edit.js',
|
'resources/js/admin-edit.js',
|
||||||
'resources/js/admin-subtitles.js',
|
'resources/js/admin-subtitles.js',
|
||||||
'resources/js/preview.js',
|
'resources/js/preview.js',
|
||||||
'resources/js/responsive.js',
|
|
||||||
'resources/js/stats.js'
|
'resources/js/stats.js'
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user