Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent duplicte click events #1522

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

myieye
Copy link
Contributor

@myieye myieye commented Mar 5, 2025

Resolves #1494

Wow, this is messy. I'm not sure what to think.
On the Download button, click events while a project is downloading get queued up and triggered after the download is finished. So...that's why there's so much weird code in there.

This is not the way, but, I'm not quite sure what is. Probably different async context management in .NET?

Summary by CodeRabbit

  • New Features

    • Introduced asynchronous count increment functionality with a loading state in the sandbox component.
    • Enhanced project download handling to prevent multiple concurrent downloads and improve user experience with loading indicators.
    • Updated UI components to use buttons instead of anchors for better interaction and loading state management.
  • Bug Fixes

    • Prevented duplicate project deletion and creation actions by disabling interactions during ongoing operations.
    • Improved project download handling by avoiding redundant downloads and temporarily blocking repeated requests during the process.

Copy link

coderabbitai bot commented Mar 5, 2025

Walkthrough

The changes involve replacing AnchorListItem with ButtonListItem across various components, enhancing user interactions by introducing loading states to prevent actions during ongoing processes. The HomeView and Server components now include logic to manage project deletions and downloads more effectively, while the Sandbox component introduces an asynchronous increment function with a loading state. The ButtonListItem component has also been updated to improve its flexibility and accessibility.

Changes

File(s) Summary
frontend/…/HomeView.svelte Replaced AnchorListItem with ButtonListItem. Introduced a loading constant to track deletion states, updated click handlers for delete and create buttons to check loading states before invoking their respective functions.
frontend/…/Server.svelte Modified downloadCrdtProject to check for existing projects in localProjects via matchesProject(), preventing duplicate downloads. Updated rendering to use ButtonListItem and added logic to disable interactions during loading.
frontend/…/Sandbox.svelte Introduced an asynchronous function incrementAsync to increment a count variable after a delay. Added reactive variables count and loading, and updated the UI to include a button that triggers the increment function while disabled during loading.
frontend/…/service-provider.ts Imported a new type IFwEvent and updated the services property in LexboxServiceProvider to include a default implementation for the JsEventListener service, providing an asynchronous method nextEventAsync.
frontend/…/ButtonListItem.svelte Updated href property type to `string

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HomeView
    participant ProjectService

    User->>HomeView: Click delete button for a project
    HomeView->>HomeView: Evaluate if project is loading
    alt Not loading
        HomeView->>ProjectService: deleteProject(project.name)
    else Loading active
        HomeView-->>User: Ignore delete action
    end

    User->>HomeView: Click create example project button
    HomeView->>HomeView: Check createProjectLoading state
    alt Creation allowed
        HomeView->>ProjectService: createExampleProject()
    else Creation blocked
        HomeView-->>User: Ignore create action
    end
Loading
sequenceDiagram
    participant User
    participant ServerView
    participant ProjectService

    User->>ServerView: Click project list item
    ServerView->>ServerView: Determine loading state & check if downloading
    alt Not loading
        ServerView->>ServerView: Check if project exists (matchesProject)
        alt Project already exists
            ServerView-->>User: No download initiated
        else Project does not exist
            ServerView->>ProjectService: downloadCrdtProject(project)
            ProjectService-->>ServerView: Download successful
            ServerView->>ServerView: Add project to localProjects
            ServerView->>ServerView: Delay resetting downloading flag (setTimeout)
        end
    else Loading active
        ServerView-->>User: Ignore download action
    end
Loading
sequenceDiagram
    participant User
    participant SandboxView

    User->>SandboxView: Click Increment Async button
    SandboxView->>SandboxView: Check loading state
    alt Not loading
        SandboxView->>SandboxView: Set loading to true
        SandboxView->>SandboxView: Call incrementAsync()
        SandboxView-->>User: Show loading feedback
    else Loading active
        SandboxView-->>User: Ignore increment action
    end
Loading

Poem

I'm a happy rabbit in code's green field,
Hoping my tweaks help bugs to yield.
With loading checks in every byte,
Our projects dance in code so light.
Hoppin' through logic with a joyful heart,
Every pull makes our code a work of art!
🐰💻


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1605a9 and 7661af1.

📒 Files selected for processing (4)
  • frontend/viewer/src/home/HomeView.svelte (4 hunks)
  • frontend/viewer/src/home/Server.svelte (4 hunks)
  • frontend/viewer/src/lib/sandbox/Sandbox.svelte (3 hunks)
  • frontend/viewer/src/lib/utils/ButtonListItem.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/viewer/src/home/Server.svelte
  • frontend/viewer/src/home/HomeView.svelte
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build UI / publish-ui
  • GitHub Check: Build API / publish-api
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Build FW Lite and run tests
  • GitHub Check: check-and-lint
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (6)
frontend/viewer/src/lib/sandbox/Sandbox.svelte (3)

16-17: New imports support loading state and button functionality

These imports add support for the async functionality with loading indicators that will help prevent duplicate click events.


53-60: Good implementation of loading state to prevent duplicate actions

The implementation correctly manages the loading state to prevent duplicate click events, which aligns with the PR objective. Setting loading = true at the start and loading = false at the end ensures buttons are disabled during async operations.


101-116: Effective demonstration of button disabling during async operations

This section demonstrates how to prevent duplicate actions by disabling buttons during async operations, showing both standard Button and ButtonListItem components with proper loading state handling.

The implementation provides a clear example of how to solve the issue mentioned in the PR objectives where click events were being queued and subsequently triggered after completion.

frontend/viewer/src/lib/utils/ButtonListItem.svelte (3)

2-2: Improved component flexibility with optional href

Making href optional allows this component to function as either a link or a button, which is a good improvement for component reusability.


5-16: Well-implemented dynamic element with accessibility support

The dynamic element implementation correctly renders either an anchor or button based on whether href is provided. The addition of role and tabindex attributes ensures proper accessibility.

This change helps prevent duplicate click events by allowing the component to be properly disabled during loading states when used as a button, addressing the core issue in the PR objectives.


19-19: Updated class name reflects component's dual nature

The class name update from 'anchor-list-item' to 'button-list-item' properly reflects the component's enhanced functionality.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Mar 5, 2025

UI unit Tests

12 tests  ±0   12 ✅ ±0   0s ⏱️ ±0s
 4 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit 7661af1. ± Comparison against base commit ce471c6.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Mar 5, 2025

C# Unit Tests

107 tests  ±0   107 ✅ ±0   5s ⏱️ ±0s
 16 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 7661af1. ± Comparison against base commit ce471c6.

♻️ This comment has been updated with latest results.

@myieye myieye marked this pull request as ready for review March 5, 2025 16:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/viewer/src/home/Server.svelte (1)

38-41: Consider a more robust approach than setTimeout.

While the timeout effectively prevents immediate re-triggering of downloads, it's a bit of a workaround. Consider using Svelte's built-in reactivity or a more explicit state management approach.

-      setTimeout(() => {
-        downloading = '';
-      }, 100);
+      // Allow UI to update before clearing download state
+      await new Promise(resolve => requestAnimationFrame(resolve));
+      downloading = '';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce471c6 and 9c669e5.

📒 Files selected for processing (2)
  • frontend/viewer/src/home/HomeView.svelte (2 hunks)
  • frontend/viewer/src/home/Server.svelte (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: GHA integration tests / playwright
  • GitHub Check: GHA integration tests / dotnet
  • GitHub Check: Build FW Lite and run tests
🔇 Additional comments (8)
frontend/viewer/src/home/HomeView.svelte (4)

150-150: Improved loading state tracking for better UX.

Using a scoped constant to track the loading state for each project is a good practice as it prevents duplication of the condition check throughout the template.


155-155: Correctly propagating loading state to the UI component.

Passing the loading state to the ListItem component ensures visual feedback to users during deletion operations, which improves UX by indicating that an action is in progress.


160-161: Good guard against duplicate delete operations.

This check successfully prevents multiple deletion operations if a user clicks rapidly or repeatedly on the delete button while a deletion is already in progress, addressing the core issue mentioned in the PR.


178-182: Effective prevention of duplicate project creation operations.

The condition in the click handler prevents users from triggering multiple project creation operations while one is already in progress. Additionally, the loading state is correctly applied to the UI to provide visual feedback.

frontend/viewer/src/home/Server.svelte (4)

26-27: Good optimization to prevent redundant downloads.

The early return if the project already exists in local projects prevents unnecessary network requests and potential data inconsistencies. This is an important check for preventing duplicate operations.


32-35: Proper local state update after successful download.

Adding the downloaded project to localProjects ensures the UI is immediately updated after a successful download, improving the user experience by providing immediate feedback without requiring a full refresh.


106-107: Effective loading state tracking for project downloads.

Similar to the HomeView component, using a local constant for tracking the loading state makes the code more readable and prevents repetition of the condition.


109-111: Complete solution for preventing duplicate downloads.

This implementation achieves three important things:

  1. Disables pointer events during loading, preventing accidental clicks
  2. Guards the download function with a loading check to prevent duplicate operations
  3. Provides visual feedback to users through the loading prop

This comprehensively addresses the core issue described in the PR objectives.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c669e5 and d1605a9.

📒 Files selected for processing (2)
  • frontend/viewer/src/lib/sandbox/Sandbox.svelte (3 hunks)
  • frontend/viewer/src/lib/services/service-provider.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build API / publish-api
  • GitHub Check: Build UI / publish-ui
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: check-and-lint
  • GitHub Check: Build FW Lite and run tests
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (6)
frontend/viewer/src/lib/sandbox/Sandbox.svelte (4)

16-16: Good addition of delay utility.

The import of the delay utility function from the time utility module is an appropriate dependency for implementing the asynchronous behavior.


52-53: Well-structured state management.

Clear state variables for tracking count and loading status. This pattern aligns with the PR objective of preventing duplicate clicks by maintaining loading state.


54-59: Good pattern for preventing duplicate actions.

This async function correctly implements a loading state pattern that:

  1. Sets loading state to true before operation
  2. Performs the async operation
  3. Updates the count
  4. Resets loading state afterward

This pattern effectively prevents duplicate actions during async operations, which aligns perfectly with the PR objective to "Prevent duplicate click events".


100-108: Effective UI implementation with loading state.

The button implementation properly:

  1. Disables the button when loading is true
  2. Displays loading state visually (via the loading prop)
  3. Triggers the async action on click
  4. Shows the current count

This demonstrates a reusable pattern that could be applied to solve the issue with Download button events mentioned in the PR objectives.

frontend/viewer/src/lib/services/service-provider.ts (2)

20-20: Good addition of IFwEvent import

Adding this type import enables proper type checking for the newly implemented Promise return type in nextEventAsync.


41-48: Default implementation helps prevent duplicate events

Good approach to provide a default no-op implementation for JsEventListener. This aligns with the PR objective to prevent duplicate click events.

Copy link
Collaborator

@hahn-kev hahn-kev left a comment

Choose a reason for hiding this comment

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

alright so I played with this a bit, we can disable buttons and that will prevent them from firing the click event, the style change also indicates that to the user which I think is helpful.

Unfortunately that doesn't work for ListItem since it's an <li> which does not support the disabled attribute. My first thought was that we should change that... maybe we can make it a button instead? I'm not sure, but I'm wishing we had ShadCN right now and we could just change it.

dispatch('refreshAll');
} finally {
downloading = '';
setTimeout(() => {
downloading = '';
Copy link
Collaborator

Choose a reason for hiding this comment

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

why the timeout? also while thinking about issues it could cause I realized that if you were to click download on 2 different projects, only one would show as downloading... We should just pull these into their own component then each project has it's own downloading state associated with the component.

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.

Can double click to get "Project already exists"
2 participants