-
Notifications
You must be signed in to change notification settings - Fork 15
#50: Add functionality to undo deleting a product #94
Conversation
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.
@Osmose Ready for your review. Thanks for all your help thus far; this is my first Redux patch ever! |
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.
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); |
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.
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, |
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.
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.
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.
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}> |
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.
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}> |
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.
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'} /> |
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.
Instead of hiding it with CSS, we can just not render the overlay div at all if showUndo
is false.
src/state/products.js
Outdated
@@ -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 |
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.
Is this not the same for every attribute here? I don't quite understand the comment.
src/state/products.js
Outdated
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'; |
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.
Maybe use the word "remove" for actual removal, and "delete" for marking as ready to be removed? Otherwise "delete" has two meanings.
src/state/products.js
Outdated
if (product.id === action.productId) { | ||
const flaggedProduct = {...product}; | ||
flaggedProduct.isDeleted = true; | ||
return flaggedProduct; |
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.
These lines can be simplified a tiny bit:
return {...product, isDeleted: true};
src/state/products.js
Outdated
@@ -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'; |
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.
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.
src/state/products.js
Outdated
|
||
/** | ||
* Remove all products marked as deleted from the store. | ||
* @param {string} productId Unique ID for the product to remove. |
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 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.
0ec6eba
to
f08ba67
Compare
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.
Implementation notes:
Undo
buton for a product.true
.true
.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:
design-pending
.