@automattic/newspack-blocks 3.0.4 → 3.0.5

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 (30) hide show
  1. package/.cache/babel/7170efa5ce57b069b83f283e91811450.json.gz +0 -0
  2. package/.cache/babel/b28476e53228af2a7d6a9e55a5d63e64.json.gz +0 -0
  3. package/.cache/babel/c19be800a50892a02f080513c40bccbd.json.gz +0 -0
  4. package/.cache/babel/daa1bb86cfbd47c4ba1fc391d8194453.json.gz +0 -0
  5. package/.eslintrc.js +3 -0
  6. package/CHANGELOG.md +21 -2
  7. package/composer.lock +12 -12
  8. package/dist/editor.asset.php +1 -1
  9. package/dist/editor.js +2 -2
  10. package/includes/class-modal-checkout.php +42 -0
  11. package/includes/class-newspack-blocks-api.php +10 -11
  12. package/includes/class-newspack-blocks.php +43 -0
  13. package/includes/plugins/class-the-events-calendar.php +84 -0
  14. package/newspack-blocks.php +4 -2
  15. package/package.json +2 -2
  16. package/src/blocks/author-profile/class-wp-rest-newspack-authors-controller.php +42 -30
  17. package/src/blocks/carousel/edit.js +2 -2
  18. package/src/blocks/homepage-articles/{edit.js → edit.tsx} +34 -33
  19. package/src/blocks/homepage-articles/templates/article.php +3 -2
  20. package/src/blocks/homepage-articles/utils.ts +8 -31
  21. package/src/components/editor-panels.js +14 -3
  22. package/src/types/index.d.ts +109 -0
  23. package/vendor/autoload.php +1 -1
  24. package/vendor/composer/autoload_real.php +4 -4
  25. package/vendor/composer/autoload_static.php +2 -2
  26. package/vendor/composer/installed.php +2 -2
  27. package/.cache/babel/2fa9c6a361abed0c298ce473e621c94c.json.gz +0 -0
  28. package/.cache/babel/538ade6c2456b5eff86c58dc45c63d8a.json.gz +0 -0
  29. package/.cache/babel/6868f766c1c67a550ee0fb3a6ac463de.json.gz +0 -0
  30. package/.cache/babel/fdd76bf50199728a7f02fe0f3bf347cd.json.gz +0 -0
@@ -51,6 +51,8 @@ final class Modal_Checkout {
51
51
  add_filter( 'wcs_place_subscription_order_text', [ __CLASS__, 'order_button_text' ], 1 );
52
52
  add_filter( 'woocommerce_order_button_text', [ __CLASS__, 'order_button_text' ] );
53
53
  add_filter( 'option_woocommerce_subscriptions_order_button_text', [ __CLASS__, 'order_button_text' ] );
54
+ add_action( 'option_woocommerce_default_customer_address', [ __CLASS__, 'ensure_base_default_customer_address' ] );
55
+ add_action( 'default_option_woocommerce_default_customer_address', [ __CLASS__, 'ensure_base_default_customer_address' ] );
54
56
 
55
57
  /** Custom handling for registered users. */
56
58
  add_filter( 'woocommerce_checkout_customer_id', [ __CLASS__, 'associate_existing_user' ] );
@@ -996,6 +998,46 @@ final class Modal_Checkout {
996
998
  return $text;
997
999
  }
998
1000
 
1001
+ /**
1002
+ * Force option for base country for new customers if unset and billing country optional while state is required
1003
+ * unless the NEWSPACK_PREVENT_FORCE_BASE_DEFAULT_CUSTOMER_ADDRESS constant is set.
1004
+ *
1005
+ * If this option is empty AND billing state is set as a required field AND billing country is not,
1006
+ * validation of the state value will fail during modal checkout.
1007
+ *
1008
+ * See Default Customer Location in: https://woo.com/document/configuring-woocommerce-settings/#general-options
1009
+ *
1010
+ * @param string $option_value The value of the default customer address option.
1011
+ *
1012
+ * @return string Option value.
1013
+ */
1014
+ public static function ensure_base_default_customer_address( $option_value ) {
1015
+ // If the option is set, we're good.
1016
+ if ( ! empty( $option_value ) ) {
1017
+ return $option_value;
1018
+ }
1019
+
1020
+ // Only in modal checkout.
1021
+ if ( ! self::is_modal_checkout() ) {
1022
+ return $option_value;
1023
+ }
1024
+
1025
+ // Escape hatch in case we want the standard behavior even in modal checkout.
1026
+ if ( defined( 'NEWSPACK_PREVENT_FORCE_BASE_DEFAULT_CUSTOMER_ADDRESS' ) && NEWSPACK_PREVENT_FORCE_BASE_DEFAULT_CUSTOMER_ADDRESS ) {
1027
+ return $option_value;
1028
+ }
1029
+
1030
+ // If billing state is required but billing country is not, we need to ensure a default location is set.
1031
+ if ( defined( '\Newspack\Donations::DONATION_BILLING_FIELDS_OPTION' ) ) {
1032
+ $billing_fields = get_option( \Newspack\Donations::DONATION_BILLING_FIELDS_OPTION, [] );
1033
+ if ( ! in_array( 'billing_country', $billing_fields, true ) && in_array( 'billing_state', $billing_fields, true ) ) {
1034
+ return 'base';
1035
+ }
1036
+ }
1037
+
1038
+ return $option_value;
1039
+ }
1040
+
999
1041
  /**
1000
1042
  * If a reader tries to make a purchase with an email address that
1001
1043
  * has been previously registered, automatically associate the transaction
@@ -278,8 +278,6 @@ class Newspack_Blocks_API {
278
278
  $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
279
279
  setup_postdata( $post );
280
280
 
281
- $post_date_gmt = '0000-00-00 00:00:00' === $post->post_date_gmt ? get_gmt_from_date( $post->post_date ) : $post->post_date_gmt;
282
-
283
281
  // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
284
282
  $excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
285
283
  $excerpt = apply_filters( 'the_excerpt', $excerpt );
@@ -288,19 +286,20 @@ class Newspack_Blocks_API {
288
286
 
289
287
  $meta = new WP_REST_Post_Meta_Fields( 'post' );
290
288
  $data = [
291
- 'author' => (int) $post->post_author,
292
- 'content' => [
289
+ 'author' => (int) $post->post_author,
290
+ 'content' => [
293
291
  'rendered' => post_password_required( $post ) ? '' : $content,
294
292
  ],
295
- 'date_gmt' => mysql_to_rfc3339( $post_date_gmt ),
296
- 'date' => mysql_to_rfc3339( $post->post_date ),
297
- 'excerpt' => [
293
+ 'date' => Newspack_Blocks::get_displayed_post_date( $post ),
294
+ 'date_formatted' => Newspack_Blocks::get_formatted_displayed_post_date( $post ),
295
+ 'article_meta_footer' => Newspack_Blocks::get_article_meta_footer( $post ),
296
+ 'excerpt' => [
298
297
  'rendered' => post_password_required( $post ) ? '' : $excerpt,
299
298
  ],
300
- 'featured_media' => (int) get_post_thumbnail_id( $post->ID ),
301
- 'id' => $post->ID,
302
- 'meta' => $meta->get_value( $post->ID, $request ),
303
- 'title' => [
299
+ 'featured_media' => (int) get_post_thumbnail_id( $post->ID ),
300
+ 'id' => $post->ID,
301
+ 'meta' => $meta->get_value( $post->ID, $request ),
302
+ 'title' => [
304
303
  'rendered' => get_the_title( $post->ID ),
305
304
  ],
306
305
  ];
@@ -1544,6 +1544,49 @@ class Newspack_Blocks {
1544
1544
  ];
1545
1545
  }
1546
1546
 
1547
+ /**
1548
+ * Get post date to be displayed.
1549
+ *
1550
+ * @param WP_Post $post Post object.
1551
+ * @return string Date string.
1552
+ */
1553
+ public static function get_displayed_post_date( $post = null ) {
1554
+ if ( $post === null ) {
1555
+ $post = get_post();
1556
+ }
1557
+ return apply_filters( 'newspack_blocks_displayed_post_date', mysql_to_rfc3339( $post->post_date ), $post );
1558
+ }
1559
+
1560
+ /**
1561
+ * Get post date to be displayed, formatted.
1562
+ *
1563
+ * @param WP_Post $post Post object.
1564
+ * @return string Formatted date.
1565
+ */
1566
+ public static function get_formatted_displayed_post_date( $post = null ) {
1567
+ if ( $post === null ) {
1568
+ $post = get_post();
1569
+ }
1570
+ $date = self::get_displayed_post_date( $post );
1571
+ $date_formatted = ( new DateTime( $date ) )->format( get_option( 'date_format' ) );
1572
+ return apply_filters( 'newspack_blocks_formatted_displayed_post_date', $date_formatted, $post );
1573
+ }
1574
+
1575
+ /**
1576
+ * Get article meta footer.
1577
+ *
1578
+ * @param WP_Post $post Post object.
1579
+ */
1580
+ public static function get_article_meta_footer( $post = null ) {
1581
+ if ( $post === null ) {
1582
+ $post = get_post();
1583
+ }
1584
+ $meta_footer = apply_filters( 'newspack_blocks_article_meta_footer', '', $post );
1585
+ if ( strlen( $meta_footer ) > 0 ) {
1586
+ return '<span style="margin: 0 6px;" class="newspack_blocks__article-meta-footer__separator">|</span>' . $meta_footer;
1587
+ }
1588
+ }
1589
+
1547
1590
  /**
1548
1591
  * Get a formatted HTML string containing amount and frequency of a donation.
1549
1592
  *
@@ -0,0 +1,84 @@
1
+ <?php
2
+ /**
3
+ * The Events Calendar integration class.
4
+ *
5
+ * @package Newspack
6
+ */
7
+
8
+ namespace Newspack_Blocks;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Main class.
14
+ */
15
+ class The_Events_Calendar {
16
+ /**
17
+ * Initialize hooks and filters.
18
+ */
19
+ public static function init() {
20
+ add_filter( 'tribe_events_register_event_type_args', [ __CLASS__, 'register_event_type_args' ] );
21
+ add_filter( 'newspack_blocks_displayed_post_date', [ __CLASS__, 'get_displayed_post_date' ], 10, 2 );
22
+ add_filter( 'newspack_blocks_formatted_displayed_post_date', [ __CLASS__, 'get_formatted_displayed_post_date' ], 10, 2 );
23
+ add_filter( 'newspack_blocks_article_meta_footer', [ __CLASS__, 'get_article_meta_footer' ], 10, 2 );
24
+ }
25
+
26
+ /**
27
+ * Enable Newspack Blocks support for The Events Calendar.
28
+ *
29
+ * @param array $args The post type args.
30
+ */
31
+ public static function register_event_type_args( $args ) {
32
+ $args['supports'][] = 'newspack_blocks';
33
+ return $args;
34
+ }
35
+
36
+ /**
37
+ * Get the displayed post date.
38
+ *
39
+ * @param string $date The date.
40
+ * @param WP_Post $post The post object.
41
+ */
42
+ public static function get_displayed_post_date( $date, $post ) {
43
+ if ( $post->post_type === 'tribe_events' ) {
44
+ return mysql_to_rfc3339( get_post_meta( $post->ID, '_EventStartDate', true ) );
45
+ }
46
+ return $date;
47
+ }
48
+
49
+ /**
50
+ * Get the formatted displayed post date.
51
+ *
52
+ * @param string $date The date.
53
+ * @param WP_Post $post The post object.
54
+ */
55
+ public static function get_formatted_displayed_post_date( $date, $post ) {
56
+ if ( $post->post_type === 'tribe_events' && function_exists( 'tribe_events_event_schedule_details' ) ) {
57
+ try {
58
+ $html = tribe_events_event_schedule_details( $post );
59
+ return wp_strip_all_tags( $html );
60
+ } catch ( \Throwable $th ) {
61
+ return $date;
62
+ }
63
+ }
64
+ return $date;
65
+ }
66
+
67
+ /**
68
+ * Get the article meta footer.
69
+ *
70
+ * @param string $footer The footer.
71
+ * @param WP_Post $post The post object.
72
+ */
73
+ public static function get_article_meta_footer( $footer, $post ) {
74
+ if ( $post->post_type === 'tribe_events' && function_exists( 'tribe_get_venue' ) ) {
75
+ $venue = tribe_get_venue( $post );
76
+ if ( ! $venue ) {
77
+ return $footer;
78
+ }
79
+ return $footer . '<span class="newspack-blocks__tec-venue">' . $venue . '</span>';
80
+ }
81
+ return $footer;
82
+ }
83
+ }
84
+ The_Events_Calendar::init();
@@ -7,7 +7,7 @@
7
7
  * Author URI: https://newspack.com/
8
8
  * Text Domain: newspack-blocks
9
9
  * Domain Path: /languages
10
- * Version: 3.0.4
10
+ * Version: 3.0.5
11
11
  *
12
12
  * @package Newspack_Blocks
13
13
  */
@@ -15,13 +15,15 @@
15
15
  define( 'NEWSPACK_BLOCKS__PLUGIN_FILE', __FILE__ );
16
16
  define( 'NEWSPACK_BLOCKS__BLOCKS_DIRECTORY', 'dist/' );
17
17
  define( 'NEWSPACK_BLOCKS__PLUGIN_DIR', plugin_dir_path( NEWSPACK_BLOCKS__PLUGIN_FILE ) );
18
- define( 'NEWSPACK_BLOCKS__VERSION', '3.0.4' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '3.0.5' );
19
19
 
20
20
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks.php';
21
21
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-api.php';
22
22
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-patterns.php';
23
23
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-modal-checkout.php';
24
24
 
25
+ require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/plugins/class-the-events-calendar.php';
26
+
25
27
  // REST Controller for Articles Block.
26
28
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php';
27
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "author": "Automattic",
5
5
  "devDependencies": {
6
6
  "@rushstack/eslint-patch": "^1.7.2",
@@ -10,7 +10,7 @@
10
10
  "@types/lodash.debounce": "^4.0.9",
11
11
  "eslint": "^7.32.0",
12
12
  "fetch-mock-jest": "^1.5.1",
13
- "html-entities": "^2.4.0",
13
+ "html-entities": "^2.5.2",
14
14
  "identity-obj-proxy": "^3.0.0",
15
15
  "lint-staged": "^15.2.2",
16
16
  "newspack-scripts": "^5.3.0",
@@ -84,12 +84,13 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
84
84
  // Total number of users and guest authors.
85
85
  $guest_author_total = 0;
86
86
  $user_total = 0;
87
- $guest_authors = [];
88
- $linked_guest_authors = [];
87
+ $guest_authors = [];
88
+ $users = [];
89
89
 
90
90
  // Get Co-authors guest authors.
91
91
  if ( $is_guest_author ) {
92
- $guest_author_args = [
92
+ $unlinked_guest_authors = [];
93
+ $guest_author_args = [
93
94
  'post_type' => 'guest-author',
94
95
  'posts_per_page' => $per_page,
95
96
  'offset' => $offset,
@@ -98,44 +99,44 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
98
99
  if ( $search && ! $author_id ) {
99
100
  $guest_author_args['s'] = $search;
100
101
  }
102
+
101
103
  if ( $author_id ) {
102
104
  $guest_author_args['p'] = $author_id;
103
105
  }
106
+
104
107
  if ( $include ) {
105
108
  $guest_author_args['post__in'] = $include;
106
109
  $guest_author_args['ignore_sticky_posts'] = true;
107
110
  }
108
111
 
109
- $guest_authors = get_posts( $guest_author_args );
110
- $guest_author_total = count( $guest_authors );
111
- }
112
+ $guest_authors = get_posts( $guest_author_args );
113
+
114
+ // If we are searching for a specific ID we want to return the guest author regardless of if it is linked or not.
115
+ if ( ! $author_id ) {
116
+ foreach ( $guest_authors as $ga ) {
117
+ $linked_guest_author = get_post_meta( $ga->ID, 'cap-linked_account', true );
118
+
119
+ if ( $linked_guest_author ) {
120
+ continue;
121
+ }
112
122
 
113
- foreach ( $guest_authors as $ga ) {
114
- $linked_guest_author = get_post_meta( $ga->ID, 'cap-linked_account', true );
123
+ $unlinked_guest_authors[] = $ga;
124
+ }
115
125
 
116
- if ( $linked_guest_author ) {
117
- $linked_guest_authors[] = $linked_guest_author;
126
+ $guest_authors = $unlinked_guest_authors;
118
127
  }
119
- }
120
128
 
121
- $users = [];
129
+ $guest_author_total = count( $guest_authors );
130
+ }
122
131
 
123
- // If passed an author ID.
132
+ // If we are searching for a specific ID we just want to return the specific user.
124
133
  if ( $author_id ) {
134
+ // Unless we've already identified a guest author.
125
135
  if ( 0 === $guest_author_total ) {
126
136
  $user = get_user_by( 'id', $author_id ); // Get the WP user.
127
137
 
128
- // We have a WP user, let's use it.
129
138
  if ( $user ) {
130
- // But wait, there's more! Let's see if this user is linked to a guest author.
131
- $linked_guest_author = self::get_linked_guest_author( $user->user_login );
132
-
133
- // If it is, let's use that instead.
134
- if ( $linked_guest_author ) {
135
- $guest_authors = [ $linked_guest_author ];
136
- } else {
137
- $users = [ $user ];
138
- }
139
+ $users = [ $user ];
139
140
  }
140
141
  }
141
142
  } else {
@@ -162,6 +163,23 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
162
163
  $user_total = $user_query->get_total();
163
164
  }
164
165
 
166
+ if ( 0 < $user_total ) {
167
+ // But wait, there's more! Let's see if this user is linked to a guest author.
168
+ $unlinked_users = [];
169
+ foreach ( $users as $user ) {
170
+ $linked_guest_author = self::get_linked_guest_author( $user->user_login );
171
+
172
+ // If it is, let's use that instead.
173
+ if ( $linked_guest_author ) {
174
+ $guest_authors[] = $linked_guest_author;
175
+ } else {
176
+ $unlinked_users[] = $user;
177
+ }
178
+ }
179
+
180
+ $users = $unlinked_users;
181
+ }
182
+
165
183
  // Format and combine results.
166
184
  $combined_authors = array_merge(
167
185
  array_reduce(
@@ -197,14 +215,8 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
197
215
  ),
198
216
  array_reduce(
199
217
  $users,
200
- function( $acc, $user ) use ( $fields, $avatar_hide_default, $linked_guest_authors ) {
218
+ function( $acc, $user ) use ( $fields, $avatar_hide_default ) {
201
219
  if ( $user ) {
202
-
203
- // This user is linked to a guest author already returned in the query, so skip it.
204
- if ( in_array( $user->data->user_login, $linked_guest_authors, true ) ) {
205
- return $acc;
206
- }
207
-
208
220
  $user_data = [
209
221
  'id' => intval( $user->data->ID ),
210
222
  'registered' => $user->data->user_registered,
@@ -252,8 +252,8 @@ class Edit extends Component {
252
252
  <Fragment>
253
253
  { autoplay && (
254
254
  <Fragment>
255
- <button ref={ this.btnPauseRef } />
256
- <button ref={ this.btnPlayRef } />
255
+ <button className="swiper-button swiper-button-pause" ref={ this.btnPauseRef } />
256
+ <button className="swiper-button swiper-button-play" ref={ this.btnPlayRef } />
257
257
  </Fragment>
258
258
  ) }
259
259
  <div className="swiper-wrapper">
@@ -23,8 +23,6 @@ import classNames from 'classnames';
23
23
  * WordPress dependencies
24
24
  */
25
25
  import { __ } from '@wordpress/i18n';
26
- // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
27
- import { dateI18n, __experimentalGetSettings } from '@wordpress/date';
28
26
  import { Component, Fragment, RawHTML } from '@wordpress/element';
29
27
  import {
30
28
  BlockControls,
@@ -63,7 +61,7 @@ import {
63
61
  pullRight,
64
62
  } from '@wordpress/icons';
65
63
 
66
- let IS_SUBTITLE_SUPPORTED_IN_THEME;
64
+ let IS_SUBTITLE_SUPPORTED_IN_THEME: boolean;
67
65
  if (
68
66
  typeof window === 'object' &&
69
67
  window.newspack_blocks_data &&
@@ -102,8 +100,8 @@ const squareIcon = (
102
100
  </SVG>
103
101
  );
104
102
 
105
- class Edit extends Component {
106
- renderPost = post => {
103
+ class Edit extends Component< HomepageArticlesProps > {
104
+ renderPost = ( post: Post ) => {
107
105
  const { attributes, isUIDisabled } = this.props;
108
106
  const {
109
107
  showImage,
@@ -125,15 +123,17 @@ class Edit extends Component {
125
123
 
126
124
  const styles = {
127
125
  minHeight:
128
- mediaPosition === 'behind' &&
129
- showImage &&
130
- post.newspack_featured_image_src &&
131
- minHeight + 'vh',
126
+ ( mediaPosition === 'behind' &&
127
+ showImage &&
128
+ post.newspack_featured_image_src &&
129
+ minHeight + 'vh' ) ||
130
+ undefined,
132
131
  paddingTop:
133
- mediaPosition === 'behind' &&
134
- showImage &&
135
- post.newspack_featured_image_src &&
136
- minHeight / 5 + 'vh',
132
+ ( mediaPosition === 'behind' &&
133
+ showImage &&
134
+ post.newspack_featured_image_src &&
135
+ minHeight / 5 + 'vh' ) ||
136
+ undefined,
137
137
  };
138
138
 
139
139
  const postClasses = classNames(
@@ -145,7 +145,6 @@ class Edit extends Component {
145
145
  );
146
146
 
147
147
  const postTitle = this.titleForPost( post );
148
- const dateFormat = __experimentalGetSettings().formats.date;
149
148
  return (
150
149
  <article className={ postClasses } key={ post.id } style={ styles }>
151
150
  { getPostStatusLabel( post ) }
@@ -242,22 +241,20 @@ class Edit extends Component {
242
241
 
243
242
  { showDate && ! post.newspack_listings_hide_publish_date && (
244
243
  <time className="entry-date published" key="pub-date">
245
- { dateI18n( dateFormat, post.date ) }
244
+ { post.date_formatted }
246
245
  </time>
247
246
  ) }
247
+ { post.article_meta_footer ? <RawHTML>{ post.article_meta_footer }</RawHTML> : null }
248
248
  </div>
249
249
  </div>
250
250
  </article>
251
251
  );
252
252
  };
253
253
 
254
- titleForPost = post => {
254
+ titleForPost = ( post: Post ) => {
255
255
  if ( ! post.title ) {
256
256
  return '';
257
257
  }
258
- if ( typeof post.title === 'string' ) {
259
- return decodeEntities( post.title.trim() );
260
- }
261
258
  if ( typeof post.title === 'object' && post.title.rendered ) {
262
259
  return decodeEntities( post.title.rendered.trim() );
263
260
  }
@@ -349,14 +346,16 @@ class Edit extends Component {
349
346
  },
350
347
  ];
351
348
 
352
- const handleAttributeChange = key => value => setAttributes( { [ key ]: value } );
349
+ const handleAttributeChange = ( key: HomepageArticlesAttributesKey ) => ( value: any ) =>
350
+ setAttributes( { [ key ]: value } );
353
351
 
354
352
  return (
355
353
  <Fragment>
356
354
  <PanelBody title={ __( 'Display Settings', 'newspack-blocks' ) } initialOpen={ true }>
355
+ { /* @ts-ignore */ }
357
356
  <QueryControls
358
357
  numberOfItems={ postsToShow }
359
- onNumberOfItemsChange={ _postsToShow =>
358
+ onNumberOfItemsChange={ ( _postsToShow: number ) =>
360
359
  setAttributes( { postsToShow: _postsToShow || 1 } )
361
360
  }
362
361
  specificMode={ specificMode }
@@ -450,7 +449,7 @@ class Edit extends Component {
450
449
  'newspack-blocks'
451
450
  ) }
452
451
  checked={ ! attributes.deduplicate }
453
- onChange={ value => setAttributes( { deduplicate: ! value } ) }
452
+ onChange={ ( value: boolean ) => setAttributes( { deduplicate: ! value } ) }
454
453
  className="newspack-blocks-deduplication-toggle"
455
454
  />
456
455
  </PanelBody>
@@ -528,7 +527,7 @@ class Edit extends Component {
528
527
  'newspack-blocks'
529
528
  ) }
530
529
  value={ minHeight }
531
- onChange={ _minHeight => setAttributes( { minHeight: _minHeight } ) }
530
+ onChange={ ( _minHeight: number ) => setAttributes( { minHeight: _minHeight } ) }
532
531
  min={ 0 }
533
532
  max={ 100 }
534
533
  required
@@ -556,7 +555,7 @@ class Edit extends Component {
556
555
  <RangeControl
557
556
  label={ __( 'Max number of words in excerpt', 'newspack-blocks' ) }
558
557
  value={ excerptLength }
559
- onChange={ value => setAttributes( { excerptLength: value } ) }
558
+ onChange={ ( value: number ) => setAttributes( { excerptLength: value } ) }
560
559
  min={ 10 }
561
560
  max={ 100 }
562
561
  />
@@ -571,14 +570,14 @@ class Edit extends Component {
571
570
  label={ __( '"Read More" link text', 'newspack-blocks' ) }
572
571
  value={ readMoreLabel }
573
572
  placeholder={ readMoreLabel }
574
- onChange={ value => setAttributes( { readMoreLabel: value } ) }
573
+ onChange={ ( value: string ) => setAttributes( { readMoreLabel: value } ) }
575
574
  />
576
575
  ) }
577
576
  <RangeControl
578
577
  className="type-scale-slider"
579
578
  label={ __( 'Type Scale', 'newspack-blocks' ) }
580
579
  value={ typeScale }
581
- onChange={ _typeScale => setAttributes( { typeScale: _typeScale } ) }
580
+ onChange={ ( _typeScale: number ) => setAttributes( { typeScale: _typeScale } ) }
582
581
  min={ 1 }
583
582
  max={ 10 }
584
583
  required
@@ -636,7 +635,7 @@ class Edit extends Component {
636
635
  componentDidMount() {
637
636
  this.props.triggerReflow();
638
637
  }
639
- componentDidUpdate( props ) {
638
+ componentDidUpdate( props: HomepageArticlesProps ) {
640
639
  if ( shouldReflow( props, this.props ) ) {
641
640
  this.props.triggerReflow();
642
641
  }
@@ -677,9 +676,9 @@ class Edit extends Component {
677
676
  'show-image': showImage,
678
677
  [ `columns-${ columns }` ]: postLayout === 'grid',
679
678
  [ `colgap-${ colGap }` ]: postLayout === 'grid',
680
- [ `ts-${ typeScale }` ]: typeScale !== '5',
679
+ [ `ts-${ typeScale }` ]: typeScale !== 5,
681
680
  [ `image-align${ mediaPosition }` ]: showImage,
682
- [ `is-${ imageScale }` ]: imageScale !== '1' && showImage,
681
+ [ `is-${ imageScale }` ]: imageScale !== 1 && showImage,
683
682
  'mobile-stack': mobileStack,
684
683
  [ `is-${ imageShape }` ]: showImage,
685
684
  'has-text-color': textColor.color !== '',
@@ -769,7 +768,7 @@ class Edit extends Component {
769
768
  <div>
770
769
  { latestPosts && ( ! RichText.isEmpty( sectionHeader ) || isSelected ) && (
771
770
  <RichText
772
- onChange={ value => setAttributes( { sectionHeader: value } ) }
771
+ onChange={ ( value: string ) => setAttributes( { sectionHeader: value } ) }
773
772
  placeholder={ __( 'Write header…', 'newspack-blocks' ) }
774
773
  value={ sectionHeader }
775
774
  tagName="h2"
@@ -802,7 +801,7 @@ class Edit extends Component {
802
801
  <RichText
803
802
  placeholder={ __( 'Load more posts', 'newspack-blocks' ) }
804
803
  value={ moreButtonText }
805
- onChange={ value => setAttributes( { moreButtonText: value } ) }
804
+ onChange={ ( value: string ) => setAttributes( { moreButtonText: value } ) }
806
805
  className="wp-block-button__link"
807
806
  allowedFormats={ [] }
808
807
  />
@@ -814,7 +813,7 @@ class Edit extends Component {
814
813
  <Toolbar>
815
814
  <AlignmentControl
816
815
  value={ textAlign }
817
- onChange={ nextAlign => {
816
+ onChange={ ( nextAlign: string ) => {
818
817
  setAttributes( { textAlign: nextAlign } );
819
818
  } }
820
819
  />
@@ -831,6 +830,8 @@ class Edit extends Component {
831
830
 
832
831
  export default compose( [
833
832
  withColors( { textColor: 'color' } ),
833
+ // @ts-ignore
834
834
  withSelect( postsBlockSelector ),
835
+ // @ts-ignore
835
836
  withDispatch( postsBlockDispatch ),
836
- ] )( Edit );
837
+ ] as any )( Edit );
@@ -235,12 +235,13 @@ call_user_func(
235
235
  ),
236
236
  )
237
237
  ),
238
- esc_attr( get_the_date( DATE_W3C ) ),
239
- esc_html( get_the_date() ),
238
+ esc_attr( Newspack_Blocks::get_displayed_post_date() ),
239
+ esc_html( Newspack_Blocks::get_formatted_displayed_post_date() ),
240
240
  esc_attr( get_the_modified_date( DATE_W3C ) ),
241
241
  esc_html( get_the_modified_date() )
242
242
  );
243
243
  endif;
244
+ echo Newspack_Blocks::get_article_meta_footer(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
244
245
  ?>
245
246
  </div><!-- .entry-meta -->
246
247
  <?php endif; ?>