Skip to content
This repository has been archived by the owner on Dec 3, 2020. It is now read-only.

#50: Add functionality to undo deleting a product #94

Merged
merged 2 commits into from
Sep 17, 2018
Merged

Conversation

biancadanforth
Copy link
Collaborator

@biancadanforth biancadanforth commented Sep 8, 2018

This patch makes it possible for the user to undelete a deleted product from their list of tracked products while the browserAction popup remains open. If the browserAction popup closes, any deleted products will be removed permanently.

50-undo

Implementation notes:

  • Add the SET_DELETION_FLAG action which dispatches when the user clicks the ‘X’ button or Undo buton for a product.
    • If X button: The product card is greyed out, cannot be clicked to open the product page, and an Undo button appears in the top right corner. The deletion flag for the product in storage is set to true.
    • If Undo button: The product card is restored to its original view and behavior. The deletion flag for the product in storage is set to true.
  • Replace the REMOVE_PRODUCT action with the REMOVE_MARKED_PRODUCTS action which dispatches when the browserAction is closed.
    • Any greyed out cards will be gone the next time the popup is opened.

In Nightly, when the REMOVE_MARKED_PRODUCTS action is dispatched, the error from Issue #60 is logged, though it does not affect the functionality of this patch.

Follow-up issues:

This patch makes it possible for the user to undelete a deleted product to their list of tracked products while the browserAction popup remains open. If the browserAction popup closes, any deleted products will be removed permanently.

Implementation notes:
* Add the DELETE_PRODUCT action which dispatches when the user clicks the ‘X’ button for a product.
  * The product card is greyed out, cannot be clicked to open the product page, and an Undo button appears in the top right corner.
* Add the UNDELETE_PRODUCT action which dispatches when the user clicks the ‘Undo’ button for a product.
  * The product card is restored to its original view and behavior.
* Replace the REMOVE_PRODUCT action with the DELETE_MARKED_PRODUCTS action which dispatches when the browserAction is closed.
  * Any greyed out cards will be gone the next time the popup is opened.

In Nightly, when the DELETE_MARKED_PRODUCTS action is dispatched, the error from Issue #60 is logged, though it does not affect the functionality of this patch.

Follow-up issues:
* #93: Show a loading screen for the first render before the app's state has been loaded.
@biancadanforth
Copy link
Collaborator Author

@Osmose Ready for your review. Thanks for all your help thus far; this is my first Redux patch ever!

Copy link
Contributor

@Osmose Osmose left a comment

Choose a reason for hiding this comment

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

Nice work! The redux changes look sound. :D

Feel free to merge this after making the changes. Ping me if you want a second pass on the suggested Redux changes.

@@ -41,6 +46,14 @@ export default class BrowserActionApp extends React.Component {

componentDidMount() {
this.props.loadStateFromStorage();
window.addEventListener('unload', this.handleUnload);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also remove this listener in componentWillUnmount. It'll ensure in tests that we don't leave any leftover listeners.

* If true, show an undo icon in the top right corner.
* Will not be shown unless the close icon is clicked.
*/
showUndo: pt.bool,
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm, I wonder if the undo should just be a wrapper component over the card instead of an extra option? Technically one could set both showClose and showUndo to true, and it's ambiguous what the behavior is in that case.

At the same time, depending on the new UX design for the Undo state, maybe we won't even need to show a product card without a close button, or below the undo state? It might be worth deferring to that decision and just landing this as is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I'd say that change could go into the follow-up issue to update the Undo view with the final UI, which we are still waiting for.

{latestPrice && (
<div className="latest-price">
{latestPrice.amount.toFormat('$0.00')}
{showUndo && (
<button className={showUndo ? 'undo-button' : 'undo-button hidden'} type="button" onClick={this.handleClickUndo}>
Copy link
Contributor

Choose a reason for hiding this comment

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

showUndo must be true for this to be displayed, so the false case will never be hit.

{showClose && (
<button className="close-button" type="button" onClick={this.handleClickClose}>
<button className={showUndo ? 'close-button hidden' : 'close-button'} type="button" onClick={this.handleClickClose}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of hiding the button with CSS, add the showUndo check to the initial condition so that we don't render any HTML for it at all.

@@ -119,6 +145,7 @@ export default class ProductCard extends React.Component {
{buttonText}
</button>
)}
<div className={showUndo ? 'opaque-overlay' : 'opaque-overlay hidden'} />
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of hiding it with CSS, we can just not render the overlay div at all if showUndo is false.

@@ -21,6 +21,7 @@ export const productShape = pt.shape({
title: pt.string.isRequired,
url: pt.string.isRequired,
image: pt.string.isRequired,
isDeleted: pt.bool.isRequired, // flag to include in returned list of products from store
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this not the same for every attribute here? I don't quite understand the comment.

const REMOVE_PRODUCT = 'commerce/products/REMOVE_PRODUCT';
const DELETE_PRODUCT = 'commerce/products/DELETE_PRODUCT';
const UNDELETE_PRODUCT = 'commerce/products/UNDELETE_PRODUCT';
const DELETE_MARKED_PRODUCTS = 'commerce/products/DELETE_MARKED_PRODUCTS';
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use the word "remove" for actual removal, and "delete" for marking as ready to be removed? Otherwise "delete" has two meanings.

if (product.id === action.productId) {
const flaggedProduct = {...product};
flaggedProduct.isDeleted = true;
return flaggedProduct;
Copy link
Contributor

Choose a reason for hiding this comment

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

These lines can be simplified a tiny bit:

return {...product, isDeleted: true};

@@ -40,7 +41,9 @@ export const extractedProductShape = pt.shape({
// Actions

export const ADD_PRODUCT = 'commerce/products/ADD_PRODUCT'; // Used by price duck
const REMOVE_PRODUCT = 'commerce/products/REMOVE_PRODUCT';
const DELETE_PRODUCT = 'commerce/products/DELETE_PRODUCT';
const UNDELETE_PRODUCT = 'commerce/products/UNDELETE_PRODUCT';
Copy link
Contributor

Choose a reason for hiding this comment

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

These two can be collapsed into a single action: SET_DELETION_FLAG. Then the action you dispatch can contain the value of the flag (true or false), rather than having an action for setting it to true and another for setting it to false.


/**
* Remove all products marked as deleted from the store.
* @param {string} productId Unique ID for the product to remove.
Copy link
Contributor

Choose a reason for hiding this comment

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

This does not take any parameters.

* Rename 'deleteMarkedProducts' to 'removeMarkedProducts'.
* Remove 'unload' event listener on 'componentWillUnmount' in BrowserActionApp.jsx.
* Simplify some of the JSX and CSS for the ProductCard component.
* Consolidate the 'DELETE_PRODUCT' and 'UNDELETE_PRODUCT' Redux actions into a single SET_DELETION_FLAG action.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants