@wp-typia/project-tools 0.13.4 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/runtime/scaffold-onboarding.js +8 -4
  2. package/dist/runtime/scaffold.d.ts +1 -0
  3. package/dist/runtime/scaffold.js +3 -0
  4. package/dist/runtime/schema-core.d.ts +1 -0
  5. package/dist/runtime/schema-core.js +188 -8
  6. package/package.json +1 -1
  7. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +16 -0
  8. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +10 -0
  9. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +51 -38
  10. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +76 -33
  11. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +206 -41
  12. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +37 -43
  13. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/transport.ts.mustache +254 -0
  14. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -8
  15. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +139 -0
  16. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +159 -0
  17. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +139 -0
  18. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +16 -0
  19. package/templates/_shared/persistence/core/src/api-types.ts.mustache +10 -0
  20. package/templates/_shared/persistence/core/src/api.ts.mustache +51 -38
  21. package/templates/_shared/persistence/core/src/data.ts.mustache +76 -33
  22. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +206 -43
  23. package/templates/_shared/persistence/core/src/transport.ts.mustache +254 -0
  24. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +159 -0
  25. package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +1 -1
  26. package/templates/_shared/workspace/persistence-auth/server.php.mustache +139 -0
  27. package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +1 -1
  28. package/templates/_shared/workspace/persistence-public/server.php.mustache +159 -0
  29. package/templates/persistence/src/edit.tsx.mustache +1 -1
  30. package/templates/persistence/src/render.php.mustache +37 -43
  31. package/templates/persistence/src/types.ts.mustache +13 -9
@@ -6,31 +6,21 @@ import {
6
6
  } from '@wp-typia/rest/react';
7
7
 
8
8
  import type {
9
+ {{pascalCase}}BootstrapQuery,
10
+ {{pascalCase}}BootstrapResponse,
9
11
  {{pascalCase}}StateQuery,
10
12
  {{pascalCase}}StateResponse,
11
13
  {{pascalCase}}WriteStateRequest,
12
14
  } from './api-types';
13
15
  import {
14
- resolveRestNonce,
16
+ bootstrapEndpoint,
15
17
  stateEndpoint,
16
18
  writeStateEndpoint,
17
19
  } from './api';
18
-
19
- {{#isAuthenticatedPersistencePolicy}}
20
- function buildWriteCallOptions( restNonce?: string ) {
21
- const nonce = resolveRestNonce( restNonce );
22
-
23
- return nonce
24
- ? {
25
- requestOptions: {
26
- headers: {
27
- 'X-WP-Nonce': nonce,
28
- },
29
- },
30
- }
31
- : undefined;
32
- }
33
- {{/isAuthenticatedPersistencePolicy}}
20
+ import {
21
+ resolveTransportCallOptions,
22
+ type PersistenceTransportTarget,
23
+ } from './transport';
34
24
 
35
25
  interface WriteStateMutationContext< Context > {
36
26
  previous:
@@ -49,9 +39,22 @@ export interface Use{{pascalCase}}StateQueryOptions<
49
39
  >,
50
40
  'resolveCallOptions'
51
41
  > {
52
- {{#isAuthenticatedPersistencePolicy}}
53
42
  restNonce?: string;
54
- {{/isAuthenticatedPersistencePolicy}}
43
+ transportTarget?: PersistenceTransportTarget;
44
+ }
45
+
46
+ export interface Use{{pascalCase}}BootstrapQueryOptions<
47
+ Selected = {{pascalCase}}BootstrapResponse,
48
+ > extends Omit<
49
+ UseEndpointQueryOptions<
50
+ {{pascalCase}}BootstrapQuery,
51
+ {{pascalCase}}BootstrapResponse,
52
+ Selected
53
+ >,
54
+ 'resolveCallOptions'
55
+ > {
56
+ restNonce?: string;
57
+ transportTarget?: PersistenceTransportTarget;
55
58
  }
56
59
 
57
60
  export interface UseWrite{{pascalCase}}StateMutationOptions<
@@ -62,7 +65,7 @@ export interface UseWrite{{pascalCase}}StateMutationOptions<
62
65
  {{pascalCase}}StateResponse,
63
66
  WriteStateMutationContext< Context >
64
67
  >,
65
- 'invalidate' | 'onError' | 'onMutate'{{#isAuthenticatedPersistencePolicy}} | 'resolveCallOptions'{{/isAuthenticatedPersistencePolicy}}
68
+ 'invalidate' | 'onError' | 'onMutate' | 'resolveCallOptions'
66
69
  > {
67
70
  onError?: (
68
71
  error: unknown,
@@ -74,9 +77,8 @@ export interface UseWrite{{pascalCase}}StateMutationOptions<
74
77
  request: {{pascalCase}}WriteStateRequest,
75
78
  client: import('@wp-typia/rest/react').EndpointDataClient
76
79
  ) => Context | Promise<Context>;
77
- {{#isAuthenticatedPersistencePolicy}}
78
80
  restNonce?: string;
79
- {{/isAuthenticatedPersistencePolicy}}
81
+ transportTarget?: PersistenceTransportTarget;
80
82
  }
81
83
 
82
84
  export function use{{pascalCase}}StateQuery<
@@ -85,20 +87,54 @@ export function use{{pascalCase}}StateQuery<
85
87
  request: {{pascalCase}}StateQuery,
86
88
  options: Use{{pascalCase}}StateQueryOptions< Selected > = {}
87
89
  ) {
88
- {{#isAuthenticatedPersistencePolicy}}
89
90
  const {
90
91
  restNonce,
92
+ transportTarget = 'editor',
91
93
  ...queryOptions
92
94
  } = options;
93
95
 
94
96
  return useEndpointQuery( stateEndpoint, request, {
95
97
  ...queryOptions,
96
- resolveCallOptions: () => buildWriteCallOptions( restNonce ),
98
+ resolveCallOptions: () =>
99
+ resolveTransportCallOptions(
100
+ transportTarget,
101
+ 'read',
102
+ stateEndpoint,
103
+ request,
104
+ {
105
+ restNonce,
106
+ transportTarget,
107
+ }
108
+ ),
109
+ } );
110
+ }
111
+
112
+ export function use{{pascalCase}}BootstrapQuery<
113
+ Selected = {{pascalCase}}BootstrapResponse,
114
+ >(
115
+ request: {{pascalCase}}BootstrapQuery,
116
+ options: Use{{pascalCase}}BootstrapQueryOptions< Selected > = {}
117
+ ) {
118
+ const {
119
+ restNonce,
120
+ transportTarget = 'editor',
121
+ ...queryOptions
122
+ } = options;
123
+
124
+ return useEndpointQuery( bootstrapEndpoint, request, {
125
+ ...queryOptions,
126
+ resolveCallOptions: () =>
127
+ resolveTransportCallOptions(
128
+ transportTarget,
129
+ 'read',
130
+ bootstrapEndpoint,
131
+ request,
132
+ {
133
+ restNonce,
134
+ transportTarget,
135
+ }
136
+ ),
97
137
  } );
98
- {{/isAuthenticatedPersistencePolicy}}
99
- {{^isAuthenticatedPersistencePolicy}}
100
- return useEndpointQuery( stateEndpoint, request, options );
101
- {{/isAuthenticatedPersistencePolicy}}
102
138
  }
103
139
 
104
140
  export function useWrite{{pascalCase}}StateMutation<
@@ -109,9 +145,8 @@ export function useWrite{{pascalCase}}StateMutation<
109
145
  const {
110
146
  onError,
111
147
  onMutate,
112
- {{#isAuthenticatedPersistencePolicy}}
113
148
  restNonce,
114
- {{/isAuthenticatedPersistencePolicy}}
149
+ transportTarget = 'editor',
115
150
  ...mutationOptions
116
151
  } = options;
117
152
 
@@ -185,8 +220,16 @@ export function useWrite{{pascalCase}}StateMutation<
185
220
  userContext,
186
221
  };
187
222
  },
188
- {{#isAuthenticatedPersistencePolicy}}
189
- resolveCallOptions: () => buildWriteCallOptions( restNonce ),
190
- {{/isAuthenticatedPersistencePolicy}}
223
+ resolveCallOptions: ( request ) =>
224
+ resolveTransportCallOptions(
225
+ transportTarget,
226
+ 'write',
227
+ writeStateEndpoint,
228
+ request,
229
+ {
230
+ restNonce,
231
+ transportTarget,
232
+ }
233
+ ),
191
234
  } );
192
235
  }
@@ -1,17 +1,20 @@
1
1
  import { getContext, store } from '@wordpress/interactivity';
2
2
  import { generatePublicWriteRequestId } from '@wp-typia/block-runtime/identifiers';
3
3
 
4
- import { fetchState, writeState } from './api';
5
- import type { {{pascalCase}}Context, {{pascalCase}}State } from './types';
4
+ import { fetchBootstrap, fetchState, writeState } from './api';
5
+ import type {
6
+ {{pascalCase}}ClientState,
7
+ {{pascalCase}}Context,
8
+ {{pascalCase}}State,
9
+ } from './types';
6
10
 
7
11
  function hasExpiredPublicWriteToken(
8
- context: {{pascalCase}}Context
12
+ expiresAt?: number
9
13
  ): boolean {
10
14
  return (
11
- context.persistencePolicy === 'public' &&
12
- typeof context.publicWriteExpiresAt === 'number' &&
13
- context.publicWriteExpiresAt > 0 &&
14
- Date.now() >= context.publicWriteExpiresAt * 1000
15
+ typeof expiresAt === 'number' &&
16
+ expiresAt > 0 &&
17
+ Date.now() >= expiresAt * 1000
15
18
  );
16
19
  }
17
20
 
@@ -20,66 +23,212 @@ function getWriteBlockedMessage(
20
23
  ): string {
21
24
  return context.persistencePolicy === 'authenticated'
22
25
  ? 'Sign in to persist this counter.'
23
- : 'Reload the page to refresh this write token.';
26
+ : 'Public writes are temporarily unavailable.';
27
+ }
28
+
29
+ const BOOTSTRAP_MAX_ATTEMPTS = 3;
30
+ const BOOTSTRAP_RETRY_DELAYS_MS = [ 250, 500 ];
31
+
32
+ async function waitForBootstrapRetry( delayMs: number ): Promise< void > {
33
+ await new Promise( ( resolve ) => {
34
+ setTimeout( resolve, delayMs );
35
+ } );
36
+ }
37
+
38
+ function getClientState(
39
+ context: {{pascalCase}}Context
40
+ ): {{pascalCase}}ClientState {
41
+ if ( context.client ) {
42
+ return context.client;
43
+ }
44
+
45
+ context.client = {
46
+ bootstrapError: '',
47
+ writeExpiry: 0,
48
+ writeNonce: '',
49
+ writeToken: '',
50
+ };
51
+
52
+ return context.client;
53
+ }
54
+
55
+ function clearBootstrapError(
56
+ context: {{pascalCase}}Context,
57
+ clientState: {{pascalCase}}ClientState
58
+ ): void {
59
+ if ( context.error === clientState.bootstrapError ) {
60
+ context.error = '';
61
+ }
62
+ clientState.bootstrapError = '';
63
+ }
64
+
65
+ function setBootstrapError(
66
+ context: {{pascalCase}}Context,
67
+ clientState: {{pascalCase}}ClientState,
68
+ message: string
69
+ ): void {
70
+ clientState.bootstrapError = message;
71
+ context.error = message;
24
72
  }
25
73
 
26
74
  const { actions, state } = store( '{{slugKebabCase}}', {
27
75
  state: {
28
- canWrite: false,
29
- count: 0,
30
- error: undefined,
31
76
  isHydrated: false,
32
- isLoading: false,
33
- isSaving: false,
34
77
  } as {{pascalCase}}State,
35
78
 
36
79
  actions: {
37
- async loadCounter() {
80
+ async loadState() {
38
81
  const context = getContext< {{pascalCase}}Context >();
39
82
  if ( context.postId <= 0 || ! context.resourceKey ) {
40
83
  return;
41
84
  }
42
85
 
43
- state.isLoading = true;
44
- state.error = undefined;
86
+ context.isLoading = true;
87
+ context.error = '';
45
88
 
46
89
  try {
47
90
  const result = await fetchState( {
48
91
  postId: context.postId,
49
92
  resourceKey: context.resourceKey,
93
+ }, {
94
+ transportTarget: 'frontend',
50
95
  } );
51
96
  if ( ! result.isValid || ! result.data ) {
52
- state.error = result.errors[ 0 ]?.expected ?? 'Unable to load counter';
97
+ context.error = result.errors[ 0 ]?.expected ?? 'Unable to load counter';
53
98
  return;
54
99
  }
55
100
  context.count = result.data.count;
56
- context.storage = result.data.storage;
57
- state.count = result.data.count;
58
101
  } catch ( error ) {
59
- state.error =
102
+ context.error =
60
103
  error instanceof Error ? error.message : 'Unknown loading error';
61
104
  } finally {
62
- state.isLoading = false;
105
+ context.isLoading = false;
63
106
  }
64
107
  },
108
+ async loadBootstrap() {
109
+ const context = getContext< {{pascalCase}}Context >();
110
+ const clientState = getClientState( context );
111
+ if ( context.postId <= 0 || ! context.resourceKey ) {
112
+ context.bootstrapReady = true;
113
+ context.canWrite = false;
114
+ clientState.bootstrapError = '';
115
+ clientState.writeExpiry = 0;
116
+ clientState.writeNonce = '';
117
+ clientState.writeToken = '';
118
+ return;
119
+ }
120
+
121
+ context.isBootstrapping = true;
122
+
123
+ let bootstrapSucceeded = false;
124
+ let lastBootstrapError =
125
+ 'Unable to initialize write access';
126
+
127
+ for ( let attempt = 1; attempt <= BOOTSTRAP_MAX_ATTEMPTS; attempt += 1 ) {
128
+ try {
129
+ const result = await fetchBootstrap( {
130
+ postId: context.postId,
131
+ resourceKey: context.resourceKey,
132
+ }, {
133
+ transportTarget: 'frontend',
134
+ } );
135
+ if ( ! result.isValid || ! result.data ) {
136
+ lastBootstrapError =
137
+ result.errors[ 0 ]?.expected ??
138
+ 'Unable to initialize write access';
139
+ if ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {
140
+ await waitForBootstrapRetry(
141
+ BOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750
142
+ );
143
+ continue;
144
+ }
145
+ break;
146
+ }
147
+
148
+ clientState.writeExpiry =
149
+ typeof result.data.publicWriteExpiresAt === 'number' &&
150
+ result.data.publicWriteExpiresAt > 0
151
+ ? result.data.publicWriteExpiresAt
152
+ : 0;
153
+ clientState.writeToken =
154
+ typeof result.data.publicWriteToken === 'string' &&
155
+ result.data.publicWriteToken.length > 0
156
+ ? result.data.publicWriteToken
157
+ : '';
158
+ clientState.writeNonce =
159
+ typeof result.data.restNonce === 'string' &&
160
+ result.data.restNonce.length > 0
161
+ ? result.data.restNonce
162
+ : '';
163
+ context.bootstrapReady = true;
164
+ context.canWrite =
165
+ result.data.canWrite === true &&
166
+ (
167
+ context.persistencePolicy === 'authenticated'
168
+ ? clientState.writeNonce.length > 0
169
+ : clientState.writeToken.length > 0 &&
170
+ ! hasExpiredPublicWriteToken( clientState.writeExpiry )
171
+ );
172
+ clearBootstrapError( context, clientState );
173
+ bootstrapSucceeded = true;
174
+ break;
175
+ } catch ( error ) {
176
+ lastBootstrapError =
177
+ error instanceof Error ? error.message : 'Unknown bootstrap error';
178
+ if ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {
179
+ await waitForBootstrapRetry(
180
+ BOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750
181
+ );
182
+ continue;
183
+ }
184
+ break;
185
+ }
186
+ }
187
+
188
+ if ( ! bootstrapSucceeded ) {
189
+ context.bootstrapReady = false;
190
+ context.canWrite = false;
191
+ clientState.writeExpiry = 0;
192
+ clientState.writeNonce = '';
193
+ clientState.writeToken = '';
194
+ setBootstrapError( context, clientState, lastBootstrapError );
195
+ }
196
+ context.isBootstrapping = false;
197
+ },
65
198
  async increment() {
66
199
  const context = getContext< {{pascalCase}}Context >();
200
+ const clientState = getClientState( context );
67
201
  if ( context.postId <= 0 || ! context.resourceKey ) {
68
202
  return;
69
203
  }
70
- if ( hasExpiredPublicWriteToken( context ) ) {
204
+ if ( ! context.bootstrapReady ) {
205
+ await actions.loadBootstrap();
206
+ }
207
+ if ( ! context.bootstrapReady ) {
208
+ context.error = 'Write access is still initializing.';
209
+ return;
210
+ }
211
+ if (
212
+ context.persistencePolicy === 'public' &&
213
+ hasExpiredPublicWriteToken( clientState.writeExpiry )
214
+ ) {
215
+ await actions.loadBootstrap();
216
+ }
217
+ if (
218
+ context.persistencePolicy === 'public' &&
219
+ hasExpiredPublicWriteToken( clientState.writeExpiry )
220
+ ) {
71
221
  context.canWrite = false;
72
- state.canWrite = false;
73
- state.error = getWriteBlockedMessage( context );
222
+ context.error = getWriteBlockedMessage( context );
74
223
  return;
75
224
  }
76
- if ( ! context.canWrite || ! state.canWrite ) {
77
- state.error = getWriteBlockedMessage( context );
225
+ if ( ! context.canWrite ) {
226
+ context.error = getWriteBlockedMessage( context );
78
227
  return;
79
228
  }
80
229
 
81
- state.isSaving = true;
82
- state.error = undefined;
230
+ context.isSaving = true;
231
+ context.error = '';
83
232
 
84
233
  try {
85
234
  const result = await writeState( {
@@ -91,24 +240,28 @@ const { actions, state } = store( '{{slugKebabCase}}', {
91
240
  : undefined,
92
241
  publicWriteToken:
93
242
  context.persistencePolicy === 'public' &&
94
- typeof context.publicWriteToken === 'string' &&
95
- context.publicWriteToken.length > 0
96
- ? context.publicWriteToken
243
+ clientState.writeToken.length > 0
244
+ ? clientState.writeToken
97
245
  : undefined,
98
246
  resourceKey: context.resourceKey,
99
- }, context.restNonce );
247
+ }, {
248
+ restNonce:
249
+ clientState.writeNonce.length > 0
250
+ ? clientState.writeNonce
251
+ : undefined,
252
+ transportTarget: 'frontend',
253
+ } );
100
254
  if ( ! result.isValid || ! result.data ) {
101
- state.error = result.errors[ 0 ]?.expected ?? 'Unable to update counter';
255
+ context.error = result.errors[ 0 ]?.expected ?? 'Unable to update counter';
102
256
  return;
103
257
  }
104
258
  context.count = result.data.count;
105
259
  context.storage = result.data.storage;
106
- state.count = result.data.count;
107
260
  } catch ( error ) {
108
- state.error =
261
+ context.error =
109
262
  error instanceof Error ? error.message : 'Unknown update error';
110
263
  } finally {
111
- state.isSaving = false;
264
+ context.isSaving = false;
112
265
  }
113
266
  },
114
267
  },
@@ -116,17 +269,29 @@ const { actions, state } = store( '{{slugKebabCase}}', {
116
269
  callbacks: {
117
270
  init() {
118
271
  const context = getContext< {{pascalCase}}Context >();
119
- context.canWrite =
120
- context.canWrite && ! hasExpiredPublicWriteToken( context );
121
- state.canWrite = context.canWrite;
122
- state.count = context.count;
272
+ context.client = {
273
+ bootstrapError: '',
274
+ writeExpiry: 0,
275
+ writeNonce: '',
276
+ writeToken: '',
277
+ };
278
+ context.bootstrapReady = false;
279
+ context.canWrite = false;
280
+ context.count = 0;
281
+ context.error = '';
282
+ context.isBootstrapping = false;
283
+ context.isLoading = false;
284
+ context.isSaving = false;
123
285
  },
124
286
  mounted() {
125
287
  state.isHydrated = true;
126
288
  if ( typeof document !== 'undefined' ) {
127
289
  document.documentElement.dataset[ '{{slugCamelCase}}Hydrated' ] = 'true';
128
290
  }
129
- void actions.loadCounter();
291
+ void Promise.allSettled( [
292
+ actions.loadState(),
293
+ actions.loadBootstrap(),
294
+ ] );
130
295
  },
131
296
  },
132
297
  } );
@@ -32,50 +32,42 @@ $post_id = is_object( $block ) && isset( $block->context['postId'] )
32
32
  : (int) get_queried_object_id();
33
33
  $storage_mode = '{{dataStorageMode}}';
34
34
  $persistence_policy = '{{persistencePolicy}}';
35
- $can_write = false;
35
+
36
+ {{phpPrefix}}_record_rendered_block_instance(
37
+ (int) $post_id,
38
+ '{{namespace}}/{{slugKebabCase}}',
39
+ $resource_key
40
+ );
41
+
36
42
  $notice_message = 'authenticated' === $persistence_policy
37
43
  ? __( 'Sign in to persist this counter.', '{{textDomain}}' )
38
- : __( 'Reload the page to refresh this write token.', '{{textDomain}}' );
44
+ : __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );
39
45
 
40
46
  if ( empty( $validation['valid'] ) || '' === $resource_key ) {
41
47
  return '';
42
48
  }
43
49
 
44
50
  $context = array(
45
- 'buttonLabel' => $button_label,
46
- 'canWrite' => false,
47
- 'count' => 0,
48
- 'persistencePolicy' => $persistence_policy,
49
- 'postId' => (int) $post_id,
50
- 'resourceKey' => $resource_key,
51
- 'showCount' => $show_count,
52
- 'storage' => $storage_mode,
51
+ 'bootstrapReady' => false,
52
+ 'buttonLabel' => $button_label,
53
+ 'canWrite' => false,
54
+ 'client' => array(
55
+ 'writeExpiry' => 0,
56
+ 'writeNonce' => '',
57
+ 'writeToken' => '',
58
+ ),
59
+ 'count' => 0,
60
+ 'error' => '',
61
+ 'isBootstrapping' => false,
62
+ 'isLoading' => false,
63
+ 'isSaving' => false,
64
+ 'persistencePolicy' => $persistence_policy,
65
+ 'postId' => (int) $post_id,
66
+ 'resourceKey' => $resource_key,
67
+ 'showCount' => $show_count,
68
+ 'storage' => $storage_mode,
53
69
  );
54
70
 
55
- if ( 'authenticated' === $persistence_policy ) {
56
- $can_write = $post_id > 0 && is_user_logged_in();
57
- if ( $can_write ) {
58
- $context['restNonce'] = wp_create_nonce( 'wp_rest' );
59
- }
60
- } elseif ( $post_id > 0 && function_exists( '{{phpPrefix}}_create_public_write_token' ) ) {
61
- $public_write = {{phpPrefix}}_create_public_write_token( (int) $post_id, $resource_key );
62
- if ( is_array( $public_write ) ) {
63
- $token = isset( $public_write['token'] ) ? (string) $public_write['token'] : '';
64
- $expires_at = isset( $public_write['expiresAt'] ) ? (int) $public_write['expiresAt'] : 0;
65
-
66
- if ( '' !== $token ) {
67
- $context['publicWriteToken'] = $token;
68
- $can_write = true;
69
- }
70
-
71
- if ( $expires_at > 0 ) {
72
- $context['publicWriteExpiresAt'] = $expires_at;
73
- }
74
- }
75
- }
76
-
77
- $context['canWrite'] = $can_write;
78
-
79
71
  $allowed_inner_html = wp_kses_allowed_html( 'post' );
80
72
 
81
73
  foreach ( $allowed_inner_html as &$allowed_attributes ) {
@@ -119,18 +111,20 @@ $wrapper_attributes = get_block_wrapper_attributes(
119
111
  <?php if ( '' !== $intro ) : ?>
120
112
  <p class="{{cssClassName}}__intro"><?php echo esc_html( $intro ); ?></p>
121
113
  <?php endif; ?>
122
- <?php if ( ! $can_write ) : ?>
123
- <p class="{{cssClassName}}__notice">
124
- <?php echo esc_html( $notice_message ); ?>
125
- </p>
126
- <?php endif; ?>
114
+ <p
115
+ class="{{cssClassName}}__notice"
116
+ data-wp-bind--hidden="!context.bootstrapReady || context.canWrite"
117
+ hidden
118
+ >
119
+ <?php echo esc_html( $notice_message ); ?>
120
+ </p>
127
121
  <p
128
122
  class="{{cssClassName}}__error"
129
123
  role="status"
130
124
  aria-live="polite"
131
125
  aria-atomic="true"
132
- data-wp-bind--hidden="!state.error"
133
- data-wp-text="state.error"
126
+ data-wp-bind--hidden="!context.error"
127
+ data-wp-text="context.error"
134
128
  hidden
135
129
  ></p>
136
130
  <?php if ( $show_count ) : ?>
@@ -140,11 +134,11 @@ $wrapper_attributes = get_block_wrapper_attributes(
140
134
  role="status"
141
135
  aria-live="polite"
142
136
  aria-atomic="true"
143
- data-wp-text="state.count"
137
+ data-wp-text="context.count"
144
138
  >0</span>
145
139
  <button
146
140
  type="button"
147
- <?php echo $can_write ? '' : 'disabled'; ?>
141
+ disabled
148
142
  data-wp-bind--disabled="!context.canWrite"
149
143
  data-wp-on--click="actions.increment"
150
144
  >