Skip to content

Fix Proposal votes, chats websocket#602

Merged
feruzm merged 2 commits into
developfrom
prop
Jan 3, 2026
Merged

Fix Proposal votes, chats websocket#602
feruzm merged 2 commits into
developfrom
prop

Conversation

@feruzm
Copy link
Copy Markdown
Member

@feruzm feruzm commented Jan 3, 2026

Summary by CodeRabbit

  • New Features

    • Added loading and initialization error messages for chats and community channels
    • Added a new query to fetch all proposal votes for a user in a single request
  • Improvements

    • Optimized proposal vote handling and lookup to avoid per-item fetching
    • Introduced a viewer-voted flag passed into proposal list items for accurate styling/UX
    • Adjusted proposal-vote fetching to ensure fresh data on mount

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

Replaced per-item proposal vote fetches with a single bulk user-votes query, adjusted proposal-votes query fetch behavior, and added explicit bootstrap-loading and init-error guards across chat components to avoid partial rendering.

Changes

Cohort / File(s) Summary
API — Proposal votes queries
apps/web/src/api/queries/get-proposal-votes-query.ts, apps/web/src/api/queries/get-user-proposal-votes-query.ts, apps/web/src/api/queries/index.ts
Added getUserProposalVotesQuery(voter) to fetch all votes for a voter in one call (cached 60s) and exported it. Modified existing get-proposal-votes-query to remove initialData and set staleTime: 0; small parameter/flow cleanup in queryFn.
Proposals page & components
apps/web/src/app/proposals/_page.tsx, apps/web/src/app/proposals/_components/proposal-list-item/index.tsx, apps/web/src/app/proposals/_components/proposal-votes/index.tsx
Fetch all user votes once on the proposals page, build a Set for quick lookup, pass new votedByViewer prop into ProposalListItem. Removed per-item vote fetching and related state from list item; one formatting-only blank-line change in proposal-votes.
Chat components — loading / init guards
apps/web/src/app/chats/[id]/channel/_components/community-channel-client.tsx, apps/web/src/app/chats/[id]/page.tsx, apps/web/src/app/chats/_components/chats-client.tsx, apps/web/src/app/chats/_components/chats-page-client.tsx
Added bootstrap/isLoading guards and explicit UI for "Loading chat…" and "Unable to initialize chat", handled bootstrap-without-channelId error path, and adjusted effect dependencies to include router and params.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Client UI
  participant QMgr as EcencyQueriesManager
  participant Hive as Hive API (database_api)

  UI->>QMgr: getUserProposalVotesQuery(voter)
  QMgr->>Hive: list_proposal_votes({start: voter, order: "voter_proposal", limit:1000})
  Hive-->>QMgr: ProposalVote[] (may include extra ordering results)
  QMgr-->>UI: ProposalVote[] (filtered where vote.voter == voter)
  note right of UI `#D6EAF8`: UI builds Set of proposal IDs\nand passes votedByViewer to items
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through code with eager paws,
Bundled votes now skip the claws,
Loading guards keep UI calm and bright,
One query fetches through the night,
A little rabbit cheers the merge tonight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is overly vague and generic. It mentions 'proposal votes' and 'chats websocket' but does not clearly convey the main changes, which involve optimizing proposal votes querying, refactoring chat loading states, and introducing a user votes query system. Replace with a more specific title that captures the primary change, such as 'Optimize proposal votes querying with getUserProposalVotesQuery' or 'Add user votes optimization and improve chat/proposal loading states'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/web/src/api/queries/get-user-proposal-votes-query.ts (1)

13-18: Remove redundant voter validation.

The enabled check on line 13 and the guard clause on lines 16-18 serve the same purpose. The enabled check is more efficient as it prevents the query from running at all, making the guard clause unnecessary.

🔎 Proposed refactoring to remove redundant guard clause
  EcencyQueriesManager.generateClientServerQuery<ProposalVote[]>({
    queryKey: [QueryIdentifiers.PROPOSAL_VOTES, "by_user", voter],
    enabled: !!voter && voter !== "", // Only run if voter is specified
    staleTime: 60 * 1000, // Cache for 1 minute
    queryFn: async () => {
-     if (!voter || voter === "") {
-       return [];
-     }
-
      const response = (await client.call("database_api", "list_proposal_votes", {
apps/web/src/app/proposals/_components/proposal-list-item/index.tsx (1)

3-3: Remove unused useMemo import.

The useMemo hook is no longer used after removing the client-side votes calculation logic. Clean up the import to keep dependencies lean.

🔎 Proposed fix
-import React, { useMemo, useState } from "react";
+import React, { useState } from "react";
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9883d and c2c3696.

📒 Files selected for processing (10)
  • apps/web/src/api/queries/get-proposal-votes-query.ts
  • apps/web/src/api/queries/get-user-proposal-votes-query.ts
  • apps/web/src/api/queries/index.ts
  • apps/web/src/app/chats/[id]/channel/_components/community-channel-client.tsx
  • apps/web/src/app/chats/[id]/page.tsx
  • apps/web/src/app/chats/_components/chats-client.tsx
  • apps/web/src/app/chats/_components/chats-page-client.tsx
  • apps/web/src/app/proposals/_components/proposal-list-item/index.tsx
  • apps/web/src/app/proposals/_components/proposal-votes/index.tsx
  • apps/web/src/app/proposals/_page.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/src/api/queries/get-user-proposal-votes-query.ts (2)
apps/web/src/api/hive.ts (1)
  • client (48-52)
apps/web/src/api/operations.ts (1)
  • vote (352-371)
apps/web/src/api/queries/get-proposal-votes-query.ts (1)
apps/web/src/api/hive.ts (1)
  • client (48-52)
apps/web/src/app/proposals/_page.tsx (2)
apps/web/src/api/queries/get-user-proposal-votes-query.ts (1)
  • getUserProposalVotesQuery (10-35)
apps/web/src/app/proposals/_components/proposal-list-item/index.tsx (1)
  • ProposalListItem (24-142)
🔇 Additional comments (11)
apps/web/src/app/chats/[id]/page.tsx (1)

36-50: LGTM! Proper loading and initialization guards.

The addition of explicit loading and bootstrap checks ensures the chat UI only renders when initialization is complete, preventing potential runtime errors and improving user feedback.

apps/web/src/app/chats/_components/chats-page-client.tsx (2)

89-96: LGTM! Consistent bootstrap guard implementation.

The loading guard ensures the channel view waits for bootstrap completion, aligning with the broader pattern across chat components in this PR.


98-106: LGTM! Improved no-channels message clarity.

The refined conditional correctly distinguishes between "still loading" vs "no channels available" states, providing clearer user feedback.

apps/web/src/app/chats/_components/chats-client.tsx (1)

375-382: LGTM! Bootstrap guard prevents premature channel list rendering.

The loading guard ensures the channel list waits for bootstrap completion, maintaining consistency with the initialization pattern applied across all chat components in this PR.

apps/web/src/api/queries/get-proposal-votes-query.ts (2)

23-26: LGTM: Improved query configuration for fresh data.

The removal of initialData and addition of staleTime: 0 ensures the query always refetches on mount, providing fresh proposal votes data. The comment clearly documents the reasoning.


29-35: Good refactoring: Cleaner parameter handling.

The introduction of startParam with explicit type annotation improves code clarity and makes the pageParam fallback logic more readable.

apps/web/src/api/queries/index.ts (1)

39-39: LGTM: New query export properly integrated.

The new get-user-proposal-votes-query export is correctly positioned and follows the existing pattern.

apps/web/src/app/proposals/_page.tsx (2)

38-47: Excellent optimization: Single bulk fetch instead of per-item queries.

This change significantly improves performance by fetching all user votes in one query and using a Set for O(1) lookup. The fallback chain from URL param to active user to empty string is well-designed.


219-223: Clean integration: votedByViewer prop properly computed and passed.

The voted state is efficiently determined using Set membership and passed to ProposalListItem. This aligns with the optimization goal of avoiding per-item queries.

Note: This depends on the 1000 vote limit in getUserProposalVotesQuery. If users can exceed 1000 votes, some proposals may incorrectly show as not voted.

apps/web/src/api/queries/get-user-proposal-votes-query.ts (1)

20-26: This API choice is correct and intentional. The two queries have distinct purposes: get-user-proposal-votes-query.ts uses database_api to fetch all votes by a specific user (using by_voter_proposal ordering), while get-proposal-votes-query.ts uses condenser_api to fetch votes for a specific proposal (using by_proposal_voter ordering). Both endpoints support their respective parameters: database_api accepts named parameters with status filtering and returns data wrapped in {proposal_votes: [...]}, while condenser_api accepts positional parameters and returns a plain array. The implementation is correct, including the defensive filter on lines 28-31 that ensures only the requesting user's votes are returned.

apps/web/src/app/proposals/_components/proposal-list-item/index.tsx (1)

21-21: Optimization correctly implemented end-to-end.

The refactor to accept votedByViewer as a prop eliminates per-item vote queries, moving to a single bulk query via getUserProposalVotesQuery. The parent component (_page.tsx) correctly implements this: it fetches all user votes once, builds a set of voted proposal IDs, and passes the boolean status to each ProposalListItem. The prop design is clean with a sensible default and clear documentation.

Comment thread apps/web/src/api/queries/get-user-proposal-votes-query.ts
Comment thread apps/web/src/app/proposals/_page.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/src/app/chats/[id]/channel/_components/community-channel-client.tsx (2)

26-26: Consider simplifying the dependency array.

The dependency array includes both data?.channelId and data. Since data?.channelId will change whenever data changes, including both is redundant and may cause the effect to run more frequently than necessary.

🔎 Proposed optimization
-  }, [data?.channelId, data, router, params.id]);
+  }, [data, router, params.id]);

The conditional check if (data?.channelId) inside the effect will still work correctly with just data in the dependencies.


40-42: Consider more robust error type checking.

The error handling relies on substring matching (error?.message.includes("username")), which can be fragile if the error message format changes. Consider using error codes or error types if available from the API.

Note: This is an existing pattern in the codebase, not introduced by this PR, but worth considering for future refactoring.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2c3696 and 00c95bd.

📒 Files selected for processing (2)
  • apps/web/src/app/chats/[id]/channel/_components/community-channel-client.tsx
  • apps/web/src/app/proposals/_page.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/proposals/_page.tsx (3)
apps/web/src/api/queries/get-account-full-query.ts (1)
  • getAccountFullQuery (6-43)
apps/web/src/api/queries/get-user-proposal-votes-query.ts (1)
  • getUserProposalVotesQuery (10-35)
apps/web/src/app/proposals/_components/proposal-list-item/index.tsx (1)
  • ProposalListItem (24-142)
🔇 Additional comments (7)
apps/web/src/app/chats/[id]/channel/_components/community-channel-client.tsx (3)

44-50: LGTM! Clear loading state.

The explicit loading state with "Preparing the community channel…" provides good user feedback during the bootstrap process and aligns with the PR's objective of adding explicit bootstrap-loading guards.


52-72: Excellent fix for the missing channelId issue!

This error handling directly addresses the concern raised in the previous review about users getting stuck in a "Redirecting…" state when channelId is missing. The implementation provides:

  • A clear error message explaining the issue
  • Contextual information (community name)
  • A recovery action via the "Go to Chats" button

Great improvement to the user experience!


74-79: LGTM! Correct redirect flow.

The final return correctly shows "Redirecting…" only when a valid channelId exists. The useEffect will handle the actual navigation on the next tick. The comment on line 74 helpfully clarifies this intent.

apps/web/src/app/proposals/_page.tsx (4)

11-11: LGTM: Imports support the bulk-vote optimization.

The new imports (getUserProposalVotesQuery and useActiveAccount) are correctly used in the voter resolution and vote-fetching logic below.

Also applies to: 18-18


33-33: LGTM: Voter resolution logic is correct.

The fallback chain (?voter param → active user → empty string) correctly determines whose votes to highlight. When voterParam is empty, the query is disabled (per getUserProposalVotesQuery implementation), avoiding unnecessary API calls. This optimization replaces per-proposal fetches with a single bulk query.

Also applies to: 38-41


224-228: LGTM: Efficient lookup using the memoized Set.

The votedByViewer prop is correctly passed via a constant-time Set lookup. This cleanly replaces per-item vote fetches with a single bulk query.

Note: getUserProposalVotesQuery fetches up to 1000 votes (see apps/web/src/api/queries/get-user-proposal-votes-query.ts). While it's unlikely a user votes on >1000 proposals, consider whether pagination or a higher limit might be needed in the future.


44-52: Vote Set construction is safe and efficient.

The implementation correctly uses optional chaining (v.proposal?.proposal_id) and filters undefined values with a type predicate. The memoized Set provides O(1) lookup for the votedByViewer prop and the proposal_id field is confirmed in the Proposal entity.

@feruzm feruzm merged commit 95a7617 into develop Jan 3, 2026
1 check passed
@feruzm feruzm deleted the prop branch January 3, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant