Fixed images on rtl mode

This commit is contained in:
User
2024-12-29 14:40:56 -05:00
parent 2b52f7f15a
commit 3a1d4cc40a
11 changed files with 82 additions and 41 deletions

View File

@@ -8,6 +8,8 @@ use App\Models\Comic;
use App\Models\Image; use App\Models\Image;
use App\Remote\CopyManga; use App\Remote\CopyManga;
use App\Remote\ImageFetcher; use App\Remote\ImageFetcher;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
@@ -118,6 +120,13 @@ class ComicController extends Controller
} }
} }
/**
*
*
* @param Request $request
* @return Response
* @throws GuzzleException
*/
public function index(Request $request): Response public function index(Request $request): Response
{ {
$params = []; $params = [];
@@ -152,7 +161,7 @@ class ComicController extends Controller
{ {
$comics = $this->copyManga->search($search, 30, $request->header('offset', 0)); $comics = $this->copyManga->search($search, 30, $request->header('offset', 0));
// Seacrh API is limited, no upsert // Search API is limited, no upsert
return Inertia::render('Comic/Index', [ return Inertia::render('Comic/Index', [
'comics' => $comics, 'comics' => $comics,
@@ -163,7 +172,7 @@ class ComicController extends Controller
public function chapters(Request $request, string $pathword = ''): Response public function chapters(Request $request, string $pathword = ''): Response
{ {
$comic = $this->copyManga->comic($pathword); $comic = $this->copyManga->comic($pathword);
$chapters = $this->copyManga->chapters($pathword, 200, 0, [], $request->get('group', 'default')); $chapters = $this->copyManga->chapters($pathword, 200, $request->header('offset', 0), [], $request->get('group', 'default'));
// Get the comic object and fill other parameters // Get the comic object and fill other parameters
try { try {
@@ -221,7 +230,8 @@ class ComicController extends Controller
return Inertia::render('Comic/Chapters', [ return Inertia::render('Comic/Chapters', [
'comic' => $comic, 'comic' => $comic,
'chapters' => $chapters, 'chapters' => $chapters,
'histories' => $histories 'histories' => $histories,
'offset' => $request->header('offset', 0)
]); ]);
} }
@@ -276,7 +286,7 @@ class ComicController extends Controller
{ {
// Get history // Get history
$histories = $request->user()->readingHistories()->with(['comic:id,name,pathword'])->orderByDesc('reading_histories.created_at') $histories = $request->user()->readingHistories()->with(['comic:id,name,pathword'])->orderByDesc('reading_histories.created_at')
->select(['reading_histories.id as hid', 'reading_histories.created_at', 'chapters.comic_id', 'chapters.name'])->paginate(50)->toArray(); ->select(['reading_histories.id as hid', 'reading_histories.created_at as read_at', 'chapters.comic_id', 'chapters.name'])->paginate(50)->toArray();
return Inertia::render('Comic/Histories', [ return Inertia::render('Comic/Histories', [
'histories' => $histories 'histories' => $histories

BIN
bun.lockb

Binary file not shown.

10
composer.lock generated
View File

@@ -1654,16 +1654,16 @@
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "2.6.0", "version": "2.6.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/commonmark.git", "url": "https://github.com/thephpleague/commonmark.git",
"reference": "d150f911e0079e90ae3c106734c93137c184f932" "reference": "d990688c91cedfb69753ffc2512727ec646df2ad"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d150f911e0079e90ae3c106734c93137c184f932", "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad",
"reference": "d150f911e0079e90ae3c106734c93137c184f932", "reference": "d990688c91cedfb69753ffc2512727ec646df2ad",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1757,7 +1757,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-12-07T15:34:16+00:00" "time": "2024-12-29T14:10:59+00:00"
}, },
{ {
"name": "league/config", "name": "league/config",

View File

@@ -1,5 +1,3 @@
import { Link } from '@inertiajs/react';
export default function GuestLayout({ children }) { export default function GuestLayout({ children }) {
return ( return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10"> <div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">

View File

@@ -1,11 +1,12 @@
import { Head, Link, useForm } from '@inertiajs/react';
import GuestLayout from "@/Layouts/GuestLayout.jsx";
import InputError from '@/components/InputError'; import InputError from '@/components/InputError';
import Checkbox from '@/components/Checkbox'; import Checkbox from '@/components/Checkbox';
import PrimaryButton from '@/components/PrimaryButton'; import PrimaryButton from '@/components/PrimaryButton';
import TextInput from '@/components/TextInput'; import TextInput from '@/components/TextInput';
import { Head, Link, useForm } from '@inertiajs/react';
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.jsx"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.jsx";
import GuestLayout from "@/Layouts/GuestLayout.jsx";
import { Alert, AlertDescription } from "@/components/ui/alert.jsx"; import { Alert, AlertDescription } from "@/components/ui/alert.jsx";
export default function Login({ status, canResetPassword }) { export default function Login({ status, canResetPassword }) {
@@ -45,7 +46,7 @@ export default function Login({ status, canResetPassword }) {
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email">E-mail Address</Label> <Label htmlFor="email">E-mail Address</Label>
<TextInput type="email" id="email" placeholder="me@yumj.in" value={ data.email } <TextInput type="email" id="email" placeholder="me@yumj.in" value={ data.email }
onChange={ (e) => setData('email', e.target.value) } required /> tabIndex="1" onChange={ (e) => setData('email', e.target.value) } required />
<InputError message={ errors.email } className="mt-2" /> <InputError message={ errors.email } className="mt-2" />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
@@ -57,26 +58,23 @@ export default function Login({ status, canResetPassword }) {
</Link> </Link>
</div> </div>
<TextInput id="password" type="password" name="password" value={ data.password } <TextInput id="password" type="password" name="password" value={ data.password }
autoComplete="current-password" autoComplete="current-password" tabIndex="2"
onChange={ (e) => setData('password', e.target.value) } /> onChange={ (e) => setData('password', e.target.value) } />
<InputError message={ errors.password } className="mt-2" /> <InputError message={ errors.password } className="mt-2" />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox id="remember" tabIndex="3" name="remember" checked={ data.remember }
id="remember" onChange={ (e) => setData('remember', e.target.checked) } />
name="remember"
checked={ data.remember }
onChange={ (e) =>
setData('remember', e.target.checked)
}
/>
<label htmlFor="remember" <label htmlFor="remember"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Remember me</label> className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
Remember me
</label>
</div> </div>
</div> </div>
<PrimaryButton type="submit" disabled={ processing } <PrimaryButton type="submit" disabled={ processing } tabIndex="4" className="w-full">
className="w-full">Login</PrimaryButton> Login
</PrimaryButton>
</div> </div>
<div className="mt-4 text-center text-sm"> <div className="mt-4 text-center text-sm">
Don&apos;t have an account?&nbsp; Don&apos;t have an account?&nbsp;

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Head, Link, router } from '@inertiajs/react'; import { Head, Link, router } from '@inertiajs/react';
import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide } from 'lucide-react'; import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight } from 'lucide-react';
import AppLayout from '@/Layouts/AppLayout.jsx'; import AppLayout from '@/Layouts/AppLayout.jsx';
@@ -12,7 +12,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { useToast } from '@/hooks/use-toast'; import { useToast } from '@/hooks/use-toast';
export default function Chapters({ auth, comic, chapters, histories }) { export default function Chapters({ auth, comic, chapters, histories, offset }) {
const [group, setGroup] = useState('default'); const [group, setGroup] = useState('default');
const [favourites, setFavourites] = useState(auth.user.favourites); const [favourites, setFavourites] = useState(auth.user.favourites);
@@ -113,11 +113,13 @@ export default function Chapters({ auth, comic, chapters, histories }) {
</tr> </tr>
<tr> <tr>
<td className="text-right pr-3">Authors</td> <td className="text-right pr-3">Authors</td>
<td>{ comic.comic.author.map(a => ( <td>
{ comic.comic.author.map(a => (
<Badge key={ a.path_word } className="m-2" variant="outline"> <Badge key={ a.path_word } className="m-2" variant="outline">
<Link href={ route('comics.author', [a.path_word]) }>{ a.name }</Link> <Link href={ route('comics.author', [a.path_word]) }>{ a.name }</Link>
</Badge> </Badge>
) ) }</td> ) ) }
</td>
</tr> </tr>
<tr> <tr>
<td className="text-right pr-3">Description</td> <td className="text-right pr-3">Description</td>
@@ -125,7 +127,11 @@ export default function Chapters({ auth, comic, chapters, histories }) {
</tr> </tr>
<tr> <tr>
<td className="text-right pr-3">Updated At</td> <td className="text-right pr-3">Updated At</td>
<td>{ comic.comic.datetime_updated }</td> <td>
<Link href={ route('comics.read', [comic.comic.path_word, comic.comic.last_chapter.uuid])}>
{ comic.comic.datetime_updated } - { comic.comic.last_chapter.name }
</Link>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -151,9 +157,23 @@ export default function Chapters({ auth, comic, chapters, histories }) {
</div> </div>
<TabsContent value={ group }> <TabsContent value={ group }>
<div className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 xl:grid-cols-12 gap-1"> <div className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 xl:grid-cols-12 gap-1">
{ (chapters.total > chapters.limit && chapters.offset > 0) && (
<Button size="sm" variant="outline" asChild>
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) - chapters.limit }}>
<ChevronsLeft /> Prev
</Link>
</Button>
) }
{ chapters.list.sort((a, b) => ascending ? (a.index - b.index) : (b.index - a.index)).map(c => ( { chapters.list.sort((a, b) => ascending ? (a.index - b.index) : (b.index - a.index)).map(c => (
<ComicChapterLink key={ c.uuid } { ...c } /> <ComicChapterLink key={ c.uuid } { ...c } />
) ) } ) ) }
{ (chapters.total > chapters.limit && chapters.offset === 0) && (
<Button size="sm" variant="outline" asChild>
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) + chapters.limit }}>
Next <ChevronsRight />
</Link>
</Button>
) }
</div> </div>
</TabsContent> </TabsContent>
</Tabs> </Tabs>

View File

@@ -97,7 +97,7 @@ export default function Histories({ auth, histories }) {
{ h.comic.name } { h.comic.name }
</Link> </Link>
</TableCell> </TableCell>
<TableCell className="hidden lg:block">{ datetimeConversion(h.created_at) }</TableCell> <TableCell className="hidden lg:block">{ datetimeConversion(h.read_at) }</TableCell>
</TableRow> </TableRow>
)) } )) }
</TableBody> </TableBody>

View File

@@ -43,7 +43,7 @@ export default function Index({ comics, offset, auth }) {
<CardContent> <CardContent>
<CardTitle><Link href={ `/comic/${ props.path_word }` }>{ props.name }</Link></CardTitle> <CardTitle><Link href={ `/comic/${ props.path_word }` }>{ props.name }</Link></CardTitle>
<CardDescription className="pt-2"> <CardDescription className="pt-2">
{ props.author.map(a => ( { props.author && props.author.map(a => (
<Badge className="m-1" key={ a.path_word } variant="outline"> <Badge className="m-1" key={ a.path_word } variant="outline">
{ a.name } { a.name }
</Badge>) </Badge>)

View File

@@ -76,7 +76,7 @@ export default function Read({ auth, comic, chapter }) {
} else if (divDimensions[0] > divDimensions[1] && readingMode === 'utd') { } else if (divDimensions[0] > divDimensions[1] && readingMode === 'utd') {
imgStyles = { width: '50%' }; imgStyles = { width: '50%' };
} else if (readingMode === 'rtl') { } else if (readingMode === 'rtl') {
imgStyles = { height: 'calc(100dvh - 90px)' }; imgStyles = { width: '100%', height: 'calc(100dvh - 90px)', objectFit: 'contain' };
} }
const handleImageClick = (e) => { const handleImageClick = (e) => {
@@ -156,6 +156,10 @@ export default function Read({ auth, comic, chapter }) {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<Button variant="ghost">
{ currentImage } / { chapter.sorted.length }
</Button>
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
@@ -204,10 +208,6 @@ export default function Read({ auth, comic, chapter }) {
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
) } ) }
<Button variant="ghost">
{ currentImage } / { chapter.sorted.length }
</Button>
</> </>
); );
} }

View File

@@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { Link, router, usePage } from '@inertiajs/react'; import { Link, router, usePage } from '@inertiajs/react';
import { BadgeCheck, ChevronsUpDown, Star, History, ChevronDown, LogOut, Search, Book } from 'lucide-react'; import { BadgeCheck, ChevronsUpDown, Star, History, ChevronDown, LogOut, Search, Book, TableOfContents } from 'lucide-react';
import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
@@ -57,7 +57,7 @@ export function AppSidebar({ auth }) {
</div> </div>
<div className="flex flex-col gap-0.5 leading-none"> <div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Comic</span> <span className="font-semibold">Comic</span>
<span>0.0.0</span> <span>0.0.1</span>
</div> </div>
</Link> </Link>
</SidebarMenuButton> </SidebarMenuButton>
@@ -73,6 +73,21 @@ export function AppSidebar({ auth }) {
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>
</SidebarHeader> </SidebarHeader>
<SidebarGroup>
<SidebarGroupLabel>Others</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<Link href={ route('comics.index')}>
<TableOfContents />
<span>Manual</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<Collapsible defaultOpen className="group/collapsible"> <Collapsible defaultOpen className="group/collapsible">
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel asChild> <SidebarGroupLabel asChild>

View File

@@ -2,7 +2,7 @@
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, minimal-ui">
<title inertia>{{ config('app.name', 'Laravel') }}</title> <title inertia>{{ config('app.name', 'Laravel') }}</title>
@routes @routes
@viteReactRefresh @viteReactRefresh