-
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
Media: Clear icon site value and setting upon deleting associated media #10016
Conversation
this.props.deleteMedia( site.ID, mediaIds ); | ||
if ( includes( mediaIds, siteIconId ) ) { | ||
this.props.resetSiteIcon( site.ID ); | ||
} |
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 a bit concerned about this code, what if we allow media deletion from elsewhere, we'll face the exact same problem. Do we have the media URLs available here, that way we could use them to compare against the siteIcon attribute in the site reducer?
Or we could solve this differently, move this "extra" behavior to the deleteMedia action creator. You could call to requestSiteSettings action creator from there. I know it's not perfect, but maybe this could allow us to "centralize" this behavior, which seem not directly related to this component? Thoughts? I may be missing something tho :)
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 had a hard time coming to a solution here. It's complicated by the fact that the data exists in many places (media objects themselves, a media ID as site setting, and a media's URL as the site icon property). But you're right that any part of the app which deletes a media would then need to be concerned with resetting the site icon, which is not great.
Similar to what I suggested at #10032 (comment), perhaps a middleware (the API middleware?) is the right place for these kinds of side effects. While we're not quite at the point where we want to substitute the API call for deleting media, from there we could at least check whether the media to be deleted matches the current site option.
Or, maybe since it's not related to network calls on its own, it could exist as a standalone middleware, or combined with other media middleware suggested in #9706.
cc @dmsnell in case you have thoughts on the role of middlewares / API middleware
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.
@aduth middleware is probably a reasonable solution for this, but it would seem a bit odd with the deleteMedia
action doing the deletion and leaving the site icon in middleware. maybe not. maybe it would actually be a quick and easy function to add to the wpcom data layer.
in essence, we should ask if deleting the media item currently set as the site icon always means that the icon should be reset. if this is the case, I would expect to have some siteIcon
reducer somewhere in the state tree listening for mediaDelete
actions and updating in Calypso-first by resetting it. in other words, deleteMedia
would not be the genesis for resetting the site icon. this is by design in Redux. the siteIcon
state/reducer is responsible for its own behavior and if the proposition holds, it should be listening for all relevant actions which affect it.
the middleware comes in as the data layer. we act as if Calypso is the raw source of truth. the site icon reducer resets its value when it see MEDIA_DELETE
with its own media id. the middleware layer for the site icon (or site setting) endpoint also listens for this same action and will trigger an API call whenever one flies by. if the API call fails, it may very well reset the site icon to its previous id, or in this case, if the media is gone, the failure mode will do something else - set some default for example.
does this help?
export function deleteMedia( siteId, mediaIds ) { | ||
return { | ||
type: MEDIA_DELETE, | ||
mediaIds: castArray( mediaIds ), |
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 new Lodash tip learned for me :)
A thought which might help with reconciling the icon formats is including the ID of the media item associated with the site icon on the site response, perhaps as Then, as much as possible, we use the site icon ID as the source of truth, with a lookup of the associated media item if available in the current state, falling back to the site object's I'm still working on an implementation to see whether this will pan out, but figured I'd throw the idea out there. |
as an aside, this would be a good use for the API middleware. no need to refetch icons within a certain time, maybe within any given session. the middleware could what which ones have been fetched and ignore new requests for the same one. I've been thinking about a |
4435341
to
b68534c
Compare
Ok, took me a little longer than I'd hoped to implement the approach suggested in my previous comment, but is now reflected in the last handful of commits. It's a reasonable departure from the previous direction. Instead of assigning an icon URL to be tracked in site state, it's now assumed that the site items are aware of the media ID associated with the site icon by its * This is not yet passed by the API, and I plan to work on this next. Though for our purposes this works fairly well as-is, given fallbacks and icon updates within browser state. Only problem is if the site is received (e.g. polling) after the icon had been updated in the same session. |
Also, yes, the changes are quite large, and there's a decent amount of overlap with related pull requests (particularly media state). Hoping with some merging / rebasing it will come down to a more reasonable size. |
D2940-code |
export default connect( ( state, { site, siteId, imgSize } ) => { | ||
// Until sites state is completely within Redux, we provide compatibility | ||
// in cases where site object is passed to use the icon.img property as URL | ||
if ( site ) { |
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.
Just asking, one could refactor the parent component and provide a "redux" site instead no?
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.
Just asking, one could refactor the parent component and provide a "redux" site instead no?
In some cases, yes. But only for sites we know to be in state, which at the moment only includes the currently selected site. Since site icons are shown in the site selector, we can't yet use Redux state there.
case SITE_ICON_SET: { | ||
const { siteId, iconUrl } = action; | ||
case SITE_SETTINGS_UPDATE: | ||
case SITE_SETTINGS_RECEIVE: { |
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.
So, If I understand properly we're receiving the site Icon ID from the site settings endpoint, I'm wondering why we're storing it in this reducer, instead of relying on the siteSettings state to handle it (like other settings)?
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.
Nevermind, after review the API diff, I understand why you had to do that :)
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.
So, If I understand properly we're receiving the site Icon ID from the site settings endpoint, I'm wondering why we're storing it in this reducer, instead of relying on the siteSettings state to handle it (like other settings)?
Nevermind, after review the API diff, I understand why you had to do that :)
Yeah, with these changes, we're treating site.icon.media_id
as the preferred source of truth for the site icon, which means we want to reconcile when incoming settings are received. Now that you mention it though, this might need to go both ways: when we receive site(s), and we know those sites in the siteSettings
reducer, we'll probably want to update the site icon value there (though this likely would have little effect since, as I mentioned, we want to prefer using it on the site state anyways).
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're treating site.icon.media_id as the preferred source of truth for the site icon, which means we want to reconcile when incoming settings are received
beautiful. I love seeing these changes happen
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.
You mentioned in a chat that you prefer to keep the 'icon' attribute on the site endpoint to avoid extra requests. But on the same time, you're considering here site.icon.media_id
as the preferred source of truth (which I agree with).
Maybe we should not have to care about the extra requests here (unless it's problematic, which I don't think it is), and maybe leave the DataLayer optimize this later.
Also, if the media_id is some general information about the site, we should add it to the site endpoint directly, don't you think?
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.
@youknowriad there was a distinction made a long time ago between site
and siteSettings
on the API-side wherein site
requests return quick data but settings
request get data that is much slower to fetch on the server side. it's unlikely right now that this will change.
leave the DataLayer optimize this later
I agree with this and think it's a great place to introduce the HTTP request batching, but there are steps involved there, so maybe some clever logic in the API layer can get us there in the meantime
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.
Also that /sites/%s/settings
is read/write while /sites/%s
is read-only.
// exclusively using the site ID | ||
return { | ||
site: getSite( state, siteId ), | ||
iconUrl: getSiteIconUrl( state, siteId, imgSize ) |
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 selector relies on two sub-state trees: sites and media, Shouldn't we add Queries to the component? probably a QuerySites
since "in theory" the component could get any siteId as prop. I and guess QueryMedia
is probably not available yet and receiveMedia is only called if we use the SiteIconSetting
component, so I wonder how is it filled in that case?
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 selector relies on two sub-state trees: sites and media, Shouldn't we add Queries to the component?
You raise a good point here. In an earlier comment, I noted a worry about the number of requests from <SiteIcon />
components since they're used for each of the user's sites in the site selector.
If we were to render a <QuerySites />
, maybe it should be limited to the condition that the site data is not yet known (and that it's passed as a site ID, i.e. Redux)?
Regarding <QueryMedia />
, we benefit from the fallback behavior which uses the site's site.icon.img
property (a URL string) if the media by ID is not known. The ID only changes when updating the site icon within Calypso, at which point we've received the media explicitly (in <SiteIconSetting />)
. I think this should be enough that it would never be inaccurate, though admittedly depends on a tangled set of assumptions.
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.
ytihhrt
??
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.
ytihhrt
??
😆
Corrected to "requests".
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.
If we were to render a , maybe it should be limited to the condition that the site data is not yet known (and that it's passed as a site ID, i.e. Redux)?
Why is this? Is it for data usage? If so, maybe we should be able to do a new fetch just for the site info data. This would be trivial in the new API data layer since we could trap some action such as SITE_ICON_REQUEST
and make an API call presumably to the same site settings endpoint from where it comes but then limiting the response fields to only the site icon data.
Sounds like it's time sooner than later to introduce the HTTP layer which turns a sequence of rapid-fire HTTP requests into a BATCH request
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.
Why is this?
Mainly because otherwise every time I open my site selector, it would issue about 30 requests for each site shown, even if we already have data for those sites from /me/sites
. We have this implied assumption in Calypso that sites data is expected to be universally requested via /me/sites
, not specifically through any one query component. Not that this is very good, mind you, but the current situation nonetheless.
While testing the API side, I noticed that for WPCOM Sites using Site Icon (and not blavatar) don't have the |
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.
@aduth I took this for a spin again and It's working as expected. And as you said, the API diff in code-D2990 will add the icon
property when relying on the site-icon setting.
ebc2890
to
97159f9
Compare
Ah, sorry I was a bit late in pushing my rebase @youknowriad . Not too much has changed at a high level, but specifically:
To expand on the last point, I had mentioned that we may need to add handlers to site settings to update the |
@@ -64,7 +66,21 @@ export const items = createReducer( {}, { | |||
...state[ siteId ], | |||
...settings | |||
} | |||
} ) | |||
} ), | |||
[ MEDIA_DELETE ]: ( state, { siteId, mediaIds } ) => { |
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.
Hmm, given my note at #10016 (comment), maybe we don't even need to handle this.
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.
If I understand correctly, you're saying that we always rely on the site
object to retrieve the siteIcon. Thus, we should not worry about resetting it on the siteSettings? Is this assumption correct? Maybe we should consider dropping the selector fallback then, since the site-settings
value could be incorrect (even if in the current workflow, it can never happen since the site object will be available in this case) ?
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.
You raise a good point. The fallback exists in case we ever receive site settings before the site itself. But if that were ever to occur, we would still want to monitor for media deletions even if we only knew the site settings. But we still do not need to account for SITE_RECEIVE
or SITES_RECEIVE
in the site settings reducer because at that point we can assume that the fallback will no longer be used.
Because of this, I'll plan to drop 15cb3f5 in a rebase and revert back to this previous behavior.
@@ -0,0 +1,16 @@ | |||
/** |
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.
Great to see selectors making their way to the global directory
I tested after the rebase and it's still working properly |
15cb3f5
to
5ed530e
Compare
5ed530e
to
b9740a2
Compare
Closes #9939
This pull request seeks to clear site icon values (both on the site object itself and the site setting) when deleting a media item which is currently assigned as the site icon. Server-side, the option is automatically cleared when the media is deleted, so we only need to recreate this behavior in the client (related source).
Testing Instructions:
Testing will be made easier by use of Redux DevTools Chrome extension.
MEDIA_DELETE
dispatched action, the diff shows that thestate.siteSettings.items[ siteId ].site_icon
was set to nullRESET_SITE_ICON
action is also dispatched, and the diff shows that thestate.sites.items[ siteId ].icon
was removed (but only if you have a site icon already assigned via non-Calypso flow)You can also verify that deleting any other media will dispatch
MEDIA_DELETE
with no effect, and will not dispatchRESET_SITE_ICON
.