Skip to content

Conversation

@rickstaa
Copy link
Member

@rickstaa rickstaa commented Feb 3, 2026

Fix participation logic so it works when orchestrators are activated and deactivated. Also improve caching behavoir.

Jipperism and others added 30 commits December 22, 2025 11:05
- Introduced a new component to present a list of orchestrators with their voting statistics, including proposals voted on, votes casted, recent votes, and voting turnout.
- Added Cube.js client for data fetching with new utility functions for querying voting history.
- Introduced VotingHistoryView component to display voting statistics, including proposals voted on and voting turnout.
- Updated package.json and pnpm-lock.yaml to include Cube.js client dependencies.
- Exported VoterSummary type for better type management in voting components.
- Integrated VotingHistoryView into the account layout for displaying voting statistics.
- Added new VotingHistory page to handle routing and data fetching for user voting history.
- Updated account layout to include a new tab for voting history, improving user navigation.
- Implemented utility functions for processing and summarizing voting data from Cube.js.
- Added OrchestratorVotingList component to display voting data for orchestrators.
- Implemented tabbed interface for Yield Overview and Voting History sections.
- Integrated voter summary processing functions to aggregate voting data.
- Updated getStaticProps to fetch and pass initial voter data to the page.
- Adds treasury proposals voting overview
- Changed the method of accessing raw data from Cube.js API to use a function call.
- Simplified response validation in getStaticProps by removing unnecessary checks and directly using the response for voter summaries.
- Fetches voting metrics (voted/eligible) from Cube.js using LivepeerVoteProposals
- Adds an interactive Stat card to OrchestratingView with a visual progress bar
- Displays participation rate percentage and integrated link to account history
- Adds  utility to the Cube query generator
…nology

- Moved to a friendlier "Sky & Tomato" color scheme to keep voting separate from our main green brand accent.
- Standardized labels to "For / Against / Abstain" everywhere, including the global transaction feed.
- Rebuilt the vote table using our core data table component for a consistent look.
- Made it easier to explore voter history with new dedicated buttons, better tooltips, and subtle animations.
- Cleaned up the layout by stripping out redundant titles and shrinking fonts where they were getting too loud.
- Polished the details: added specific styling for "No reason provided" and refined how voter profiles are we showing voter names and txn links.
- Eliminated the agent log useEffect that was posting debug information.
- Streamlined imports by removing unnecessary useEffect import.
…List

- Changed badge colors to use a "Sky & Tomato" color scheme for better visual distinction.
- Updated badge labels from "Yes/No" to "For/Against" to standardize terminology across the application.
- Enhance typography hierarchy and spacing in the 'Your vote' section.
- Implement translucent button styles with high-contrast labels and subtle borders.
- Improve progress bar visibility and color contrast for better scannability.
- Adjust voting reason input for improved visual balance and focus states.

Fixes #464
- Eliminated the 'Voting History' tab from the account layout.
- Removed related imports and conditional rendering for improved code clarity.
- Introduced GraphQL queries for fetching treasury vote events and vote events.
- Implemented corresponding React hooks for easy integration in components.
- Enhanced the data structure to include relevant fields such as proposal details and voter information.
- Added functionality to fetch and display treasury vote events and vote events in the HistoryView component.
- Enhanced data handling with hooks to manage and extend event data, including proposal details and attributes.
- Updated rendering logic to accommodate new event types and improve user experience with detailed information on votes.
- Deleted the Cube.js client and associated query generator as their functionality has been replaced by direct GraphQL queries.
- Updated components to utilize new treasury proposal and vote queries, enhancing data fetching and management.
- Refactored the OrchestratingView component to integrate treasury votes and proposals directly through Apollo hooks.
- Updated the TreasuryVotesQuery to include the voter ID for better data management.
- Integrated ENS name fetching for voters in the VoteTable component, improving user experience.
- Refactored vote rendering logic in DesktopVoteTable and MobileVoteTable to utilize the new voting support mapping.
- Enhanced error handling in VoteTable to display error messages more clearly.
- Improved overall data structure and state management for votes and events.
thebeyondr and others added 21 commits January 28, 2026 15:27
…rast

- Add mobile card layout matching MobileVoteView, use TransactionBadge component
- Hide timeline on mobile, improve contrast with $loContrast modal background
- Maintain desktop timeline layout
- Display formatted timestamp (MM/DD/YYYY h:mm a) below proposal ID
- Show on both mobile and desktop layouts
- Move transaction timestamp to bottom row grouped with transaction badge
- Narrow VoteModal desktop width from 50% to 40% with 600px max-width
- Improve reason text readability with shorter line lengths
- Better visual hierarchy by grouping transaction metadata together
- Redesign VoteDetail and MobileVoteView cards with improved visual hierarchy
- Make vote badge the hero element (top-left, larger size with icon)
- Remove redundant visual signals (left border, truncated proposal ID)
- Implement collapsible reason display (collapsed by default) for cleaner layout
- Simplify footer to single-line transaction badge and timestamp
- Add transaction timestamp to VoteItem for better context
- Refactor spacing and layout for consistency across mobile and desktop views
- Align card styling with Livepeer design system patterns
* refactor: improve mobile vote item hover behaviour

Ensure the hover behaviour is in the voting item is consistent with the
hover behavoir used in the rest of the explorer.

* refactor: reuse DataTable pagination for mobile vote tabke

Reuse the existing arrow-only pagination from DataTable in the mobile voting table
to ensure consistent behavior and styling.
- Add gap between badge and title on mobile
- Align badge left edge with title text
* refactor: simplify transactionlist treasury parsing

Updates the events query so that we can use it directly for parsing the
treasury voting events.

* chore: fix history view type errors

Ensure types in the history view correctly account for the new proposal
event.
- Introduced a new VoteHistoryModal component to enhance user experience by allowing users to view their voting history in a modal format.
- Updated VotePopover to utilize the new VoteHistoryModal instead of the previous VoteModal.
Remove debug query which was accidentally added in
#529.
Ensures that the linking behavoir for the other events is similar to the
voting behavoir. Also create an extra ipfs helper so code is cleaner.
Fix participation logic so it works when orchestrators are activated and
deactivated. Also improve caching behavoir.
@rickstaa rickstaa requested a review from ECWireless as a code owner February 3, 2026 20:59
@vercel
Copy link

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explorer-arbitrum-one Ready Ready Preview, Comment Feb 3, 2026 9:01pm

Request Review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the governance participation calculation logic to properly handle orchestrator activation and deactivation cycles, ensuring that only proposals from periods when an orchestrator was active are counted toward their participation rate. The refactoring also improves caching behavior by using cache-and-network fetch policy consistently.

Changes:

  • Replaced the generic transcoderActivatedEvents query with a specific transcoderActivationHistory query that fetches both activation and deactivation events
  • Created a new useGovernanceParticipation hook that encapsulates the logic for calculating governance participation based on activation windows
  • Refactored OrchestratingView component to use the new hook instead of inline query logic

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
queries/transcoderActivationHistory.graphql New GraphQL query that fetches both activation and deactivation events for a delegate in chronological order
queries/transcoderActivatedEvents.graphql Removed generic query that only fetched activation events with flexible parameters
hooks/useGovernanceParticipation.tsx New hook implementing window-based participation calculation logic with activation/deactivation tracking
components/OrchestratingView/index.tsx Simplified to use new hook, extracted participation calculation to improve readability
apollo/subgraph.ts Updated generated TypeScript types reflecting query changes and added JSDoc formatting improvements

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +74 to +75
const hasMultipleWindows =
activations.length > 1 || (deactivations?.length ?? 0) > 0;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The condition for determining when to skip building windows may be incorrect. When there's exactly one activation and zero deactivations (the most common case for an active orchestrator), hasMultipleWindows is false, so buildActiveWindows is not called, and an empty array is used for windows. However, in the treasuryParticipation calculation, when hasMultipleWindows is false, the code counts ALL proposals and votes without filtering. This assumes that if there's only one activation, all proposals since that activation should be counted.

However, the condition activations.length > 1 || (deactivations?.length ?? 0) > 0 is true when there are multiple activations OR any deactivations. If there's one activation and one deactivation, windows would be built correctly. But the logic doesn't account for the case where there's one activation but the orchestrator is currently deactivated - in that scenario, it should still build windows to properly exclude proposals after deactivation.

Copilot uses AI. Check for mistakes.

const firstActivationRound = activations[0]?.activationRound;
const hasMultipleWindows =
activations.length > 1 || (deactivations?.length ?? 0) > 0;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The optional chaining operator and nullish coalescing are used inconsistently. On line 69, deactivations uses nullish coalescing ?? 0 when checking the length on line 75, but deactivations is already defined as a memoized empty array on line 69 if the data is undefined. The ?? 0 is unnecessary since deactivations.length will never be null or undefined - it will always be a number (at minimum 0 for an empty array).

Suggested change
activations.length > 1 || (deactivations?.length ?? 0) > 0;
activations.length > 1 || deactivations.length > 0;

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +59
variables: { delegate: delegateId ?? "" },
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});

const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
variables: { where: { voter: delegateId ?? "" } },
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The query will execute with an empty string for the delegate variable when delegateId is undefined, even though skip is set to true. While the skip prevents the query from running, it's better to ensure the variables are not set at all when the query is skipped, or to use a more defensive approach. Consider passing null or not passing the variables object when hasDelegate is false.

Suggested change
variables: { delegate: delegateId ?? "" },
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});
const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
variables: { where: { voter: delegateId ?? "" } },
...(hasDelegate ? { variables: { delegate: delegateId! } } : {}),
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});
const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
...(hasDelegate ? { variables: { where: { voter: delegateId! } } } : {}),

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +59
variables: { delegate: delegateId ?? "" },
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});

const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
variables: { where: { voter: delegateId ?? "" } },
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

Similar to the activation history query, this query passes an empty string for voter when delegateId is undefined, even though skip is set to true. Consider using the same defensive pattern throughout for consistency.

Suggested change
variables: { delegate: delegateId ?? "" },
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});
const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
variables: { where: { voter: delegateId ?? "" } },
variables: hasDelegate ? { delegate: delegateId } : undefined,
fetchPolicy: "cache-and-network",
skip: !hasDelegate,
});
const { data: votesData, loading: votesLoading } = useTreasuryVotesQuery({
variables: hasDelegate ? { where: { voter: delegateId } } : undefined,

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +32
start = round;
} else if (start !== null && round > start) {
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The buildActiveWindows function has a logic issue when handling consecutive activations without intervening deactivations. If there are multiple activation events without a deactivation between them, only the first activation's start round will be recorded and subsequent activations will be ignored. This will lead to incorrect window calculations.

For example, if there are two activation events at rounds 100 and 200, the current logic will set start=100 when it sees round 100, then set start=200 when it sees round 200, overwriting the first one. The window from round 100 won't be properly closed or handled.

Additionally, the condition round > start on line 32 means that if a deactivation happens at the same round as the activation, it won't create a window. This edge case should be explicitly handled or documented.

Suggested change
start = round;
} else if (start !== null && round > start) {
// Only open a new window if we're not already active.
if (start === null) {
start = round;
}
} else if (start !== null && round >= start) {

Copilot uses AI. Check for mistakes.
round: Number(d.deactivationRound),
type: "deactivation" as const,
})),
].sort((a, b) => a.round - b.round || (a.type === "deactivation" ? -1 : 1));
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The sort comparator has a tie-breaking rule that prioritizes deactivations over activations when they occur in the same round. However, this behavior may not align with the actual blockchain event ordering. If activation and deactivation events can occur in the same round, the order should be based on transaction ordering or event log index, not an arbitrary prioritization. This could lead to incorrect window calculations if the actual order differs from this assumption.

Suggested change
].sort((a, b) => a.round - b.round || (a.type === "deactivation" ? -1 : 1));
].sort((a, b) => a.round - b.round);

Copilot uses AI. Check for mistakes.
Base automatically changed from 310-showcase-orchestrator-voting-activity-in-the-ui to main February 4, 2026 03:16
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.

6 participants