diff --git a/admin-bar.css b/admin-bar.css
new file mode 100644
index 0000000..0f5b9dd
--- /dev/null
+++ b/admin-bar.css
@@ -0,0 +1,42 @@
+
+#wpadminbar .od-url-metrics-loading,
+#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;
+ width: 6px;
+ height: 20px;
+ border: solid 1px black;
+ background-color: gray;
+ opacity: 0.5;
+}
+#wpadminbar .od-viewport-group-indicator.od-populated {
+ background-color: orange;
+}
+#wpadminbar .od-viewport-group-indicator.od-complete {
+ background-color: lime;
+}
+
+#wpadminbar #wp-admin-bar-od-url-metrics .od-viewport-group-indicator:hover {
+ opacity: 1;
+ outline: solid 2px currentColor;
+}
+@media screen and (max-width: 782px) {
+ #wpadminbar #wp-admin-bar-od-url-metrics .ab-icon {
+ display: none;
+ }
+ #wpadminbar #wp-admin-bar-od-url-metrics {
+ display: block;
+ }
+ #wpadminbar .od-viewport-group-indicator {
+ height: 32px;
+ width: 8px;
+ }
+}
diff --git a/admin-bar.js b/admin-bar.js
new file mode 100644
index 0000000..2c6a484
--- /dev/null
+++ b/admin-bar.js
@@ -0,0 +1,53 @@
+/**
+ * @typedef {Object} ViewportStatus
+ * @property {number} min_width - Min width.
+ * @property {boolean} complete - Complete
+ * @property {number} count - Count
+ * @property {string} device_label - Device label.
+ * @property {number} sample_size - Sample size.
+ * @property {string|null} last_modified - Last modified label.
+ */
+
+/**
+ * @param {Object} data
+ * @param {string} data.tooltip
+ * @param {string|null} data.edit_post_link
+ * @param {ViewportStatus[]} data.viewport_statuses
+ */
+export default function ( data ) {
+ const adminBarItem = document.getElementById(
+ 'wp-admin-bar-od-url-metrics'
+ );
+ const adminBarItemLink = adminBarItem.querySelector( 'a' );
+ adminBarItemLink.title = data.tooltip;
+
+ adminBarItem.classList.remove( 'od-url-metrics-loading' );
+
+ for ( const viewportStatus of data.viewport_statuses ) {
+ const span = document.createElement( 'span' );
+ span.classList.add(
+ 'od-viewport-group-indicator',
+ `od-viewport-min-width-${ viewportStatus.min_width }`
+ );
+ if ( viewportStatus.complete ) {
+ span.classList.add( 'od-complete' );
+ } else if ( viewportStatus.count > 0 ) {
+ span.classList.add( 'od-populated' );
+ } else {
+ span.classList.add( 'od-empty' );
+ }
+
+ span.title = viewportStatus.device_label;
+ if ( viewportStatus.complete ) {
+ span.title += ', complete'; // TODO: i18n.
+ }
+ if ( viewportStatus.last_modified ) {
+ span.title += ', ' + viewportStatus.last_modified;
+ }
+ adminBarItemLink.appendChild( span );
+ }
+
+ if ( data.edit_post_link ) {
+ adminBarItemLink.href = data.edit_post_link;
+ }
+}
diff --git a/od-admin-ui.php b/od-admin-ui.php
index 2b31eec..9f12f04 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;
@@ -545,171 +545,145 @@ static function (): void {
add_action(
'admin_bar_menu',
static function ( WP_Admin_Bar $wp_admin_bar ): void {
- if ( ! od_can_optimize_response() ) {
- return;
+ if ( od_can_optimize_response() ) {
+ create_admin_bar_item( $wp_admin_bar );
+ add_action( 'od_start_template_optimization', __NAMESPACE__ . '\populate_admin_bar_item' );
}
+ },
+ 100
+);
- $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;
+/**
+ * Populates admin bar item.
+ *
+ * @param WP_Admin_Bar $wp_admin_bar Admin bar.
+ */
+function create_admin_bar_item( WP_Admin_Bar $wp_admin_bar ): void {
+ $args = array(
+ 'id' => 'od-url-metrics',
+ 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '',
+ 'href' => '#',
+ 'meta' => array(
+ 'title' => __( 'Please wait. Loading page.', 'od-admin-ui' ),
+ 'class' => 'od-url-metrics-loading',
+ ),
+ );
+
+ $wp_admin_bar->add_node( $args );
+}
+
+/**
+ * Populate admin bar item.
+ *
+ * @param OD_Template_Optimization_Context $context Template optimization context.
+ */
+function populate_admin_bar_item( OD_Template_Optimization_Context $context ): void {
+ $context->append_head_html( '' );
+
+ $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' );
}
- $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();
}
+ );
- $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post );
- usort(
- $url_metrics,
- static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int {
- return $b->get_timestamp() <=> $a->get_timestamp();
- }
- );
+ $url_metrics_collection = $context->url_metric_group_collection;
- // 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() );
+ $all_complete = true;
- $args = array(
- 'id' => 'od-url-metrics',
- 'title' => '' . __( 'URL Metrics', 'od-admin-ui' ) . '',
- 'href' => $edit_link,
+ $exports['viewport_statuses'] = array();
+ foreach ( $url_metrics_collection as $group ) {
+ $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,
);
-
- $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 )
- )
+ $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() );
+ )
+ );
+ $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;
- $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(
- $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() ) );
+ $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();
}
+ );
- $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 ( isset( $group_url_metrics[0] ) ) {
+ /* translators: %s: human-readable time difference. */
+ $viewport_status['last_modified'] = sprintf( __( '%s ago', 'default' ), human_time_diff( (int) $group_url_metrics[0]->get_timestamp() ) );
}
- $wp_admin_bar->add_node( $args );
- add_action( 'wp_footer', __NAMESPACE__ . '\print_admin_bar_styles' );
- },
- 100
-);
+ $exports['viewport_statuses'][] = $viewport_status;
+ }
-/**
- * Print admin bar styles.
- */
-function print_admin_bar_styles(): void {
- if ( ! is_admin_bar_showing() ) {
- return;
+ if ( $all_complete ) {
+ $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() ) {
+ $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() ) {
+ $exports['tooltip'] = __( 'Not every viewport group has been populated.', 'od-admin-ui' );
+ } else {
+ $exports['tooltip'] = __( 'No URL Metrics have been collected yet.', 'od-admin-ui' );
}
- ?>
-
- get_minimum_viewport_width(), $group->get_maximum_viewport_width() ),
+ $group->get_minimum_viewport_width()
+ );
+ }
+ return $css;
}