Skip to content

Follow Me: Make it interactive! #1691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f0eb94a
Follow Me: Make it interactive!
obenland May 10, 2025
8508ac4
Remove i18n dependency from view
obenland May 10, 2025
2c46a44
Account for button size setting
obenland May 10, 2025
02ef722
Move most block attributes to context
obenland May 10, 2025
79367f3
More updates
obenland May 10, 2025
47134bc
Minor style adjustments
obenland May 10, 2025
42b2ab6
Remove old follow-me.js
obenland May 10, 2025
36cb8b8
update
obenland May 11, 2025
facbe31
Various style fixes
obenland May 11, 2025
e932d1e
Reuse state
obenland May 11, 2025
0b54a7c
Better default profile
obenland May 11, 2025
c24710c
Make things less complicated
obenland May 11, 2025
4a0dcbd
Keep focus in the modal
obenland May 11, 2025
b8f2c24
Save dev
obenland May 11, 2025
6e0a3db
Some more sassiness
obenland May 11, 2025
a3b11ad
Bump version and add preview
obenland May 11, 2025
9357304
Add changelog
matticbot May 11, 2025
e286654
Update styles to better reflect existing design
obenland May 11, 2025
016167b
Follow Me: Use button block (#1692)
obenland May 12, 2025
2ce8bdc
Merge branch 'trunk' into try/follow-me-interactivity
obenland May 15, 2025
83c7b95
Update build
obenland May 15, 2025
b98e0a9
Bail if there is no actor
obenland May 15, 2025
9a2dca8
Account for inherit user
obenland May 15, 2025
f2580e4
Align styles better with trunk
obenland May 15, 2025
4ebcd1a
Small style update
obenland May 15, 2025
994af6e
More style updates
obenland May 15, 2025
2923839
Keep modal color separate
obenland May 15, 2025
b276af0
Make dependency on apiFetch explicit
obenland May 15, 2025
f293b20
More standardized buttons and fonts
obenland May 15, 2025
6a33da1
Minor style and focus improvements
obenland May 15, 2025
f86bc55
Consolidate directions callback
obenland May 19, 2025
15c0875
Add dark mode styles.
obenland May 19, 2025
c6b54f2
Rebuild build files
obenland May 19, 2025
6d85d64
Tabs > Spaces
obenland May 19, 2025
0f202b4
Merge branch 'trunk' into try/follow-me-interactivity
obenland May 19, 2025
9a9490c
Tabs > Spaces
obenland May 19, 2025
dfee0d2
Improve migration
obenland May 19, 2025
e64e458
Cache computed styles
obenland May 19, 2025
f605d81
Genuinely my best good faith effort to make colors work across themes.
obenland May 20, 2025
8a703a3
Merge branch 'trunk' into try/follow-me-interactivity
obenland May 20, 2025
efb0a99
Make webfinger input readonly so it can be highlighted
obenland May 20, 2025
0061fc3
Accept webfingers without leading `@`
obenland May 20, 2025
f84b8be
Add placeholder styles
obenland May 20, 2025
374c0e2
Add min-width to button
obenland May 20, 2025
7ff16aa
Remove any errors when closing modal
obenland May 20, 2025
6495e81
Fix a few more theme styles
obenland May 20, 2025
7b977cc
Fix close button in 2021
obenland May 20, 2025
8b2e47b
Give close button the theme treatment
obenland May 20, 2025
631ec1b
Delete plugin.js.map
obenland May 20, 2025
11c4231
Move trapFocus in utils
obenland May 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/1691-from-description
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: major
Type: changed

The Follow Me block now uses the latest Block Editor technology for display on the frontend.
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ vendor

# Temporary ignores while breaking out each component.
assets
src/follow-me
src/followers
src/reactions
src/remote-reply
Expand Down
35 changes: 16 additions & 19 deletions build/follow-me/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"name": "activitypub/follow-me",
"apiVersion": 3,
"version": "1.0.0",
"version": "2.0.0",
"title": "Follow me on the Fediverse",
"category": "widgets",
"description": "Display your Fediverse profile so that visitors can follow you.",
"textdomain": "activitypub",
"icon": "groups",
"example": {
"attributes": {
"buttonOnly": false
}
},
"supports": {
"html": false,
"interactivity": true,
"color": {
"gradients": true,
"link": true,
Expand All @@ -30,6 +36,11 @@
"__experimentalDefaultControls": {
"fontSize": true
}
},
"innerBlocks": {
"allowedBlocks": [
"core/button"
]
}
},
"attributes": {
Expand All @@ -40,29 +51,15 @@
"buttonOnly": {
"type": "boolean",
"default": false
},
"buttonText": {
"type": "string",
"default": "Follow"
},
"buttonSize": {
"type": "string",
"default": "default",
"enum": [
"small",
"default",
"compact"
]
}
},
"usesContext": [
"postType",
"postId"
],
"editorScript": "file:./index.js",
"viewScript": "file:./view.js",
"style": [
"file:./style-view.css",
"wp-components"
]
"viewScriptModule": "file:./view.js",
"viewScript": "wp-api-fetch",
"style": "file:./style-view.css",
"render": "file:./render.php"
}
2 changes: 1 addition & 1 deletion build/follow-me/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'd69f8905fbe5ea6410fa');
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '49624f0cc66c91bddf95');
5 changes: 2 additions & 3 deletions build/follow-me/index.js

Large diffs are not rendered by default.

210 changes: 210 additions & 0 deletions build/follow-me/render.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php
/**
* Server-side rendering of the `activitypub/follow-me` block.
*
* @package ActivityPub
*/

use Activitypub\Blocks;
use Activitypub\Collection\Actors;

/* @var array $attributes Block attributes. */
$attributes = wp_parse_args( $attributes );

// Get the user ID from the selected user attribute.
$selected_user = $attributes['selectedUser'] ?? 'site';
$user_id = Blocks::get_user_id( $selected_user );
$button_only = $attributes['buttonOnly'] ?? false;

// Generate a unique ID for the block.
$block_id = 'activitypub-follow-me-block-' . wp_unique_id();

// Get block style information.
$style = wp_get_global_styles();
$background_color = $attributes['backgroundColor'] ?? $style['color']['background'] ?? '';

// Get button style from block attributes.
$button_style = $attributes['style'] ?? array();

$actor = Actors::get_by_id( $user_id );
if ( is_wp_error( $actor ) ) {
return;
}

// Set up the Interactivity API state.
$state = wp_interactivity_state(
'activitypub/follow-me',
array(
'namespace' => ACTIVITYPUB_REST_NAMESPACE,
'i18n' => array(
'copied' => __( 'Copied!', 'activitypub' ),
'copy' => __( 'Copy', 'activitypub' ),
'emptyProfileError' => __( 'Please enter a profile URL or handle.', 'activitypub' ),
'invalidProfileError' => __( 'Please enter a valid URL or handle.', 'activitypub' ),
'genericError' => __( 'An error occurred. Please try again.', 'activitypub' ),
),
)
);

// Add the block wrapper attributes.
$wrapper_attributes = get_block_wrapper_attributes(
array(
'id' => $block_id,
'class' => 'activitypub-follow-me-block-wrapper',
'data-wp-interactive' => 'activitypub/follow-me',
'data-wp-init' => 'callbacks.initButtonStyles',
'data-wp-on-document--keydown' => 'callbacks.documentKeydown',
'data-wp-on-document--click' => 'callbacks.documentClick',
)
);

$wrapper_context = wp_interactivity_data_wp_context(
array(
'blockId' => $block_id,
'isModalOpen' => false,
'remoteProfile' => '',
'isLoading' => false,
'isError' => false,
'errorMessage' => '',
'copyButtonText' => $state['i18n']['copy'],
'userId' => $user_id,
'buttonOnly' => $button_only,
'buttonStyle' => $button_style,
'backgroundColor' => $background_color,
'webfinger' => '@' . $actor->get_webfinger(),
)
);

/* @var string $content Inner blocks content. */
if ( empty( $content ) ) {
$button_text = $attributes['buttonText'] ?? __( 'Follow', 'activitypub' );
$content = '<div class="wp-block-button"><button class="wp-block-button__link wp-element-button">' . esc_html( $button_text ) . '</button></div>';
}
$content = Blocks::add_directions(
$content,
array( 'class_name' => 'wp-element-button' ),
array(
'data-wp-on--click' => 'actions.toggleModal',
'data-wp-bind--aria-expanded' => 'context.isModalOpen',
'aria-label' => __( 'Follow me on the Fediverse', 'activitypub' ),
'aria-haspopup' => 'dialog',
'aria-controls' => 'modal-heading',
)
);

?>
<div
<?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput ?>
<?php echo $wrapper_context; // phpcs:ignore WordPress.Security.EscapeOutput ?>
>
<div class="activitypub-profile">
<?php if ( ! $button_only ) : ?>
<img
class="activitypub-profile__avatar"
src="<?php echo esc_url( $actor->get_icon()['url'] ); ?>"
alt="<?php echo esc_attr( $actor->get_name() ); ?>"
/>
<div class="activitypub-profile__content">
<div class="activitypub-profile__name"><?php echo esc_html( $actor->get_name() ); ?></div>
<div class="activitypub-profile__handle"><?php echo esc_html( '@' . $actor->get_webfinger() ); ?></div>
</div>
<?php endif; ?>

<?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput ?>
</div>

<div
class="activitypub-modal__overlay"
data-wp-bind--hidden="!context.isModalOpen"
role="dialog"
aria-modal="true"
aria-labelledby="modal-heading"
>
<div class="activitypub-modal__frame">
<div class="activitypub-modal__header">
<h2 id="modal-heading" class="activitypub-modal__title">
<?php
printf(
/* translators: %s: Profile name. */
esc_html__( 'Follow %s', 'activitypub' ),
esc_html( $actor->get_name() )
);
?>
</h2>
<button
type="button"
class="activitypub-modal__close wp-element-button wp-block-button__link"
data-wp-on--click="actions.closeModal"
aria-label="<?php echo esc_attr__( 'Close dialog', 'activitypub' ); ?>"
>
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" role="img" aria-hidden="true" focusable="false">
<path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path>
</svg>
</button>
</div>
<div class="activitypub-modal__content">
<div class="activitypub-dialog__section">
<h4><?php echo esc_html__( 'My Profile', 'activitypub' ); ?></h4>
<div class="activitypub-dialog__description">
<?php echo esc_html__( 'Copy and paste my profile into the search field of your favorite fediverse app or server.', 'activitypub' ); ?>
</div>
<div class="activitypub-dialog__button-group">
<input
type="text"
id="profile-handle"
value="<?php echo esc_attr( '@' . $actor->get_webfinger() ); ?>"
tabindex="-1"
readonly
aria-readonly="true"
/>
<button
type="button"
class="wp-element-button wp-block-button__link"
data-wp-on--click="actions.copyToClipboard"
aria-label="<?php echo esc_attr__( 'Copy handle to clipboard', 'activitypub' ); ?>"
>
<span data-wp-text="context.copyButtonText"></span>
</button>
</div>
</div>
<div class="activitypub-dialog__section">
<h4><?php echo esc_html__( 'Your Profile', 'activitypub' ); ?></h4>
<div class="activitypub-dialog__description">
<?php echo esc_html__( 'Or, if you know your own profile, we can start things that way!', 'activitypub' ); ?>
</div>
<div class="activitypub-dialog__button-group">
<input
type="text"
id="remote-profile"
placeholder="<?php echo esc_attr__( '@[email protected]', 'activitypub' ); ?>"
data-wp-bind--value="context.remoteProfile"
data-wp-on--input="actions.updateRemoteProfile"
data-wp-on--keydown="actions.handleKeyDown"
data-wp-bind--aria-invalid="context.isError"
/>
<button
type="button"
class="wp-element-button wp-block-button__link"
data-wp-on--click="actions.submitRemoteProfile"
aria-label="<?php echo esc_attr__( 'Follow', 'activitypub' ); ?>"
data-wp-bind--disabled="context.isLoading"
>
<span data-wp-bind--hidden="context.isLoading">
<?php echo esc_html__( 'Follow', 'activitypub' ); ?>
</span>
<span data-wp-bind--hidden="!context.isLoading">
<?php echo esc_html__( 'Loading...', 'activitypub' ); ?>
</span>
</button>
</div>
<div
class="activitypub-dialog__error"
data-wp-bind--hidden="!context.isError"
data-wp-text="context.errorMessage"
></div>
</div>
</div>
</div>
</div>
</div>
<?php
1 change: 0 additions & 1 deletion build/follow-me/style-view-rtl.css

This file was deleted.

2 changes: 1 addition & 1 deletion build/follow-me/style-view.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/follow-me/view.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '4e430690e282cfe013db');
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '66f75ff35a5e5e082c49', 'type' => 'module');
Loading
Loading