Skip to content

Commit

Permalink
Refactor Gist component to hooks (#1859)
Browse files Browse the repository at this point in the history
  • Loading branch information
olafleur committed Apr 8, 2021
1 parent 9b8ac4c commit 558bf47
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 95 deletions.
10 changes: 9 additions & 1 deletion src/blocks/gist/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class Edit extends Component {
}
}

static __gistCallbackId = 0;

// Each time we request a new Gist, we have to provide a new
// global function name to serve as the JSONP callback.
static __nextGist() {
return 'embed_gist_callback_' + this.__gistCallbackId++;
}

updateURL( newURL ) {
this.props.setAttributes( { url: newURL, file: '' } );

Expand Down Expand Up @@ -94,7 +102,7 @@ class Edit extends Component {
{ preview ? (
url && (
<div className={ classnames( className, meta ? null : 'no-meta' ) }>
<Gist url={ url } file={ file } onError={ () => {
<Gist url={ url } file={ file } callbackId={ Edit.__nextGist() } onError={ () => {
handleErrors();
} } />
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
Expand Down
167 changes: 73 additions & 94 deletions src/blocks/gist/gist.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,139 +8,118 @@ import { GithubIcon as icon } from '@godaddy-wordpress/coblocks-icons';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { Placeholder, Spinner, Icon } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';

// -- MAIN --
// Extending PureComponent allow us to prevent re-rendering when the props DONT change.
export default class Gist extends Component {
constructor( props ) {
super( props );
this.url = props.url;
this.file = props.file;
this.stylesheetAdded = false; // Ensures we only add the Gist's stylesheet one time.
this.state = {
loading: true, // We have not fetched the Gist yet.
gistContent: '', // Raw HTML of the Gist.
};
this._handleError = this._handleError.bind( this );
}
const Gist = ( props ) => {
const [ isLoading, setIsLoading ] = useState( true );
const [ gistContent, setGistContent ] = useState( '' );
const [ stylesheetAdded, setStylesheetAdded ] = useState( false );

// Each time we request a new Gist, we have to provide a new
// global function name to serve as the JSONP callback.
static __gistCallbackId = 0;
const url = props.url;
const file = props.file;
const gistCallback = props.callbackId;

static __nextGist() {
return 'embed_gist_callback_' + this.__gistCallbackId++;
}
useEffect( () => {
_buildGist();
}, [] );

// The Gist JSON data includes a stylesheet file.
// We ensure to add that file only one time in our page.
static __addStylesheet( href ) {
if ( ! this.stylesheetAdded ) {
const __addStylesheet = ( href ) => {
if ( ! stylesheetAdded ) {
const link = document.createElement( 'link' );
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = href;
document.head.appendChild( link );
this.stylesheetAdded = true;
setStylesheetAdded( true );
}
}
};

componentDidMount() {
// Request the Gist iframe.
this._buildGist();
}
const _buildGist = () => {
window[ gistCallback ] = ( gist ) => {
__addStylesheet( gist.stylesheet );
setIsLoading( false );
setGistContent( gist.div );
};

const gistScript = document.createElement( 'script' );
gistScript.type = 'text/javascript';
const transformedURL = _transformedURL( gistCallback );
if ( ! transformedURL ) {
return;
}
gistScript.src = transformedURL;
gistScript.onerror = function() {
_handleError();
};
document.head.appendChild( gistScript );
};

_handleError() {
const { onError } = this.props;
this.setState( {
loading: false,
} );
const _handleError = () => {
const { onError } = props;
setIsLoading( false );
onError();
}
};

_getID() {
const { _handleError } = this;
const _getID = () => {
// Extract a string in form `username/uniqueValue` from the provided Gist url.
if ( this.url.match( /(\.com\/)(.*?)([^#]+)/ ) === null ) {
if ( url.match( /(\.com\/)(.*?)([^#]+)/ ) === null ) {
_handleError();
return;
}
return this.url.match( /(\.com\/)(.*?)([^#]+)/ ).pop();
}
return url.match( /(\.com\/)(.*?)([^#]+)/ ).pop();
};

_getFile() {
const _getFile = () => {
// If `file` prop was provided return that.
if ( this.file !== undefined ) {
return `&file=${ this.file }`;
if ( file !== undefined ) {
return `&file=${ file }`;
}

// Else construct the file parameter from the `url` prop.
const file = this.url.split( '#' ).pop();
const fileSplit = url.split( '#' ).pop();

// If the file parameter exist in Gist url return that file.
if ( file.match( /file*/ ) !== null ) {
return `&file=${ file.replace( 'file-', '' ).replace( '-', '.' ) }`;
if ( fileSplit.match( /file*/ ) !== null ) {
return `&file=${ fileSplit.replace( 'file-', '' ).replace( '-', '.' ) }`;
}

// Else the user wants to link the whole Gist repository.
return '';
}
};

_tranformedURL( gistCallback ) {
const _transformedURL = ( callback ) => {
// Construct a gist url that will allow us to redner the Gist into our page.
const id = this._getID();
const id = _getID();
if ( ! id ) {
return false;
}
const file = this._getFile();
return `https://gist.github.com/${ id }.json?callback=${ gistCallback }${ file }`;
const fileName = _getFile();
return `https://gist.github.com/${ id }.json?callback=${ callback }${ fileName }`;
};

if ( isLoading ) {
return (
<Placeholder
key="placeholder"
icon={ <Icon icon={ icon } /> }
label={ __( 'Loading Gist', 'coblocks' ) }
>
<Spinner />
</Placeholder>
);
}

_buildGist() {
const { _handleError } = this;
const gistCallback = Gist.__nextGist();
window[ gistCallback ] = ( gist ) => {
Gist.__addStylesheet( gist.stylesheet );
this.setState( {
loading: false,
gistContent: gist.div,
} );
};

const gistScript = document.createElement( 'script' );
gistScript.type = 'text/javascript';
const transformedURL = this._tranformedURL( gistCallback );
if ( ! transformedURL ) {
return;
}
gistScript.src = transformedURL;
gistScript.onerror = function() {
_handleError();
};
document.head.appendChild( gistScript );
if ( gistContent ) {
// Render as html.
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml\
return <div dangerouslySetInnerHTML={ { __html: gistContent } } />;
}

render() {
if ( this.state.loading ) {
return (
<Placeholder
key="placeholder"
icon={ <Icon icon={ icon } /> }
label={ __( 'Loading Gist', 'coblocks' ) }
>
<Spinner />
</Placeholder>
);
}
if ( this.state.gistContent ) {
// Render as html.
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml\
return <div dangerouslySetInnerHTML={ { __html: this.state.gistContent } } />;
}
}
}
return '';
};

export default Gist;

// - PROP TYPES -
Gist.propTypes = {
Expand Down

0 comments on commit 558bf47

Please sign in to comment.