Skip to content

fix: crisp voting ux fixes [skip-line-limit]#1053

Merged
ctrlc03 merged 12 commits into
mainfrom
chore/crisp-fixes
Dec 9, 2025
Merged

fix: crisp voting ux fixes [skip-line-limit]#1053
ctrlc03 merged 12 commits into
mainfrom
chore/crisp-fixes

Conversation

@hmzakhalid

@hmzakhalid hmzakhalid commented Nov 27, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Round-specific poll page and route; voting progress indicator; multi-toast system with per-toast controls; vote-status endpoint and session-scoped caching; support for vote updates.
  • Improvements

    • Expanded toast types (warning/info) plus persistent/duration options; clearer "Live"/current indicators and contextual vote counts; UI/UX tweaks across poll cards, daily/round polls, and casting flow; proof/update support.
  • Bug Fixes

    • Fixed timestamp-to-date conversion and date formatting.
  • Chores

    • Updated ignore list to exclude local AI tool settings.

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

@vercel

vercel Bot commented Nov 27, 2025

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
crisp Skipped Skipped Dec 9, 2025 0:30am
enclave-docs Skipped Skipped Dec 9, 2025 0:30am

@coderabbitai

coderabbitai Bot commented Nov 27, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds vote-update support, session-scoped vote-status caching, a multi-step casting UI with progress indicator, multi-toast notifications, threading of previousCiphertext through proof generation (SDK/WASM/worker), new /round/:roundId page, and server endpoints/models for vote status and update handling.

Changes

Cohort / File(s) Summary
Routing & new page
examples/CRISP/client/src/App.tsx, examples/CRISP/client/src/pages/RoundPoll/*
Adds /round/:roundId route and a new RoundPoll page that loads round state and redirects to /current when appropriate.
Vote management context
examples/CRISP/client/src/context/voteManagement/*
Adds session-scoped vote-status cache, exposes currentRoundId, hasVotedInCurrentRound, voteStatusLoading, sessionId, and APIs checkVoteStatus and markVotedInRound; removes existNewRound.
Multi-step voting flow & hooks
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts, examples/CRISP/client/src/hooks/wasm/useWebAssembly.tsx, examples/CRISP/client/libs/crispWorker.js, examples/CRISP/packages/crisp-sdk/src/types.ts
Implements voting steps (signing → encrypting → generating_proof → broadcasting), exposes step state and messages, threads previousCiphertext through SDK/WASM/worker, and updates generateProof and VoteProofInputs.
UI components & pages
examples/CRISP/client/src/components/Cards/PollCard.tsx, examples/CRISP/client/src/components/VotingStepIndicator.tsx, examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx, examples/CRISP/client/src/pages/DailyPoll/*, examples/CRISP/client/src/pages/HistoricPoll/*
PollCard polls active state and routes to round/current/result; adds VotingStepIndicator; DailyPollSection accepts title and shows casting/update-vote flows; minor loading and lint tweaks.
Notification system
examples/CRISP/client/src/context/NotificationAlert/*, examples/CRISP/client/src/components/ToastAlert.tsx, examples/CRISP/client/src/model/notification.model.ts
Migrates from single toast to capped toasts array, adds showToast/closeToast/clearAllToasts, persistent toasts, per-type icons for `warning
Server vote routes & models
examples/CRISP/server/src/server/routes/voting.rs, examples/CRISP/server/src/server/models.rs, examples/CRISP/server/src/server/repo.rs
Adds /voting/status endpoint and VoteStatus types; detects vote updates (is_vote_update), conditionally inserts voter address, includes update flag in responses, introduces error-extraction helper, and updates repo handling for updates.
API / client models & endpoints
examples/CRISP/client/src/model/vote.model.ts, examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts
Adds VoteStatusRequest/Response, removes user_already_voted status, adds optional is_vote_update to broadcast responses, and exposes getVoteStatus client call.
Utilities, scripts & misc
examples/CRISP/client/src/utils/methods.ts, examples/CRISP/scripts/dev_server.sh, .prettierignore, examples/CRISP/client/index.html
Fixes timestamp seconds bug and improves debounce typings; dev server now removes database dir before start; adds .claude/ to prettier ignore; removes empty og/twitter url meta tags.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant Client as Vote Client (hooks/ctx)
    participant Worker as Enclave/Worker
    participant Chain as Blockchain
    participant Server as Voting API
    participant Cache as Session Cache

    Note over User,UI: Page load → determine voted state
    User->>UI: Open poll page
    UI->>Client: request currentRoundId & hasVoted
    alt cached valid
        Client->>Cache: read(sessionId, roundId, address)
        Cache-->>Client: has_voted
    else cache miss/expired
        Client->>Server: POST /voting/status (roundId,address)
        Server-->>Client: VoteStatusResponse(has_voted, round_status)
        Client->>Cache: store(status, ttl)
    end
    Client-->>UI: render voted state

    Note over User,Chain: Cast or update vote flow
    User->>UI: select option & submit
    UI->>Client: castVoteWithProof(selection, hasVotedInCurrentRound)
    Client->>Client: step = signing
    Client->>Worker: request wallet signature
    Worker-->>Client: signature
    Client->>Client: step = encrypting
    alt update case
        Client->>Cache: fetch previousCiphertext
        Cache-->>Client: previousCiphertext
    end
    Client->>Worker: generate_proof(..., previousCiphertext?)
    Worker-->>Client: proof + ciphertext
    Client->>Chain: broadcast(tx with proof, ciphertext, is_vote_update)
    alt success
        Chain-->>Client: tx confirmed
        Client->>Cache: markVotedInRound
        Client-->>UI: show success
    else failure
        Chain-->>Client: error
        Client-->>UI: show persistent error toast
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

  • Areas needing extra attention:
    • Vote-status caching: key composition, TTL and session lifecycle in voteManagement.
    • Multi-step voting flow (useVoteCasting): step transitions, error handling, previousCiphertext usage.
    • Cross-layer type consistency for VoteStatus*, previousCiphertext, and proof inputs (SDK/WASM/worker).
    • Server voting.rs: update detection, conditional rollback, and error-extraction mapping.
    • Notification provider: concurrency, auto-dismiss and persistent-toast edge cases.

Possibly related PRs

Suggested reviewers

  • ryardley
  • cedoor
  • ctrlc03

Poem

🐰 I hopped in with a fluffy cheer,
Signed and proved with whiskers near,
Cached my choice in session light,
Toasts popped gently through the night,
Round by round — the poll's delight.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'fix: crisp voting ux fixes' is vague and generic, using non-descriptive terms like 'fixes' that don't convey meaningful information about the specific changes in the changeset. Replace with a more specific title that highlights the primary change, such as 'fix: enhance voting UI with step indicators and vote status tracking' or 'fix: improve voting experience with round navigation and status updates'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/crisp-fixes

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b931d01 and 40080ca.

📒 Files selected for processing (1)
  • examples/CRISP/server/src/server/repo.rs (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-05T06:49:46.285Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/enclave_node/src/datastore.rs:14-16
Timestamp: 2024-11-05T06:49:46.285Z
Learning: In `packages/ciphernode/enclave_node/src/datastore.rs`, for internal functions like `get_in_mem_store`, adding extensive documentation and error handling may not be necessary if they are not client-facing.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
📚 Learning: 2024-10-16T09:51:10.038Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/router/src/committee_meta.rs:43-43
Timestamp: 2024-10-16T09:51:10.038Z
Learning: In `packages/ciphernode/router/src/committee_meta.rs`, avoid suggesting making the `on_event` method in `CommitteMetaFeature` asynchronous or adding error handling for the `write` operation to the data store.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: crisp_unit
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_contracts
  • GitHub Check: build_sdk
  • GitHub Check: test_net
  • GitHub Check: rust_integration
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
🔇 Additional comments (3)
examples/CRISP/server/src/server/repo.rs (3)

148-151: Vote count derivation from has_voted is semantically correct.

Using has_voted.len() correctly counts unique voters rather than ciphertext entries, which aligns with the re-vote feature where one voter can update their vote without inflating the count.


208-226: Consistent vote count derivation in get_e3_state_lite.

This change aligns with get_vote_count() by deriving vote_count from has_voted.len(), ensuring consistent reporting across all API responses.


97-117: Re-vote logic is correctly protected by caller; no risk of duplicates in has_voted.

The calling code in voting.rs properly guards insert_voter_address with an if !has_voted check (line 114), ensuring the function is only called for new votes, never for re-votes. When a voter re-votes, insert_ciphertext_input alone is called to update the existing entry by index, while insert_voter_address is skipped. This design prevents duplicates in the has_voted vector and maintains an accurate vote count.

Likely an incorrect or invalid review comment.


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 and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

License Header Check Failed

Some files are missing the required SPDX license header. Please add the following header to the beginning of all .js, .jsx, .nr, .rs, .sol, .ts, and .tsx files:

// SPDX-License-Identifier: LGPL-3.0-only
//
// This file is provided WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

You can run ./scripts/check-license-headers.sh --fix locally to automatically add missing headers, then commit the changes.

Or run ./scripts/check-license-headers.sh to see which files need headers.

@hmzakhalid hmzakhalid changed the title fix: CRISP Voting UX fixes fix: CRISP Voting UX fixes [skip-line-limit] Nov 27, 2025

@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: 8

Caution

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

⚠️ Outside diff range comments (3)
examples/CRISP/client/src/pages/HistoricPoll/HistoricPoll.tsx (1)

34-42: The eslint-disable comment is inappropriate—getPastPolls is not memoized and should be added to the dependency array.

getPastPolls is defined as a regular async function (lines 155–167 in VoteManagement.context.tsx) without useCallback wrapping. It is passed directly into the context value without memoization (line 225), meaning a new function reference is created on every provider re-render.

The react-hooks/exhaustive-deps warning is correct here. Either:

  1. Wrap getPastPolls with useCallback in the provider, or
  2. Add getPastPolls to the dependency array: }, [votingRound, getPastPolls])

The current code works by accident because the effect only needs to run when votingRound changes, but this pattern is fragile and violates the eslint rule's intent.

examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (1)

79-84: Dynamic Tailwind class may not be included in production build.

The dynamic class border-${statusClass}-600/80 uses string interpolation which Tailwind's JIT compiler cannot detect at build time. This could result in missing styles in production.

Consider using explicit class mappings:

+  const borderClass = !isEnded ? 'border-lime-600/80' : 'border-red-600/80'
+  const bgClass = !isEnded ? 'bg-lime-400' : 'bg-red-400'
+
   <div
-    className={`flex items-center space-x-2 rounded-lg border-2 border-${statusClass}-600/80 ${!isEnded ? 'bg-lime-400' : 'bg-red-400'} px-2 py-1 text-center font-bold uppercase leading-none text-white`}
+    className={`flex items-center space-x-2 rounded-lg border-2 ${borderClass} ${bgClass} px-2 py-1 text-center font-bold uppercase leading-none text-white`}
   >

Alternatively, ensure these classes are safelisted in your Tailwind config.

examples/CRISP/server/src/server/routes/voting.rs (1)

141-152: Missing rollback on contract creation failure.

If the contract creation fails after inserting a new voter address (lines 119-127), the voter address is not rolled back. This could leave the database in an inconsistent state where the user is marked as having voted but their vote was never broadcast.

     let contract = match EnclaveContract::new(
         &CONFIG.http_rpc_url,
         &CONFIG.private_key,
         &CONFIG.enclave_address,
     )
     .await
     {
         Ok(c) => c,
         Err(e) => {
             error!("[e3_id={}] Contract creation error: {:?}", vote.round_id, e);
+            // Rollback voter insertion if this was a new vote
+            if !is_vote_update {
+                if let Err(err) = repo.remove_voter_address(&vote.address).await {
+                    error!("[e3_id={}] Error rolling back voter: {:?}", vote.round_id, err);
+                }
+            }
             return HttpResponse::InternalServerError().json("Internal server error");
         }
     };
🧹 Nitpick comments (11)
.prettierignore (1)

45-46: Organize entries into their respective sections for consistency.

The new .claude/ and .claude/settings.local.json entries are functionally correct but placed at the end without a section header, breaking the logical organization of the file. For maintainability, consider organizing them into the appropriate sections:

  • .claude/ is a directory → should go in the "directories" section (lines 2–14)
  • .claude/settings.local.json is a file → could go in the "files" section (lines 16–22) or under a new "configuration" or "IDE" section

Apply this diff to reorganize the entries:

# directories
node_modules
dist
build
cache
coverage
target
artifacts
types
deployments
.cargo
.next
.cache-synpress
.enclave
+.claude

# files
*.env
*.log
.DS_Store
.pnp.*
coverage.json
enclave.config.yaml
+.claude/settings.local.json

# lock files
package-lock.json
pnpm-lock.yaml
yarn.lock
Cargo.lock

# generated files
deployed_contracts.json
*.last-run.json

# test results
test-results/

# secrets files
**/example.secrets.json
**/*.secrets.json

# submodules
examples/CRISP/packages/crisp-contracts/lib/risc0-ethereum
templates/default/lib/risc0-ethereum
-
-.claude/
-.claude/settings.local.json
templates/default/client/index.html (1)

5-10: Consider providing appropriately-sized favicon assets.

All icon declarations reference the same /favicon.png. For optimal display across devices, consider providing properly sized assets (e.g., favicon-16x16.png, favicon-32x32.png, etc.) to avoid browser scaling artifacts.

examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (2)

30-39: Add error handling for round loading.

If getRoundStateLite throws an error, setLoading(false) won't execute, leaving the component stuck in a loading state. Consider wrapping in try/catch or using finally.

   useEffect(() => {
     const loadRound = async () => {
       if (parsedRoundId !== null) {
         setLoading(true)
-        await getRoundStateLite(parsedRoundId)
-        setLoading(false)
+        try {
+          await getRoundStateLite(parsedRoundId)
+        } finally {
+          setLoading(false)
+        }
       }
     }
     loadRound()
   }, [parsedRoundId, getRoundStateLite])

53-57: Optional: Remove unnecessary Fragment.

The Fragment wrapper is not needed when returning a single child element.

-  return (
-    <Fragment>
-      <DailyPollSection loading={false} endTime={endTime} title={title} />
-    </Fragment>
-  )
+  return <DailyPollSection loading={false} endTime={endTime} title={title} />
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)

50-55: Consider cleanup for timeout on unmount.

The setTimeout callback may attempt to update state after the component unmounts, which could cause a warning in React (though not a crash in React 18+). For robustness, consider tracking and clearing timeouts on cleanup.

One approach is to track timeout IDs in a ref and clear them in a cleanup effect, or use an AbortController pattern. However, since React 18+ ignores state updates on unmounted components without warnings, this is optional.

examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)

88-91: Connector line uses current step's style instead of reflecting transition state.

The connector line between steps uses the current step's styles.line, but visually it would be more intuitive if completed connectors (between two completed steps) show the completed style, while the connector leading to the active step shows the pending/transitioning style.

-              {index < steps.length - 1 && <div className={`flex-1 h-0.5 mx-2 ${styles.line}`} />}
+              {index < steps.length - 1 && (
+                <div className={`flex-1 h-0.5 mx-2 ${status === 'complete' ? 'bg-lime-500' : 'bg-slate-200'}`} />
+              )}
examples/CRISP/client/src/components/Cards/PollCard.tsx (1)

24-38: LGTM! Consider a longer interval for poll status checks.

The implementation correctly handles cleanup and early return. However, a 1-second interval for timestamp comparison may be more frequent than necessary. Since poll end times are typically known in advance, a 5-10 second interval would reduce re-renders while still providing timely updates.

-    const interval = setInterval(checkPollStatus, 1000)
+    const interval = setInterval(checkPollStatus, 5000)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (3)

110-116: Prefer structured error handling over eslint-disable.

Instead of suppressing the unused variable warning, consider logging the error for debugging purposes or using an underscore prefix convention.

-          // eslint-disable-next-line @typescript-eslint/no-unused-vars
-        } catch (signError) {
-          console.log('User rejected signature or signing failed')
+        } catch (signError: unknown) {
+          console.log('User rejected signature or signing failed:', signError)

118-134: Consider adding step messages for all steps for consistent UX.

The stepMessage is only set for the signing step ('Please sign the message in your wallet...') and cleared/empty for encrypting, generating_proof, and broadcasting. If the VotingStepIndicator displays the message, users may see an empty message during these steps.

         // Step 2: Encrypting vote
         setVotingStep('encrypting')
-        setStepMessage('')
+        setStepMessage('Encrypting your vote...')
 
         const voteEncrypted = await handleVoteEncryption(pollSelected, user.address, signature, message)
         if (!voteEncrypted) {
           throw new Error('Failed to encrypt vote.')
         }
 
         // Step 3: Generating proof
         setVotingStep('generating_proof')
+        setStepMessage('Generating zero-knowledge proof...')
 
         // small delay for UX
         await new Promise((resolve) => setTimeout(resolve, 500))
 
         // Step 4: Broadcasting
         setVotingStep('broadcasting')
+        setStepMessage('Broadcasting to the network...')

130-131: Document the UX delay rationale.

The 500ms artificial delay is for UX, but a brief comment explaining why this improves user experience would help future maintainers understand this isn't a placeholder.

         // Step 3: Generating proof
         setVotingStep('generating_proof')
 
-        // small delay for UX
+        // Brief delay to ensure users see the proof generation step,
+        // as the actual proof is generated during encryption
         await new Promise((resolve) => setTimeout(resolve, 500))
examples/CRISP/client/src/components/ToastAlert.tsx (1)

17-17: Unused id prop declared in component.

The id prop is declared in ToastAlertProps but not used within the component body. If it's only needed for external toast management (e.g., in the notification context), consider documenting this or removing it from the component's props if it's only used as a key externally.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ac6408 and 389e68e.

📒 Files selected for processing (24)
  • .prettierignore (1 hunks)
  • examples/CRISP/client/src/App.tsx (2 hunks)
  • examples/CRISP/client/src/components/Cards/PollCard.tsx (2 hunks)
  • examples/CRISP/client/src/components/ToastAlert.tsx (1 hunks)
  • examples/CRISP/client/src/components/VotingStepIndicator.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.types.ts (1 hunks)
  • examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (8 hunks)
  • examples/CRISP/client/src/context/voteManagement/VoteManagement.types.ts (3 hunks)
  • examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (3 hunks)
  • examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (3 hunks)
  • examples/CRISP/client/src/model/notification.model.ts (1 hunks)
  • examples/CRISP/client/src/model/vote.model.ts (1 hunks)
  • examples/CRISP/client/src/pages/DailyPoll/DailyPoll.tsx (1 hunks)
  • examples/CRISP/client/src/pages/HistoricPoll/HistoricPoll.tsx (3 hunks)
  • examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (6 hunks)
  • examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (1 hunks)
  • examples/CRISP/client/src/pages/RoundPoll/index.ts (1 hunks)
  • examples/CRISP/client/src/utils/methods.ts (3 hunks)
  • examples/CRISP/scripts/dev_server.sh (1 hunks)
  • examples/CRISP/server/src/server/models.rs (1 hunks)
  • examples/CRISP/server/src/server/repo.rs (2 hunks)
  • examples/CRISP/server/src/server/routes/voting.rs (5 hunks)
  • templates/default/client/index.html (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-25T10:28:56.174Z
Learnt from: ctrlc03
Repo: gnosisguild/enclave PR: 657
File: Cargo.toml:32-34
Timestamp: 2025-08-25T10:28:56.174Z
Learning: The examples/CRISP directory has its own Cargo.toml workspace configuration with members like "server", "wasm-crypto", "program/core", "program/client", etc. The root workspace intentionally excludes "examples/CRISP/server", "examples/CRISP/program", and "examples/CRISP/wasm-crypto" to prevent double workspace membership, which is the correct approach for self-contained example workspaces.

Applied to files:

  • .prettierignore
  • examples/CRISP/scripts/dev_server.sh
📚 Learning: 2025-10-29T23:35:30.146Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 936
File: scripts/run-crisp-test.sh:1-3
Timestamp: 2025-10-29T23:35:30.146Z
Learning: In the scripts/run-crisp-test.sh file, the use of `rm -rf *` is acceptable as it's intentionally designed as a definitive reset-and-test script for clean checkouts.

Applied to files:

  • examples/CRISP/scripts/dev_server.sh
📚 Learning: 2024-11-05T06:49:46.285Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/enclave_node/src/datastore.rs:14-16
Timestamp: 2024-11-05T06:49:46.285Z
Learning: In `packages/ciphernode/enclave_node/src/datastore.rs`, for internal functions like `get_in_mem_store`, adding extensive documentation and error handling may not be necessary if they are not client-facing.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
📚 Learning: 2024-09-26T03:11:29.311Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 107
File: packages/ciphernode/sortition/src/distance.rs:1-1
Timestamp: 2024-09-26T03:11:29.311Z
Learning: In `packages/ciphernode/core/src/events.rs`, the import statements use the correct and updated `alloy::primitives` module.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
📚 Learning: 2024-10-22T02:10:34.864Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs:82-83
Timestamp: 2024-10-22T02:10:34.864Z
Learning: In the file `packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs`, when reviewing test functions like `generate_pk_share`, minor performance optimizations (e.g., minimizing mutex locks) are not a priority.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
📚 Learning: 2024-10-22T03:41:21.226Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/router/src/repositories.rs:40-71
Timestamp: 2024-10-22T03:41:21.226Z
Learning: In `packages/ciphernode/router/src/repositories.rs`, prefer to keep method implementations as they are if they are straightforward and maintainable, even if refactoring could reduce duplication.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
🧬 Code graph analysis (6)
examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (2)
examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (1)
  • useVoteManagementContext (242-242)
examples/CRISP/client/src/utils/methods.ts (1)
  • convertTimestampToDate (18-22)
examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (1)
examples/CRISP/client/src/model/vote.model.ts (2)
  • VoteStatusRequest (39-42)
  • VoteStatusResponse (44-49)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (3)
examples/CRISP/client/src/utils/create-generic-context.ts (1)
  • createGenericContext (9-23)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.types.ts (2)
  • NotificationAlertContextType (10-14)
  • NotificationAlertProviderProps (16-18)
examples/CRISP/client/src/model/notification.model.ts (1)
  • NotificationAlert (7-14)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (4)
examples/CRISP/client/src/model/vote.model.ts (2)
  • VoteStateLite (51-66)
  • VotingRound (15-18)
examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (1)
  • useVoteManagementContext (242-242)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)
  • useNotificationAlertContext (98-98)
examples/CRISP/client/src/model/poll.model.ts (1)
  • Poll (32-36)
examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (1)
  • VotingStep (16-16)
examples/CRISP/server/src/server/routes/voting.rs (2)
examples/CRISP/client/src/model/vote.model.ts (2)
  • VoteStatusRequest (39-42)
  • VoteStatusResponse (44-49)
examples/CRISP/server/src/server/repo.rs (1)
  • has_voted (229-232)
🪛 GitHub Actions: License Header Check
examples/CRISP/client/src/pages/RoundPoll/index.ts

[error] 1-1: License header missing. Please add the SPDX header as per project guidelines.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build_sdk
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_net
  • GitHub Check: test_contracts
  • GitHub Check: rust_unit
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: crisp_rust_unit
  • GitHub Check: rust_integration
  • GitHub Check: Build & Push Image
🔇 Additional comments (32)
examples/CRISP/scripts/dev_server.sh (1)

7-7: LGTM – Fresh database state for dev workflow.

The pre-start cleanup ensures a clean database state, which aligns well with the new vote status tracking and session-scoped vote caching introduced in this PR. For a development server script, this is the appropriate pattern.

Consider adding a comment to explain why the cleanup is needed:

~(cd ./server && rm -rf database && cargo run --bin server)
+(cd ./server && rm -rf database && cargo run --bin server)  # Fresh database for clean vote state
examples/CRISP/server/src/server/repo.rs (1)

97-107: LGTM!

The reformatted method chain improves readability without changing functionality.

examples/CRISP/client/src/model/notification.model.ts (1)

7-14: LGTM!

The expanded type union and new optional fields provide flexibility for enhanced toast management while remaining backward-compatible with existing consumers.

examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.types.ts (1)

10-14: LGTM!

The new closeToast and clearAllToasts methods provide granular control over toast lifecycle management, aligning well with the expanded notification system.

examples/CRISP/client/src/utils/methods.ts (2)

18-22: Good bug fix for convertTimestampToDate.

The original code incorrectly used date.getMinutes() as the base for adding seconds, which would have caused incorrect date calculations. Using date.getSeconds() is the correct approach.


116-121: Good type safety improvement for debounce.

Using generics with Parameters<T> ensures the returned function preserves the argument types of the wrapped function, providing better TypeScript inference for consumers.

examples/CRISP/client/src/context/voteManagement/VoteManagement.types.ts (2)

12-16: LGTM!

The VoteStatus type is well-structured for session-scoped vote status caching with the lastChecked timestamp enabling cache expiration logic.


28-31: LGTM!

The new context fields and methods provide a clean API for vote status management with appropriate loading states and cache control via forceRefresh.

Also applies to: 51-52

examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (5)

21-29: LGTM!

The session ID generation and cache key strategy are well-designed. Using a combination of timestamp and random string ensures uniqueness across page loads, and the 5-minute cache duration is reasonable for vote status freshness.


73-107: LGTM!

The checkVoteStatus implementation correctly handles:

  • Early returns for invalid inputs
  • Cache validation with TTL
  • Loading state management via finally block
  • Error handling with safe fallback

The useCallback dependencies are complete.


109-126: LGTM!

The markVotedInRound implementation correctly updates both the cache and the hasVotedInCurrentRound state. The conditional check ensures only the current round's status is updated in state, while the cache stores status for any round.


186-202: LGTM!

The effect properly handles async state updates with a cancellation flag to prevent race conditions when dependencies change before the async operation completes.


176-184: LGTM!

Good practice to clear the vote status cache when the user disconnects. This prevents stale data from being used if a different user connects.

examples/CRISP/client/src/App.tsx (1)

17-17: LGTM!

The new route for round polls is correctly integrated. The barrel import pattern is clean, and the route placement between /current and /historic is logical.

Also applies to: 41-41

examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)

64-71: Good use of useMemo for context value.

Memoizing the context value prevents unnecessary re-renders of consumers when the provider re-renders for unrelated reasons.

examples/CRISP/client/src/pages/DailyPoll/DailyPoll.tsx (1)

13-16: Clean simplification of loading state derivation.

Replacing explicit state management with a derived loading value is cleaner and less error-prone. The memoized endTime calculation is consistent with RoundPoll.tsx.

examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (1)

39-39: I'll help you verify if getRoundStateLite is properly memoized in the context. Let me start by locating the VoteManagementContext and checking how getRoundStateLite is defined.
<function_calls>


#!/bin/bash

Find VoteManagementContext file

find . -type f -name "VoteManagement" -o -name "Context" | grep -i vote | head -20


</function_calls>

Now let me search more broadly for the context definition:
<function_calls>


#!/bin/bash

Search for getRoundStateLite definition across the codebase

rg "getRoundStateLite" -n -B 3 -A 3 --type=ts --type=tsx | head -100


</function_calls>

Let me also check the RoundPoll.tsx file to understand the context of this dependency array:
<function_calls>


#!/bin/bash

Find and read the RoundPoll.tsx file

find . -path "*RoundPoll/RoundPoll.tsx" -exec cat -n {} ;


</function_calls>

examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (1)

29-29: LGTM!

The new getVoteStatus endpoint follows the established patterns in this hook. The implementation is consistent with other API calls and properly typed.

Also applies to: 40-40, 49-49

examples/CRISP/server/src/server/models.rs (2)

61-65: LGTM! Consider address validation at the handler level.

The struct is well-defined. Ensure the route handler that consumes VoteStatusRequest validates the address field format (e.g., valid Ethereum address) before processing to prevent invalid queries.


67-73: LGTM!

The VoteStatusResponse struct properly mirrors the client-side TypeScript interface with appropriate field types and optional round_status.

examples/CRISP/client/src/components/Cards/PollCard.tsx (2)

21-22: Good use of derived state for display logic.

The displayVoteCount correctly prioritizes the live context vote_count for the current active round while falling back to totalVotes for historical data.


45-60: Verify that setPollResult should only be called for ended polls.

setPollResult is only called when navigating to /result/${roundId}, not for active rounds. Ensure this is intentional and that the result page for ended polls can properly use this pre-set data.

examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (3)

16-16: LGTM!

The VotingStep type is well-defined and covers all states of the voting flow including idle, confirming, and error states.


18-42: LGTM! Good error message normalization.

The extractCleanErrorMessage function provides user-friendly messages for common blockchain errors while gracefully handling unknown errors with a length check.


197-208: LGTM!

The dependency array is complete and includes all necessary dependencies for the useCallback hook, including resetVotingState which is itself memoized.

examples/CRISP/client/src/model/vote.model.ts (1)

31-49: Well-structured type definitions for vote status tracking.

The new VoteStatusRequest and VoteStatusResponse interfaces properly define the API contract for the /voting/status endpoint. The addition of is_vote_update to BroadcastVoteResponse cleanly supports distinguishing between new votes and updates. Types align correctly with the server-side models in voting.rs.

examples/CRISP/client/src/components/ToastAlert.tsx (1)

20-29: Clean implementation of persistent toast behavior.

The early return pattern for persistent toasts is appropriate and correctly prevents the auto-dismiss timer. The dependency array properly includes persistent.

examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (2)

88-97: Good UX feedback for vote status.

The "You voted" and "Checking..." indicators provide clear visual feedback to users about their voting status. The styling is consistent with the existing badge patterns.


106-107: Well-implemented voting flow with step-by-step progress.

The integration of VotingStepIndicator during the casting process and the conditional button labels ("Cast Vote" / "Update Vote" / "Processing Vote...") provide excellent user feedback. The disabled state logic is comprehensive.

Also applies to: 117-130

examples/CRISP/server/src/server/routes/voting.rs (3)

42-78: Well-implemented vote status endpoint.

The get_vote_status handler properly validates the request, handles database errors gracefully, and returns a structured response including both the vote status and round status. Good logging for debugging.


177-201: Good user-friendly error message extraction.

The extract_error_message function appropriately maps technical blockchain errors to user-friendly messages. The pattern matching covers common failure scenarios.


211-226: Correct rollback logic for vote updates.

The conditional rollback that skips removal for vote updates is correct - we don't want to remove the voter address when updating an existing vote fails, as they remain a valid voter.

Comment thread examples/CRISP/client/src/components/ToastAlert.tsx
Comment thread examples/CRISP/client/src/components/VotingStepIndicator.tsx
Comment thread examples/CRISP/client/src/pages/RoundPoll/index.ts
Comment thread examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx
Comment thread examples/CRISP/server/src/server/routes/voting.rs Outdated
Comment thread templates/default/client/index.html
@hmzakhalid hmzakhalid changed the title fix: CRISP Voting UX fixes [skip-line-limit] fix: crisp voting ux fixes [skip-line-limit] Nov 27, 2025
@vercel vercel Bot temporarily deployed to Preview – enclave-docs November 27, 2025 16:48 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs November 28, 2025 18:19 Inactive

@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: 1

🧹 Nitpick comments (2)
examples/CRISP/client/src/components/ToastAlert.tsx (1)

20-29: Unused id prop in component signature.

The id prop is declared in ToastAlertProps and passed to the component but is never destructured or used within the component body. If it's only needed for external keying purposes, consider removing it from the component props entirely or document why it's included.

-const ToastAlert: React.FC<ToastAlertProps> = ({ message, type, linkUrl, onClose, persistent = false }) => {
+const ToastAlert: React.FC<ToastAlertProps> = ({ message, type, linkUrl, onClose, persistent = false, id }) => {

Or if id truly isn't needed inside the component, remove it from the props type:

 type ToastAlertProps = {
   type: 'success' | 'danger' | 'warning' | 'info'
   linkUrl?: string
   message: string
   onClose: () => void
   persistent?: boolean
-  id?: string
 }
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)

44-48: Toast queue trimming may dismiss persistent toasts prematurely.

When exceeding MAX_TOASTS, prev.slice(1) removes the oldest toast regardless of its persistent flag. If persistent toasts should remain until explicitly dismissed, consider skipping them when trimming:

       setToasts((prev) => {
-        const newToasts = prev.length >= MAX_TOASTS ? prev.slice(1) : prev
+        let newToasts = prev
+        if (prev.length >= MAX_TOASTS) {
+          const nonPersistentIndex = prev.findIndex((t) => !t.persistent)
+          if (nonPersistentIndex !== -1) {
+            newToasts = prev.filter((_, i) => i !== nonPersistentIndex)
+          } else {
+            newToasts = prev.slice(1) // All persistent, remove oldest
+          }
+        }

         return [...newToasts, toastWithId]
       })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89ddda4 and e21d5ff.

📒 Files selected for processing (5)
  • examples/CRISP/client/index.html (0 hunks)
  • examples/CRISP/client/src/components/ToastAlert.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1 hunks)
  • examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (1 hunks)
  • examples/CRISP/server/src/server/routes/voting.rs (5 hunks)
💤 Files with no reviewable changes (1)
  • examples/CRISP/client/index.html
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx
  • examples/CRISP/server/src/server/routes/voting.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Build & Push Image
  • GitHub Check: test_net
  • GitHub Check: build_sdk
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: test_contracts
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_enclave_cli
  • GitHub Check: integration_prebuild
  • GitHub Check: rust_integration
  • GitHub Check: crisp_rust_unit
  • GitHub Check: rust_unit
🔇 Additional comments (7)
examples/CRISP/client/src/components/ToastAlert.tsx (3)

9-17: Type expansion and new props look good.

The addition of warning and info types with corresponding icons and the new persistent/id props align well with the notification system enhancements. The id prop being optional here is appropriate since it's primarily used by the parent context for keying.


31-56: Well-structured style configuration with icon mappings.

The alertStyles object cleanly maps each type to its visual properties including the new icon field. Using null for success/danger icons while providing Warning/Info components for the new types is a reasonable approach.


62-92: Previous positioning and interaction issues have been addressed.

The relative class on the outer container and pointer-events-auto are now correctly applied, fixing the persistent indicator positioning and ensuring the toast remains clickable when rendered within a pointer-events-none parent. The layout with flex, gap, and proper link attributes (rel='noopener noreferrer') follows best practices.

examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (4)

13-16: Constants and ID generator are well-defined.

The MAX_TOASTS limit and DEFAULT_DURATION provide good configurability. The generateToastId function using timestamp + random suffix is sufficient for client-side toast identification.


23-35: Previous fallback behavior concern has been addressed.

The closeToast function now correctly returns prev unchanged (line 32) when no non-persistent toast is found, preventing unintended dismissal of persistent toasts in the fallback case.


64-71: Context memoization is correctly implemented.

The useMemo with proper dependency array prevents unnecessary re-renders of consumers when the toast list changes but the functions remain stable.


76-93: Toast container with accessibility attributes looks good.

The fixed container with flex-col-reverse ensures newest toasts appear at the bottom. ARIA attributes (role, aria-label, aria-live) improve accessibility. The pointer-events-none on the container combined with pointer-events-auto in ToastAlert (verified in the other file) correctly enables interaction.

Comment thread examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx Outdated

@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: 0

🧹 Nitpick comments (4)
examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)

17-22: Consider adding 'confirming' step or removing it from VotingStep type.

The VotingStep type includes 'confirming' but it's not represented in the steps array and never set in useVoteCasting.ts (the flow goes directly from 'broadcasting' to 'complete'). If this step is intended for future use, consider adding a TODO comment; otherwise, remove it from the type to avoid confusion.

examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (2)

82-101: The isVoteUpdate parameter is misleading.

The isVoteUpdate parameter on line 83 is only used for the log message (lines 100-101), but the actual success message on line 162 uses broadcastVoteResponse.is_vote_update from the server response. This creates confusion about what determines update vs. new vote behavior.

Consider removing the parameter if the server is the source of truth, or document clearly that it's only for logging purposes.

-    async (pollSelected: Poll | null, isVoteUpdate: boolean = false) => {
+    async (pollSelected: Poll | null) => {
       if (!pollSelected) {
         console.log('Cannot cast vote: Poll option not selected.')
         showToast({ type: 'danger', message: 'Please select a poll option first.' })
         return
       }
       if (!user || !roundState) {
         console.error('Cannot cast vote: Missing user or round state.')
         showToast({
           type: 'danger',
           message: 'Cannot cast vote. Ensure you are connected, and the round is active.',
           persistent: true,
         })
         return
       }

       setIsLoading(true)
-      const actionText = isVoteUpdate ? 'Updating vote' : 'Processing vote'
-      console.log(`${actionText}...`)
+      console.log('Processing vote...')

131-136: Artificial delay may not be necessary.

The 500ms delay creates a synthetic pause that doesn't reflect actual work being done. If the proof generation is instantaneous, users will briefly see a misleading "generating proof" state. Consider removing this delay or only showing the step if it takes meaningful time.

examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (1)

78-78: Redundant spacing classes may cause inconsistent layout.

Using both space-x-2 and gap-2 together results in double horizontal spacing (1rem total) between flex items. When items wrap, this could create inconsistent spacing.

Consider using only gap-2 for consistent spacing in both directions:

-            <div className='flex items-center justify-center space-x-2 flex-wrap gap-2'>
+            <div className='flex items-center justify-center flex-wrap gap-2'>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e21d5ff and c88f2e2.

📒 Files selected for processing (3)
  • examples/CRISP/client/src/components/VotingStepIndicator.tsx (1 hunks)
  • examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (3 hunks)
  • examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (1)
  • VotingStep (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test_net
  • GitHub Check: build_sdk
  • GitHub Check: test_contracts
  • GitHub Check: build_enclave_cli
  • GitHub Check: rust_integration
  • GitHub Check: build_e3_support_dev
  • GitHub Check: integration_prebuild
  • GitHub Check: rust_unit
  • GitHub Check: crisp_rust_unit
  • GitHub Check: Build & Push Image
🔇 Additional comments (6)
examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)

24-35: LGTM! Error state handling has been properly addressed.

The lastActiveStep prop now correctly handles the error case, ensuring the step indicator accurately reflects which step failed rather than showing all steps as pending.

examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (2)

18-42: Good error message normalization.

The extractCleanErrorMessage helper provides user-friendly error messages for common blockchain errors. The pattern-based matching is clear and maintainable.


217-225: LGTM! Hook API is well-structured.

The expanded return interface provides all necessary state and actions for consumers to integrate the multi-step voting flow.

examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (3)

88-97: LGTM! Clear vote status indicators.

The "You voted" and "Checking..." badges provide clear visual feedback for the user's voting state. Styling is consistent with existing status badges.


106-107: Good mutually exclusive loading states.

The VotingStepIndicator and LoadingAnimation are correctly shown in mutually exclusive conditions, preventing UI overlap during different loading scenarios.


55-62: Parameter naming mismatch with hook.

hasVotedInCurrentRound is passed to castVoteWithProof as the isVoteUpdate parameter, but as noted in the hook review, this parameter only affects logging. If the hook parameter is removed per the earlier suggestion, this call can be simplified.

If isVoteUpdate remains in the hook, consider renaming for clarity to match usage.

@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: 1

🧹 Nitpick comments (1)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)

15-15: Consider a more collision-resistant ID generation.

The current approach using Date.now() + Math.random() is adequate for typical usage, but could theoretically produce duplicate IDs if multiple toasts are generated in rapid succession (same millisecond).

If you want greater robustness, consider adding a simple counter:

+let toastCounter = 0
+
-const generateToastId = (): string => `toast-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
+const generateToastId = (): string => `toast-${Date.now()}-${++toastCounter}`
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c88f2e2 and c8dcf34.

📒 Files selected for processing (2)
  • examples/CRISP/client/src/components/ToastAlert.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test_net
  • GitHub Check: build_enclave_cli
  • GitHub Check: build_sdk
  • GitHub Check: integration_prebuild
  • GitHub Check: test_contracts
  • GitHub Check: rust_unit
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: rust_integration
  • GitHub Check: crisp_rust_unit
🔇 Additional comments (9)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (4)

22-34: LGTM! The fallback logic correctly handles persistent toasts.

The closeToast implementation properly addresses the previous concern: when no id is provided and no non-persistent toast exists, it returns prev unchanged instead of removing a persistent toast.


49-51: LGTM! Clean implementation.

The clearAllToasts function correctly resets the toast state.


53-60: LGTM! Proper context value memoization.

The useMemo correctly prevents unnecessary re-renders by memoizing the context value, with appropriate dependencies on the callback functions.


65-83: LGTM! Well-structured toast container with proper accessibility.

The implementation includes:

  • Appropriate fixed positioning and z-index for toast visibility
  • Correct pointer-events handling (container with pointer-events-none, individual toasts with pointer-events-auto)
  • Good accessibility with ARIA attributes (role, aria-label, aria-live)
  • flex-col-reverse provides intuitive bottom-up toast stacking
examples/CRISP/client/src/components/ToastAlert.tsx (5)

9-21: LGTM! Appropriate type and prop extensions.

The additions properly support the expanded toast functionality:

  • New icon imports for warning and info types
  • Extended type union with 'warning' | 'info'
  • Optional props (persistent, duration, id) for flexible toast behavior
  • Reasonable DEFAULT_DURATION of 5 seconds

23-33: LGTM! Auto-dismiss logic is correctly implemented.

The component properly handles:

  • Default persistent = false parameter
  • Early return for persistent toasts (no auto-dismiss)
  • Configurable duration with sensible default
  • Timer cleanup to prevent memory leaks
  • Correct effect dependencies

35-63: LGTM! Alert styles are well-structured and consistent.

The extended alert styles properly support all four types with:

  • Consistent structure across success, danger, warning, and info
  • Appropriate color schemes for each type
  • Icon components for warning (Warning) and info (Info)
  • null icons for success and danger (intentional, no icon shown)

66-91: LGTM! Rendering structure is solid with proper security considerations.

The implementation includes:

  • pointer-events-auto and relative positioning (addresses previous comments)
  • Flexible layout accommodating icons, messages, and links
  • Security best practice: rel='noopener noreferrer' on external links prevents reverse tabnabbing
  • Conditional icon rendering based on type
  • Proper close button implementation

92-94: LGTM! Persistent indicator is well-implemented.

The persistent toast indicator:

  • Conditionally renders only for persistent toasts
  • Uses absolute positioning correctly (parent has relative)
  • Provides clear visual feedback (pulsing red dot)
  • Includes helpful tooltip via title attribute

@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: 0

♻️ Duplicate comments (1)
examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1)

42-46: Persistent toasts are not protected from overflow removal.

When the queue reaches MAX_TOASTS, line 43 uses prev.slice(1), which removes the oldest toast regardless of whether it's persistent. This is inconsistent with closeToast (lines 27-31), which explicitly protects persistent toasts from automatic removal.

If persistent toasts should remain until manually dismissed, apply this diff:

     setToasts((prev) => {
-      const newToasts = prev.length >= MAX_TOASTS ? prev.slice(1) : prev
+      let newToasts = prev
+      if (prev.length >= MAX_TOASTS) {
+        const nonPersistentIndex = prev.findIndex((t) => !t.persistent)
+        newToasts = nonPersistentIndex !== -1
+          ? prev.filter((_, i) => i !== nonPersistentIndex)
+          : prev.slice(1)
+      }
 
       return [...newToasts, toastWithId]
     })
🧹 Nitpick comments (3)
examples/CRISP/client/src/pages/HistoricPoll/HistoricPoll.tsx (1)

44-46: Remove redundant useEffect.

This effect sets visiblePolls to the first 12 polls whenever pastPolls changes. However, the effect on lines 48-51 already handles this logic (when page=0, it computes the same slice). Having both effects causes redundant state updates.

Apply this diff to remove the redundant effect:

-  useEffect(() => {
-    setVisiblePolls(pastPolls.slice(0, 12))
-  }, [pastPolls])
-
   useEffect(() => {
     const newVisiblePolls = pastPolls.slice(0, (page + 1) * 12)
     setVisiblePolls(newVisiblePolls)
examples/CRISP/server/src/server/routes/voting.rs (1)

20-25: get_vote_status endpoint logic is sound

The new /voting/status route and get_vote_status handler cleanly reuse the repository APIs (has_voted, get_e3_state_lite) and return a well-shaped VoteStatusResponse. Only minor micro-optimization possible (reuse the store.e3(round_id) handle instead of calling twice), but not required.

Also applies to: 28-73

examples/CRISP/client/src/components/Cards/PollCard.tsx (1)

15-23: Live/round-aware PollCard behavior looks correct (with a small reuse edge case)

  • isActive + the 1s interval cleanly transitions cards from live to finished and stops polling once inactive.
  • Using currentRoundId to decide between /current, /round/:roundId, and /result/:roundId matches the intended UX, and displayVoteCount correctly prefers live roundState.vote_count when applicable.
  • One minor edge case: if a PollCard instance were ever reused with a different endTime but the same React key, isActive would not be recomputed from the new endTime. In typical list usage keyed by roundId, this shouldn’t happen, but if reuse is possible you may want an effect that updates isActive when endTime changes.

Also applies to: 24-38, 45-60, 64-82

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8dcf34 and 1ce8f20.

📒 Files selected for processing (27)
  • .prettierignore (1 hunks)
  • examples/CRISP/client/index.html (0 hunks)
  • examples/CRISP/client/libs/crispWorker.js (2 hunks)
  • examples/CRISP/client/src/App.tsx (2 hunks)
  • examples/CRISP/client/src/components/Cards/PollCard.tsx (2 hunks)
  • examples/CRISP/client/src/components/ToastAlert.tsx (1 hunks)
  • examples/CRISP/client/src/components/VotingStepIndicator.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.context.tsx (1 hunks)
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.types.ts (1 hunks)
  • examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (8 hunks)
  • examples/CRISP/client/src/context/voteManagement/VoteManagement.types.ts (2 hunks)
  • examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (3 hunks)
  • examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (2 hunks)
  • examples/CRISP/client/src/hooks/wasm/useWebAssembly.tsx (1 hunks)
  • examples/CRISP/client/src/model/notification.model.ts (1 hunks)
  • examples/CRISP/client/src/model/vote.model.ts (1 hunks)
  • examples/CRISP/client/src/pages/DailyPoll/DailyPoll.tsx (1 hunks)
  • examples/CRISP/client/src/pages/HistoricPoll/HistoricPoll.tsx (3 hunks)
  • examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (6 hunks)
  • examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx (1 hunks)
  • examples/CRISP/client/src/pages/RoundPoll/index.ts (1 hunks)
  • examples/CRISP/client/src/utils/methods.ts (3 hunks)
  • examples/CRISP/packages/crisp-sdk/src/types.ts (1 hunks)
  • examples/CRISP/scripts/dev_server.sh (1 hunks)
  • examples/CRISP/server/src/server/models.rs (1 hunks)
  • examples/CRISP/server/src/server/repo.rs (2 hunks)
  • examples/CRISP/server/src/server/routes/voting.rs (3 hunks)
💤 Files with no reviewable changes (1)
  • examples/CRISP/client/index.html
✅ Files skipped from review due to trivial changes (1)
  • examples/CRISP/server/src/server/repo.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • examples/CRISP/client/src/pages/RoundPoll/index.ts
  • .prettierignore
  • examples/CRISP/client/src/model/notification.model.ts
  • examples/CRISP/client/src/pages/RoundPoll/RoundPoll.tsx
  • examples/CRISP/server/src/server/models.rs
  • examples/CRISP/client/src/App.tsx
  • examples/CRISP/scripts/dev_server.sh
  • examples/CRISP/client/src/pages/DailyPoll/DailyPoll.tsx
  • examples/CRISP/client/src/model/vote.model.ts
  • examples/CRISP/client/src/context/NotificationAlert/NotificationAlert.types.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/server/src/server/routes/voting.rs
📚 Learning: 2024-11-25T09:56:11.195Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 184
File: packages/ciphernode/net/src/network_relay.rs:0-0
Timestamp: 2024-11-25T09:56:11.195Z
Learning: In `NetworkPeer::new` in `packages/ciphernode/net/src/network_peer.rs`, peers are converted to multiaddrs, and validation of the address format occurs during this conversion. Duplicate addresses are managed as connections are deduplicated.

Applied to files:

  • examples/CRISP/server/src/server/routes/voting.rs
🧬 Code graph analysis (6)
examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (1)
examples/CRISP/client/src/model/vote.model.ts (2)
  • VoteStatusRequest (38-41)
  • VoteStatusResponse (43-48)
examples/CRISP/client/src/context/voteManagement/VoteManagement.types.ts (2)
examples/CRISP/client/src/model/poll.model.ts (3)
  • PollResult (14-20)
  • PollRequestResult (22-30)
  • Poll (32-36)
examples/CRISP/client/src/model/vote.model.ts (3)
  • VotingRound (15-18)
  • BroadcastVoteRequest (24-28)
  • BroadcastVoteResponse (31-36)
examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (1)
  • VotingStep (18-18)
examples/CRISP/server/src/server/routes/voting.rs (3)
examples/CRISP/server/src/server/repo.rs (1)
  • has_voted (229-232)
examples/CRISP/crates/evm_helpers/src/lib.rs (1)
  • address (58-60)
crates/evm-helpers/src/contracts.rs (1)
  • address (194-196)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (3)
examples/CRISP/client/src/model/poll.model.ts (1)
  • Poll (32-36)
examples/CRISP/packages/crisp-sdk/src/vote.ts (2)
  • generateProof (129-138)
  • encryptVote (84-88)
examples/CRISP/packages/crisp-sdk/src/constants.ts (1)
  • SIGNATURE_MESSAGE (30-30)
examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (3)
examples/CRISP/client/src/utils/methods.ts (1)
  • hasPollEnded (24-28)
examples/CRISP/client/src/model/poll.model.ts (1)
  • Poll (32-36)
examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (1)
  • useVoteCasting (46-230)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_contracts
  • GitHub Check: test_net
  • GitHub Check: integration_prebuild
  • GitHub Check: build_ciphernode_image
  • GitHub Check: build_sdk
  • GitHub Check: crisp_unit
  • GitHub Check: rust_integration
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
🔇 Additional comments (30)
examples/CRISP/client/src/pages/HistoricPoll/HistoricPoll.tsx (2)

81-81: LGTM!

The className formatting improvement enhances code cleanliness without affecting functionality.


34-42: [rewritten comment]
[classification tag]

examples/CRISP/client/src/components/ToastAlert.tsx (2)

21-33: LGTM! Auto-dismiss timer is correctly implemented.

The timer logic properly respects the persistent flag, uses the provided duration or defaults to 5 seconds, and cleans up on unmount to prevent memory leaks.


62-91: LGTM! Toast rendering and interaction are well-implemented.

The component correctly handles:

  • Conditional icon rendering for warning/info types
  • Secure external links with rel="noopener noreferrer"
  • Proper positioning with relative for the persistent indicator
  • pointer-events-auto to enable interaction despite the parent's pointer-events-none
examples/CRISP/client/src/utils/methods.ts (1)

18-22: Timestamp fix and generic debounce look good

  • convertTimestampToDate now correctly adds secondsToAdd using getSeconds, matching the seconds-based timestamp.
  • formatDate change is cosmetic and fine.
  • Generic debounce with Parameters<T> tightens typing without changing behavior; restricting to void callbacks is appropriate for this usage.

Also applies to: 36-52, 116-122

examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts (1)

8-15: Vote status endpoint wiring is consistent

The GetVoteStatus endpoint and getVoteStatus helper are wired consistently with the existing API helpers (types, HTTP method, and payload shape all align). Exposing getVoteStatus from the hook matches the other methods.

Also applies to: 23-30, 32-50

examples/CRISP/packages/crisp-sdk/src/types.ts (1)

171-196: previousCiphertext wiring in SDK types is consistent

Adding previousCiphertext?: Uint8Array to ProofInputs, MaskVoteProofInputs, and VoteProofInputs keeps the types aligned and optional, which matches how the worker/wasm layer forwards this value.

examples/CRISP/client/src/hooks/wasm/useWebAssembly.tsx (1)

26-33: generateProof wiring matches worker; ensure it pairs with the voteId fix

Threading previousCiphertext through generateProof and into the worker looks correct. With the worker normalized to handle both number and bigint voteId values, using voteId: bigint here is fine and keeps the API consistent with other bigint-based values.

Also applies to: 38-57

examples/CRISP/client/src/components/VotingStepIndicator.tsx (1)

11-35: Step indicator logic now handles error and non-linear steps correctly

  • Using lastActiveStep to derive currentIndex fixes the prior issue where 'error' wasn’t in the step list.
  • For 'idle'/'confirming', all steps render as pending, which is a reasonable default.
  • Styling and icon selection cleanly map complete/active/error/pending to distinct visuals.

Also applies to: 37-64, 66-103

examples/CRISP/client/libs/crispWorker.js (1)

14-15: Normalize voteId type to avoid always casting to the same vote

The worker assumes voteId is 0 or 1 (number) but if the hook now types it as bigint, then voteId === 0 will never be true for 0n or 1n, causing both choices to map to the "yes" vote branch.

To make this robust regardless of whether callers send number or bigint, normalize first:

-        const { voteId, publicKey, address, signature, previousCiphertext } = data
+        const { voteId, publicKey, address, signature, previousCiphertext } = data
@@
-        const balance = 1n
-        const vote = voteId === 0 ? { yes: 0n, no: balance } : { yes: balance, no: 0n }
+        const balance = 1n
+        const choice = typeof voteId === 'bigint' ? Number(voteId) : voteId
+        const vote = choice === 0 ? { yes: 0n, no: balance } : { yes: balance, no: 0n }
@@
-        const proof = await generateVoteProof({
+        const proof = await generateVoteProof({
           vote,
           publicKey,
           signature,
           merkleLeaves,
           balance,
           previousCiphertext,
         })

This keeps the existing semantics while allowing voteId to be passed as either 0/1 or 0n/1n.

examples/CRISP/server/src/server/routes/voting.rs (1)

88-90: File not found in repository

The file examples/CRISP/server/src/server/routes/voting.rs referenced in this review comment does not exist in the current repository state. Unable to verify the claimed issues regarding:

  • Hex decode failure rollback behavior for updates
  • Contract creation error handling for new votes
  • Inconsistent use of the handle_vote_error function with the is_vote_update flag

To proceed with verification, confirm that:

  • The file exists in the repository (check branch, commit, or file path)
  • The code snippet locations (lines 88-90, 91-122, etc.) are accurate for the current revision
examples/CRISP/client/src/context/voteManagement/VoteManagement.types.ts (3)

12-16: LGTM! Type definition supports the vote status caching mechanism.

The VoteStatus type is well-structured with appropriate fields for tracking vote state, round ID, and cache timestamp.


28-31: LGTM! Context API expanded to support vote status tracking.

The new fields (currentRoundId, hasVotedInCurrentRound, voteStatusLoading, sessionId) and methods (checkVoteStatus, markVotedInRound) provide a clean API for managing vote status across the application.

Also applies to: 51-52


40-46: LGTM! Signature updated to support vote updates.

The addition of the optional previousCiphertext parameter enables the vote update functionality while maintaining backward compatibility.

examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx (4)

23-23: LGTM! Title prop added for flexibility.

The addition of the title prop with a sensible default value ('Daily Poll') allows for component reusability across different contexts.

Also applies to: 26-26


27-27: LGTM! Vote status indicators enhance user feedback.

The integration of hasVotedInCurrentRound and voteStatusLoading provides clear visual feedback about the user's voting state. The "You voted" and "Checking..." badges improve the UX.

Also applies to: 88-97


106-107: LGTM! Loading states are well-coordinated.

The conditional rendering ensures that either the VotingStepIndicator (during casting) or LoadingAnimation (during initial load) is displayed, preventing UI conflicts.


118-132: LGTM! Dynamic button and helper text improve user guidance.

The button label correctly toggles between "Cast Vote", "Update Vote", and "Processing Vote..." based on the voting state. The helper text adapting to show "Select an option to update your vote" when the user has already voted provides clear guidance.

examples/CRISP/client/src/context/voteManagement/VoteManagement.context.tsx (4)

21-29: LGTM! Session-scoped cache key generation is well-designed.

The generateSessionId function creates a unique identifier per page load, and getVoteCacheKey properly scopes cache entries by session, round, and address. The 5-minute cache duration (VOTE_CACHE_DURATION) is reasonable for vote status data.


73-107: LGTM! Robust vote status checking with caching.

The checkVoteStatus function properly implements:

  • Cache lookup with TTL validation
  • Force refresh capability
  • Loading state management
  • Error handling with console logging
  • Cache updates on successful API response

The implementation efficiently reduces redundant API calls while maintaining data freshness.


109-126: LGTM! Vote status marking logic is correct.

The markVotedInRound function properly updates both the cache and the hasVotedInCurrentRound state. The conditional update on line 122 ensures the state only changes when the specified round matches the current round.


186-202: LGTM! Auto-refresh logic with proper cleanup.

The useEffect hook correctly:

  • Checks vote status when the user or current round changes
  • Uses a cancellation flag to prevent race conditions
  • Cleans up on unmount or when dependencies change
  • Resets hasVotedInCurrentRound when the user or round is not available

The inclusion of checkVoteStatus in the dependency array is correct since it's memoized with useCallback.

examples/CRISP/client/src/hooks/voting/useVoteCasting.ts (8)

18-18: LGTM! VotingStep type supports multi-step UI flow.

The VotingStep type definition provides clear states for the voting process, enabling granular progress tracking and user feedback.


20-44: LGTM! Error message normalization improves UX.

The extractCleanErrorMessage helper function effectively:

  • Handles common blockchain error patterns (gas issues, nonce conflicts, reverts)
  • Truncates excessively long messages
  • Provides user-friendly fallback messages

This enhances the user experience by presenting clearer error feedback.


106-122: LGTM! Well-structured signature flow with error handling.

The signing step properly:

  • Updates the voting step and UI message
  • Handles user rejection gracefully
  • Calls resetVotingState on failure to clean up UI state

143-146: LGTM! Broadcasting step with success handling.

The broadcasting flow properly:

  • Updates voting steps for user feedback
  • Handles success response with appropriate actions (marking voted, showing toast, navigating)
  • Distinguishes between initial votes and vote updates in the success message
  • Calls markVotedInRound to update the cache

Also applies to: 157-173


175-190: LGTM! Enhanced error handling with persistent toasts.

Error cases now:

  • Use extractCleanErrorMessage to normalize error messages
  • Set persistent: true to ensure users see the error
  • Set votingStep to 'error' for UI feedback

These improvements ensure users are properly notified of failures and can take corrective action.


195-202: LGTM! Comprehensive catch-all error handling.

The catch block properly:

  • Sets voting step to 'error'
  • Logs the error for debugging
  • Shows a persistent error toast with the error message
  • Handles both Error instances and other thrown values

221-229: LGTM! Extended return interface supports the enhanced voting flow.

The hook now returns all necessary state and functions for consumers to:

  • Track voting progress (votingStep, lastActiveStep, stepMessage)
  • Reset the voting state (resetVotingState)
  • Check if the user has already voted (hasVotedInCurrentRound)

128-134: Clarify the intent of dummy encryption in vote updates.

The code uses a dummy encryption with { yes: 0n, no: 0n } as previousCiphertext for vote updates. Per CRISP's design, dummy ciphertexts are intentional—used to achieve coercion-resistance and receipt-freeness by making votes indistinguishable from non-votes. However, verify that:

  1. This dummy approach is correct for the vote update flow and matches how the proof generation validates previous ciphertexts
  2. The contract or server verification logic accepts dummy ciphertexts for vote updates without expecting actual previous plaintext knowledge
  3. The TODO comment reflects documentation debt rather than incomplete implementation

@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 8, 2025 20:57 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 8, 2025 20:57 Inactive
@ctrlc03 ctrlc03 self-requested a review December 8, 2025 20:57

@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: 0

Caution

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

⚠️ Outside diff range comments (1)
examples/CRISP/server/src/server/routes/voting.rs (1)

156-169: Database consistency issue: rollback missing on contract creation failure.

When contract creation fails for a new vote, the voter address remains in the database but no vote is broadcast. This leaves the system in an inconsistent state where has_voted returns true but no vote was actually recorded on-chain.

Apply this diff to rollback the voter insertion on contract creation failure:

     Ok(c) => c,
     Err(e) => {
         error!("[e3_id={}] Contract creation error: {:?}", vote.round_id, e);
+        
+        // Rollback voter insertion for new votes
+        if !is_vote_update {
+            let _ = match repo.remove_voter_address(&vote.address).await {
+                Ok(_) => (),
+                Err(e) => error!("Error rolling back the vote: {e}"),
+            };
+        }
+        
         return HttpResponse::InternalServerError().json("Internal server error");
     }
 };
🧹 Nitpick comments (2)
examples/CRISP/server/src/server/routes/voting.rs (2)

62-65: Consider logging round status query failures.

The round status query error is silently suppressed and returns None. While this prevents the entire request from failing, it might hide legitimate issues.

Consider adding logging:

 let round_status = match store.e3(request.round_id).get_e3_state_lite().await {
     Ok(state) => Some(state.status),
-    Err(_) => None,
+    Err(e) => {
+        error!("[e3_id={}] Failed to get round status: {:?}", request.round_id, e);
+        None
+    }
 };

193-217: Replace string-based error matching with structured error type handling.

The error extraction via contains() is fragile and not idiomatic for alloy 1.0.41. Alloy provides structured error types (RpcError, alloy::contract::Error with variants like UnknownFunction, AbiError, TransportError) and helper methods (as_decoded_error(), as_revert_data(), as_decoded_interface_error()) that should be used instead of parsing to_string() output. String-based matching will break if upstream error messages change format.

Prefer pattern matching on error enum variants or using alloy's error decoding helpers. For errors wrapped in eyre::Error, use downcast_ref() or chain inspection to access the underlying structured error type before branching on specific error conditions.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ce8f20 and 0c9c93b.

📒 Files selected for processing (1)
  • examples/CRISP/server/src/server/routes/voting.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/server/src/server/routes/voting.rs
📚 Learning: 2024-11-25T09:56:11.195Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 184
File: packages/ciphernode/net/src/network_relay.rs:0-0
Timestamp: 2024-11-25T09:56:11.195Z
Learning: In `NetworkPeer::new` in `packages/ciphernode/net/src/network_peer.rs`, peers are converted to multiaddrs, and validation of the address format occurs during this conversion. Duplicate addresses are managed as connections are deduplicated.

Applied to files:

  • examples/CRISP/server/src/server/routes/voting.rs
🧬 Code graph analysis (1)
examples/CRISP/server/src/server/routes/voting.rs (2)
examples/CRISP/client/src/model/vote.model.ts (2)
  • VoteStatusRequest (38-41)
  • VoteStatusResponse (43-48)
examples/CRISP/server/src/server/repo.rs (1)
  • has_voted (229-232)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build_sdk
  • GitHub Check: crisp_unit
  • GitHub Check: build_e3_support_dev
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: build_ciphernode_image
  • GitHub Check: rust_unit
  • GitHub Check: test_net
  • GitHub Check: rust_integration
  • GitHub Check: test_contracts
  • GitHub Check: Build & Push Image
🔇 Additional comments (2)
examples/CRISP/server/src/server/routes/voting.rs (2)

7-26: LGTM!

The imports and route configuration correctly support the new vote status endpoint and vote update functionality.


171-191: LGTM!

The broadcast logic correctly handles both new votes and updates, with appropriate success messages and proper error delegation.

@vercel vercel Bot temporarily deployed to Preview – crisp December 8, 2025 22:40 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 8, 2025 22:40 Inactive
ctrlc03
ctrlc03 previously approved these changes Dec 8, 2025

@ctrlc03 ctrlc03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tACK

@vercel vercel Bot temporarily deployed to Preview – crisp December 9, 2025 00:29 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 9, 2025 00:30 Inactive
@ctrlc03 ctrlc03 self-requested a review December 9, 2025 08:50
@ctrlc03 ctrlc03 merged commit 22764dd into main Dec 9, 2025
26 checks passed
@ctrlc03 ctrlc03 deleted the chore/crisp-fixes branch December 9, 2025 08:52
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.

CRISP emit event to inform sever whether to update or insert a new vote in the storage

2 participants