fix: bug: profile page only shows user's 10 most recent pack...#2820
fix: bug: profile page only shows user's 10 most recent pack...#2820gittihub-jpg wants to merge 1 commit into
Conversation
Fixes npmx-dev#2817 Signed-off-by: gittihub-jpg <rico@springer-mail.net>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThe pull request implements infinite-scroll pagination for user profile likes. The backend likes endpoint gains cursor and limit query parameters; a new client-side ChangesProfile likes infinite-scroll pagination
Suggested reviewers
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hello! Thank you for opening your first PR to npmx, @gittihub-jpg! 🚀 Here’s what will happen next:
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
👋🏼 Hello @gittihub-jpg! Thank you for the PR. May I ask if you've familiarized yourself with our policy? |
|
Hi, I take it your referring to the misconfigured description. |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/pages/profile/`[identity]/index.vue:
- Line 324: The v-for rendering PackageLikeCard over allLikesRecords lacks a
unique key; update the loop to add a :key using the item's unique identifier
(use like.value.subjectRef) so Vue can track elements correctly—i.e., in the
PackageLikeCard v-for add :key="like.value.subjectRef" (or a stable fallback if
subjectRef can be missing) to the component.
- Around line 114-116: The computed isLoadingInitialLikes currently returns true
whenever allLikesRecords.value is empty and likesError.value is falsy, which
incorrectly shows the skeleton for users who truly have zero likes; add an
explicit reactive loading flag (e.g., isFetchingInitialLikes ref) and set it to
true immediately before the likes fetch begins and false in both the success and
error paths of the fetch function (the function that loads likes/initial data),
then update isLoadingInitialLikes to include this flag
(isFetchingInitialLikes.value && allLikesRecords.value.length === 0 &&
!likesError.value) so the skeleton only appears while the request is in flight.
In `@server/api/social/profile/`[identifier]/likes.get.ts:
- Around line 14-15: The current limit computation for variable "limit" can
become NaN when query.limit is a non-numeric string; update the logic in the
likes.get handler to parse and validate the value (e.g., const parsed =
Number(query.limit); if (!Number.isFinite(parsed) || Number.isNaN(parsed)) use
default 20) and then clamp with Math.max/Math.min (or use parseInt with base 10
and similar validation) so that "limit" is always an integer between 1 and 100;
locate and replace the current ternary that sets const limit with this validated
parsing-and-clamping logic.
- Line 21: The user-likes endpoint is broken because likes.get.ts parses and
forwards a cursor but likesUtil.getUserLikes (in
server/utils/atproto/utils/likes.ts) only accepts (miniDoc, limit) and calls
client.list(...) without a cursor; update getUserLikes to accept an optional
cursor parameter and pass it into client.list as the pagination cursor option,
and ensure likes.get.ts continues to forward the parsed cursor to
likesUtil.getUserLikes. Also harden limit parsing by replacing
Number(query.limit) with a safer parse that falls back to a default (e.g., 25)
when the input is non-numeric, and keep existing clamping logic to enforce
min/max bounds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ccdea5c6-178c-48bc-a466-b4a337c4ddd6
📒 Files selected for processing (4)
app/components/Package/Likes.vueapp/pages/profile/[identity]/index.vueapp/utils/atproto/likes.tsserver/api/social/profile/[identifier]/likes.get.ts
| const isLoadingInitialLikes = computed( | ||
| () => allLikesRecords.value.length === 0 && !likesError.value, | ||
| ) |
There was a problem hiding this comment.
Loading state logic incorrectly shows skeleton for users with zero likes.
isLoadingInitialLikes is true whenever allLikesRecords.value.length === 0 && !likesError.value. If a user genuinely has no likes, this condition remains true indefinitely, causing the skeleton to display forever.
Add an explicit loading flag that is set before the fetch and cleared after.
Proposed fix
const allLikesRecords = ref<Array<{ value: { subjectRef: string } }>>([])
const likesCursor = shallowRef<string | null>(null)
const likesLoadingMore = shallowRef(false)
const likesError = shallowRef(false)
+const likesLoadingInitial = shallowRef(true)
async function loadInitialLikes() {
+ likesLoadingInitial.value = true
try {
const result = await fetchProfileLikes(identity.value, null, 20)
allLikesRecords.value = result.records
likesCursor.value = result.cursor
likesError.value = false
} catch {
likesError.value = true
+ } finally {
+ likesLoadingInitial.value = false
}
}
-const isLoadingInitialLikes = computed(
- () => allLikesRecords.value.length === 0 && !likesError.value,
-)
+const isLoadingInitialLikes = computed(() => likesLoadingInitial.value)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/pages/profile/`[identity]/index.vue around lines 114 - 116, The computed
isLoadingInitialLikes currently returns true whenever allLikesRecords.value is
empty and likesError.value is falsy, which incorrectly shows the skeleton for
users who truly have zero likes; add an explicit reactive loading flag (e.g.,
isFetchingInitialLikes ref) and set it to true immediately before the likes
fetch begins and false in both the success and error paths of the fetch function
(the function that loads likes/initial data), then update isLoadingInitialLikes
to include this flag (isFetchingInitialLikes.value &&
allLikesRecords.value.length === 0 && !likesError.value) so the skeleton only
appears while the request is in flight.
| <div v-else-if="likes?.records" class="grid grid-cols-1 lg:grid-cols-2 gap-4"> | ||
| <PackageLikeCard v-for="like in likes.records" :packageUrl="like.value.subjectRef" /> | ||
| <div v-else-if="allLikesRecords.length > 0" class="grid grid-cols-1 lg:grid-cols-2 gap-4"> | ||
| <PackageLikeCard v-for="like in allLikesRecords" :packageUrl="like.value.subjectRef" /> |
There was a problem hiding this comment.
Missing :key binding in v-for loop.
Vue requires a unique key for each item in a v-for to efficiently track and update DOM elements. Use subjectRef as the key since it should be unique per liked package.
Proposed fix
- <PackageLikeCard v-for="like in allLikesRecords" :packageUrl="like.value.subjectRef" />
+ <PackageLikeCard
+ v-for="like in allLikesRecords"
+ :key="like.value.subjectRef"
+ :packageUrl="like.value.subjectRef"
+ />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/pages/profile/`[identity]/index.vue at line 324, The v-for rendering
PackageLikeCard over allLikesRecords lacks a unique key; update the loop to add
a :key using the item's unique identifier (use like.value.subjectRef) so Vue can
track elements correctly—i.e., in the PackageLikeCard v-for add
:key="like.value.subjectRef" (or a stable fallback if subjectRef can be missing)
to the component.
| const limit = | ||
| typeof query.limit === 'string' ? Math.min(Math.max(Number(query.limit), 1), 100) : 20 |
There was a problem hiding this comment.
Guard against NaN when parsing limit.
If query.limit is a non-numeric string (e.g. "abc"), Number() returns NaN, and Math.min(Math.max(NaN, 1), 100) evaluates to NaN. This would propagate an invalid limit downstream.
Proposed fix
- const limit =
- typeof query.limit === 'string' ? Math.min(Math.max(Number(query.limit), 1), 100) : 20
+ const parsedLimit = typeof query.limit === 'string' ? Number(query.limit) : NaN
+ const limit = Number.isNaN(parsedLimit) ? 20 : Math.min(Math.max(parsedLimit, 1), 100)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const limit = | |
| typeof query.limit === 'string' ? Math.min(Math.max(Number(query.limit), 1), 100) : 20 | |
| const parsedLimit = typeof query.limit === 'string' ? Number(query.limit) : NaN | |
| const limit = Number.isNaN(parsedLimit) ? 20 : Math.min(Math.max(parsedLimit, 1), 100) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/api/social/profile/`[identifier]/likes.get.ts around lines 14 - 15,
The current limit computation for variable "limit" can become NaN when
query.limit is a non-numeric string; update the logic in the likes.get handler
to parse and validate the value (e.g., const parsed = Number(query.limit); if
(!Number.isFinite(parsed) || Number.isNaN(parsed)) use default 20) and then
clamp with Math.max/Math.min (or use parseInt with base 10 and similar
validation) so that "limit" is always an integer between 1 and 100; locate and
replace the current ternary that sets const limit with this validated
parsing-and-clamping logic.
| const likesUtil = new PackageLikesUtils() | ||
|
|
||
| return likesUtil.getUserLikes(minidoc) | ||
| return likesUtil.getUserLikes(minidoc, limit, cursor) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the getUserLikes function signature and implementation
ast-grep --pattern $'async getUserLikes($$$) {
$$$
}'Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate the likes util file and the getUserLikes definition(s)
echo "== Searching for getUserLikes definition =="
rg -n "getUserLikes" server/utils/atproto/utils/likes.ts server -S || true
# 2) Print the relevant section around the definition in likes.ts
echo
echo "== Showing surrounding lines in server/utils/atproto/utils/likes.ts =="
python3 - <<'PY'
import re, pathlib
path = pathlib.Path("server/utils/atproto/utils/likes.ts")
if not path.exists():
print("File not found:", path)
raise SystemExit(0)
txt = path.read_text(encoding="utf-8", errors="replace").splitlines()
for i,line in enumerate(txt, start=1):
if "getUserLikes" in line:
start=max(1,i-20); end=min(len(txt), i+60)
print(f"\n--- {path}:{start}-{end} (match at line {i}) ---")
for j in range(start,end+1):
print(f"{j:5d}: {txt[j-1]}")
PY
# 3) Inspect endpoint file around the call site
echo
echo "== Showing endpoint server/api/social/profile/[identifier]/likes.get.ts around call =="
python3 - <<'PY'
import pathlib
path = pathlib.Path("server/api/social/profile/[identifier]/likes.get.ts")
if not path.exists():
print("File not found:", path)
raise SystemExit(0)
txt = path.read_text(encoding="utf-8", errors="replace").splitlines()
# print around the return line(s)
for i,line in enumerate(txt, start=1):
if "getUserLikes" in line:
start=max(1,i-25); end=min(len(txt), i+10)
print(f"\n--- {path}:{start}-{end} (match at line {i}) ---")
for j in range(start,end+1):
print(f"{j:5d}: {txt[j-1]}")
PY
# 4) Try a more specific ast-grep for the signature: `getUserLikes(` and capture params count.
echo
echo "== ast-grep: look for `getUserLikes(` and show function body =="
ast-grep --pattern $'getUserLikes($_, $_) { $$$ }' server/utils/atproto/utils/likes.ts || true
ast-grep --pattern $'getUserLikes($_, $_, $_) { $$$ }' server/utils/atproto/utils/likes.ts || trueRepository: npmx-dev/npmx.dev
Length of output: 4159
Fix cursor pagination for user likes endpoint
server/api/social/profile/[identifier]/likes.get.tsparsescursorand passes it tolikesUtil.getUserLikes(minidoc, limit, cursor), butserver/utils/atproto/utils/likes.tsdefinesgetUserLikes(miniDoc, limit)(no cursor param) and callsclient.list(...)without a cursor option—pagination can’t advance past the first page.limitclamping can produceNaNfor non-numericquery.limitvalues (Number(...)→NaN), so it should fall back to a safe default.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/api/social/profile/`[identifier]/likes.get.ts at line 21, The
user-likes endpoint is broken because likes.get.ts parses and forwards a cursor
but likesUtil.getUserLikes (in server/utils/atproto/utils/likes.ts) only accepts
(miniDoc, limit) and calls client.list(...) without a cursor; update
getUserLikes to accept an optional cursor parameter and pass it into client.list
as the pagination cursor option, and ensure likes.get.ts continues to forward
the parsed cursor to likesUtil.getUserLikes. Also harden limit parsing by
replacing Number(query.limit) with a safer parse that falls back to a default
(e.g., 25) when the input is non-numeric, and keep existing clamping logic to
enforce min/max bounds.
Closes #2817
🔗 Linked issue
#2817
🧭 Context
The profile page hardcodes a 10-like limit without pagination or visual indicators, obscuring actual engagement data.
📚 Description
Replaced the hardcoded limit with proper pagination support for ATproto feed requests. Implemented infinite scroll on the profile view and added a total count indicator to the likes component.
📝 Special notes
server/api/social/profile/[identifier]/likes.get.ts— Removed hardcoded limit, added pagination parametersapp/utils/atproto/likes.ts— Updated to handle paginated ATproto requestsapp/pages/profile/[identity]/index.vue— Added infinite scroll implementationapp/components/Package/Likes.vue— Rendered full list with total count indicator