Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/guidelines/resources.js.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

## Navigation and requests

- Every Inertia `<Link>` 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 `<Link>` 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.
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/LogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/app-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function NavLink({
<Link
href={item.href}
onClick={onClick}
prefetch={item.title !== 'Log'}
prefetch={onClick === undefined}
cacheFor="10s"
viewTransition
aria-current={item.isActive ? 'page' : undefined}
Expand Down
12 changes: 12 additions & 0 deletions tests/Feature/Controllers/LogControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@
expect($user->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;
Expand Down
Loading