Index: package.json
===================================================================
--- package.json	(revision 7139)
+++ package.json	(working copy)
@@ -27,6 +27,7 @@
 		"grunt-wp-i18n": "~1.0.3",
 		"matchdep": "~2.0.0",
 		"node-sass": "~4.14.1",
+		"parcel-bundler": "^1.10.1",
 		"postcss-scss": "~1.0.2",
 		"stylelint": "~13.6.1",
 		"stylelint-config-wordpress": "~15.0.0"
@@ -35,6 +36,9 @@
 		"node": ">=12.0.0",
 		"npm": ">=6.9.0"
 	},
+	"scripts": {
+		"blocks": "parcel build src/includes/admin/assets/js/blocks.jsx --out-dir src/includes/admin/assets/js --public-url ."
+	},
 	"keywords": [
 		"community",
 		"forums",
Index: src/bbpress.php
===================================================================
--- src/bbpress.php	(revision 7139)
+++ src/bbpress.php	(working copy)
@@ -340,6 +340,7 @@
 		require $this->includes_dir . 'common/template.php';
 		require $this->includes_dir . 'common/widgets.php';
 		require $this->includes_dir . 'common/shortcodes.php';
+		require $this->includes_dir . 'common/blocks.php';
 
 		// Forums
 		require $this->includes_dir . 'forums/capabilities.php';
@@ -521,6 +522,7 @@
 				'show_in_nav_menus'   => true,
 				'public'              => true,
 				'show_ui'             => current_user_can( 'bbp_forums_admin' ),
+				'show_in_rest'        => true,
 				'can_export'          => true,
 				'hierarchical'        => true,
 				'query_var'           => true,
@@ -734,12 +736,13 @@
 	}
 
 	/**
-	 * Register the bbPress shortcodes
+	 * Register the bbPress shortcodes and blocks
 	 *
 	 * @since 2.0.0 bbPress (r3031)
 	 */
 	public function register_shortcodes() {
 		$this->shortcodes = new BBP_Shortcodes();
+		$this->blocks = new BBP_Blocks( $this->shortcodes );
 	}
 
 	/**
Index: src/includes/admin/assets/css/blocks.css
===================================================================
--- src/includes/admin/assets/css/blocks.css	(nonexistent)
+++ src/includes/admin/assets/css/blocks.css	(working copy)
@@ -0,0 +1,11 @@
+
+.bbpress-block {
+	background: #fff;
+	border: 1px solid #888;
+	padding: 1em 2em;
+}
+
+.bbpress-block h3 {
+	color: #444;
+	cursor: default;
+}

Property changes on: src/includes/admin/assets/css/blocks.css
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/includes/admin/assets/js/blocks.js
===================================================================
--- src/includes/admin/assets/js/blocks.js	(nonexistent)
+++ src/includes/admin/assets/js/blocks.js	(working copy)
@@ -0,0 +1,4 @@
+parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c<t.length;c++)try{f(t[c])}catch(e){i||(i=e)}if(t.length){var l=f(t[t.length-1]);"object"==typeof exports&&"undefined"!=typeof module?module.exports=l:"function"==typeof define&&define.amd?define(function(){return l}):n&&(this[n]=l)}if(parcelRequire=f,i)throw i;return f}({"aE2q":[function(require,module,exports) {
+var e=wp.blocks.registerBlockType,t=wp.components.SelectControl,n=wp.i18n.__;e("bbpress/forum-index",{title:n("Forums List"),icon:"buddicons-bbpress-logo",category:"common",attributes:{},edit:function(){return React.createElement("div",{class:"bbpress-block"},React.createElement("h3",null,n("bbPress Forum Index")))},save:function(){return null}}),e("bbpress/forum",{title:n("Single Forum"),icon:"buddicons-bbpress-logo",category:"common",attributes:{id:{type:"integer",default:0}},edit:function(e){return React.createElement("div",{class:"bbpress-block"},React.createElement("h3",null,n("bbPress Single Forum")),React.createElement(t,{label:n("Forum"),labelPosition:"top",value:e.attributes.id,options:bbpBlocks.data.forums,onChange:function(t){e.setAttributes({id:t})}}))},save:function(){return null}});
+},{}]},{},["aE2q"], null)
+//# sourceMappingURL=blocks.js.map
\ No newline at end of file

Property changes on: src/includes/admin/assets/js/blocks.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/includes/admin/assets/js/blocks.js.map
===================================================================
--- src/includes/admin/assets/js/blocks.js.map	(nonexistent)
+++ src/includes/admin/assets/js/blocks.js.map	(working copy)
@@ -0,0 +1 @@
+{"version":3,"sources":["blocks.jsx"],"names":["registerBlockType","wp","blocks","SelectControl","components","__","i18n","title","icon","category","attributes","edit","save","id","type","default","props","bbpBlocks","data","forums","handleForumChange","setAttributes"],"mappings":";AAGCA,IAAAA,EACGC,GAAGC,OADNF,kBAIAG,EACGF,GAAGG,WADND,cAIAE,EACGJ,GAAGK,KADND,GAIDL,EAAmB,sBAAuB,CACzCO,MAAOF,EAAI,eACXG,KAAM,yBACNC,SAAU,SAEVC,WAAY,GAEZC,KAAM,WAEJ,OAAA,MAAA,cAAA,MAAA,CAAK,MAAM,iBACV,MAAMN,cAAAA,KAAAA,KAAAA,EAAI,0BAKbO,KAAM,WAAM,OAAA,QAObZ,EAAmB,gBAAiB,CACnCO,MAAOF,EAAI,gBACXG,KAAM,yBACNC,SAAU,SAEVC,WAAY,CACXG,GAAI,CACHC,KAAM,UACNC,QAAS,IAIXJ,KAAM,SAAUK,GAMd,OAAA,MAAA,cAAA,MAAA,CAAK,MAAM,iBACV,MAAMX,cAAAA,KAAAA,KAAAA,EAAI,yBACV,MAAC,cAAA,EAAD,CACC,MAAQA,EAAI,SACZ,cAAc,MACd,MAAQW,EAAMN,WAAWG,GACzB,QAAUI,UAAUC,KAAKC,OACzB,SAZMC,SAAmBP,GAC3BG,EAAMK,cAAc,CAAER,GAAAA,SAiBxBD,KAAM,WAAM,OAAA","file":"blocks.js","sourceRoot":"","sourcesContent":["/* global bbpBlocks */\n\nconst {\n\tregisterBlockType\n} = wp.blocks;\n\nconst {\n\tSelectControl\n} = wp.components;\n\nconst {\n\t__\n} = wp.i18n;\n\n// Replaces [bbp-forum-index] – This will display your entire forum index.\nregisterBlockType( 'bbpress/forum-index', {\n\ttitle: __( 'Forums List' ),\n\ticon: 'buddicons-bbpress-logo',\n\tcategory: 'common',\n\n\tattributes: {},\n\n\tedit: function() {\n\t\treturn (\n\t\t\t<div class=\"bbpress-block\">\n\t\t\t\t<h3>{ __( 'bbPress Forum Index' ) }</h3>\n\t\t\t</div>\n\t\t);\n\t},\n\n\tsave: () => null\n} );\n\n// Replaces [bbp-forum-form] – Display the ‘New Forum’ form.\n\n\n// Replaces [bbp-single-forum id=$forum_id] – Display a single forums topics. eg. [bbp-single-forum id=32]\nregisterBlockType( 'bbpress/forum', {\n\ttitle: __( 'Single Forum' ),\n\ticon: 'buddicons-bbpress-logo',\n\tcategory: 'common',\n\n\tattributes: {\n\t\tid: {\n\t\t\ttype: 'integer',\n\t\t\tdefault: 0,\n\t\t}\n\t},\n\n\tedit: function( props ) {\n\t\tfunction handleForumChange( id ) {\n\t\t\tprops.setAttributes({ id })\n\t\t}\n\n\t\treturn (\n\t\t\t<div class=\"bbpress-block\">\n\t\t\t\t<h3>{ __( 'bbPress Single Forum' ) }</h3>\n\t\t\t\t<SelectControl\n\t\t\t\t\tlabel={ __( 'Forum' ) }\n\t\t\t\t\tlabelPosition=\"top\"\n\t\t\t\t\tvalue={ props.attributes.id }\n\t\t\t\t\toptions={ bbpBlocks.data.forums }\n\t\t\t\t\tonChange={ handleForumChange }\n\t\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t},\n\n\tsave: () => null\n\n} );\n\n// Topics\n// Replaces [bbp-topic-index] – Display the most recent 15 topics across all your forums with pagination.\n// Replaces [bbp-topic-form] – Display the ‘New Topic’ form where you can choose from a drop down menu the forum that this topic is to be associated with.\n// Replaces [bbp-topic-form forum_id=$forum_id] – Display the ‘New Topic Form’ for a specific forum ID.\n// Replaces [bbp-single-topic id=$topic_id] – Display a single topic. eg. [bbp-single-topic id=4096]\n\n// Replies\n// Replaces [bbp-reply-form] – Display the ‘New Reply’ form.\n// Replaces [bbp-single-reply id=$reply_id] – Display a single reply eg. [bbp-single-reply id=32768]\n\n// Topic Tags\n// Replaces [bbp-topic-tags] – Display a tag cloud of all topic tags.\n// Replaces [bbp-single-tag id=$tag_id] – Display a list of all topics associated with a specific tag. eg. [bbp-single-tag id=64]\n\n// Views\n// Replaces [bbp-single-view] – Single view – Display topics associated with a specific view. Current included ‘views’ with bbPress are “popular” [bbp-single-view id=’popular’] and “No Replies” [bbp-single-view id=’no-replies’]\n\n// Search\n// Replaces [bbp-search] – Display the search input form.\n// Replaces [bbp-search-form] – Display the search form template.\n\n// Account\n// Replaces [bbp-login] – Display the login screen.\n// Replaces [bbp-register] – Display the register screen.\n// Replaces [bbp-lost-pass] – Display the lost password screen.\n\n// Statistics\n// Replaces [bbp-stats] – Display the forum statistics.\n\n\n"]}
\ No newline at end of file
Index: src/includes/admin/assets/js/blocks.jsx
===================================================================
--- src/includes/admin/assets/js/blocks.jsx	(nonexistent)
+++ src/includes/admin/assets/js/blocks.jsx	(working copy)
@@ -0,0 +1,102 @@
+/* global bbpBlocks */
+
+const {
+	registerBlockType
+} = wp.blocks;
+
+const {
+	SelectControl
+} = wp.components;
+
+const {
+	__
+} = wp.i18n;
+
+// Replaces [bbp-forum-index] – This will display your entire forum index.
+registerBlockType( 'bbpress/forum-index', {
+	title: __( 'Forums List' ),
+	icon: 'buddicons-bbpress-logo',
+	category: 'common',
+
+	attributes: {},
+
+	edit: function() {
+		return (
+			<div class="bbpress-block">
+				<h3>{ __( 'bbPress Forum Index' ) }</h3>
+			</div>
+		);
+	},
+
+	save: () => null
+} );
+
+// Replaces [bbp-forum-form] – Display the ‘New Forum’ form.
+
+
+// Replaces [bbp-single-forum id=$forum_id] – Display a single forums topics. eg. [bbp-single-forum id=32]
+registerBlockType( 'bbpress/forum', {
+	title: __( 'Single Forum' ),
+	icon: 'buddicons-bbpress-logo',
+	category: 'common',
+
+	attributes: {
+		id: {
+			type: 'integer',
+			default: 0,
+		}
+	},
+
+	edit: function( props ) {
+		function handleForumChange( id ) {
+			props.setAttributes({ id })
+		}
+
+		return (
+			<div class="bbpress-block">
+				<h3>{ __( 'bbPress Single Forum' ) }</h3>
+				<SelectControl
+					label={ __( 'Forum' ) }
+					labelPosition="top"
+					value={ props.attributes.id }
+					options={ bbpBlocks.data.forums }
+					onChange={ handleForumChange }
+				/>
+			</div>
+		);
+	},
+
+	save: () => null
+
+} );
+
+// Topics
+// Replaces [bbp-topic-index] – Display the most recent 15 topics across all your forums with pagination.
+// Replaces [bbp-topic-form] – Display the ‘New Topic’ form where you can choose from a drop down menu the forum that this topic is to be associated with.
+// Replaces [bbp-topic-form forum_id=$forum_id] – Display the ‘New Topic Form’ for a specific forum ID.
+// Replaces [bbp-single-topic id=$topic_id] – Display a single topic. eg. [bbp-single-topic id=4096]
+
+// Replies
+// Replaces [bbp-reply-form] – Display the ‘New Reply’ form.
+// Replaces [bbp-single-reply id=$reply_id] – Display a single reply eg. [bbp-single-reply id=32768]
+
+// Topic Tags
+// Replaces [bbp-topic-tags] – Display a tag cloud of all topic tags.
+// Replaces [bbp-single-tag id=$tag_id] – Display a list of all topics associated with a specific tag. eg. [bbp-single-tag id=64]
+
+// Views
+// Replaces [bbp-single-view] – Single view – Display topics associated with a specific view. Current included ‘views’ with bbPress are “popular” [bbp-single-view id=’popular’] and “No Replies” [bbp-single-view id=’no-replies’]
+
+// Search
+// Replaces [bbp-search] – Display the search input form.
+// Replaces [bbp-search-form] – Display the search form template.
+
+// Account
+// Replaces [bbp-login] – Display the login screen.
+// Replaces [bbp-register] – Display the register screen.
+// Replaces [bbp-lost-pass] – Display the lost password screen.
+
+// Statistics
+// Replaces [bbp-stats] – Display the forum statistics.
+
+
Index: src/includes/common/blocks.php
===================================================================
--- src/includes/common/blocks.php	(nonexistent)
+++ src/includes/common/blocks.php	(working copy)
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * bbPress Blocks
+ *
+ * @package bbPress
+ * @subpackage Blocks
+ */
+
+// Exit if accessed directly
+defined( 'ABSPATH' ) || exit;
+
+if ( ! class_exists( 'BBP_Blocks' ) ) :
+/**
+ * bbPress Shortcode Class
+ *
+ * @since 2.0.0 bbPress (r3031)
+ */
+class BBP_Blocks {
+
+	/** Vars ******************************************************************/
+
+	/**
+	 * @var BBP_Shortcodes Most of our blocks are just visual representations of existing shortcodes.
+	 */
+	public $shortcodes;
+
+	/** Functions *************************************************************/
+
+	/**
+	 * Set up the blocks for the Block Editor.
+	 */
+	public function __construct( BBP_Shortcodes $shortcodes = NULL ) {
+		$this->shortcodes = $shortcodes;
+		$this->register_blocks();
+	}
+
+	public function register_blocks() {
+		if ( ! function_exists( 'register_block_type' ) ) {
+			return;
+		}
+
+		wp_register_style( 'bbp-blocks', plugins_url( '../admin/assets/css/blocks.css', __FILE__ ) );
+
+		wp_register_script( 'bbp-blocks', plugins_url( '../admin/assets/js/blocks.js', __FILE__ ), [ 'wp-blocks', 'wp-components', 'wp-i18n' ], filemtime( __DIR__ . '/../admin/assets/js/blocks.js' ) );
+		wp_localize_script( 'bbp-blocks', 'bbpBlocks', array(
+			'data' => array(
+				'forum_post_type' => bbp_get_forum_post_type(),
+				'forums' => self::get_localize_script_data( 'forums' ),
+			)
+		) );
+
+		// Note: While not ideal, we're storing all of our block js in the same file.  Fewer assets to load, but
+		// it does mean that if the block is deregistered in php, it will still exist in js in the block editor.
+
+		register_block_type( 'bbpress/forum-index', array(
+			'render_callback' => array( $this, 'display_forum_index' ),
+			'editor_script'   => 'bbp-blocks',
+			'editor_style'    => 'bbp-blocks',
+		) );
+
+		register_block_type( 'bbpress/forum', array(
+			'render_callback' => array( $this, 'display_forum' ),
+			'editor_script'   => 'bbp-blocks',
+			'editor_style'    => 'bbp-blocks',
+		) );
+	}
+
+	public static function get_localize_script_data( $data ) {
+		switch( $data ) {
+			case 'forums':
+				$forums = get_pages( array(
+					'post_type'   => bbp_get_forum_post_type(),
+					'numberposts' => -1,
+					'post_status' => array( 'publish', 'private' ),
+				) );
+
+				$return = array( array( 'value' => 0, 'label' => __( '« Select a Forum »' ) ) );
+				foreach ( $forums as $forum ) {
+					$return[] = array(
+						'value' => (int) $forum->ID,
+						'label' => $forum->post_title,
+					);
+				}
+				return $return;
+				break;
+			default:
+				return null;
+		}
+	}
+
+	/**
+	 * Passthrough function for `display_forum_index` -- the forum list view.
+	 *
+	 * @return string The markup for the forum list view.
+	 */
+	public function display_forum_index() {
+		return $this->shortcodes->display_forum_index();
+	}
+
+	/**
+	 * Passthrough function for `display_forum` -- the single forum view.
+	 *
+	 * @param $attributes (array) An array with -- at minimum -- an `id` key set to the forum id.
+	 * @return string The markup for the single forum.
+	 */
+	public function display_forum( $attributes ) {
+		// If for some reason there isn't a forum id, just display it all.
+		if ( empty( $attributes['id'] ) ) {
+			return $this->display_forum_index();
+		}
+
+		return $this->shortcodes->display_forum( $attributes );
+	}
+
+}
+endif;
\ No newline at end of file

Property changes on: src/includes/common/blocks.php
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
