From 489e0546146f46eac1104ce42362e0d19d7b9d00 Mon Sep 17 00:00:00 2001 From: User Date: Sat, 4 Jan 2025 13:02:50 -0500 Subject: [PATCH] 0.1.0 --- .../Commands/CleanupReadingHistories.php | 77 ++++++++++++++++++ app/Helper/Timezones.php | 41 ++++++++++ app/Http/Controllers/ComicController.php | 25 +++++- app/Http/Controllers/ProfileController.php | 10 +++ app/Http/Resources/UserCollection.php | 1 + app/Models/User.php | 49 ++++++++++- bun.lockb | Bin 170744 -> 171074 bytes composer.json | 2 +- composer.lock | 27 +++--- .../2025_01_04_161757_update_users_table.php | 21 +++++ package.json | 3 +- public/manifest.json | 2 +- resources/js/Pages/Comic/Chapters.jsx | 66 ++++++++++++--- resources/js/Pages/Comic/Histories.jsx | 36 +++++--- resources/js/Pages/Comic/Index.jsx | 2 +- resources/js/Pages/Pages/Updates.jsx | 14 ++++ resources/js/Pages/Profile/Edit.jsx | 4 +- .../Partials/UpdateProfileInformationForm.jsx | 42 ++++++++-- resources/js/components/ui/app-sidebar.jsx | 12 +-- routes/web.php | 4 +- 20 files changed, 377 insertions(+), 61 deletions(-) create mode 100644 app/Console/Commands/CleanupReadingHistories.php create mode 100644 app/Helper/Timezones.php create mode 100644 database/migrations/2025_01_04_161757_update_users_table.php diff --git a/app/Console/Commands/CleanupReadingHistories.php b/app/Console/Commands/CleanupReadingHistories.php new file mode 100644 index 0000000..f7f0113 --- /dev/null +++ b/app/Console/Commands/CleanupReadingHistories.php @@ -0,0 +1,77 @@ +argument('userIds'); + + if (empty($userIds)) { + $this->error('You must specify user IDs or "all".'); + return 1; + } + + // Handle "all" case + if ($userIds === ['all']) { + $users = User::all(); + foreach ($users as $user) { + $this->cleanUpForUser($user); + } + } else { + // Ensure user IDs are unique + $uniqueUserIds = array_unique($userIds); + + foreach ($uniqueUserIds as $userId) { + $user = User::find($userId); + if ($user) { + $this->cleanUpForUser($user); + } else { + $this->error("User with ID {$userId} not found."); + } + } + } + + $this->info('Cleanup completed successfully.'); + return 0; + } + + /** + * Clean up duplicate reading histories for a single user. + * + * @param \App\Models\User $user + * @return void + */ + protected function cleanUpForUser(User $user): void + { + $result = $user->cleanUpReadingHistories(); + + $keptIds = $result['kept_ids']->toArray(); // Convert Collection to array + + $this->info("User ID {$result['user_id']} - Kept IDs: " . implode(', ', $keptIds) . " - Deleted: {$result['deleted_count']} records."); + } +} diff --git a/app/Helper/Timezones.php b/app/Helper/Timezones.php new file mode 100644 index 0000000..4e5a769 --- /dev/null +++ b/app/Helper/Timezones.php @@ -0,0 +1,41 @@ +user()->readingHistories()->with(['comic:id,name,pathword'])->orderByDesc('reading_histories.created_at') - ->select(['reading_histories.id as hid', 'reading_histories.created_at as read_at', 'chapters.comic_id', 'chapters.name'])->paginate(50)->toArray(); + ->select(['reading_histories.id as hid', 'chapters.comic_id', 'chapters.name'])->paginate(50)->toArray(); return Inertia::render('Comic/Histories', [ 'histories' => $this->scToZh($histories) @@ -374,7 +374,7 @@ class ComicController extends Controller * @param Request $request * @return RedirectResponse */ - public function destroyHistories(Request $request): RedirectResponse + public function patchHistories(Request $request): RedirectResponse { if (!is_array($request->get('ids')) && $request->get('ids') === 'all') { $histories = $request->user()->readingHistories()->delete(); @@ -385,6 +385,27 @@ class ComicController extends Controller return redirect()->route('comics.histories'); } + public function destroyHistory(Request $request, string $pathword): Response + { + $comicId = Comic::where('pathword', $pathword)->firstOrFail(['id'])->id; + $request->user()->readingHistories()->where('reading_histories.comic_id', $comicId)->delete(); + + // Get history + $histories = $request->user()->readingHistories()->where('reading_histories.comic_id', $comicId) + ->distinct()->select('chapter_uuid')->get()->pluck('chapter_uuid'); + + return Inertia::render('Comic/Chapters', [ + 'histories' => $histories + ]); + } + + public function destroyHistories(Request $request): RedirectResponse + { + $result = $request->user()->cleanUpReadingHistories(); + + return redirect()->route('comics.histories'); + } + /** * Fetch tags * diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 873b4f7..6f98c98 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Helper\Timezones; use App\Http\Requests\ProfileUpdateRequest; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Http\RedirectResponse; @@ -18,8 +19,11 @@ class ProfileController extends Controller */ public function edit(Request $request): Response { + $tz = new Timezones(); + return Inertia::render('Profile/Edit', [ 'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail, + 'timezones' => $tz->toArray(), 'status' => session('status'), ]); } @@ -35,6 +39,12 @@ class ProfileController extends Controller $request->user()->email_verified_at = null; } + // Settings + $settings = $request->user()->settings; + $settings['timezone'] = $request->get('timezone'); + $request->user()->settings = $settings; + $request->user()->save(); + $request->user()->save(); return Redirect::route('profile.edit'); diff --git a/app/Http/Resources/UserCollection.php b/app/Http/Resources/UserCollection.php index 10917dd..a6f2be2 100644 --- a/app/Http/Resources/UserCollection.php +++ b/app/Http/Resources/UserCollection.php @@ -18,6 +18,7 @@ class UserCollection extends ResourceCollection 'id' => $request->user()->id, 'name' => $request->user()->name, 'email' => $request->user()->email, + 'settings' => $request->user()->settings, 'favourites' => $request->user()->favourites()->get(['pathword'])->pluck('pathword') ]; } diff --git a/app/Models/User.php b/app/Models/User.php index 2bc51e9..537bbaf 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,16 +3,16 @@ namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; -use Database\Factories\UserFactory; -use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Query\JoinClause; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Facades\DB; class User extends Authenticatable implements MustVerifyEmail { - /** @use HasFactory */ + use HasFactory, Notifiable; /** @@ -24,6 +24,7 @@ class User extends Authenticatable implements MustVerifyEmail 'name', 'email', 'password', + 'settings', ]; /** @@ -36,6 +37,10 @@ class User extends Authenticatable implements MustVerifyEmail 'remember_token', ]; + protected $attributes = [ + 'settings' => "{timezone:'UTC'}", + ]; + /** * Get the attributes that should be cast. * @@ -46,6 +51,7 @@ class User extends Authenticatable implements MustVerifyEmail return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'settings' => 'array', ]; } @@ -59,4 +65,41 @@ class User extends Authenticatable implements MustVerifyEmail return $this->belongsToMany(Chapter::class, 'reading_histories')->withTimestamps(); } + public function cleanUpReadingHistories(): array + { + // Get the user's ID + $userId = $this->id; + + // Step 1: Identify records to keep + $idsToKeep = ReadingHistory::query() + ->select('reading_histories.id') // Specify table name explicitly + ->joinSub( + ReadingHistory::query() + ->select('comic_id', 'user_id', 'chapter_id', DB::raw('MIN(created_at) as earliest_created_at')) + ->where('user_id', $userId) // Specify user_id with table name + ->groupBy('comic_id', 'user_id', 'chapter_id'), + 'b', + function (JoinClause $join) { + $join->on('reading_histories.comic_id', '=', 'b.comic_id') + ->on('reading_histories.user_id', '=', 'b.user_id') // Disambiguate user_id + ->on('reading_histories.chapter_id', '=', 'b.chapter_id') + ->on('reading_histories.created_at', '=', 'b.earliest_created_at'); + } + ) + ->where('reading_histories.user_id', $userId) // Specify table name explicitly + ->pluck('id'); + + // Step 2: Delete duplicates for the user + $deletedCount = ReadingHistory::where('user_id', $userId) + ->whereNotIn('id', $idsToKeep) + ->delete(); + + // Return the result as an array + return [ + 'user_id' => $userId, + 'kept_ids' => $idsToKeep, + 'deleted_count' => $deletedCount, + ]; + } + } diff --git a/bun.lockb b/bun.lockb index 50c49bb5efb4d5175c114ad09208e97a0ae7298c..4c59ecf2b93da2c1d236ba2321acebc44605fea0 100755 GIT binary patch delta 24585 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 X;20jB9TDT$)DXAzzZ&Hj8U23%VJiX- diff --git a/composer.json b/composer.json index d7b1b49..0bafdf0 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "keywords": ["laravel", "framework"], "license": "MIT", "require": { - "php": "^8.2", + "php": "^8.3", "ext-dom": "*", "ext-openssl": "*", "inertiajs/inertia-laravel": "^2.0", diff --git a/composer.lock b/composer.lock index d07d00d..a6a3151 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cf7d41fc8c70877b1ded788c249463de", + "content-hash": "b4f2ee211728714c2fa05fbad3914d2e", "packages": [ { "name": "brick/math", @@ -1189,16 +1189,16 @@ }, { "name": "laravel/framework", - "version": "v11.36.1", + "version": "v11.37.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "df06f5163f4550641fdf349ebc04916a61135a64" + "reference": "6cb103d2024b087eae207654b3f4b26646119ba5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/df06f5163f4550641fdf349ebc04916a61135a64", - "reference": "df06f5163f4550641fdf349ebc04916a61135a64", + "url": "https://api.github.com/repos/laravel/framework/zipball/6cb103d2024b087eae207654b3f4b26646119ba5", + "reference": "6cb103d2024b087eae207654b3f4b26646119ba5", "shasum": "" }, "require": { @@ -1248,7 +1248,6 @@ "voku/portable-ascii": "^2.0.2" }, "conflict": { - "mockery/mockery": "1.6.8", "tightenco/collect": "<5.5.33" }, "provide": { @@ -1400,7 +1399,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-12-17T22:32:08+00:00" + "time": "2025-01-02T20:10:21+00:00" }, { "name": "laravel/prompts", @@ -6193,16 +6192,16 @@ }, { "name": "tightenco/ziggy", - "version": "v2.4.1", + "version": "v2.4.2", "source": { "type": "git", "url": "https://github.com/tighten/ziggy.git", - "reference": "8e002298678fd4d61155bb1d6e3837048235bff7" + "reference": "6612c8c9b2d5b3e74fd67c58c11465df1273f384" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tighten/ziggy/zipball/8e002298678fd4d61155bb1d6e3837048235bff7", - "reference": "8e002298678fd4d61155bb1d6e3837048235bff7", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/6612c8c9b2d5b3e74fd67c58c11465df1273f384", + "reference": "6612c8c9b2d5b3e74fd67c58c11465df1273f384", "shasum": "" }, "require": { @@ -6257,9 +6256,9 @@ ], "support": { "issues": "https://github.com/tighten/ziggy/issues", - "source": "https://github.com/tighten/ziggy/tree/v2.4.1" + "source": "https://github.com/tighten/ziggy/tree/v2.4.2" }, - "time": "2024-11-21T15:51:20+00:00" + "time": "2025-01-02T20:06:52+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -9751,7 +9750,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2", + "php": "^8.3", "ext-dom": "*", "ext-openssl": "*" }, diff --git a/database/migrations/2025_01_04_161757_update_users_table.php b/database/migrations/2025_01_04_161757_update_users_table.php new file mode 100644 index 0000000..4f43cb9 --- /dev/null +++ b/database/migrations/2025_01_04_161757_update_users_table.php @@ -0,0 +1,21 @@ +json('settings')->after('remember_token')->nullable(); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('settings'); + }); + } +}; diff --git a/package.json b/package.json index 97b3399..b0fad8b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss": "^3.4.17", - "vite": "^6.0.6" + "vite": "^6.0.7" }, "dependencies": { "@hookform/resolvers": "^3.9.1", @@ -42,6 +42,7 @@ "clsx": "^2.1.1", "lodash": "^4.17.21", "lucide-react": "^0.468.0", + "luxon": "^3.5.0", "react-hook-form": "^7.54.2", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", diff --git a/public/manifest.json b/public/manifest.json index 322ac39..67dade0 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -13,7 +13,7 @@ "type": "image/png" } ], - "start_url": "http://127.0.0.1:8000", + "start_url": "https://c.yumj.in/", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" diff --git a/resources/js/Pages/Comic/Chapters.jsx b/resources/js/Pages/Comic/Chapters.jsx index 0861b62..11830b3 100644 --- a/resources/js/Pages/Comic/Chapters.jsx +++ b/resources/js/Pages/Comic/Chapters.jsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { Head, Link, router } from '@inertiajs/react'; -import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight } from 'lucide-react'; +import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight, Trash2 } from 'lucide-react'; import AppLayout from '@/Layouts/AppLayout.jsx'; @@ -23,11 +23,27 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) { const favouriteOnClickHandler = (pathword) => { axios.post(route('comics.postFavourite'), { pathword: pathword }).then(res => { setFavourites(res.data); + toast({ + title: "All set", + description: `${comic.comic.name} is now in / remove your favorite list.`, + }); }); + } - toast({ - title: "All set", - description: `${comic.comic.name} is now in / remove your favorite list.`, + const removeAllHistoriesOnClickHandler = (pathword) => { + router.visit(route('comics.destroyHistory', { pathword: pathword }), { + method: 'DELETE', + replace: false, + preserveScroll: true, + preserveState: true, + showProgress: false, + only: ['histories'], + onSuccess: () => { + toast({ + title: "All set", + description: `All histories have been removed.`, + }); + } }); } @@ -101,6 +117,12 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) { Alias { comic.comic.alias } + + Status + + { comic.comic.region.display } / { comic.comic.status.display } + + Category @@ -115,9 +137,9 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) { Authors { comic.comic.author.map(a => ( - - { a.name } - + + { a.name } + ) ) } @@ -149,10 +171,32 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) { )) } -
- +
+ + + + + + +

Set order as { ascending ? 'Descending' : 'Ascending' }

+
+
+
+ + + + + + + +

Remove all histories for this comic

+
+
+
diff --git a/resources/js/Pages/Comic/Histories.jsx b/resources/js/Pages/Comic/Histories.jsx index 67fba0f..de4a9d8 100644 --- a/resources/js/Pages/Comic/Histories.jsx +++ b/resources/js/Pages/Comic/Histories.jsx @@ -2,6 +2,8 @@ import { useState } from 'react'; import { Head, Link, router } from '@inertiajs/react'; import AppLayout from '@/Layouts/AppLayout.jsx'; +import { DateTime } from "luxon"; + import { BreadcrumbItem, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb"; import { Button } from '@/components/ui/button'; import Checkbox from "@/components/Checkbox"; @@ -25,19 +27,11 @@ export default function Histories({ auth, histories }) { } const datetimeConversion = (iso8601) => { - const date = new Date(iso8601); - - const year = date.getUTCFullYear(); - const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Months are 0-based - const day = String(date.getUTCDate()).padStart(2, '0'); - const hours = String(date.getUTCHours()).padStart(2, '0'); - const minutes = String(date.getUTCMinutes()).padStart(2, '0'); - - return `${year}-${month}-${day} ${hours}:${minutes}`; + return DateTime.fromISO(iso8601).setZone(auth.user.settings.timezone).toFormat('dd-MM-yyyy HH:mm'); } const deleteButtonOnClickHandler = () => { - router.visit(route('comics.destroyHistories'), { + router.visit(route('comics.patchHistories'), { data: (ids.length > 0) ? { ids: ids } : { ids: 'all' }, method: "PATCH", only: ['histories'], @@ -50,6 +44,20 @@ export default function Histories({ auth, histories }) { }); } + const removeDuplicatedButtonOnClickHandler = () => { + router.visit(route('comics.destroyHistories'), { + data: (ids.length > 0) ? { ids: ids } : { ids: 'all' }, + method: "DELETE", + only: ['histories'], + onSuccess: data => { + toast({ + title: "All set", + description: `The duplicated records has been deleted.`, + }); + } + }); + } + return ( @@ -67,6 +75,10 @@ export default function Histories({ auth, histories }) { + + @@ -74,7 +86,7 @@ export default function Histories({ auth, histories }) { Select Chapter Comic - Read at + Read at @@ -97,7 +109,7 @@ export default function Histories({ auth, histories }) { { h.comic.name } - { datetimeConversion(h.read_at) } + { datetimeConversion(h.pivot.created_at) } )) } diff --git a/resources/js/Pages/Comic/Index.jsx b/resources/js/Pages/Comic/Index.jsx index bd01a9b..6a013bf 100644 --- a/resources/js/Pages/Comic/Index.jsx +++ b/resources/js/Pages/Comic/Index.jsx @@ -14,7 +14,7 @@ export default function Index({ comics, offset, auth }) { const url = new URL(window.location); //searchParams - const [favourites, setFavourites] = useState(auth.user.favourites); + const [favourites, setFavourites] = useState(auth.user?.favourites); const { toast } = useToast(); const favouriteOnClickHandler = (pathword) => { diff --git a/resources/js/Pages/Pages/Updates.jsx b/resources/js/Pages/Pages/Updates.jsx index 3834770..c504b4a 100644 --- a/resources/js/Pages/Pages/Updates.jsx +++ b/resources/js/Pages/Pages/Updates.jsx @@ -11,6 +11,20 @@ export default function Updates({ auth }) { Updates
+ + + 0.1.0 + Release: 04 Jan 2025 + + +
    +
  • iOS fixed for search auto fours
  • +
  • Beta: Timezones, only available for histories now
  • +
  • Beta: Remove histories for selected comic
  • +
  • Beta: Remove duplicated reading histories
  • +
+
+
0.0.2 FR1 diff --git a/resources/js/Pages/Profile/Edit.jsx b/resources/js/Pages/Profile/Edit.jsx index 8c00443..05e70de 100644 --- a/resources/js/Pages/Profile/Edit.jsx +++ b/resources/js/Pages/Profile/Edit.jsx @@ -6,7 +6,7 @@ import AppLayout from "@/Layouts/AppLayout.jsx"; import { BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator } from "@/components/ui/breadcrumb.jsx"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -export default function Edit({ auth, mustVerifyEmail, status }) { +export default function Edit({ auth, mustVerifyEmail, status, timezones }) { return ( @@ -27,7 +27,7 @@ export default function Edit({ auth, mustVerifyEmail, status }) { diff --git a/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.jsx b/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.jsx index 233d527..8bfe8da 100644 --- a/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.jsx +++ b/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.jsx @@ -11,24 +11,29 @@ import { CardFooter, CardHeader, CardTitle, -} from "@/components/ui/card"; +} from '@/components/ui/card'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; -export default function UpdateProfileInformation({ - mustVerifyEmail, - status, - className = '', -}) { +export default function UpdateProfileInformation({ mustVerifyEmail, status, timezones }) { const user = usePage().props.auth.user; const { data, setData, patch, errors, processing, recentlySuccessful } = useForm({ name: user.name, email: user.email, + timezone: user.settings.timezone }); const submit = (e) => { e.preventDefault(); - patch(route('profile.update')); }; @@ -51,6 +56,7 @@ export default function UpdateProfileInformation({
+
@@ -88,6 +94,28 @@ export default function UpdateProfileInformation({ ) }
) } + +
+
+ + +
+ +
Save diff --git a/resources/js/components/ui/app-sidebar.jsx b/resources/js/components/ui/app-sidebar.jsx index e0d942b..c3d565a 100644 --- a/resources/js/components/ui/app-sidebar.jsx +++ b/resources/js/components/ui/app-sidebar.jsx @@ -57,18 +57,20 @@ export function AppSidebar({ auth }) {
Comic - 0.0.2 FR1 + 0.1.0
- -
searchOnSubmitHandler(e)} > + + searchOnSubmitHandler(e) }> - setSearch(e.target.value) } id="search" placeholder="Search" className="pl-8" /> - + setSearch(e.target.value) } id="search" + placeholder="Search" className="pl-8" /> +
diff --git a/routes/web.php b/routes/web.php index b6830f1..4151f6f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -27,7 +27,9 @@ Route::controller(ComicController::class)->middleware('auth')->name('comics.')-> // Histories Route::get('/histories', 'histories')->name('histories'); - Route::patch('/histories', 'destroyHistories')->name('destroyHistories'); // Only patch accept params + Route::patch('/histories', 'patchHistories')->name('patchHistories'); + Route::delete('/history/{pathword}', 'destroyHistory')->name('destroyHistory'); + Route::delete('/histories', 'destroyHistories')->name('destroyHistories'); }); Route::controller(PagesController::class)->middleware('auth')->name('pages.')->group(function () {