Skip to content

Commit a29a16e

Browse files
authored
Follow Me: Make it interactive! (#1691)
1 parent 79b9b35 commit a29a16e

24 files changed

+1343
-454
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: major
2+
Type: changed
3+
4+
The Follow Me block now uses the latest Block Editor technology for display on the frontend.

.prettierignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ vendor
55

66
# Temporary ignores while breaking out each component.
77
assets
8-
src/follow-me
98
src/followers
109
src/reactions
1110
src/remote-reply

build/follow-me/block.json

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22
"$schema": "https://schemas.wp.org/trunk/block.json",
33
"name": "activitypub/follow-me",
44
"apiVersion": 3,
5-
"version": "1.0.0",
5+
"version": "2.0.0",
66
"title": "Follow me on the Fediverse",
77
"category": "widgets",
88
"description": "Display your Fediverse profile so that visitors can follow you.",
99
"textdomain": "activitypub",
1010
"icon": "groups",
11+
"example": {
12+
"attributes": {
13+
"buttonOnly": false
14+
}
15+
},
1116
"supports": {
1217
"html": false,
18+
"interactivity": true,
1319
"color": {
1420
"gradients": true,
1521
"link": true,
@@ -30,6 +36,11 @@
3036
"__experimentalDefaultControls": {
3137
"fontSize": true
3238
}
39+
},
40+
"innerBlocks": {
41+
"allowedBlocks": [
42+
"core/button"
43+
]
3344
}
3445
},
3546
"attributes": {
@@ -40,29 +51,15 @@
4051
"buttonOnly": {
4152
"type": "boolean",
4253
"default": false
43-
},
44-
"buttonText": {
45-
"type": "string",
46-
"default": "Follow"
47-
},
48-
"buttonSize": {
49-
"type": "string",
50-
"default": "default",
51-
"enum": [
52-
"small",
53-
"default",
54-
"compact"
55-
]
5654
}
5755
},
5856
"usesContext": [
5957
"postType",
6058
"postId"
6159
],
6260
"editorScript": "file:./index.js",
63-
"viewScript": "file:./view.js",
64-
"style": [
65-
"file:./style-view.css",
66-
"wp-components"
67-
]
61+
"viewScriptModule": "file:./view.js",
62+
"viewScript": "wp-api-fetch",
63+
"style": "file:./style-view.css",
64+
"render": "file:./render.php"
6865
}

build/follow-me/index.asset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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');
1+
<?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');

build/follow-me/index.js

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/follow-me/render.php

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
<?php
2+
/**
3+
* Server-side rendering of the `activitypub/follow-me` block.
4+
*
5+
* @package ActivityPub
6+
*/
7+
8+
use Activitypub\Blocks;
9+
use Activitypub\Collection\Actors;
10+
11+
/* @var array $attributes Block attributes. */
12+
$attributes = wp_parse_args( $attributes );
13+
14+
// Get the user ID from the selected user attribute.
15+
$selected_user = $attributes['selectedUser'] ?? 'site';
16+
$user_id = Blocks::get_user_id( $selected_user );
17+
$button_only = $attributes['buttonOnly'] ?? false;
18+
19+
// Generate a unique ID for the block.
20+
$block_id = 'activitypub-follow-me-block-' . wp_unique_id();
21+
22+
// Get block style information.
23+
$style = wp_get_global_styles();
24+
$background_color = $attributes['backgroundColor'] ?? $style['color']['background'] ?? '';
25+
26+
// Get button style from block attributes.
27+
$button_style = $attributes['style'] ?? array();
28+
29+
$actor = Actors::get_by_id( $user_id );
30+
if ( is_wp_error( $actor ) ) {
31+
return;
32+
}
33+
34+
// Set up the Interactivity API state.
35+
$state = wp_interactivity_state(
36+
'activitypub/follow-me',
37+
array(
38+
'namespace' => ACTIVITYPUB_REST_NAMESPACE,
39+
'i18n' => array(
40+
'copied' => __( 'Copied!', 'activitypub' ),
41+
'copy' => __( 'Copy', 'activitypub' ),
42+
'emptyProfileError' => __( 'Please enter a profile URL or handle.', 'activitypub' ),
43+
'invalidProfileError' => __( 'Please enter a valid URL or handle.', 'activitypub' ),
44+
'genericError' => __( 'An error occurred. Please try again.', 'activitypub' ),
45+
),
46+
)
47+
);
48+
49+
// Add the block wrapper attributes.
50+
$wrapper_attributes = get_block_wrapper_attributes(
51+
array(
52+
'id' => $block_id,
53+
'class' => 'activitypub-follow-me-block-wrapper',
54+
'data-wp-interactive' => 'activitypub/follow-me',
55+
'data-wp-init' => 'callbacks.initButtonStyles',
56+
'data-wp-on-document--keydown' => 'callbacks.documentKeydown',
57+
'data-wp-on-document--click' => 'callbacks.documentClick',
58+
)
59+
);
60+
61+
$wrapper_context = wp_interactivity_data_wp_context(
62+
array(
63+
'blockId' => $block_id,
64+
'isModalOpen' => false,
65+
'remoteProfile' => '',
66+
'isLoading' => false,
67+
'isError' => false,
68+
'errorMessage' => '',
69+
'copyButtonText' => $state['i18n']['copy'],
70+
'userId' => $user_id,
71+
'buttonOnly' => $button_only,
72+
'buttonStyle' => $button_style,
73+
'backgroundColor' => $background_color,
74+
'webfinger' => '@' . $actor->get_webfinger(),
75+
)
76+
);
77+
78+
/* @var string $content Inner blocks content. */
79+
if ( empty( $content ) ) {
80+
$button_text = $attributes['buttonText'] ?? __( 'Follow', 'activitypub' );
81+
$content = '<div class="wp-block-button"><button class="wp-block-button__link wp-element-button">' . esc_html( $button_text ) . '</button></div>';
82+
}
83+
$content = Blocks::add_directions(
84+
$content,
85+
array( 'class_name' => 'wp-element-button' ),
86+
array(
87+
'data-wp-on--click' => 'actions.toggleModal',
88+
'data-wp-bind--aria-expanded' => 'context.isModalOpen',
89+
'aria-label' => __( 'Follow me on the Fediverse', 'activitypub' ),
90+
'aria-haspopup' => 'dialog',
91+
'aria-controls' => 'modal-heading',
92+
)
93+
);
94+
95+
?>
96+
<div
97+
<?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput ?>
98+
<?php echo $wrapper_context; // phpcs:ignore WordPress.Security.EscapeOutput ?>
99+
>
100+
<div class="activitypub-profile">
101+
<?php if ( ! $button_only ) : ?>
102+
<img
103+
class="activitypub-profile__avatar"
104+
src="<?php echo esc_url( $actor->get_icon()['url'] ); ?>"
105+
alt="<?php echo esc_attr( $actor->get_name() ); ?>"
106+
/>
107+
<div class="activitypub-profile__content">
108+
<div class="activitypub-profile__name"><?php echo esc_html( $actor->get_name() ); ?></div>
109+
<div class="activitypub-profile__handle"><?php echo esc_html( '@' . $actor->get_webfinger() ); ?></div>
110+
</div>
111+
<?php endif; ?>
112+
113+
<?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput ?>
114+
</div>
115+
116+
<div
117+
class="activitypub-modal__overlay"
118+
data-wp-bind--hidden="!context.isModalOpen"
119+
role="dialog"
120+
aria-modal="true"
121+
aria-labelledby="modal-heading"
122+
>
123+
<div class="activitypub-modal__frame">
124+
<div class="activitypub-modal__header">
125+
<h2 id="modal-heading" class="activitypub-modal__title">
126+
<?php
127+
printf(
128+
/* translators: %s: Profile name. */
129+
esc_html__( 'Follow %s', 'activitypub' ),
130+
esc_html( $actor->get_name() )
131+
);
132+
?>
133+
</h2>
134+
<button
135+
type="button"
136+
class="activitypub-modal__close wp-element-button wp-block-button__link"
137+
data-wp-on--click="actions.closeModal"
138+
aria-label="<?php echo esc_attr__( 'Close dialog', 'activitypub' ); ?>"
139+
>
140+
<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">
141+
<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>
142+
</svg>
143+
</button>
144+
</div>
145+
<div class="activitypub-modal__content">
146+
<div class="activitypub-dialog__section">
147+
<h4><?php echo esc_html__( 'My Profile', 'activitypub' ); ?></h4>
148+
<div class="activitypub-dialog__description">
149+
<?php echo esc_html__( 'Copy and paste my profile into the search field of your favorite fediverse app or server.', 'activitypub' ); ?>
150+
</div>
151+
<div class="activitypub-dialog__button-group">
152+
<input
153+
type="text"
154+
id="profile-handle"
155+
value="<?php echo esc_attr( '@' . $actor->get_webfinger() ); ?>"
156+
tabindex="-1"
157+
readonly
158+
aria-readonly="true"
159+
/>
160+
<button
161+
type="button"
162+
class="wp-element-button wp-block-button__link"
163+
data-wp-on--click="actions.copyToClipboard"
164+
aria-label="<?php echo esc_attr__( 'Copy handle to clipboard', 'activitypub' ); ?>"
165+
>
166+
<span data-wp-text="context.copyButtonText"></span>
167+
</button>
168+
</div>
169+
</div>
170+
<div class="activitypub-dialog__section">
171+
<h4><?php echo esc_html__( 'Your Profile', 'activitypub' ); ?></h4>
172+
<div class="activitypub-dialog__description">
173+
<?php echo esc_html__( 'Or, if you know your own profile, we can start things that way!', 'activitypub' ); ?>
174+
</div>
175+
<div class="activitypub-dialog__button-group">
176+
<input
177+
type="text"
178+
id="remote-profile"
179+
placeholder="<?php echo esc_attr__( '@[email protected]', 'activitypub' ); ?>"
180+
data-wp-bind--value="context.remoteProfile"
181+
data-wp-on--input="actions.updateRemoteProfile"
182+
data-wp-on--keydown="actions.handleKeyDown"
183+
data-wp-bind--aria-invalid="context.isError"
184+
/>
185+
<button
186+
type="button"
187+
class="wp-element-button wp-block-button__link"
188+
data-wp-on--click="actions.submitRemoteProfile"
189+
aria-label="<?php echo esc_attr__( 'Follow', 'activitypub' ); ?>"
190+
data-wp-bind--disabled="context.isLoading"
191+
>
192+
<span data-wp-bind--hidden="context.isLoading">
193+
<?php echo esc_html__( 'Follow', 'activitypub' ); ?>
194+
</span>
195+
<span data-wp-bind--hidden="!context.isLoading">
196+
<?php echo esc_html__( 'Loading...', 'activitypub' ); ?>
197+
</span>
198+
</button>
199+
</div>
200+
<div
201+
class="activitypub-dialog__error"
202+
data-wp-bind--hidden="!context.isError"
203+
data-wp-text="context.errorMessage"
204+
></div>
205+
</div>
206+
</div>
207+
</div>
208+
</div>
209+
</div>
210+
<?php

build/follow-me/style-view-rtl.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

build/follow-me/style-view.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/follow-me/view.asset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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');
1+
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '66f75ff35a5e5e082c49', 'type' => 'module');

0 commit comments

Comments
 (0)