PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
jetpack / jetpack_vendor / automattic / jetpack-agents-manager / src / class-sidebar-open-preservation.php

class-sidebar-open-preservation.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.3, at jetpack_vendor/automattic/jetpack-agents-manager/src/class-sidebar-open-preservation.php

171 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sidebar Open Preservation file.
4 *
5 * @package automattic/jetpack-agents-manager
6 */
7
8 namespace Automattic\Jetpack\Agents_Manager;
9
10 /**
11 * Preserves the Agents Manager sidebar-open body classes across full wp-admin
12 * navigations so the next page load can pre-apply them server-side (avoiding a
13 * flicker before the React app boots).
14 *
15 * The open state comes from Open_State_Store::get_cached(), and the pre-render only
16 * runs when the Agents Manager app is actually loading on this request — so the
17 * pre-rendered shell is always reconciled by the app that mounts to manage it,
18 * never left orphaned.
19 *
20 * The server can know the persisted "open && docked" preference, but not the
21 * live viewport width or the editor's fullscreen mode. So a tiny synchronous
22 * gate script is printed on `in_admin_header` (before the content paints) to
23 * re-evaluate the real dock gates and strip the pre-rendered classes when the
24 * chat will actually float — pre-paint, so there is no flash.
25 */
26 class Sidebar_Open_Preservation {
27 /**
28 * Class instance.
29 *
30 * @var Sidebar_Open_Preservation
31 */
32 private static $instance;
33
34 /**
35 * Body class marking the docked sidebar shell.
36 *
37 * @var string
38 */
39 private const SIDEBAR_CONTAINER_CLASS = 'agents-manager-sidebar-container';
40
41 /**
42 * Body class marking the sidebar as open.
43 *
44 * @var string
45 */
46 private const SIDEBAR_OPEN_CLASS = 'agents-manager-sidebar-container--sidebar-open';
47
48 /**
49 * Creates instance.
50 *
51 * @return void
52 */
53 public static function init() {
54 if ( self::$instance === null ) {
55 self::$instance = new self();
56 }
57 }
58
59 /**
60 * Sidebar_Open_Preservation constructor.
61 */
62 public function __construct() {
63 // Run last so our class sits at the end of the `admin_body_class` list. Otherwise
64 // a later filter could append its class without a leading space and glue it onto
65 // ours, breaking the CSS selector that pre-opens the sidebar.
66 add_filter( 'admin_body_class', array( $this, 'add_preopen_body_classes' ), PHP_INT_MAX );
67
68 // Reconcile the pre-rendered shell against the live dock gates before
69 // the page content paints.
70 add_action( 'in_admin_header', array( $this, 'print_sidebar_docking_gate_script' ) );
71 }
72
73 /**
74 * Inject pre-open assistant classes in initial admin body markup.
75 *
76 * @param string $classes Existing admin body classes.
77 * @return string
78 */
79 public function add_preopen_body_classes( string $classes ): string {
80 if ( ! $this->should_pre_render_docked_shell() ) {
81 return $classes;
82 }
83
84 $body_classes_with_sidebar_classes = implode(
85 ' ',
86 array_filter(
87 array(
88 $classes,
89 self::SIDEBAR_CONTAINER_CLASS,
90 self::SIDEBAR_OPEN_CLASS,
91 )
92 )
93 );
94
95 return ' ' . $body_classes_with_sidebar_classes . ' ';
96 }
97
98 /**
99 * Print the synchronous sidebar-docking reconciliation script.
100 *
101 * Only emitted when the docked shell was pre-rendered. Because we optimistically
102 * inject the docked sidebar body classes, this script reconciles the gates that
103 * the React hook applies and removes those classes so the chat floats instead.
104 *
105 * The script lives in src/js/sidebar-docking-gate.ts and is inlined (not
106 * referenced via `src`) on purpose: it must run render-blocking before paint,
107 * and a same- or cross-origin fetch would add latency to that blocking window.
108 * Reading the bundled file and printing it inline keeps it a real, lintable JS
109 * file with zero request cost.
110 *
111 * @return void
112 */
113 public function print_sidebar_docking_gate_script() {
114 if ( ! $this->should_pre_render_docked_shell() ) {
115 return;
116 }
117
118 global $wp_filesystem;
119
120 if ( empty( $wp_filesystem ) ) {
121 require_once ABSPATH . 'wp-admin/includes/file.php';
122 WP_Filesystem();
123 }
124
125 $script_path = __DIR__ . '/../build/sidebar-docking-gate.js';
126
127 if ( empty( $wp_filesystem ) || ! $wp_filesystem->exists( $script_path ) ) {
128 return;
129 }
130
131 $script = $wp_filesystem->get_contents( $script_path );
132 if ( ! is_string( $script ) || '' === $script ) {
133 return;
134 }
135
136 wp_print_inline_script_tag( $script );
137 }
138
139 /**
140 * Whether the docked-open shell should be pre-rendered on this request.
141 *
142 * True only when the app is loading (so it will take over the shell once
143 * mounted) and the cached state is both open and docked — the only state
144 * that reshapes the admin layout. A cold session (no cache), a closed
145 * sidebar, or a floating (undocked) chat all pre-render nothing.
146 *
147 * @return bool
148 */
149 private function should_pre_render_docked_shell(): bool {
150 if ( ! $this->should_preserve_sidebar_open_state() ) {
151 return false;
152 }
153
154 $state = Open_State_Store::get_cached();
155
156 return $state && true === $state['agents_manager_open'] && true === $state['agents_manager_docked'];
157 }
158
159 /**
160 * Whether sidebar open preservation should run for this request.
161 *
162 * Gated on the same decision that loads the app (its active variant), so the
163 * pre-rendered shell only appears where the app will mount to reconcile it.
164 *
165 * @return bool
166 */
167 private function should_preserve_sidebar_open_state(): bool {
168 return null !== Agents_Manager::get_active_variant();
169 }
170 }
171