Skip to content

Fix/pictique follow button#1069

Closed
Sahil2004 wants to merge 6 commits into
mainfrom
fix/pictiqueFollowButton
Closed

Fix/pictique follow button#1069
Sahil2004 wants to merge 6 commits into
mainfrom
fix/pictiqueFollowButton

Conversation

@Sahil2004

@Sahil2004 Sahil2004 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Description of change

Critical for deployment

You need to deploy both pictique client and api for the bug to be fixed.

General description of the change

Fixed the follow and following button bug. Currently the follow and following button weren't working, so just fixed that.

Issue Number

Closes #1031
Closes #1057

Type of change

  • Fix (a change which fixes an issue)

How the change has been tested

Manually.

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • New Features

    • Profile pages now show accurate follower and following counts.
    • Follow buttons now reflect the current follow state and display a pending/loading indicator while follow requests are in progress.
  • Bug Fixes

    • Prevented users from following themselves.
    • Follow state is now correctly determined per viewer, so profiles properly show whether you’re following someone.
    • Improved follow behavior to recover from follow-graph inconsistencies.
    • Fixed follow requests to use the correct endpoint.

@Sahil2004 Sahil2004 self-assigned this Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Sahil2004, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 66d0d125-68ad-4aed-939a-051b302811cc

📥 Commits

Reviewing files that changed from the base of the PR and between c1e018e and 7b0b31b.

📒 Files selected for processing (1)
  • platforms/pictique/api/src/services/UserService.ts
📝 Walkthrough

Walkthrough

The follow flow now returns viewer-aware profile data, repairs follow relations on both sides, and updates the client to use the new follow state and counts. Separately, the Chat relation mapping and a web3 subscriber ID lookup are corrected.

Changes

Follow Persistence Fix

Layer / File(s) Summary
Follow service and profile response logic
platforms/pictique/api/src/controllers/UserController.ts, platforms/pictique/api/src/services/UserService.ts
Controller passes requester id to getProfileById; followUser blocks self-follow, repairs missing follow entries on both sides, and returns the updated target profile; getProfileById accepts optional viewerId and returns counts plus computed isFollowing.
Client follow contract and API call
platforms/pictique/client/src/lib/types.ts, platforms/pictique/client/src/lib/stores/users.ts
userProfile gains isFollowing; followUser posts to /api/users/{followingId}/follow instead of a generic endpoint with a body payload.
Profile component follow state and stats
platforms/pictique/client/src/lib/fragments/Profile/Profile.svelte
Props replace isFollowing/didFollowed with followPending; the follow button derives state from profileData.isFollowing with a pending fallback, and stats render from profileData.
Profile page follow handler wiring
platforms/pictique/client/src/routes/(protected)/profile/[id]/+page.svelte
Introduces followPending, updates handleFollow guards and response handling, and passes followPending to Profile.

Unrelated Backend Fixes

Layer / File(s) Summary
Chat participants relation inverse side
platforms/pictique/api/src/database/entities/Chat.ts
participants now maps back to user.chats.
Subscriber junction-table id lookup fix
platforms/pictique/api/src/web3adapter/watchers/subscriber.ts
getGlobalId now uses parentEntity.id in the junction-table timeout path.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant UserController
  participant UserService
  participant Database

  Client->>UserController: POST /api/users/{followingId}/follow
  UserController->>UserService: followUser(followerId, followingId)
  UserService->>UserService: check self-follow
  UserService->>Database: load follower/following relations
  UserService->>Database: save repaired follow sides
  UserService->>UserService: getProfileById(followingId, viewerId)
  UserService->>Database: fetch followers/following ids and posts
  UserService-->>UserController: updated profile with isFollowing and counts
  UserController-->>Client: profile response
Loading

Suggested reviewers: coodos, sosweetham

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Chat relation and Web3 subscriber changes are unrelated to the follow-button issues. Remove or justify the Chat.ts and subscriber.ts edits unless they are needed for the follow-button fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly matches the follow-button fix in this PR.
Description check ✅ Passed All required template sections are present and filled with reasonable detail.
Linked Issues check ✅ Passed The changes persist follow state and update follower counts, matching both linked issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pictiqueFollowButton

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Sahil2004 Sahil2004 marked this pull request as ready for review July 6, 2026 08:03
@Sahil2004 Sahil2004 requested a review from coodos as a code owner July 6, 2026 08:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
platforms/pictique/api/src/services/UserService.ts (1)

356-407: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Follow repair logic has a TOCTOU race — wrap in a transaction.

follower/following are read, checked, and conditionally saved with two independent awaited calls and no transaction. Two concurrent follow requests (double-tap, duplicate retry, multiple tabs) can both observe "missing" on the same side before either save commits, then both push+save — risking duplicate join-table rows or a unique-constraint violation that would resurface as a 500, i.e. the same kind of flaky persistence this PR is meant to fix.

🔒 Wrap the read/repair logic in a transaction
     followUser = async (followerId: string, followingId: string) => {
         if (followerId === followingId) {
             throw new Error("Cannot follow yourself");
         }
 
-        const follower = await this.userRepository.findOne({
-            where: { id: followerId },
-            relations: ["following"],
-        });
-
-        const following = await this.userRepository.findOne({
-            where: { id: followingId },
-            relations: ["followers"],
-        });
-
-        if (!follower || !following) {
-            throw new Error("User not found");
-        }
-
-        if (!follower.following) {
-            follower.following = [];
-        }
-        if (!following.followers) {
-            following.followers = [];
-        }
-
-        const missingFromFollowing = !follower.following.some(
-            (user) => user.id === followingId,
-        );
-        const missingFromFollowers = !following.followers.some(
-            (user) => user.id === followerId,
-        );
-
-        if (missingFromFollowing) {
-            follower.following.push(following);
-            await this.userRepository.save(follower);
-        }
-        if (missingFromFollowers) {
-            following.followers.push(follower);
-            await this.userRepository.save(following);
-        }
+        await AppDataSource.transaction(async (manager) => {
+            const follower = await manager.findOne(User, {
+                where: { id: followerId },
+                relations: ["following"],
+                lock: { mode: "pessimistic_write" },
+            });
+            const following = await manager.findOne(User, {
+                where: { id: followingId },
+                relations: ["followers"],
+                lock: { mode: "pessimistic_write" },
+            });
+
+            if (!follower || !following) {
+                throw new Error("User not found");
+            }
+
+            follower.following ??= [];
+            following.followers ??= [];
+
+            if (!follower.following.some((u) => u.id === followingId)) {
+                follower.following.push(following);
+                await manager.save(follower);
+            }
+            if (!following.followers.some((u) => u.id === followerId)) {
+                following.followers.push(follower);
+                await manager.save(following);
+            }
+        });
 
         return this.getProfileById(followingId, followerId);
     };
🤖 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 `@platforms/pictique/api/src/services/UserService.ts` around lines 356 - 407,
The follow repair flow in followUser currently reads, checks, and saves
follower/following with separate awaits, which leaves a TOCTOU race. Move the
read/repair logic into a single transaction using this.userRepository or the ORM
transaction API, and perform both the relationship checks and any needed saves
within that transaction so concurrent follow requests can’t both repair the same
missing link. Keep the behavior and return path from getProfileById the same,
but ensure the transactional block covers the follower, following,
missingFromFollowing, and missingFromFollowers updates.
🤖 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 `@platforms/pictique/api/src/services/UserService.ts`:
- Around line 409-425: The profile payload returned by getProfileById is missing
username, but Profile.svelte expects profileData.username. Update
UserService.getProfileById to include username in the selected fields, or map
the existing handle value onto username before returning the user object. Make
sure the response shape from getProfileById matches what the profile UI
consumes.

In `@platforms/pictique/client/src/routes/`(protected)/profile/[id]/+page.svelte:
- Line 27: The follow action is incorrectly reusing the page-level error state,
which causes the entire profile view to switch into the top-level error branch
after a failed follow. In the profile page component, keep the existing
fetch/load error separate and add a dedicated follow-specific state in the
handleFollow flow so failures only affect the follow UI. Update the Profile
rendering path and its error display logic to surface follow errors inline near
the button instead of triggering the {:else if error} fallback.

---

Outside diff comments:
In `@platforms/pictique/api/src/services/UserService.ts`:
- Around line 356-407: The follow repair flow in followUser currently reads,
checks, and saves follower/following with separate awaits, which leaves a TOCTOU
race. Move the read/repair logic into a single transaction using
this.userRepository or the ORM transaction API, and perform both the
relationship checks and any needed saves within that transaction so concurrent
follow requests can’t both repair the same missing link. Keep the behavior and
return path from getProfileById the same, but ensure the transactional block
covers the follower, following, missingFromFollowing, and missingFromFollowers
updates.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff4fb6db-d047-4627-a782-516776a52f6b

📥 Commits

Reviewing files that changed from the base of the PR and between c6f8c3e and bff3587.

📒 Files selected for processing (8)
  • platforms/pictique/api/src/controllers/UserController.ts
  • platforms/pictique/api/src/database/entities/Chat.ts
  • platforms/pictique/api/src/services/UserService.ts
  • platforms/pictique/api/src/web3adapter/watchers/subscriber.ts
  • platforms/pictique/client/src/lib/fragments/Profile/Profile.svelte
  • platforms/pictique/client/src/lib/stores/users.ts
  • platforms/pictique/client/src/lib/types.ts
  • platforms/pictique/client/src/routes/(protected)/profile/[id]/+page.svelte

Comment thread platforms/pictique/api/src/services/UserService.ts
@coodos coodos closed this Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants