null,
'displayStyle' => $default_display_style,
'showActions' => false,
)
);
/* @var \WP_Block $block Current block. */
$block = $block ?? '';
/* @var string $content Block content. */
$content = $content ?? '';
if ( empty( $content ) ) {
// Fallback for v1.0.0 blocks.
$_title = $attributes['title'] ?? __( 'Fediverse Reactions', 'activitypub' );
$content = '
' . esc_html( $_title ) . '
';
unset( $attributes['title'] );
} else {
$content = implode( PHP_EOL, wp_list_pluck( $block->parsed_block['innerBlocks'], 'innerHTML' ) );
// Hide empty headings.
if ( empty( wp_strip_all_tags( $content ) ) ) {
$content = '';
}
}
// Get the Post ID from attributes or use the current post.
$_post_id = $attributes['postId'] ?? get_the_ID();
// Don't leak reaction metadata for posts that are not currently publicly queryable.
if ( ! is_post_publicly_queryable( $_post_id ) ) {
return;
}
// Generate a unique ID for the block.
$block_id = 'activitypub-reactions-block-' . wp_unique_id();
/*
* Determine display style - compact style hides avatars.
* For auto-hooked blocks without explicit style, use avatar setting to determine style.
*/
$has_style_class = isset( $attributes['className'] ) && strpos( $attributes['className'], 'is-style-' ) !== false;
if ( ! $has_style_class ) {
$attributes['className'] = trim( ( $attributes['className'] ?? '' ) . ' is-style-' . $default_display_style );
$attributes['displayStyle'] = $default_display_style;
}
$show_avatars = 'facepile' === $attributes['displayStyle'];
// Fetch reactions.
$reactions = array();
foreach ( Comment::get_comment_types() as $_type => $type_object ) {
$_comments = get_comments(
array(
'post_id' => $_post_id,
'type' => $_type,
'status' => 'approve',
'parent' => 0,
)
);
if ( empty( $_comments ) ) {
continue;
}
$count = count( $_comments );
// phpcs:disable WordPress.WP.I18n
$label = sprintf(
_n(
$type_object['count_single'],
$type_object['count_plural'],
$count,
'activitypub'
),
number_format_i18n( $count )
);
// phpcs:enable WordPress.WP.I18n
$reactions[ $_type ] = array(
'label' => $label,
'count' => $count,
'items' => array_map(
static function ( $comment ) {
return array(
'name' => html_entity_decode( $comment->comment_author ),
'url' => $comment->comment_author_url,
'avatar' => get_avatar_url( $comment ),
);
},
$_comments
),
);
}
if ( empty( $reactions ) && ! $attributes['showActions'] ) {
echo '';
return;
}
// Set up the Interactivity API config.
$config = array(
'defaultAvatarUrl' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
'namespace' => ACTIVITYPUB_REST_NAMESPACE,
);
if ( $attributes['showActions'] ) {
$config['i18n'] = array(
'copied' => __( 'Copied!', 'activitypub' ),
'copy' => __( 'Copy', 'activitypub' ),
'emptyProfileError' => __( 'Please enter a profile URL or handle.', 'activitypub' ),
'genericError' => __( 'An error occurred. Please try again.', 'activitypub' ),
'intentLabelLike' => __( 'Like this post', 'activitypub' ),
'intentLabelAnnounce' => __( 'Boost this post', 'activitypub' ),
'invalidProfileError' => __( 'Please enter a valid profile URL or handle.', 'activitypub' ),
);
}
wp_interactivity_config( 'activitypub/reactions', $config );
// Set up the Interactivity API state.
wp_interactivity_state( 'activitypub/reactions', array( 'reactions' => array( $_post_id => $reactions ) ) );
// Render a subset of the most recent reactions for facepile.
$reactions = array_map(
static function ( $reaction ) use ( $attributes ) {
$count = 20;
if ( 'wide' === $attributes['align'] ) {
$count = 40;
} elseif ( 'full' === $attributes['align'] ) {
$count = 60;
}
$reaction['items'] = array_slice( array_reverse( $reaction['items'] ), 0, $count );
return $reaction;
},
$reactions
);
// Initialize the context for the block.
$context = array(
'blockId' => $block_id,
'modal' => array(
'isCompact' => true,
'isOpen' => false,
'items' => array(),
'title' => '',
),
'postId' => $_post_id,
'reactions' => $reactions,
);
if ( $attributes['showActions'] ) {
$context['modal']['intent'] = '';
$context['copyButtonText'] = __( 'Copy', 'activitypub' );
$context['errorMessage'] = '';
$context['isError'] = false;
$context['isLoading'] = false;
$context['postUrl'] = get_post_id( $_post_id );
$context['remoteProfile'] = '';
$context['shouldSaveProfile'] = true;
}
// Map comment types to remote intent types.
$intent_map = array(
'like' => 'like',
'repost' => 'announce',
'quote' => 'announce',
);
// Build reactions content.
ob_start();
?>
$reaction ) :
/* translators: %s: reaction type. */
$aria_label = sprintf( __( 'View all %s', 'activitypub' ), Comment::get_comment_type_attr( $_type, 'label' ) );
$intent = isset( $intent_map[ $_type ] ) ? $intent_map[ $_type ] : '';
?>
$intent ) : ?>
$modal_content,
);
if ( $attributes['showActions'] ) {
$modal_args['title_binding'] = 'context.modal.title';
} else {
$modal_args['is_compact'] = true;
}
ob_start();
Blocks::render_modal( $modal_args );
$inner_content = $reactions_content . ob_get_clean();
$wrapper_attrs = array(
'id' => $block_id,
'class' => $attributes['className'] ?? '',
'data-wp-interactive' => 'activitypub/reactions',
'data-wp-context' => wp_json_encode( $context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ),
'data-wp-init' => 'callbacks.initReactions',
);
$wrapper_attributes = get_block_wrapper_attributes( $wrapper_attrs );
// Render the block with common wrapper.
?>
>