-
Notifications
You must be signed in to change notification settings - Fork 99
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
Add ability to serve the default offline page when the user or site is offline. #48
Conversation
…erve-offline-template
wp-includes/js/service-worker.js
Outdated
handler: matchedRoute.handler | ||
}; | ||
|
||
// @todo Confirm approach for conflicting routes: for example perhaps we shouldn't consider multiple matches but same strategy a conflict. Maybe we should still use the first matching route and warn about others. |
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.
Agreed. I think it's a good idea to let the first match handle the request, and then to warn about the conflicting handlers.
* | ||
* @return string Script. | ||
*/ | ||
protected function configure_offline_page() { |
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 ideally tie-in to a precaching API (e.g. register_precaching_for_routes
) instead of having its own separate logic.
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.
Updated the logic within dec04fb to use register_precached_routes
for the precaching part.
Wondering if we should rename register_precaching_for_routes
to get_precaching_for_routes_script
or something similar, otherwise maybe it's too similar with register_precached_routes
. Also, one is registering the route for caching and the other is generating and returning the script. Thoughts?
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.
Yes, good idea.
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.
Updated within a71a0cd.
return; | ||
} | ||
|
||
if ( self::STRATEGY_PRECACHE === $strategy ) { |
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 including precache
in this method, I think it should probably be a separate method like register_precached_route
. This then can be a straight wrapper for workbox.precaching.precache()
: https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching#.precache
*/ | ||
protected function register_precaching_for_routes( $routes ) { | ||
|
||
$routes_list = '['; |
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 would be better to construct an array here, like $routes_list = array()
. And then for each $route
do:
$routes_list[] = array(
'url' => $base_url . $route,
'revision' => $revision,
);
And then at the end just do:
return sprintf( "wp.serviceWorker.precaching.precacheAndRoute( %s );\n", wp_json_encode( $routes_list ) );
if ( isset( $args['max_entries'] ) || isset( $args['max_age'] ) ) { | ||
$script .= 'args.plugins = [ | ||
new wp.serviceWorker.expiration.Plugin({'; | ||
if ( isset( $args['max_age'] ) ) { |
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.
The the max_age
shouldn't be hard-coded here in $args
I don't think. Instead, $args
should have a plugins
array like:
array(
'plugins' => array(
'expiration' => array(
'maxAgeSeconds' => 123,
'maxEntries' => 45,
),
)
)
This would then allow any plugin to be referenced and passing all of the args that the plugins accept. The $args
would be whatever the caching strategy options
are accepted, for example: https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies.CacheFirst#constructor_1
So $args
should be renamed $strategy_args
for clarity.
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 being the case, I think the $regex
should not be passed in the $args
now. Perhaps there should be a separate methods like register_cached_route_pattern()
and register_cached_route_url()
to differentiate between the two types of args allowed?
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.
Made the general Workbox related changes within 6c2592f in #43.
Makes sense that the plugins shouldn't be hardcoded and it should be possible to add other plugins, too. Made these changes within 6c2592f.
On regex: what do you think of just adding an additional param to the method so that if a developer would switch from between regex/url then only a param would require changing instead of the method? For example: register_cached_route( $route, $strategy, $strategy_args = array(), $is_regex = false )
.
Thinking that since the only difference it makes when registering routes to self::$registered_caching_routes
would be a change if is_regex
is true/false
for the relevant route, then maybe an additional param would be enough.
Or are you thinking that maybe it's more straightforward and better structured to have two separate methods? Thoughts?
(Added $is_regex
param as a temporary change within 6c2592f to move this out of the $strategy_args
, will it change it accordingly.)
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 wary of having too many positional parameters, and if $is_regex
is the 4th argument, then it is far away from the $route
that it is managing. We could have such a method still but let it be protected
and then the other public methods could pass this 4th arg. (Note that workbox.routing.registerRoute()
has a 3rd argument, after the strategy, and it is the HTTP method to cache, which it is GET
by default.) It's unfortunate that there is no native RegExp
type in PHP.
If going with a 4th parameter, then it should be an $options
array instead. This could contain the method
and is_regex
args. But then again, I suspect that few plugins would be handling non-GET
requests and if they wanted to they could register custom JS directly into the SW.
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.
Makes sense. Split the method into two within 0fd9320.
…erve-offline-template
…plugin assets on runtime.
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 starting work on this.
if ( $offline_page_id ) { | ||
|
||
// Cache relevant theme assets. | ||
$this->register_cached_route( '/wp-content/.*\.(?:css|gif|png|jpg|jpeg)', self::STRATEGY_STALE_WHILE_REVALIDATE ); |
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 will be problematic because it will store all uploaded files in the cache. We'll need to limit this to the actual theme directory URL.
* @return string Script. | ||
*/ | ||
protected function configure_offline_page() { | ||
$offline_page_id = (int) get_option( WP_Offline_Page::OPTION_NAME, 0 ); |
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 short-circuit if this is not a valid page.
*/ | ||
protected function get_offline_post_image_routes( $post ) { | ||
$routes = array(); | ||
preg_match_all( '/< *img[^>]*src *= *["\']?([^"\']*)/i', $post->post_content, $matches ); |
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 think we need to actually extract the attachment image IDs, as we'll need to also obtain the images defined in the srcset
and sizes
attributes (via wp_get_attachment_image_srcset()
and wp_calculate_image_sizes()
respectively).
$routes[] = array( | ||
'url' => $this->remove_url_scheme( $featured_img_url ), | ||
'revision' => $post->post_modified, | ||
); |
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.
Per above, we'll also need to calculate the image URLs for the srcset
and sizes
, as the post-thumbnail
image may not actually be the size used on the offline page.
@@ -0,0 +1,26 @@ | |||
/* global OFFLINE_PAGE_URL */ | |||
( () => { |
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 old JS habits are carrying through here. We only need to wrap in a {
block since the const
variables are scoped lexically. No need for IIFE.
( () => { | ||
|
||
// Add custom offline / error response serving. | ||
const networkFirstHandler = wp.serviceWorker.strategies.networkFirst( { |
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 think we need to re-use precache
as the cacheName
for this so that it will use what was already cached.
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. It isn't required to use cacheName: wp.serviceWorker.core.cacheNames.precache
because all of the caches are being looked at for finding the offline page.
The offline page will be pre-cached.
$this->register_precached_routes( array_merge( | ||
array( | ||
array( | ||
'url' => $this->remove_url_scheme( get_the_permalink( $offline_page_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 think actually we won't need remove_url_scheme
because all of the URLs should be getting served as HTTPS anyway.
* Skip looking for assets in content for now. * Remove unnecessary remove_url_scheme. * Consolidate precache URLs into single get_offline_page_precache_entries method. * Add helper get_offline_page method for getting published offline page.
* The offline page will be served from the precache so no caching will be done during navigation. * Blacklist /wp-admin/ from serving the offline page; a separate admin offline page will be needed.
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 is working well for serving the offline page. There is more work to do on the offline page and the API for precaching, but we'll continue iterating in subsequent PRs.
Fixes #45 .
srcset
).wp.serviceWorker.routing
with instance ofWPRouting
and frozen public API on the same way Workbox does.is_offline()
template conditional.