refactor frontend to use WP REST API instead of WP AJAX

Query new ticks using GET request to the REST API.
Also we now trigger one request per ticker, if more than one is
available on the same page.

HTML markup (list element, content container) is generated client side,
s.t. we can work on the generic response data model
This commit is contained in:
Stefan Kalscheuer 2020-11-10 15:30:12 +01:00
parent 547fc521d0
commit 68efc83273
3 changed files with 122 additions and 135 deletions

View File

@ -4,7 +4,7 @@
"browser": true
},
"globals": {
"sclivetickerAjax": "readonly",
"scliveticker": "readonly",
"wp": "readonly"
},
"extends": [

View File

@ -205,16 +205,6 @@ class SCLiveticker {
} else {
$show_feed = 1 === self::$options['show_feed'];
}
$output = '<div class="wp-block-scliveticker-ticker';
if ( 1 === self::$options['enable_ajax'] ) {
$output .= ' sclt-ajax" '
. 'data-sclt-ticker="' . $ticker . '" '
. 'data-sclt-limit="' . $limit . '" '
. 'data-sclt-last="' . time();
}
$output .= '"><ul>';
$args = array(
'post_type' => 'scliveticker_tick',
'posts_per_page' => $limit,
@ -229,12 +219,24 @@ class SCLiveticker {
$wp_query = new WP_Query( $args );
$ticks = '';
$last = null;
while ( $wp_query->have_posts() ) {
$wp_query->the_post();
$output .= self::tick_html( get_the_time( 'd.m.Y H:i' ), get_the_title(), get_the_content() );
$ticks .= self::tick_html( get_the_time( 'd.m.Y H:i' ), get_the_title(), get_the_content() );
if ( is_null( $last ) ) {
$last = get_post_modified_time( 'Y-m-d\TH:i:s', true );
}
}
$output .= '</ul></div>';
$output = '<div class="wp-block-scliveticker-ticker';
if ( 1 === self::$options['enable_ajax'] ) {
$output .= ' sclt-ajax" '
. 'data-sclt-ticker="' . $ticker . '" '
. 'data-sclt-limit="' . $limit . '" '
. 'data-sclt-last="' . $last;
}
$output .= '"><ul>' . $ticks . '</ul></div>';
// Show RSS feed link, if configured.
if ( $show_feed ) {
@ -262,7 +264,7 @@ class SCLiveticker {
if ( self::$shortcode_present || self::$widget_present || self::block_present() ) {
wp_enqueue_script(
'scliveticker-js',
SCLIVETICKER_BASE . 'scripts/liveticker.min.js',
SCLIVETICKER_BASE . 'scripts/liveticker.js',
array(),
self::VERSION,
true
@ -271,10 +273,11 @@ class SCLiveticker {
// Add endpoint to script.
wp_localize_script(
'scliveticker-js',
'sclivetickerAjax',
'scliveticker',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'scliveticker_update-ticks' ),
'api' => rest_url(),
'poll_interval' => self::$options['poll_interval'] * 1000,
)
);
@ -435,9 +438,9 @@ class SCLiveticker {
*/
private static function tick_html( $time, $title, $content, $is_widget = false ) {
return '<li class="sclt-tick">'
. '<p><span class="sclt-tick_time">' . esc_html( $time ) . '</span>'
. '<span class="sclt-tick-title">' . esc_html( $title ) . '</span></p>'
. '<p class="sclt-tick-content">' . $content . '</p></li>';
. '<span class="sclt-tick-time">' . esc_html( $time ) . '</span>'
. '<span class="sclt-tick-title">' . esc_html( $title ) . '</span>'
. '<div class="sclt-tick-content">' . $content . '</div></li>';
}
/**

View File

@ -4,11 +4,9 @@
* @class
*/
( function() {
var ajaxURL = sclivetickerAjax.ajax_url;
var nonce = sclivetickerAjax.nonce;
var pollInterval = sclivetickerAjax.poll_interval;
var apiURL;
var pollInterval;
var ticker;
var widgets;
/**
* Initialize iveticker JS component.
@ -19,67 +17,42 @@
var updateNow = false;
// Opt out if AJAX pobject not present.
if ( 'undefined' === typeof sclivetickerAjax ) {
if ( 'undefined' === typeof scliveticker ) {
return;
}
// Extract AJAX settings.
ajaxURL = sclivetickerAjax.ajax_url;
nonce = sclivetickerAjax.nonce;
pollInterval = sclivetickerAjax.poll_interval;
// Extract settings.
apiURL = scliveticker.api + 'wp/v2/scliveticker_tick';
pollInterval = scliveticker.poll_interval;
// Get ticker elements.
ticker = [].map.call(
document.querySelectorAll( 'div.wp-block-scliveticker-ticker.sclt-ajax' ),
function( elem ) {
var list = elem.querySelector( 'ul' );
var last = Number( elem.getAttribute( 'data-sclt-last' ) );
if ( ! list ) {
list = document.createElement( 'ul' );
elem.appendChild( list );
}
if ( 0 === last ) {
elem = parseElement( elem, false );
if ( '0' === elem.lastPoll ) {
updateNow = true;
}
return {
s: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: last,
e: list,
};
return elem;
}
);
// Get widget elements.
widgets = [].map.call(
ticker.concat(
[].map.call(
document.querySelectorAll( 'div.wp-widget-scliveticker-ticker.sclt-ajax' ),
function( elem ) {
var list = elem.querySelector( 'ul' );
var last = Number( elem.getAttribute( 'data-sclt-last' ) );
if ( ! list ) {
list = document.createElement( 'ul' );
elem.appendChild( list );
}
if ( 0 === last ) {
elem = parseElement( elem, true );
if ( 0 === elem.lastPoll ) {
updateNow = true;
}
return {
w: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: last,
e: list,
};
return elem;
}
)
);
// Trigger update, if necessary.
if ( ( 0 < ticker.length || widgets.length ) && 0 < pollInterval ) {
if ( ( 0 < ticker.length ) && 0 < pollInterval ) {
if ( updateNow ) {
update();
} else {
@ -89,56 +62,55 @@
};
/**
* Update liveticker on current page via AJAX call.
* Parse an HTML element containing a liveticker.
*
* @param {HTMLElement} elem The element.
* @param {boolean} widget Is the element a widget?
* @return {{ticker: string, lastPoll: number, ticks: any, limit: string, isWidget: *}} Ticker descriptor object.
*/
var parseElement = function( elem, widget ) {
var list = elem.querySelector( 'ul' );
var last = elem.getAttribute( 'data-sclt-last' );
if ( ! list ) {
list = document.createElement( 'ul' );
elem.appendChild( list );
}
return {
ticker: elem.getAttribute( 'data-sclt-ticker' ),
limit: elem.getAttribute( 'data-sclt-limit' ),
lastPoll: last,
ticks: list,
isWidget: widget,
};
};
/**
* Update liveticker on current page via REST API call.
*
* @return {void}
*/
var update = function() {
// Extract ticker-slug, limit and timestamp of last poll.
var updateReq = 'action=sclt_update-ticks&_ajax_nonce=' + nonce;
var i, j;
// Iterate over available tickers.
ticker.forEach(
function( t ) {
var xhr = new XMLHttpRequest();
for ( i = 0; i < ticker.length; i++ ) {
updateReq = updateReq +
'&update[' + i + '][s]=' + ticker[ i ].s +
'&update[' + i + '][l]=' + ticker[ i ].l +
'&update[' + i + '][t]=' + ticker[ i ].t;
}
for ( j = 0; j < widgets.length; j++ ) {
updateReq = updateReq +
'&update[' + ( i + j ) + '][w]=' + widgets[ j ].w +
'&update[' + ( i + j ) + '][l]=' + widgets[ j ].l +
'&update[' + ( i + j ) + '][t]=' + widgets[ j ].t;
}
// Issue AJAX request.
xhr.open( 'POST', ajaxURL, true );
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
xhr.onreadystatechange = function() {
var query = '?ticker=' + encodeURI( t.ticker ) +
'&limit=' + encodeURI( t.limit ) +
'&last=' + encodeURI( t.lastPoll );
xhr.open( 'GET', apiURL + query, true );
xhr.addEventListener(
'load',
function() {
var updateResp;
if ( XMLHttpRequest.DONE === this.readyState && 200 === this.status ) {
try {
updateResp = JSON.parse( this.responseText );
if ( updateResp ) {
updateResp.reverse();
updateResp.forEach(
function( u ) {
ticker.forEach(
function( t ) {
if ( t.s === u.s ) {
t.t = u.t; // Update last poll timestamp.
updateHTML( t, u ); // Update HTML markup.
}
}
);
widgets.forEach(
function( t ) {
if ( t.w === u.w ) {
t.t = u.t;
updateHTML( t, u );
}
}
);
addTick( t, u );
}
);
}
@ -148,38 +120,50 @@
console.warn( 'Liveticker AJAX update failed, stopping automatic updates.' );
}
}
};
xhr.send( updateReq );
);
xhr.send();
}
);
};
/**
* Do actual update of HTML code.
*
* @param {Object} t Ticker or Widget reference.
* @param {number} t.l Limit of entries to display.
* @param {HTMLElement} t.e HTML element of the ticker/widget.
* @param {Object} u Update entity.
* @param {string} u.h HTML code to append.
* @param {number} u.t Timetsamp of last update.
* @return {void}
*/
var updateHTML = function( t, u ) {
var addTick = function( t, u ) {
// Parse new DOM-part.
var n = document.createElement( 'ul' );
n.innerHTML = u.h;
var li = document.createElement( 'li' );
var time = document.createElement( 'span' );
var title = document.createElement( 'span' );
var content = document.createElement( 'div' );
var cls = t.isWidget ? 'sclt-widget' : 'sclt-tick';
// Prepend new ticks to container.
while ( n.hasChildNodes() ) {
t.e.prepend( n.lastChild );
}
li.classList.add( cls );
time.classList.add( cls + '-time' );
time.innerText = u.modified_rendered;
title.classList.add( cls + '-title' );
title.innerText = u.title.rendered;
content.classList.add( cls + '-content' );
content.innerHTML = u.content.rendered;
li.appendChild( time );
li.appendChild( title );
li.appendChild( content );
t.e.parentNode.setAttribute( 'data-sclt-last', u.t );
// Prepend new tick to container.
t.ticks.prepend( li );
// Update last poll time.
t.lastPoll = u.date_gmt;
t.ticks.parentNode.setAttribute( 'data-sclt-last', u.date_gmt );
// Remove tail, if limit is set.
if ( 0 < t.l ) {
[].slice.call( t.e.getElementsByTagName( 'li' ), t.l ).forEach(
function( li ) {
li.remove();
if ( 0 < t.limit ) {
[].slice.call( t.ticks.getElementsByTagName( 'li' ), t.limit ).forEach(
function( l ) {
l.remove();
}
);
}