scaffolding and first pass for the Follow Me block

This commit is contained in:
Matt Wiebe 2023-08-09 21:44:46 -05:00
parent fcc9603920
commit 773abd38d9
9 changed files with 187 additions and 25 deletions

View file

@ -32,6 +32,12 @@ class Blocks {
'render_callback' => array( self::class, 'render_follower_block' ), 'render_callback' => array( self::class, 'render_follower_block' ),
) )
); );
\register_block_type_from_metadata(
ACTIVITYPUB_PLUGIN_DIR . '/build/follow-me',
array(
'render_callback' => array( self::class, 'render_follow_me_block' ),
)
);
} }
private static function get_user_id( $user_string ) { private static function get_user_id( $user_string ) {
@ -42,6 +48,10 @@ class Blocks {
return 0; return 0;
} }
public static function render_follow_me_block( $attrs, $content, $block ) {
return '<div class="lol">lol!!!!</div>';
}
public static function render_follower_block( $attrs, $content, $block ) { public static function render_follower_block( $attrs, $content, $block ) {
$followee_user_id = self::get_user_id( $attrs['selectedUser'] ); $followee_user_id = self::get_user_id( $attrs['selectedUser'] );
$per_page = absint( $attrs['per_page'] ); $per_page = absint( $attrs['per_page'] );

View file

@ -21,7 +21,7 @@ class Blog_User extends User {
* *
* @var string * @var string
*/ */
protected $type = 'Group'; protected $type = 'Foooob/';
/** /**
* Is Account discoverable? * Is Account discoverable?

23
src/follow-me/block.json Normal file
View file

@ -0,0 +1,23 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"name": "activitypub/follow-me",
"apiVersion": 3,
"version": "1.0.0",
"title": "Follow me on the Fediverse",
"category": "widgets",
"description": "Display your Fediverse identifier so that visitors can follow you.",
"textdomain": "activitypub",
"icon": "groups",
"supports": {
"html": false
},
"attributes": {
"selectedUser": {
"type": "string",
"default": "site"
}
},
"editorScript": "file:./index.js",
"viewScript": "file:./view.js",
"style": "file:./style-index.css"
}

98
src/follow-me/edit.js Normal file
View file

@ -0,0 +1,98 @@
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { SelectControl, PanelBody, Button } from '@wordpress/components';
import { useUserOptions } from '../shared/use-user-options';
import apiFetch from '@wordpress/api-fetch';
import { useEffect, useState } from '@wordpress/element';
const { namespace } = window._activityPubOptions;
import './style.scss';
function fetchProfile( userId ) {
const fetchOptions = {
headers: { Accept: 'application/activity+json' },
path: `/${ namespace }/users/${ userId }`,
};
return apiFetch( fetchOptions );
}
const DEFAULT_PROFILE_DATA = {
avatar: '',
handle: '@well@hello.dolly',
name: __( 'Hello Dolly Fan Account', 'fediverse' ),
url: '#',
};
function getNormalizedProfile( profile ) {
if ( ! profile ) {
return DEFAULT_PROFILE_DATA;
}
profile.handle = generateHandle( profile );
const data = { ...DEFAULT_PROFILE_DATA, ...profile };
data.avatar = data.icon.url;
return data;
}
function generateHandle( profile ) {
try {
const urlObject = new URL( profile.url );
const { host, pathname } = urlObject;
const first = pathname.replace( /^\//, '' );
return `${ first}@${ host }`;
} catch ( e ) {
return '@error@error';
}
}
export default function Edit( { attributes, setAttributes } ) {
const [ profile, setProfile ] = useState( getNormalizedProfile() );
const { selectedUser } = attributes;
const userId = selectedUser === 'site' ? 0 : selectedUser;
function setProfileData( profile ) {
setProfile( getNormalizedProfile( profile ) );
}
useEffect( () => {
fetchProfile( userId ).then( setProfileData );
}, [ userId ] );
const blockProps = useBlockProps();
const usersOptions = useUserOptions();
return (
<div { ...blockProps }>
<InspectorControls key="setting">
<PanelBody title={ __( 'Followers Options', 'activitypub' ) }>
<SelectControl
label= { __( 'Select User', 'activitypub' ) }
value={ selectedUser }
options={ usersOptions }
onChange={ ( value ) => setAttributes( { selectedUser: value } ) }
/>
</PanelBody>
</InspectorControls>
<Profile { ...profile } />
</div>
);
}
function Profile( profile ) {
const { handle, avatar, name } = profile;
return (
<div className="activitypub-profile">
<img className="activitypub-profile__avatar" src={ avatar } />
<div className="activitypub-profile__content">
<div className="activitpub-profile__name">{ name }</div>
<div className="activitypub-profile__handle">{ handle }</div>
</div>
<Follow profile={ profile } />
</div>
);
}
function Follow( { profile } ) {
return (
<Button className="activitypub-profile__follow" isPrimary onClick={ () => console.log( profile ) } >
{ __( 'Follow', 'fediverse' ) }
</Button>
);
}

5
src/follow-me/index.js Normal file
View file

@ -0,0 +1,5 @@
import { registerBlockType } from '@wordpress/blocks';
import { people } from '@wordpress/icons';
import edit from './edit';
const save = () => null;
registerBlockType( 'activitypub/follow-me', { edit, save, icon: people } );

23
src/follow-me/style.scss Normal file
View file

@ -0,0 +1,23 @@
.editor-styles-wrapper {
.activitypub-profile {
display: flex;
align-items: self-start;
border: 1px solid;
border-radius: 4px;
padding: 1rem;
.activitypub-profile__avatar {
height: 75px;
width: 75px;
margin-right: 1rem;
}
.activitypub-profile__name {
margin: 0;
line-height: 1;
}
.activitypub-profile__follow {
margin-left: auto;
align-self: center;
}
}
}

1
src/follow-me/view.js Normal file
View file

@ -0,0 +1 @@
console.log( 'view' );

View file

@ -1,31 +1,9 @@
import { SelectControl, RangeControl, PanelBody, TextControl } from '@wordpress/components'; import { SelectControl, RangeControl, PanelBody, TextControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data'; import { useState } from '@wordpress/element';
import { useMemo, useState } from '@wordpress/element';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Followers } from './followers'; import { Followers } from './followers';
import { useUserOptions } from '../shared/use-user-options';
const enabled = window._activityPubOptions?.enabled;
function useUserOptions() {
const users = enabled?.users ? useSelect( ( select ) => select( 'core' ).getUsers( { who: 'authors' } ) ) : [];
return useMemo( () => {
if ( ! users ) {
return [];
}
const withBlogUser = enabled?.site ? [ {
label: __( 'Whole Site', 'activitypub' ),
value: 'site'
} ] : [];
return users.reduce( ( acc, user ) => {
acc.push({
label: user.name,
value: user.id
} );
return acc;
}, withBlogUser );
}, [ users ] );
}
export default function Edit( { attributes, setAttributes } ) { export default function Edit( { attributes, setAttributes } ) {
const { order, per_page, selectedUser, title } = attributes; const { order, per_page, selectedUser, title } = attributes;

View file

@ -0,0 +1,24 @@
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
const enabled = window._activityPubOptions?.enabled;
export function useUserOptions() {
const users = enabled?.users ? useSelect( ( select ) => select( 'core' ).getUsers( { who: 'authors' } ) ) : [];
return useMemo( () => {
if ( ! users ) {
return [];
}
const withBlogUser = enabled?.site ? [ {
label: __( 'Whole Site', 'activitypub' ),
value: 'site'
} ] : [];
return users.reduce( ( acc, user ) => {
acc.push({
label: user.name,
value: user.id
} );
return acc;
}, withBlogUser );
}, [ users ] );
}