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

fix: Add unsubscribe functionality to NewsletterModal #210

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

Conversation

aryanprince
Copy link

@aryanprince aryanprince commented Aug 11, 2024

Summary

This PR fixes a bug where unsubscribing from a newsletter via the newsletter modal failed to work as expected. Specifically, the bug prevented the decrement of unsubscribe credits, updating of the newsletter status, triggering of analytics and more. You can find more details about the issue here.

The main issue was that the unsubscribe buttons in the app were performing different actions. With this fix, both unsubscribe buttons (table row action and the one in the newsletter modal) now use the same useUnsubscribeButton hook, ensuring consistent behavior across the app.

Fixes #209

Changes

  • Updated the onClick property for the unsubscribe button within the newsletter modal to use the onUnsubscribe function from the useUnsubscribeButton hook, ensuring all expected actions are executed.
  • Passed the mutate prop from the parent BulkUnsubscribeSection component to the NewsletterModal component.
  • Refactored all instances of the NewsletterModal component cus changes in its props, which are now required (was optional before).

Demo

Before the fix

The GIF below shows the unsubscribe process using the table row action button, which works as expected:

Error - Unsubbing using direct unsub button

In contrast, the GIF below demonstrates unsubscribing via the newsletter modal, which does not work as expected. It doesn't update the unsubscribe count or update the newsletter's status:

Error - Unsubbing using email dialog menu

After the fix

The GIF below illustrates the corrected behavior after applying this PR. Unsubscribing via the newsletter modal now does all of the same actions as clicking the unsubscribe button from the table row action button:

Fixed - Unsubbing using email dialog menu

Summary by CodeRabbit

  • New Features

    • Introduced conditional rendering for the NewsletterModal across multiple components to enhance performance and user experience.
    • Added a required newsletter prop and an optional mutate prop to the NewsletterModal, improving data handling and interactivity.
    • Enhanced the unsubscribe button with a dynamic loading state for better user feedback during the unsubscribe process.
    • Integrated new hooks for managing subscription access and the unsubscribe process in the NewsletterModal.
  • Bug Fixes

    • Improved clarity and efficiency by eliminating unnecessary renders of the NewsletterModal when conditions are not met.
- Pass the `mutate` prop from parent component to the `NewsletterModal` component
- Uses the `useUnsubscribeButton` hook to manage the newsletter unsubscription
- On clicking the unsubscribe button from the modal, calls the `onUnsubscribe` function from the `useUnsubscribeButton` hook

Fixes elie222#209
- Updates `NewsletterModal` component's usage throughout application as one of its props is no longer optional
Copy link

vercel bot commented Aug 11, 2024

@aryanprince is attempting to deploy a commit to the Inbox Zero Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Aug 11, 2024

CLA assistant check
All committers have signed the CLA.

@aryanprince aryanprince changed the title fix: Add unsubscribe functionality to NewsletterModal (#209) Aug 11, 2024
Copy link
Contributor

coderabbitai bot commented Aug 11, 2024

Walkthrough

The recent changes enhance the rendering logic of the NewsletterModal across various components by implementing conditional rendering based on state values. This ensures the modal is displayed only when relevant, improving performance and user experience. Additionally, the NewsletterModal now requires a newsletter prop, and a new mutate function prop allows for state updates upon user actions, addressing a critical bug related to unsubscribe functionalities.

Changes

Files Change Summary
apps/web/app/(app)/bulk-unsubscribe/BulkUnsubscribeSection.tsx Added conditional rendering for NewsletterModal based on openedNewsletter. Introduced a new mutate prop.
apps/web/app/(app)/cold-email-blocker/ColdEmailList.tsx Implemented conditional rendering for NewsletterModal based on openedRow.
apps/web/app/(app)/cold-email-blocker/ColdEmailRejected.tsx Modified rendering logic to conditionally display NewsletterModal when openedRow is truthy.
apps/web/app/(app)/new-senders/NewSenders.tsx Updated NewsletterModal rendering to be conditional on openedNewsletter.
apps/web/app/(app)/stats/NewsletterModal.tsx Changed newsletter prop to required and added optional mutate prop. Enhanced unsubscribe logic with new hooks.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant NewsletterModal
    participant AppState

    User->>AppState: Open NewsletterModal
    AppState->>NewsletterModal: Check opened state
    alt If opened state is true
        AppState->>NewsletterModal: Render modal
        User->>NewsletterModal: Click unsubscribe
        NewsletterModal->>AppState: Trigger unsubscribe action
        AppState->>NewsletterModal: Update state and credits
    else If opened state is false
        AppState->>NewsletterModal: Do not render modal
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Unsubscribe credits not decrementing when using the newsletter modal button (#[209])
Ensure modal only shows relevant information and actions (#[209])

🐰✨ In the code with a hop and a skip,
The modal’s now clever, oh what a trip!
Only when needed, it opens with grace,
Unsubscribing with ease, a smile on your face!
Hooray for the changes, let’s dance and rejoice,
With each little fix, we’re lifting our voice! 🎉✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 23504a2 and 2f53ad3.

Files selected for processing (5)
  • apps/web/app/(app)/bulk-unsubscribe/BulkUnsubscribeSection.tsx (1 hunks)
  • apps/web/app/(app)/cold-email-blocker/ColdEmailList.tsx (1 hunks)
  • apps/web/app/(app)/cold-email-blocker/ColdEmailRejected.tsx (1 hunks)
  • apps/web/app/(app)/new-senders/NewSenders.tsx (1 hunks)
  • apps/web/app/(app)/stats/NewsletterModal.tsx (2 hunks)
Additional context used
Biome
apps/web/app/(app)/stats/NewsletterModal.tsx

[error] 42-42: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 33-34: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the transpilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

Additional comments not posted (7)
apps/web/app/(app)/cold-email-blocker/ColdEmailRejected.tsx (1)

68-73: Conditional Rendering of NewsletterModal

The conditional rendering of NewsletterModal based on openedRow is a good practice. It prevents unnecessary rendering of the modal when there is no relevant data to display, enhancing performance and user experience.

apps/web/app/(app)/bulk-unsubscribe/BulkUnsubscribeSection.tsx (1)

226-233: Conditional Rendering and New mutate Prop

The conditional rendering of NewsletterModal based on openedNewsletter enhances performance by rendering the modal only when necessary. The introduction of the mutate prop allows for state updates, improving the component's functionality.

apps/web/app/(app)/stats/NewsletterModal.tsx (3)

39-42: Required newsletter Prop and Optional mutate Prop

The change to make the newsletter prop required ensures that the component always receives necessary data, enhancing robustness. The optional mutate prop allows for flexible state handling, which is a valuable addition.

Tools
Biome

[error] 42-42: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


45-45: Default Value for mutate

Providing a default value for mutate ensures that the component can function even if the prop is not explicitly passed, which is a good practice for maintaining component stability.


56-62: Integration of useUnsubscribeButton Hook

The integration of the useUnsubscribeButton hook centralizes the unsubscribe logic, improving maintainability and ensuring consistent behavior across different parts of the application.

apps/web/app/(app)/cold-email-blocker/ColdEmailList.tsx (1)

146-151: Conditional rendering of NewsletterModal improves performance.

The change ensures that the NewsletterModal is only rendered when openedRow is truthy, preventing unnecessary renders and enhancing performance.

apps/web/app/(app)/new-senders/NewSenders.tsx (1)

214-220: Conditional rendering of NewsletterModal enhances performance.

The change ensures that the NewsletterModal is only rendered when openedNewsletter is truthy, preventing unnecessary renders and improving performance.

onClose: (isOpen: boolean) => void;
refreshInterval?: number;
mutate?: () => Promise<any>;
Copy link
Contributor

@coderabbitai coderabbitai bot Aug 11, 2024

Choose a reason for hiding this comment

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

Avoid Using any Type

The use of any should be avoided to maintain type safety. Consider specifying a more precise type for the mutate function to enhance type checking.

- mutate?: () => Promise<any>;
+ mutate?: () => Promise<void>;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mutate?: () => Promise<any>;
mutate?: () => Promise<void>;
Tools
Biome

[error] 42-42: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

Copy link
Author

Choose a reason for hiding this comment

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

I agree bot. I tried my best to make the suggestion of mutate?: () => Promise<void>; work but there were a lot of side effects with it. Would be happy to see a fix for this.

The issue here is that the mutate function passed down to this NewsletterModal component is to mutate multiple newsletters, but this component only needs to mutate a single newsletter.

The only fixes I could think of was to:

  1. Create a new API route to mutate a single newsletter as well, along with the existing API route to mutate multiple newsletters.
  2. Update the useUnsubscribeButton hook's logic, and all of its usages.

I think this will need refactoring in the future regardless. Would be happy to have a look at this later.

Copy link
Contributor

Choose a reason for hiding this comment

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

@aryanprince, thanks for sharing your insights and potential solutions. It seems like a refactoring effort could indeed be beneficial to address the scope of the mutate function. To ensure this doesn't get lost, would you like me to create a follow-up issue to track this refactoring task? This way, we can revisit it when the time is right and ensure the NewsletterModal component handles mutations more precisely. Let me know how you'd like to proceed!

Comment on lines 81 to 87
{!unsubscribeLoading && <span>Unsubscribe</span>}
{!!unsubscribeLoading && (
<div className="flex cursor-not-allowed items-center opacity-50">
<ButtonLoader />
<span>Unsubscribing...</span>
</div>
)}
Copy link
Owner

Choose a reason for hiding this comment

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

Could use a ternary to make this cleaner. ie: unsubscribeLoading ? Unsub : Loading...

If you're trying to add disabled state, then you could add disabled={unsubscribeLoading} to the button itself.

Copy link
Author

Choose a reason for hiding this comment

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

Updated it now! Used a ternary here instead and added the disabled state to the button.

Copy link
Owner

@elie222 elie222 left a comment

Choose a reason for hiding this comment

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

2 comments

onClose={() => setOpenedNewsletter(undefined)}
refreshInterval={props.refreshInterval}
/>
{openedNewsletter && (
Copy link
Owner

Choose a reason for hiding this comment

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

Did you need to add {openedNewsletter && (?
I think it stops the animation when it's done like that. Or was this added to fix a bug?

Copy link
Author

Choose a reason for hiding this comment

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

Reason

This is cus the <NewsletterModal /> component now requires (non-optional) the openedNewsletter prop, which was previously optional.

The reason for this change is that the component now uses the existing useUnsubscribeButton hook, which does need a required openedNewsletter prop.

Initially, I tried keeping openedNewsletter as optional (to have less diffs like the one you brought up here), but that would have required introducing a second hook for unsubscribing, which would complicate the logic unnecessarily.

Minor Benefit

Additionally, making openedNewsletter a required prop also ensures that the <NewsletterModal /> is only rendered when all necessary data is present, making it more robust and predictable in terms of when it renders throughout the app.

Quick Demo

Also, below GIF shows that the animations are still intact. No issues with that according to my testing.

CleanShot 2024-08-23 at 12 29 31

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 2f53ad3 and 1b7c1b8.

Files selected for processing (1)
  • apps/web/app/(app)/stats/NewsletterModal.tsx (2 hunks)
Additional context used
Biome
apps/web/app/(app)/stats/NewsletterModal.tsx

[error] 42-42: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 4-5: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the transpilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

Additional comments not posted (2)
apps/web/app/(app)/stats/NewsletterModal.tsx (2)

42-42: Avoid using any type for mutate prop.

The use of any should be avoided to maintain type safety. Consider specifying a more precise type for the mutate function to enhance type checking.

- mutate?: () => Promise<any>;
+ mutate?: () => Promise<void>;
Tools
Biome

[error] 42-42: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


74-90: Enhancement: Improved unsubscribe button logic.

The unsubscribe button now includes a loading state and is disabled during the unsubscribe action, enhancing user experience by preventing multiple submissions and providing feedback. The implementation is correct.

apps/web/app/(app)/stats/NewsletterModal.tsx Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants