-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Refactor logic for checking storage add-on availability #97450
Refactor logic for checking storage add-on availability #97450
Conversation
Jetpack Cloud live (direct link)
Automattic for Agencies live (direct link)
|
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: App Entrypoints (~1551 bytes added 📈 [gzipped])
Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used. Sections (~49677 bytes added 📈 [gzipped])
Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to. Async-loaded Components (~6909 bytes added 📈 [gzipped])
React components that are loaded lazily, when a certain part of UI is displayed for the first time. Legend What is parsed and gzip size?Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory. Generated by performance advisor bot at iscalypsofastyet.com. |
Thanks for working on this, @fredrikekelund . I will prioritize reviewing this. Sorry that I deployed overlapped clean-up before noticing your work though:
Hopefully I won't cause you too much code conflict troubles. |
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.
Tested the scenarios listed in the table from the Testing Instructions section, and they work as expected 👍
} | ||
|
||
type AddOnPurchaseStatus = { | ||
available: boolean; | ||
hidden?: boolean; |
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.
I don't see this flag used anywhere. I suppose we can remove it :)
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.
Good eyes! This is a leftover from a solution I pursued previously.
storage?: SiteMediaStorage | ||
): boolean { | ||
if ( ! storage ) { | ||
return true; |
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.
Could you help me better understand the reason of returning true
here? Without context, my interpretation is that this hook would return true
when the app hasn't or can't provide it with the correct media storage data, which sounds suspicious.
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.
Good question. storage
is undefined while the /media-storage
API endpoint is loading. I agree it makes more sense to handle the loading state elsewhere 👍
const currentMaxStorage = storage.maxStorageBytes / Math.pow( 1024, 3 ); | ||
const maxStorageExcludingAddons = storage.maxStorageBytesExcludingAddons / Math.pow( 1024, 3 ); | ||
const existingAddOnStorage = currentMaxStorage - maxStorageExcludingAddons; | ||
|
||
return existingAddOnStorage < quantity; |
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 should work most of the time here. However, I'd like to point out that expecting JS division to always produce integral values is in general not recommended, since they are double-precision floats underneath.
Also, if the sole usage of maxStorageExcludingAddons
introduced in D168645-code is for doing this computation, could we simply return existingAddOnStorage
from the endpoint instead? That would eliminate the floating number computation here all together as well.
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.
I'm not sure how big of a problem this is in practice, but I take your point that we might as well return existingAddOnStorage
directly from the API endpoint, since that's what we ultimately want anyway. I've updated the back-end PR (now at 168696-gh-Automattic/wpcom).
Let me know your thoughts!
}, [ availableStorageUpgrade, storageAddOns ] ); | ||
return storageAddOns | ||
.filter( ( addOn ) => addOn !== null ) | ||
.filter( ( addOn ): addOn is AddOnMeta => |
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.
storageAddOns
should be always in the type of [AddOnMeta]
here, so filtering on the dynamic type here won't be necessary. If not, we should fix useStorageAddOns
instead to fully leverage the power of type checking of TS.
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.
My local TS didn't complain without addOn is AddOnMeta
, but the CI process did, which is why I added this in 3a40d56
Typescript has (or has had) trouble with inferring types in filtered arrays. So, I'd happily remove this, but I don't think we can without upsetting the CI jobs.
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.
That's unfortunate. In that case, let's leave it there for now.
const mediaStorage = useSiteMediaStorage( { siteIdOrSlug: selectedSiteId } ); | ||
|
||
if ( addOnMeta.productSlug !== PRODUCT_1GB_SPACE ) { | ||
return true; |
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.
I'm wondering if returning true
here is correct, since that means the passed in addOnMeta
doesn't belong to a storage add-on, but the hook considers it as an available storage add-on. Should it return false 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.
Tricky. I pondered this for a bit and landed on returning an enum. Let me know what you think, @southp
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 better now. I have a feeling that we can further generalize and simplify it as a general add-on level concept so we don't have to treat storage add-on as a special case here. However, let's not hold ourselves back and further bloat this PR :)
type AddOnPurchaseStatus = { | ||
available: boolean; | ||
hidden?: boolean; | ||
text?: ReturnType< typeof i18n.translate >; |
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.
A side note that I think the user-facing text we display shouldn't have been part of this hook. The text is part of UI, so should be derived from the calypso side based on the status value.
It's not related to this PR, though; just wanted to raise it :)
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.
Good point. This is already a meaty PR, though, so I'll leave it for another one
This PR modifies the release build for the following Calypso Apps: For info about this notification, see here: PCYsg-OT6-p2
To test WordPress.com changes, run |
No worries, the conflicts weren't too bad. I've addressed your comments, @southp—this should be good for another look now! |
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.
Thanks for addressing all of my comments. I really appreciate your effort not only fixing the bug but also making the code better in a balanced way 🙇🏼
Fixes #97225
Proposed Changes
The main bug this PR fixes is that free sites with the 100 GB add-on keep displaying the 50 GB add-on on
/add-ons/:site_slug
—indicating that it's available for purchase even though it's not. I suspect this is also true for Premium sites, but I haven't tested it.While working on this issue, I found the existing availability logic for storage add-ons to be quite spaghetti-esque, so this PR also takes a stab at simplifying that logic. I've implemented the following refactorings:
storage-enabled
feature flag. It has been enabled in all environments since Jun, 2023 (Add-Ons: Enable Storage Add On Feature Flag on All Environments #78775)AddOnsMain
,AddOnsGrid
, andAddOnCard
useAddOns
. Why? Because we previously combined other hooks likeuseAddOnPurchaseStatus
with the availability checks inuseAddOns
. With my refactoring, we clarify the purpose ofuseAddOns
solely as a data provider.useStorageAddOnAvailability
hook for checking if a single storage add-on is available for purchase when compared to the maximum site storage offered on WordPress.com.Testing Instructions
I've tagged a few different people for review. @taipeicoder, if you have a moment to review this, I'd appreciate it, since you worked on this logic previously. @rcrdortiz, if you have some groundskeeping time left this afternoon, I'd also appreciate a review.
public-api.wordpress.com
3.1. Add-ons page:
/add-ons/:site_slug
3.2. Site overview page:
/sites/overview/:site_slug
Pre-merge Checklist