Add fast AJAX feature, apply to follows

60-80 % speed boost!
This commit is contained in:
Tetrakern 2023-08-20 22:21:13 +02:00
parent db151eea1a
commit 4e7744b67d
6 changed files with 158 additions and 5 deletions

View File

@ -1,5 +1,68 @@
<?php
// =============================================================================
// FAST AJAX REQUESTS
// > Skips any unnecessary theme initialization!
// =============================================================================
define(
'FICTIONEER_FAST_AJAX_FUNCTIONS',
array(
'fictioneer_ajax_get_follows',
'fictioneer_ajax_toggle_follow',
'fictioneer_ajax_clear_my_follows',
'fictioneer_ajax_mark_follows_read',
'fictioneer_ajax_get_follows_notifications',
'fictioneer_ajax_get_follows_list'
)
);
if (
isset( $_REQUEST['fcn_fast_ajax'] ) &&
isset( $_REQUEST['action'] ) &&
! ( defined('REST_REQUEST') && REST_REQUEST )
) {
fictioneer_do_fast_ajax();
}
function fictioneer_do_fast_ajax() {
$action = $_REQUEST['action'];
// Allowed action?
if (
! defined('FICTIONEER_FAST_AJAX_FUNCTIONS') ||
! in_array( $action, FICTIONEER_FAST_AJAX_FUNCTIONS, true ) ||
strpos( $action, 'fictioneer_ajax' ) !== 0
) {
return;
}
// Include required files
require_once __DIR__ . '/includes/functions/_utility.php';
require_once __DIR__ . '/includes/functions/_query_helpers.php';
if ( get_option( 'fictioneer_enable_follows' ) && strpos( $action, '_follow' ) !== false ) {
require_once __DIR__ . '/includes/functions/_content_helpers.php';
require_once __DIR__ . '/includes/functions/users/_follows.php';
}
// Function exists?
if ( ! function_exists( $action ) ) {
return;
}
// Set DOING_AJAX true
if ( ! defined( 'DOING_AJAX' ) ) {
define( 'DOING_AJAX', true );
}
// Call function
call_user_func( $action );
// Terminate in case something goes wrong
die();
}
// =============================================================================
// CONSTANTS/SETTINGS
// =============================================================================

View File

@ -78,7 +78,9 @@ function fictioneer_ajax_get_follows() {
wp_date( 'c', $user_follows['seen'] / 1000 )
);
if ( $latest ) $new = count( $latest );
if ( $latest ) {
$new = count( $latest );
}
}
$user_follows['new'] = $new;

2
js/follows.min.js vendored
View File

@ -1 +1 @@
const fcn_desktopFollowList=_$$$("follow-menu-scroll"),fcn_mobileFollowList=_$$$("mobile-menu-follows-list"),fcn_followsMenuItem=_$$$("follow-menu-button");var fcn_userFollowsTimeout,fcn_follows;function fcn_initializeFollows(){fcn_follows=fcn_getFollows(),fcn_fetchFollowsFromDatabase()}function fcn_getFollows(){const o=localStorage.getItem("fcnStoryFollows");return o&&fcn_isValidJSONString(o)?JSON.parse(o):{lastLoaded:0,data:{},seen:Date.now(),new:!1}}function fcn_fetchFollowsFromDatabase(){fcn_ajaxLimitThreshold<fcn_follows.lastLoaded?fcn_updateFollowsView():fcn_ajaxGet({action:"fictioneer_ajax_get_follows"}).then((o=>{if(o.success){let t=o.data.follows;t=fcn_isValidJSONString(t)?t:"{}",t=JSON.parse(t),(fcn_follows="object"==typeof t&&t.data&&Object.keys(t.data).length>0?t:{data:{},seen:Date.now(),new:!1}).lastLoaded=Date.now()}})).catch((()=>{localStorage.removeItem("fcnStoryFollows"),fcn_follows=!1})).then((()=>{fcn_updateFollowsView(),localStorage.removeItem("fcnBookshelfContent")}))}function fcn_updateFollowsView(){if(!fcn_follows)return;_$$(".button-follow-story").forEach((o=>{o.classList.toggle("_followed",fcn_follows?.data.hasOwnProperty(o.dataset.storyId))})),_$$(".card").forEach((o=>{o.classList.toggle("has-follow",fcn_follows?.data.hasOwnProperty(o.dataset.storyId))})),localStorage.setItem("fcnStoryFollows",JSON.stringify(fcn_follows));const o=parseInt(fcn_follows.new)>0;_$$(".mark-follows-read, .follows-alert-number, .mobile-menu-button").forEach((t=>{t.classList.toggle("_new",o),o>0&&(t.dataset.newCount=fcn_follows.new)}))}function fcn_toggleFollow(o){const t=fcn_getFollows();if(localStorage.removeItem("fcnBookshelfContent"),JSON.stringify(fcn_follows.data[o])!==JSON.stringify(t.data[o]))return fcn_follows=t,fcn_showNotification(__("Follows re-synchronized.","fictioneer")),void fcn_updateFollowsView();(fcn_follows=t).data.hasOwnProperty(o)?delete fcn_follows.data[o]:fcn_follows.data[o]={timestamp:Date.now()},fcn_follows.lastLoaded=0,fcn_updateFollowsView(),clearTimeout(fcn_userFollowsTimeout),fcn_userFollowsTimeout=setTimeout((()=>{fcn_ajaxPost({action:"fictioneer_ajax_toggle_follow",story_id:o,set:fcn_follows.data.hasOwnProperty(o)}).then((o=>{o.data.error&&fcn_showNotification(o.data.error,5,"warning")})).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning")}))}),fictioneer_ajax.post_debounce_rate)}function fcn_setupFollowsHTML(){fcn_followsMenuItem.classList.contains("_loaded")||fcn_ajaxGet({action:"fictioneer_ajax_get_follows_notifications"}).then((o=>{o.data.html&&(fcn_desktopFollowList.innerHTML=o.data.html,fcn_mobileFollowList.innerHTML=o.data.html)})).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning"),fcn_desktopFollowList.remove(),fcn_mobileFollowList.remove()})).then((()=>{fcn_followsMenuItem.classList.add("_loaded")}))}function fcn_markFollowsRead(){fcn_followsMenuItem.classList.contains("_new")&&fcn_followsMenuItem.classList.contains("_loaded")&&(_$$(".mark-follows-read, .follows-alert-number, .follow-item, .mobile-menu-button").forEach((o=>{o.classList.remove("_new")})),fcn_follows.new=0,fcn_follows.lastLoaded=0,localStorage.setItem("fcnStoryFollows",JSON.stringify(fcn_follows)),fcn_ajaxPost({action:"fictioneer_ajax_mark_follows_read"}).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning")})))}fcn_isLoggedIn&&fcn_initializeFollows(),fcn_followsMenuItem?.addEventListener("mouseover",(()=>{fcn_setupFollowsHTML()}),{once:!0}),fcn_followsMenuItem?.addEventListener("focus",(()=>{fcn_setupFollowsHTML()}),{once:!0}),_$('.mobile-menu__frame-button[data-frame-target="follows"]')?.addEventListener("click",(()=>{fcn_setupFollowsHTML()}),{once:!0}),_$$(".button-follow-story").forEach((o=>{o.addEventListener("click",(o=>{fcn_toggleFollow(o.currentTarget.dataset.storyId)}))})),_$$(".mark-follows-read").forEach((o=>{o.addEventListener("click",(()=>{fcn_markFollowsRead()}))}));
const fcn_desktopFollowList=_$$$("follow-menu-scroll"),fcn_mobileFollowList=_$$$("mobile-menu-follows-list"),fcn_followsMenuItem=_$$$("follow-menu-button");var fcn_userFollowsTimeout,fcn_follows;function fcn_initializeFollows(){fcn_follows=fcn_getFollows(),fcn_fetchFollowsFromDatabase()}function fcn_getFollows(){const o=localStorage.getItem("fcnStoryFollows");return o&&fcn_isValidJSONString(o)?JSON.parse(o):{lastLoaded:0,data:{},seen:Date.now(),new:!1}}function fcn_fetchFollowsFromDatabase(){fcn_ajaxLimitThreshold<fcn_follows.lastLoaded?fcn_updateFollowsView():fcn_ajaxGet({action:"fictioneer_ajax_get_follows",fcn_fast_ajax:1}).then((o=>{if(o.success){let t=o.data.follows;t=fcn_isValidJSONString(t)?t:"{}",t=JSON.parse(t),(fcn_follows="object"==typeof t&&t.data&&Object.keys(t.data).length>0?t:{data:{},seen:Date.now(),new:!1}).lastLoaded=Date.now()}})).catch((()=>{localStorage.removeItem("fcnStoryFollows"),fcn_follows=!1})).then((()=>{fcn_updateFollowsView(),localStorage.removeItem("fcnBookshelfContent")}))}function fcn_updateFollowsView(){if(!fcn_follows)return;_$$(".button-follow-story").forEach((o=>{o.classList.toggle("_followed",fcn_follows?.data.hasOwnProperty(o.dataset.storyId))})),_$$(".card").forEach((o=>{o.classList.toggle("has-follow",fcn_follows?.data.hasOwnProperty(o.dataset.storyId))})),localStorage.setItem("fcnStoryFollows",JSON.stringify(fcn_follows));const o=parseInt(fcn_follows.new)>0;_$$(".mark-follows-read, .follows-alert-number, .mobile-menu-button").forEach((t=>{t.classList.toggle("_new",o),o>0&&(t.dataset.newCount=fcn_follows.new)}))}function fcn_toggleFollow(o){const t=fcn_getFollows();if(localStorage.removeItem("fcnBookshelfContent"),JSON.stringify(fcn_follows.data[o])!==JSON.stringify(t.data[o]))return fcn_follows=t,fcn_showNotification(__("Follows re-synchronized.","fictioneer")),void fcn_updateFollowsView();(fcn_follows=t).data.hasOwnProperty(o)?delete fcn_follows.data[o]:fcn_follows.data[o]={timestamp:Date.now()},fcn_follows.lastLoaded=0,fcn_updateFollowsView(),clearTimeout(fcn_userFollowsTimeout),fcn_userFollowsTimeout=setTimeout((()=>{fcn_ajaxPost({action:"fictioneer_ajax_toggle_follow",fcn_fast_ajax:1,story_id:o,set:fcn_follows.data.hasOwnProperty(o)}).then((o=>{o.data.error&&fcn_showNotification(o.data.error,5,"warning")})).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning")}))}),fictioneer_ajax.post_debounce_rate)}function fcn_setupFollowsHTML(){fcn_followsMenuItem.classList.contains("_loaded")||fcn_ajaxGet({action:"fictioneer_ajax_get_follows_notifications",fcn_fast_ajax:1}).then((o=>{o.data.html&&(fcn_desktopFollowList.innerHTML=o.data.html,fcn_mobileFollowList.innerHTML=o.data.html)})).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning"),fcn_desktopFollowList.remove(),fcn_mobileFollowList.remove()})).then((()=>{fcn_followsMenuItem.classList.add("_loaded")}))}function fcn_markFollowsRead(){fcn_followsMenuItem.classList.contains("_new")&&fcn_followsMenuItem.classList.contains("_loaded")&&(_$$(".mark-follows-read, .follows-alert-number, .follow-item, .mobile-menu-button").forEach((o=>{o.classList.remove("_new")})),fcn_follows.new=0,fcn_follows.lastLoaded=0,localStorage.setItem("fcnStoryFollows",JSON.stringify(fcn_follows)),fcn_ajaxPost({action:"fictioneer_ajax_mark_follows_read",fcn_fast_ajax:1}).catch((o=>{o.status&&o.statusText&&fcn_showNotification(`${o.status}: ${o.statusText}`,5,"warning")})))}fcn_isLoggedIn&&fcn_initializeFollows(),fcn_followsMenuItem?.addEventListener("mouseover",(()=>{fcn_setupFollowsHTML()}),{once:!0}),fcn_followsMenuItem?.addEventListener("focus",(()=>{fcn_setupFollowsHTML()}),{once:!0}),_$('.mobile-menu__frame-button[data-frame-target="follows"]')?.addEventListener("click",(()=>{fcn_setupFollowsHTML()}),{once:!0}),_$$(".button-follow-story").forEach((o=>{o.addEventListener("click",(o=>{fcn_toggleFollow(o.currentTarget.dataset.storyId)}))})),_$$(".mark-follows-read").forEach((o=>{o.addEventListener("click",(()=>{fcn_markFollowsRead()}))}));

View File

@ -0,0 +1,42 @@
<?php
/**
* Plugin Name: Fictioneer Fast AJAX
* Description: Skips plugins and theme initialization for faster requests.
* Version: 1.0
* Author: Tetrakern
* Author URI: https://github.com/Tetrakern
* Donate link: https://ko-fi.com/tetrakern
* License: GNU General Public License v3.0 or later
* License URI: http://www.gnu.org/licenses/gpl.html
*/
// Check if fast AJAX is activated
if ( ! isset( $_REQUEST['fcn_fast_ajax'] ) ) {
return;
}
// Check if action is set and allowed
if (
! isset( $_REQUEST['action'] ) ||
strpos( $_REQUEST['action'], 'fictioneer_ajax' ) !== 0
) {
return;
}
function fictioneer_exclude_plugins( $plugins ) {
// Setup
$allow_list = array(
'fictioneer-email-subscriptions/fictioneer-email-subscriptions.php'
);
// Remove not allowed plugins
foreach ( $plugins as $index => $plugin ) {
if ( ! in_array( $plugin, $allow_list ) ) {
unset( $plugins[ $index ] );
}
}
// Continue filter
return $plugins;
}
add_filter( 'option_active_plugins', 'fictioneer_exclude_plugins' );

View File

@ -66,7 +66,8 @@ function fcn_fetchFollowsFromDatabase() {
// Request
fcn_ajaxGet({
'action': 'fictioneer_ajax_get_follows'
'action': 'fictioneer_ajax_get_follows',
'fcn_fast_ajax': 1
})
.then((response) => {
// Check for success
@ -198,6 +199,7 @@ function fcn_toggleFollow(storyId) {
fcn_userFollowsTimeout = setTimeout(() => {
fcn_ajaxPost({
'action': 'fictioneer_ajax_toggle_follow',
'fcn_fast_ajax': 1,
'story_id': storyId,
'set': fcn_follows.data.hasOwnProperty(storyId)
})
@ -231,7 +233,8 @@ function fcn_setupFollowsHTML() {
// Request
fcn_ajaxGet({
'action': 'fictioneer_ajax_get_follows_notifications'
'action': 'fictioneer_ajax_get_follows_notifications',
'fcn_fast_ajax': 1
})
.then((response) => {
// Any Follows HTML retrieved?
@ -285,7 +288,8 @@ function fcn_markFollowsRead() {
// Request
fcn_ajaxPost({
'action': 'fictioneer_ajax_mark_follows_read'
'action': 'fictioneer_ajax_mark_follows_read',
'fcn_fast_ajax': 1
})
.catch((error) => {
if (error.status && error.statusText) {

View File

@ -822,3 +822,45 @@ function fcn_html(...args) {
template.innerHTML = String.raw(...args).trim();
return template.content.firstChild;
}
// =============================================================================
// DEV: AJAX BENCHMARK
// =============================================================================
// /**
// * Benchmark AJAX request response times
// *
// * Default: benchmarkAjax(20, {'action': '...'});
// *
// * Fast: benchmarkAjax(20, {'fcn_fast_ajax': 1, 'action': '...'});
// *
// * @param {number} n - The number of times the AJAX request should be made.
// * @param {Object} data - The payload for the AJAX request.
// * @param {String} url - Optional. The AJAX URL if different from the default.
// * @param {Object} headers - Optional. Headers for the request.
// * @returns {Promise<number>} Promise that resolves with the average response time in milliseconds.
// */
// async function benchmarkAjax(n = 1, data = {}, url = null, headers = {}) {
// let totalTime = 0;
// console.log(`Starting benchmark with ${n} AJAX requests...`);
// for (let i = 0; i < n; i++) {
// const startTime = performance.now();
// try {
// await fcn_ajaxGet(data, url, headers);
// const endTime = performance.now();
// totalTime += (endTime - startTime);
// } catch (error) {
// console.error('Error during AJAX request:', error);
// }
// }
// const averageTime = totalTime / n;
// console.log(`Finished benchmarking. Average AJAX response time over ${n} requests: ${averageTime.toFixed(2)} ms`);
// return averageTime;
// }