diff --git a/.ai/guidelines/resources.js.blade.php b/.ai/guidelines/resources.js.blade.php index 0468cc909..295ce20c9 100644 --- a/.ai/guidelines/resources.js.blade.php +++ b/.ai/guidelines/resources.js.blade.php @@ -16,7 +16,7 @@ ## Navigation and requests -- Every Inertia `` to a GET page prefetches on hover (`prefetch`). A GET page never writes; the only exceptions are `log/{log}` (remembers the log), `notifications` (marks read, remembers the filter), `remotes/@{partner}` and `remotes/orgs/{org}` (mark read), and every link to them keeps `prefetch={false}`. `starred` also keeps it, because starring changes the list. A new GET page that must write joins this list. +- Every Inertia `` to a GET page prefetches on hover (`prefetch`). A GET page never writes; the only exceptions are `log/{log}` (remembers the log), `notifications` (marks read, remembers the filter), `remotes/@{partner}` and `remotes/orgs/{org}` (mark read), and every link to them keeps `prefetch={false}`. The navigation's Log is the exception: it opens the remembered log and a prefetch never changes the remembered log, so it prefetches everywhere except on a log page, where it scrolls to the top instead. `starred` also keeps it, because starring changes the list. A new GET page that must write joins this list. - Never prefetch a non-GET link, an external URL, a guest's sign-in link, a mention of a handle nobody here has (guests get a missing page, and a feed would ask for it on every hover), or a URL that is not an Inertia page (images, feeds, downloads, JSON). - A click target that is not a link (the commit card surface) prefetches on `mousedown`, never on hover: scrolling a feed hovers every card. - After an action that changes what a prefetched page shows, flush that page (`router.flush(url)`, `router.flushAll()`, or `invalidateCacheTags`), so the next click does not show the old state. diff --git a/app/Http/Controllers/LogController.php b/app/Http/Controllers/LogController.php index e6b5038ad..4aee29dd1 100644 --- a/app/Http/Controllers/LogController.php +++ b/app/Http/Controllers/LogController.php @@ -41,7 +41,7 @@ public function show(ShowLogRequest $request, #[CurrentUser] ?User $user, Log $l if ($log === Log::Following) { return Inertia::location(route('login')); } - } elseif ($user->log !== $log) { + } elseif ($user->log !== $log && ! $request->prefetch()) { $action->handle($user, $log); } diff --git a/resources/js/components/app-nav.tsx b/resources/js/components/app-nav.tsx index d064cbc9c..505085dcc 100644 --- a/resources/js/components/app-nav.tsx +++ b/resources/js/components/app-nav.tsx @@ -194,7 +194,7 @@ function NavLink({ refresh()->log)->toBe($log); })->with(Log::cases()); +it('does not remember a log that is only prefetched', function (): void { + $user = User::factory()->create(['log' => Log::Latest]); + + $this->actingAs($user)->withHeaders([ + 'X-Inertia' => 'true', + 'X-Inertia-Version' => (string) new HandleInertiaRequests()->version(request()), + 'Purpose' => 'prefetch', + ])->get(route('log.show', Log::Following))->assertOk(); + + expect($user->refresh()->log)->toBe(Log::Latest); +}); + it('does not update the user when the log is unchanged', function (): void { $user = User::factory()->create(['log' => Log::Trending]); $updatedAt = $user->updated_at;