From 948bc576103a80699f7fa9aa7967b741df03d7c8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 12 Mar 2025 16:10:44 -0700 Subject: [PATCH 1/5] Leverage OD_Template_Optimization_Context --- od-admin-ui.php | 219 +++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 107 deletions(-) diff --git a/od-admin-ui.php b/od-admin-ui.php index 2b31eec..8f84294 100644 --- a/od-admin-ui.php +++ b/od-admin-ui.php @@ -23,11 +23,11 @@ use DateTime; use DateTimeZone; use Exception; -use OD_Tag_Visitor_Registry; use OD_URL_Metric; use OD_URL_Metric_Group; use OD_URL_Metric_Group_Collection; use OD_URL_Metrics_Post_Type; +use OD_Template_Optimization_Context; use WP_Post; use WP_Post_Type; use WP_Screen; @@ -543,119 +543,133 @@ static function (): void { ); add_action( - 'admin_bar_menu', - static function ( WP_Admin_Bar $wp_admin_bar ): void { - if ( ! od_can_optimize_response() ) { - return; + 'od_start_template_optimization', + static function ( OD_Template_Optimization_Context $context ): void { + add_action( + 'admin_bar_menu', + static function ( WP_Admin_Bar $wp_admin_bar ) use ( $context ): void { + populate_admin_bar_item( $wp_admin_bar, $context ); + }, + 100 + ); + } +); + +/** + * Populates admin bar item. + * + * @param WP_Admin_Bar $wp_admin_bar Admin bar. + * @param OD_Template_Optimization_Context $context Template optimization context. + */ +function populate_admin_bar_item( WP_Admin_Bar $wp_admin_bar, OD_Template_Optimization_Context $context ): void { + if ( null === $context->url_metrics_id ) { + return; + } + $post = get_post( $context->url_metrics_id ); + if ( ! ( $post instanceof WP_Post ) ) { + return; + } + + $edit_link = get_edit_post_link( $post, 'raw' ); + if ( null === $edit_link ) { + return; + } + + $url_metrics = $context->url_metric_group_collection->get_flattened_url_metrics(); + usort( + $url_metrics, + static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { + return $b->get_timestamp() <=> $a->get_timestamp(); } + ); - $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $post = OD_URL_Metrics_Post_Type::get_post( $slug ); - if ( ! ( $post instanceof WP_Post ) ) { - return; + $url_metrics_collection = $context->url_metric_group_collection; + + $args = array( + 'id' => 'od-url-metrics', + 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '', + 'href' => $edit_link, + ); + + $all_complete = true; + + $args['title'] .= ' '; + foreach ( $url_metrics_collection as $group ) { + $etags = array_values( + array_unique( + array_map( + static function ( OD_URL_Metric $url_metric ) { + return $url_metric->get_etag(); + }, + iterator_to_array( $group ) + ) + ) + ); + $is_complete = ( + $group->is_complete() + || + // Handle case using the DevMode plugin when the freshness TTL is zeroed out. + ( $group->get_freshness_ttl() === 0 && $group->count() === $group->get_sample_size() && isset( $etags[0] ) && $etags[0] === $url_metrics_collection->get_current_etag() ) + ); + $all_complete = $all_complete && $is_complete; + if ( $is_complete ) { + $class_name = 'od-complete'; + } elseif ( $group->count() > 0 ) { + $class_name = 'od-populated'; + } else { + $class_name = 'od-empty'; } - $edit_link = get_edit_post_link( $post, 'raw' ); - if ( null === $edit_link ) { - return; + $class_name .= sprintf( ' od-viewport-min-width-%d', $group->get_minimum_viewport_width() ); + + $tooltip = get_device_label( $group ) . ': ' . $group->count() . '/' . $group->get_sample_size(); + if ( $is_complete ) { + $tooltip .= sprintf( ', %s', __( 'complete', 'od-admin-ui' ) ); } - $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ); + $group_url_metrics = iterator_to_array( $group ); usort( - $url_metrics, + $group_url_metrics, static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { return $b->get_timestamp() <=> $a->get_timestamp(); } ); + if ( isset( $group_url_metrics[0] ) ) { + /* translators: %s: human-readable time difference. */ + $tooltip .= ', ' . sprintf( __( '%s ago', 'default' ), human_time_diff( (int) $group_url_metrics[0]->get_timestamp() ) ); + } - // TODO: We should not have to re-construct this. - $tag_visitor_registry = new OD_Tag_Visitor_Registry(); - do_action( 'od_register_tag_visitors', $tag_visitor_registry ); - $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $GLOBALS['wp_query'], od_get_current_theme_template() ); - $url_metrics_collection = new OD_URL_Metric_Group_Collection( $url_metrics, $current_etag, od_get_breakpoint_max_widths(), od_get_url_metrics_breakpoint_sample_size(), od_get_url_metric_freshness_ttl() ); - - $args = array( - 'id' => 'od-url-metrics', - 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '', - 'href' => $edit_link, + $args['title'] .= sprintf( + '', + esc_attr( "od-viewport-group-indicator $class_name" ), + esc_attr( $tooltip ) ); + } - $all_complete = true; - - $args['title'] .= ' '; - foreach ( $url_metrics_collection as $group ) { - $etags = array_values( - array_unique( - array_map( - static function ( OD_URL_Metric $url_metric ) { - return $url_metric->get_etag(); - }, - iterator_to_array( $group ) - ) - ) - ); - $is_complete = ( - $group->is_complete() - || - // Handle case using the DevMode plugin when the freshness TTL is zeroed out. - ( $group->get_freshness_ttl() === 0 && $group->count() === $group->get_sample_size() && isset( $etags[0] ) && $etags[0] === $current_etag ) - ); - $all_complete = $all_complete && $is_complete; - if ( $is_complete ) { - $class_name = 'od-complete'; - } elseif ( $group->count() > 0 ) { - $class_name = 'od-populated'; - } else { - $class_name = 'od-empty'; - } - $class_name .= sprintf( ' od-viewport-min-width-%d', $group->get_minimum_viewport_width() ); - - $tooltip = get_device_label( $group ) . ': ' . $group->count() . '/' . $group->get_sample_size(); - if ( $is_complete ) { - $tooltip .= sprintf( ', %s', __( 'complete', 'od-admin-ui' ) ); - } - - $group_url_metrics = iterator_to_array( $group ); - usort( - $group_url_metrics, - static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { - return $b->get_timestamp() <=> $a->get_timestamp(); - } - ); - if ( isset( $group_url_metrics[0] ) ) { - /* translators: %s: human-readable time difference. */ - $tooltip .= ', ' . sprintf( __( '%s ago', 'default' ), human_time_diff( (int) $group_url_metrics[0]->get_timestamp() ) ); - } - - $args['title'] .= sprintf( - '', - esc_attr( "od-viewport-group-indicator $class_name" ), - esc_attr( $tooltip ) - ); - } + if ( $all_complete ) { + $args['meta']['title'] = __( 'Every viewport group is complete, being fully populated without any stale URL metrics.', 'od-admin-ui' ); + } elseif ( $url_metrics_collection->is_every_group_populated() ) { + $args['meta']['title'] = __( 'Every viewport group is populated although some URL Metrics are stale or not enough samples have been collected.', 'od-admin-ui' ); + } elseif ( $url_metrics_collection->is_any_group_populated() ) { + $args['meta']['title'] = __( 'Not every viewport group has been populated.', 'od-admin-ui' ); + } else { + $args['meta']['title'] = __( 'No URL Metrics have been collected yet.', 'od-admin-ui' ); + } - if ( $all_complete ) { - $args['meta']['title'] = __( 'Every viewport group is complete, being fully populated without any stale URL metrics.', 'od-admin-ui' ); - } elseif ( $url_metrics_collection->is_every_group_populated() ) { - $args['meta']['title'] = __( 'Every viewport group is populated although some URL Metrics are stale or not enough samples have been collected.', 'od-admin-ui' ); - } elseif ( $url_metrics_collection->is_any_group_populated() ) { - $args['meta']['title'] = __( 'Not every viewport group has been populated.', 'od-admin-ui' ); - } else { - $args['meta']['title'] = __( 'No URL Metrics have been collected yet.', 'od-admin-ui' ); + $wp_admin_bar->add_node( $args ); + add_action( + 'wp_footer', + static function () use ( $context ): void { + print_admin_bar_styles( $context ); } - - $wp_admin_bar->add_node( $args ); - add_action( 'wp_footer', __NAMESPACE__ . '\print_admin_bar_styles' ); - }, - 100 -); + ); +} /** * Print admin bar styles. + * + * @param OD_Template_Optimization_Context $context Context. */ -function print_admin_bar_styles(): void { - if ( ! is_admin_bar_showing() ) { - return; - } +function print_admin_bar_styles( OD_Template_Optimization_Context $context ): void { ?> ' ); + + $exports = array(); + + if ( null !== $context->url_metrics_id ) { + $post = get_post( $context->url_metrics_id ); + if ( $post instanceof WP_Post ) { + $exports['edit_post_link'] = get_edit_post_link( $post, 'raw' ); + } } $url_metrics = $context->url_metric_group_collection->get_flattened_url_metrics(); @@ -585,17 +599,18 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { $url_metrics_collection = $context->url_metric_group_collection; - $args = array( - 'id' => 'od-url-metrics', - 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '', - 'href' => $edit_link, - ); - $all_complete = true; - $args['title'] .= ' '; + $exports['viewport_statuses'] = array(); foreach ( $url_metrics_collection as $group ) { - $etags = array_values( + $viewport_status = array( + 'min_width' => $group->get_minimum_viewport_width(), + 'count' => $group->count(), + 'device_label' => get_device_label( $group ), + 'sample_size' => $group->get_sample_size(), + 'last_modified' => null, + ); + $etags = array_values( array_unique( array_map( static function ( OD_URL_Metric $url_metric ) { @@ -605,26 +620,15 @@ static function ( OD_URL_Metric $url_metric ) { ) ) ); - $is_complete = ( + $is_complete = ( $group->is_complete() || // Handle case using the DevMode plugin when the freshness TTL is zeroed out. ( $group->get_freshness_ttl() === 0 && $group->count() === $group->get_sample_size() && isset( $etags[0] ) && $etags[0] === $url_metrics_collection->get_current_etag() ) ); $all_complete = $all_complete && $is_complete; - if ( $is_complete ) { - $class_name = 'od-complete'; - } elseif ( $group->count() > 0 ) { - $class_name = 'od-populated'; - } else { - $class_name = 'od-empty'; - } - $class_name .= sprintf( ' od-viewport-min-width-%d', $group->get_minimum_viewport_width() ); - $tooltip = get_device_label( $group ) . ': ' . $group->count() . '/' . $group->get_sample_size(); - if ( $is_complete ) { - $tooltip .= sprintf( ', %s', __( 'complete', 'od-admin-ui' ) ); - } + $viewport_status['complete'] = $is_complete; $group_url_metrics = iterator_to_array( $group ); usort( @@ -633,89 +637,53 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { return $b->get_timestamp() <=> $a->get_timestamp(); } ); + if ( isset( $group_url_metrics[0] ) ) { /* translators: %s: human-readable time difference. */ - $tooltip .= ', ' . sprintf( __( '%s ago', 'default' ), human_time_diff( (int) $group_url_metrics[0]->get_timestamp() ) ); + $viewport_status['last_modified'] = sprintf( __( '%s ago', 'default' ), human_time_diff( (int) $group_url_metrics[0]->get_timestamp() ) ); } - $args['title'] .= sprintf( - '', - esc_attr( "od-viewport-group-indicator $class_name" ), - esc_attr( $tooltip ) - ); + $exports['viewport_statuses'][] = $viewport_status; } if ( $all_complete ) { - $args['meta']['title'] = __( 'Every viewport group is complete, being fully populated without any stale URL metrics.', 'od-admin-ui' ); + $exports['tooltip'] = __( 'Every viewport group is complete, being fully populated without any stale URL metrics.', 'od-admin-ui' ); } elseif ( $url_metrics_collection->is_every_group_populated() ) { - $args['meta']['title'] = __( 'Every viewport group is populated although some URL Metrics are stale or not enough samples have been collected.', 'od-admin-ui' ); + $exports['tooltip'] = __( 'Every viewport group is populated although some URL Metrics are stale or not enough samples have been collected.', 'od-admin-ui' ); } elseif ( $url_metrics_collection->is_any_group_populated() ) { - $args['meta']['title'] = __( 'Not every viewport group has been populated.', 'od-admin-ui' ); + $exports['tooltip'] = __( 'Not every viewport group has been populated.', 'od-admin-ui' ); } else { - $args['meta']['title'] = __( 'No URL Metrics have been collected yet.', 'od-admin-ui' ); + $exports['tooltip'] = __( 'No URL Metrics have been collected yet.', 'od-admin-ui' ); } - $wp_admin_bar->add_node( $args ); - - add_action( - 'wp_before_admin_bar_render', - static function () use ( $context ): void { - print_admin_bar_styles( $context ); - } + $js = sprintf( + 'import init from %s; init( %s );', + wp_json_encode( plugins_url( 'admin-bar.js', __FILE__ ) ), + wp_json_encode( $exports ) ); + $context->append_body_html( '' ); } /** - * Print admin bar styles. + * Gets the admin bar styles. * * @param OD_Template_Optimization_Context $context Context. + * @return string CSS. */ -function print_admin_bar_styles( OD_Template_Optimization_Context $context ): void { - ?> - - get_minimum_viewport_width(), $group->get_maximum_viewport_width() ), + $group->get_minimum_viewport_width() + ); + } + return $css; } From 1a79f2f78bcf5f0a461dbf7bc6edf018f0397739 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 13 Mar 2025 12:45:08 -0700 Subject: [PATCH 4/5] Fix showing viewport indicator on mobile --- admin-bar.css | 5 +++++ admin-bar.js | 2 +- od-admin-ui.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/admin-bar.css b/admin-bar.css index 4251c9c..0f5b9dd 100644 --- a/admin-bar.css +++ b/admin-bar.css @@ -3,6 +3,11 @@ #wpadminbar .od-url-metrics-loading a { cursor: wait; } + +#wpadminbar #wp-admin-bar-od-url-metrics:has(.od-viewport-group-indicator) .ab-label { + margin-right: 5px !important; +} + #wpadminbar .od-viewport-group-indicator { display: inline-block; vertical-align: middle; diff --git a/admin-bar.js b/admin-bar.js index 6b47928..a6973cf 100644 --- a/admin-bar.js +++ b/admin-bar.js @@ -46,7 +46,7 @@ export default function ( data ) { if ( viewportStatus.last_modified ) { span.title += ', ' + viewportStatus.last_modified; } - viewportIndicatorsContainer.appendChild( span ); + adminBarItemLink.appendChild( span ); } if ( data.edit_post_link ) { diff --git a/od-admin-ui.php b/od-admin-ui.php index f9ca80b..9f12f04 100644 --- a/od-admin-ui.php +++ b/od-admin-ui.php @@ -561,7 +561,7 @@ static function ( WP_Admin_Bar $wp_admin_bar ): void { function create_admin_bar_item( WP_Admin_Bar $wp_admin_bar ): void { $args = array( 'id' => 'od-url-metrics', - 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . ' ', + 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '', 'href' => '#', 'meta' => array( 'title' => __( 'Please wait. Loading page.', 'od-admin-ui' ), From 80d82252aec9cad513515a17edde391ff54f27d0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 13 Mar 2025 15:06:39 -0700 Subject: [PATCH 5/5] Remove unsued constant --- admin-bar.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/admin-bar.js b/admin-bar.js index a6973cf..2c6a484 100644 --- a/admin-bar.js +++ b/admin-bar.js @@ -20,8 +20,6 @@ export default function ( data ) { ); const adminBarItemLink = adminBarItem.querySelector( 'a' ); adminBarItemLink.title = data.tooltip; - const viewportIndicatorsContainer = - adminBarItem.querySelector( '.od-url-metrics' ); adminBarItem.classList.remove( 'od-url-metrics-loading' );