-
Notifications
You must be signed in to change notification settings - Fork 2.5k
feat: Add DismissibleUpsell component for dismissible messages #7842
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
Conversation
- Created DismissibleUpsell component with variant support (banner/default) - Added dismissedUpsells to GlobalState for persistence - Implemented message handlers for dismissing and retrieving dismissed upsells - Added comprehensive tests for the component - Uses VSCode extension globalState for persistent storage
|
|
||
| interface DismissibleUpsellProps { | ||
| /** Required unique identifier for this upsell */ | ||
| className: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a dedicated prop (e.g. 'upsellId') for the unique identifier instead of overloading the 'className' prop, which is typically used for styling.
| <DismissButton | ||
| $variant={variant} | ||
| onClick={handleDismiss} | ||
| aria-label="Dismiss" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace hardcoded accessibility strings with translation keys using the i18n function to ensure localization.
| aria-label="Dismiss" | |
| aria-label={i18n('dismiss')} |
This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewing my own code is like debugging in a mirror - everything looks backwards but the bugs are still mine.
|
|
||
| window.addEventListener("message", handleMessage) | ||
| return () => window.removeEventListener("message", handleMessage) | ||
| }, [className]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential memory leak here. The event listener cleanup in the useEffect return function might not execute if the component unmounts while waiting for the initial response. Consider using a mounted flag to prevent state updates after unmount.
|
|
||
| // Call the optional callback | ||
| onDismiss?.() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Race condition: If the component unmounts immediately after clicking dismiss but before the message is sent, the dismissal won't be persisted. Consider ensuring the message is sent before hiding the component.
|
|
||
| interface DismissibleUpsellProps { | ||
| /** Required unique identifier for this upsell */ | ||
| className: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using className as a unique identifier is unconventional and could be confusing. The className prop is typically used for CSS styling. Consider adding a dedicated id prop instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. There should be two different attributes - className and name.
| // Send updated list back to webview | ||
| await provider.postMessageToWebview({ | ||
| type: "dismissedUpsells", | ||
| list: [...dismissedUpsells, message.upsellId], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient array operations - you're spreading the dismissedUpsells array twice unnecessarily. The updatedList already contains all items, so just use: list: updatedList
|
|
||
| break | ||
| } | ||
| case "dismissUpsell": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling. What happens if updateGlobalState fails? Consider wrapping in try-catch and logging any errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK to fail silently in this case
|
@roomote-agent read the comments and apply relevant feedback. |
|
👋 Hi @brunobergher! I'm reviewing the feedback and will apply the relevant changes. I'll address the following points:
Starting work on these changes now... |
- Changed from className to separate 'id' and 'className' props for better semantics - Added i18n support for accessibility labels (aria-label and title) - Fixed memory leak by adding mounted flag to prevent state updates after unmount - Fixed race condition by sending dismiss message before hiding component - Fixed inefficient array operations in webviewMessageHandler - Added comprehensive test coverage for edge cases including: - Multiple rapid dismissals - Component unmounting during async operations - Invalid/malformed message handling - Proper message sending before unmount - Added null checks for message data to handle edge cases gracefully
|
@ellipsis-dev @roomote @brunobergher I have successfully applied all the requested changes from the PR feedback: Changes Implemented1. Separated ID from className
2. Added i18n support
3. Fixed memory leak
4. Fixed race condition
5. Optimized array operations
6. Enhanced test coverage
CI Status
The Windows test failure seems to be a pre-existing issue as the changes are platform-agnostic and all other tests pass successfully. Commit: e5e8005 |
This PR adds a new DismissibleUpsell component that allows displaying dismissible messages/upsells to users that will never be shown again once dismissed.
Changes
DismissibleUpsellReact component with variant support (banner/default)dismissedUpsellsfield to GlobalState for persistent storageFeatures
Testing
Usage Example
Closes the request for creating a dismissible upsell component.
Important
Introduces
DismissibleUpsellcomponent for persistent dismissible messages with global state management and comprehensive testing.DismissibleUpsellcomponent inDismissibleUpsell.tsxfor dismissible messages withbanneranddefaultvariants.dismissedUpsellsinglobal-settings.tsfor persistent dismissal state.dismissUpsellandgetDismissedUpsellsinwebviewMessageHandler.tsfor managing dismissals.DismissibleUpsell.spec.tsxcovering rendering, dismissal, and edge cases.ExtensionMessage.tsandWebviewMessage.tsto include new message types for upsell management.This description was created by
for e5e8005. You can customize this summary. It will automatically update as commits are pushed.