Changeset 8711
- Timestamp:
- 05/01/2019 10:10:19 PM (6 years ago)
- Location:
- sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/block-content.js
r8668 r8711 2 2 * External dependencies 3 3 */ 4 import { get } from 'lodash';4 import { get } from 'lodash'; 5 5 import classnames from 'classnames'; 6 6 … … 9 9 */ 10 10 const { Component } = wp.element; 11 const { __ } = wp.i18n;11 const { __ } = wp.i18n; 12 12 13 13 /** 14 14 * Internal dependencies 15 15 */ 16 import {ItemTitle, ItemHTMLContent, ItemPermalink} from '../shared/block-content'; 17 import { tokenSplit, arrayTokenReplace, intersperse, listify } from '../shared/i18n'; 18 import GridContentLayout from '../shared/grid-layout/block-content'; 19 import FeaturedImage from '../shared/featured-image'; 20 16 import { ItemTitle, ItemHTMLContent, ItemPermalink, BlockNoContent } from '../shared/block-content'; 17 import { tokenSplit, arrayTokenReplace, intersperse, listify } from '../shared/i18n'; 18 import GridContentLayout from '../shared/grid-layout/block-content'; 19 import FeaturedImage from '../shared/featured-image'; 20 import { filterEntities } from '../blocks-store'; 21 22 /** 23 * Component for the section of each session post that displays information about the session's speakers. 24 * 25 * @param {Object} session 26 * 27 * @return {Element} 28 */ 21 29 function SessionSpeakers( { session } ) { 22 30 let speakerData = get( session, '_embedded.speakers', [] ); 23 31 24 32 speakerData = speakerData.map( ( speaker ) => { 25 const { link = '' } = speaker; 26 let { title = {} } = speaker; 33 if ( speaker.hasOwnProperty( 'code' ) ) { 34 // The wporg username given for this speaker returned an error. 35 return null; 36 } 37 38 const { link } = speaker; 39 let { title = {} } = speaker; 27 40 28 41 title = title.rendered.trim() || __( 'Unnamed', 'wordcamporg' ); … … 33 46 34 47 return ( 35 <a 36 key={ link } 37 href={ link } 38 > 48 <a key={ link } href={ link }> 39 49 { title } 40 50 </a> … … 55 65 } 56 66 67 /** 68 * Component for the section of each session post that displays metadata including date, time, and location (track). 69 * 70 * @param {Object} session 71 * 72 * @return {Element} 73 */ 57 74 function SessionMeta( { session } ) { 58 75 let metaContent; … … 98 115 } 99 116 117 /** 118 * Component for the section of each session post that displays a session's assigned categories. 119 * 120 * @param {Object} session 121 * 122 * @return {Element} 123 */ 100 124 function SessionCategory( { session } ) { 101 125 let categoryContent; … … 130 154 } 131 155 156 /** 157 * Component for displaying the block content. 158 */ 132 159 class SessionsBlockContent extends Component { 133 hasSpeaker( session ) { 160 /** 161 * Run additional operations during component initialization. 162 * 163 * @param {Object} props 164 */ 165 constructor( props ) { 166 super( props ); 167 168 this.getFilteredPosts = this.getFilteredPosts.bind( this ); 169 } 170 171 /** 172 * Determine if a session has related speaker data. 173 * 174 * @param {Object} session 175 * 176 * @return {boolean} 177 */ 178 static hasSpeaker( session ) { 134 179 return get( session, '_embedded.speakers', [] ).length > 0; 135 180 } 136 181 182 /** 183 * Filter and sort the content that will be rendered. 184 * 185 * @returns {Array} 186 */ 187 getFilteredPosts() { 188 const { attributes, entities } = this.props; 189 const { wcb_session: posts } = entities; 190 const { mode, item_ids, sort } = attributes; 191 192 const args = {}; 193 194 if ( Array.isArray( item_ids ) && item_ids.length > 0 ) { 195 let fieldName; 196 197 switch ( mode ) { 198 case 'wcb_session': 199 fieldName = 'id'; 200 break; 201 case 'wcb_track': 202 fieldName = 'session_track'; 203 break; 204 case 'wcb_session_category': 205 fieldName = 'session_category'; 206 break; 207 } 208 209 args.filter = [ 210 { 211 fieldName : fieldName, 212 fieldValue : item_ids, 213 }, 214 ]; 215 } 216 217 if ( 'session_time' !== sort ) { 218 args.sort = sort; 219 } 220 221 let filtered = filterEntities( posts, args ); 222 223 if ( Array.isArray( filtered ) && 'session_time' === sort ) { 224 filtered = filtered.sort( ( a, b ) => { 225 return Number( a.meta._wcpt_session_time ) - Number( b.meta._wcpt_session_time ); 226 } ); 227 } 228 229 return filtered; 230 } 231 232 /** 233 * Render the block content. 234 * 235 * @return {Element} 236 */ 137 237 render() { 138 const { attributes , sessionPosts} = this.props;238 const { attributes } = this.props; 139 239 const { show_speaker, show_images, image_align, featured_image_width, content, show_meta, show_category } = attributes; 240 241 const posts = this.getFilteredPosts(); 242 const isLoading = ! Array.isArray( posts ); 243 const hasPosts = ! isLoading && posts.length > 0; 244 245 if ( isLoading || ! hasPosts ) { 246 return ( 247 <BlockNoContent loading={ isLoading } /> 248 ); 249 } 140 250 141 251 return ( … … 144 254 { ...this.props } 145 255 > 146 { sessionPosts.map( ( post ) =>256 { posts.map( ( post ) => 147 257 <div 148 258 key={ post.slug } … … 161 271 /> 162 272 163 { show_speaker && this. hasSpeaker( post ) &&273 { show_speaker && this.constructor.hasSpeaker( post ) && 164 274 <SessionSpeakers session={ post } /> 165 275 } -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/block-controls.js
r8526 r8711 8 8 */ 9 9 const { Button, Placeholder } = wp.components; 10 const { __ } = wp.i18n;10 const { __ } = wp.i18n; 11 11 12 12 /** 13 13 * Internal dependencies 14 14 */ 15 import { BlockControls, Placeholder NoContent, PlaceholderSpecificMode } from '../shared/block-controls';16 import SessionsBlockContent from './block-content';17 import SessionsSelect from './sessions-select';18 import { LABEL } 15 import { BlockControls, PlaceholderSpecificMode } from '../shared/block-controls'; 16 import SessionsBlockContent from './block-content'; 17 import SessionsSelect from './sessions-select'; 18 import { LABEL } from './index'; 19 19 20 /** 21 * Component for displaying a UI within the block. 22 */ 20 23 class SessionsBlockControls extends BlockControls { 24 /** 25 * Render the internal block UI. 26 * 27 * @return {Element} 28 */ 21 29 render() { 22 const { icon, attributes, setAttributes , sessionPosts} = this.props;30 const { icon, attributes, setAttributes } = this.props; 23 31 const { mode } = attributes; 24 25 const hasPosts = Array.isArray( sessionPosts ) && sessionPosts.length;26 27 if ( mode && ! hasPosts ) {28 return (29 <PlaceholderNoContent30 icon={ icon }31 label={ LABEL }32 loading={ ! Array.isArray( sessionPosts ) }33 />34 );35 }36 32 37 33 let output; -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/edit.js
r8601 r8711 1 /**2 * External dependencies3 */4 import { isUndefined, pickBy, split } from 'lodash';5 6 1 /** 7 2 * WordPress dependencies 8 3 */ 9 const apiFetch = wp.apiFetch; 10 const { withSelect } = wp.data; 4 const { withSelect } = wp.data; 11 5 const { Component, Fragment } = wp.element; 12 const { addQueryArgs } = wp.url;13 6 14 7 /** 15 8 * Internal dependencies 16 9 */ 17 import SessionsBlockControls from './block-controls';10 import SessionsBlockControls from './block-controls'; 18 11 import SessionsInspectorControls from './inspector-controls'; 19 import GridToolbar from '../shared/grid-layout/toolbar';12 import GridToolbar from '../shared/grid-layout/toolbar'; 20 13 import { ICON } from './index'; 14 import { WC_BLOCKS_STORE } from '../blocks-store'; 21 15 22 16 const blockData = window.WordCampBlocks.sessions || {}; 23 const MAX_POSTS = 100;24 17 25 const ALL_POSTS_QUERY = { 26 orderby : 'title', 27 order : 'asc', 28 per_page : MAX_POSTS, 29 _embed : true, 30 }; 31 32 const ALL_TERMS_QUERY = { 33 orderby : 'name', 34 order : 'asc', 35 per_page : MAX_POSTS, 36 }; 37 18 /** 19 * Top-level component for the editing UI for the block. 20 */ 38 21 class SessionsEdit extends Component { 39 constructor( props ) { 40 super( props ); 41 42 this.fetchSessionDetails(); 43 } 44 45 fetchSessionDetails() { 46 const allSessionPosts = apiFetch( { 47 path: addQueryArgs( `/wp/v2/sessions`, ALL_POSTS_QUERY ), 48 } ); 49 const allSessionTracks = apiFetch( { 50 path: addQueryArgs( `/wp/v2/session_track`, ALL_TERMS_QUERY ), 51 } ); 52 const allSessionCategories = apiFetch( { 53 path: addQueryArgs( `/wp/v2/session_category`, ALL_TERMS_QUERY ), 54 } ); 55 56 this.state = { 57 allSessionPosts: allSessionPosts, // Promise 58 allSessionTracks: allSessionTracks, // Promise 59 allSessionCategories: allSessionCategories, // Promise 60 } 61 } 62 22 /** 23 * Render the block's editing UI. 24 * 25 * @return {Element} 26 */ 63 27 render() { 64 28 const { mode } = this.props.attributes; … … 69 33 icon={ ICON } 70 34 { ...this.props } 71 { ...this.state }72 35 /> 73 36 { mode && 74 <Fragment>75 <SessionsInspectorControls { ...this.props } />76 <GridToolbar { ...this.props } />77 </Fragment>37 <Fragment> 38 <SessionsInspectorControls { ...this.props } /> 39 <GridToolbar { ...this.props } /> 40 </Fragment> 78 41 } 79 42 </Fragment> … … 82 45 } 83 46 84 const sessionsSelect = ( select, props ) => { 85 const { mode, item_ids, sort } = props.attributes; 86 const { getEntityRecords } = select( 'core' ); 47 const sessionsSelect = ( select ) => { 48 const { getEntities } = select( WC_BLOCKS_STORE ); 87 49 88 const args = { 89 per_page : MAX_POSTS, // -1 is not allowed for per_page. 90 _embed : true, 91 context : 'view', 92 _wcpt_session_type : 'session', 50 const entities = { 51 wcb_session : getEntities( 'postType', 'wcb_session', { _embed: true } ), 52 wcb_track : getEntities( 'taxonomy', 'wcb_track' ), 53 wcb_session_category : getEntities( 'taxonomy', 'wcb_session_category' ), 93 54 }; 94 55 95 if ( 'session_time' !== sort ) { 96 const [ orderby, order ] = split( sort, '_', 2 ); 97 args.orderby = orderby; 98 args.order = order; 99 } 100 101 if ( Array.isArray( item_ids ) ) { 102 switch ( mode ) { 103 case 'wcb_session': 104 args.include = item_ids; 105 break; 106 case 'wcb_track': 107 args.session_track = item_ids; 108 break; 109 case 'wcb_session_category': 110 args.session_category = item_ids; 111 break; 112 } 113 } 114 115 const sessionsQuery = pickBy( args, ( value ) => ! isUndefined( value ) ); 116 117 const sessionPosts = getEntityRecords( 'postType', 'wcb_session', sessionsQuery ); 118 119 // todo Is there a way to do this sorting via REST API parameters? 120 if ( Array.isArray( sessionPosts ) && 'session_time' === sort ) { 121 sessionPosts.sort( ( a, b ) => { 122 return Number( a.meta._wcpt_session_time ) - Number( b.meta._wcpt_session_time ); 123 } ); 124 } 125 126 return { blockData, sessionPosts }; 56 return { 57 blockData, 58 entities, 59 }; 127 60 }; 128 61 -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/index.js
r8627 r8711 14 14 15 15 const supports = { 16 'align': [ 'wide', 'full' ],16 align: [ 'wide', 'full' ], 17 17 }; 18 18 -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/inspector-controls.js
r8647 r8711 10 10 * Internal dependencies 11 11 */ 12 import GridInspectorControl from '../shared/grid-layout/inspector-control';12 import GridInspectorControl from '../shared/grid-layout/inspector-control'; 13 13 import FeaturedImageInspectorControls from '../shared/featured-image/inspector-control'; 14 14 15 /** 16 * Component for block controls that appear in the Inspector Panel. 17 */ 15 18 class SessionsInspectorControls extends Component { 19 /** 20 * Render the controls. 21 * 22 * @return {Element} 23 */ 16 24 render() { 17 25 const { attributes, setAttributes, blockData } = this.props; -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/assets/src/sessions/sessions-select.js
r8633 r8711 2 2 * External dependencies 3 3 */ 4 import { get, includes } from 'lodash';4 import { every, flatMap, includes } from 'lodash'; 5 5 6 6 /** 7 7 * WordPress dependencies 8 8 */ 9 const { Dashicon } = wp.components;10 9 const { Component } = wp.element; 11 const { __ } = wp.i18n;10 const { __ } = wp.i18n; 12 11 13 12 /** 14 13 * Internal dependencies 15 14 */ 16 import ItemSelect from '../shared/item-select';15 import ItemSelect, { buildOptions, Option } from '../shared/item-select'; 17 16 17 /** 18 * Component for selecting posts/terms for populating the block content. 19 */ 18 20 class SessionsSelect extends Component { 21 /** 22 * Run additional operations during component initialization. 23 * 24 * @param {Object} props 25 */ 19 26 constructor( props ) { 20 27 super( props ); 21 28 22 this.state = { 23 wcb_session : [], 24 wcb_track : [], 25 wcb_session_category : [], 26 loading : true, 27 }; 28 29 this.buildSelectOptions = this.buildSelectOptions.bind( this ); 30 this.fetchSelectOptions( props ); 29 this.buildSelectOptions = this.buildSelectOptions.bind( this ); 30 this.getCurrentSelectValue = this.getCurrentSelectValue.bind( this ); 31 this.isLoading = this.isLoading.bind( this ); 31 32 } 32 33 33 fetchSelectOptions( props ) { 34 const { allSessionPosts, allSessionTracks, allSessionCategories } = props; 35 const promises = []; 34 /** 35 * Build or retrieve the options that will populate the Select dropdown. 36 * 37 * @return {Array} 38 */ 39 buildSelectOptions() { 40 const { entities } = this.props; 41 const { wcb_session, wcb_track, wcb_session_category } = entities; 36 42 37 promises.push( allSessionPosts.then( 38 ( fetchedPosts ) => { 39 const posts = fetchedPosts.map( ( post ) => { 40 const image = get( post, '_embedded[\'wp:featuredmedia\'].media_details.sizes.thumbnail.source_url', '' ); 43 const optionGroups = [ 44 { 45 entityType : 'post', 46 type : 'wcb_session', 47 label : __( 'Sessions', 'wordcamporg' ), 48 items : wcb_session, 49 }, 50 { 51 entityType : 'term', 52 type : 'wcb_track', 53 label : __( 'Tracks', 'wordcamporg' ), 54 items : wcb_track, 55 }, 56 { 57 entityType : 'term', 58 type : 'wcb_session_category', 59 label : __( 'Session Categories', 'wordcamporg' ), 60 items : wcb_session_category, 61 }, 62 ]; 41 63 42 return { 43 label : post.title.rendered.trim() || __( '(Untitled)', 'wordcamporg' ), 44 value : post.id, 45 type : 'wcb_session', 46 image : image, 47 }; 48 } ); 49 50 this.setState( { wcb_session: posts } ); 51 } 52 ).catch() ); 53 54 [ allSessionTracks, allSessionCategories ].forEach( ( promise ) => { 55 promises.push( promise.then( 56 ( fetchedTerms ) => { 57 const terms = fetchedTerms.map( ( term ) => { 58 return { 59 label : term.name.trim() || __( '(Untitled)', 'wordcamporg' ), 60 value : term.id, 61 type : term.taxonomy, 62 count : term.count || 0, 63 }; 64 } ); 65 66 const [ firstTerm ] = terms; 67 this.setState( { [ firstTerm.type ]: terms } ); 68 } 69 ).catch() ); 70 } ); 71 72 Promise.all( promises ).then( () => { 73 this.setState( { loading: false } ); 74 } ); 64 return buildOptions( optionGroups ); 75 65 } 76 66 77 buildSelectOptions( mode ) { 78 const { getOwnPropertyDescriptors } = Object; 79 const options = []; 67 /** 68 * Determine the currently selected options in the Select dropdown based on block attributes. 69 * 70 * @return {Array} 71 */ 72 getCurrentSelectValue() { 73 const { attributes } = this.props; 74 const { mode, item_ids } = attributes; 80 75 81 const labels = { 82 wcb_session : __( 'Sessions', 'wordcamporg' ), 83 wcb_track : __( 'Tracks', 'wordcamporg' ), 84 wcb_session_category : __( 'Session Categories', 'wordcamporg' ), 85 }; 86 87 for ( const type in getOwnPropertyDescriptors( this.state ) ) { 88 if ( ( ! mode || type === mode ) && this.state[ type ].length ) { 89 options.push( { 90 label : labels[ type ], 91 options : this.state[ type ], 92 } ); 93 } 94 } 95 96 return options; 97 } 98 99 render() { 100 const { icon, label, attributes, setAttributes } = this.props; 101 const { mode, item_ids } = attributes; 102 const options = this.buildSelectOptions( mode ); 76 const options = flatMap( this.buildSelectOptions(), ( group ) => { 77 return group.options; 78 } ); 103 79 104 80 let value = []; 105 81 106 82 if ( mode && item_ids.length ) { 107 const modeOptions = get( options, '[0].options', [] ); 108 109 value = modeOptions.filter( ( option ) => { 110 return includes( item_ids, option.value ); 83 value = options.filter( ( option ) => { 84 return mode === option.type && includes( item_ids, option.value ); 111 85 } ); 112 86 } 87 88 return value; 89 } 90 91 /** 92 * Check if all of the entity groups have finished loading. 93 * 94 * @return {boolean} 95 */ 96 isLoading() { 97 const { entities } = this.props; 98 99 return ! every( entities, ( value ) => { 100 return Array.isArray( value ); 101 } ); 102 } 103 104 /** 105 * Render an ItemSelect component with block-specific settings. 106 * 107 * @return {Element} 108 */ 109 render() { 110 const { icon, label, setAttributes } = this.props; 113 111 114 112 return ( … … 116 114 className="wordcamp-sessions-select" 117 115 label={ label } 118 value={ value } 119 buildSelectOptions={ this.buildSelectOptions } 116 value={ this.getCurrentSelectValue() } 120 117 onChange={ ( changed ) => setAttributes( changed ) } 121 mode={ mode }122 118 selectProps={ { 123 isLoading : this.state.loading, 124 formatGroupLabel : ( groupData ) => { 119 options : this.buildSelectOptions(), 120 isLoading : this.isLoading(), 121 formatOptionLabel : ( optionData ) => { 125 122 return ( 126 <span className="wordcamp-item-select-option-group-label"> 127 { groupData.label } 128 </span> 129 ); 130 }, 131 formatOptionLabel: ( optionData ) => { 132 return ( 133 <SessionsOption 134 icon={ icon } 123 <Option 124 icon={ includes( [ 'wcb_track', 'wcb_session_category' ], optionData.type ) ? icon : null } 135 125 { ...optionData } 136 126 /> … … 143 133 } 144 134 145 function SessionsOption( { type, icon, label = '', image = '', count = 0 } ) {146 let optImage, optContent;147 148 switch ( type ) {149 case 'wcb_session' :150 if ( image ) {151 optImage = (152 <img153 className="wordcamp-item-select-option-image"154 src={ image }155 alt={ label }156 width={ 24 }157 height={ 24 }158 />159 );160 } else {161 optImage = (162 <div className="wordcamp-item-select-option-icon-container">163 <Dashicon164 className="wordcamp-item-select-option-icon"165 icon={ icon }166 size={ 16 }167 />168 </div>169 );170 }171 optContent = (172 <span className="wordcamp-item-select-option-label">173 { label }174 </span>175 );176 break;177 178 case 'wcb_track' :179 case 'wcb_session_category' :180 optImage = (181 <div className="wordcamp-item-select-option-icon-container">182 <Dashicon183 className="wordcamp-item-select-option-icon"184 icon={ icon }185 size={ 16 }186 />187 </div>188 );189 optContent = (190 <span className="wordcamp-item-select-option-label">191 { label }192 <span className="wordcamp-item-select-option-label-term-count">193 { count }194 </span>195 </span>196 );197 break;198 }199 200 return (201 <div className="wordcamp-item-select-option">202 { optImage }203 { optContent }204 </div>205 );206 }207 208 135 export default SessionsSelect; -
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/blocks/includes/sessions.php
r8647 r8711 112 112 ), 113 113 [ 114 'align' => get_shared_definition( 'align_block', 'attribute' ), 114 115 'className' => get_shared_definition( 'string_empty', 'attribute' ), 115 116 'featured_image_width' => array( … … 217 218 */ 218 219 function get_session_posts( array $attributes ) { 220 if ( empty( $attributes['mode'] ) ) { 221 return []; 222 } 223 219 224 $post_args = [ 220 225 'post_type' => 'wcb_session',
Note: See TracChangeset
for help on using the changeset viewer.