Merge branch 'MDL-79194-402' of https://github.com/laurentdavid/moodle into MOODLE_402_STABLE

This commit is contained in:
Jun Pataleta
2023-10-27 23:31:56 +08:00
7 changed files with 97 additions and 15 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+42 -7
View File
@@ -235,6 +235,8 @@ export default class Component extends BaseComponent {
{watch: `transaction:start`, handler: this._startProcessing},
{watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},
{watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},
// Section visibility.
{watch: `section.visible:updated`, handler: this._reloadSection},
// Reindex sections and cms.
{watch: `state:updated`, handler: this._indexContents},
];
@@ -522,12 +524,12 @@ export default class Component extends BaseComponent {
if (debouncedReload) {
return debouncedReload;
}
const pendingReload = new Pending(pendingKey);
const reload = () => {
const pendingReload = new Pending(pendingKey);
this.debouncedReloads.delete(pendingKey);
const cmitem = this.getElement(this.selectors.CM, cmId);
if (!cmitem) {
return;
return pendingReload.resolve();
}
const promise = Fragment.loadFragment(
'core_courseformat',
@@ -540,17 +542,45 @@ export default class Component extends BaseComponent {
}
);
promise.then((html, js) => {
// Other state change can reload the CM or the section before this one.
if (!document.contains(cmitem)) {
pendingReload.resolve();
return false;
}
Templates.replaceNode(cmitem, html, js);
this._indexContents();
pendingReload.resolve();
return;
}).catch();
return true;
}).catch(() => {
pendingReload.resolve();
});
return pendingReload;
};
debouncedReload = debounce(reload, 200);
debouncedReload = debounce(
reload,
200,
{
cancel: true, pending: true
}
);
this.debouncedReloads.set(pendingKey, debouncedReload);
return debouncedReload;
}
/**
* Cancel the active reload CM debounced function, if any.
* @param {Number} cmId
*/
_cancelDebouncedReloadCm(cmId) {
const pendingKey = `courseformat/content:reloadCm_${cmId}`;
const debouncedReload = this.debouncedReloads.get(pendingKey);
if (!debouncedReload) {
return;
}
debouncedReload.cancel();
this.debouncedReloads.delete(pendingKey);
}
/**
* Reload a course section contents.
*
@@ -564,6 +594,10 @@ export default class Component extends BaseComponent {
const pendingReload = new Pending(`courseformat/content:reloadSection_${element.id}`);
const sectionitem = this.getElement(this.selectors.SECTION, element.id);
if (sectionitem) {
// Cancel any pending reload because the section will reload cms too.
for (const cmId of element.cmlist) {
this._cancelDebouncedReloadCm(cmId);
}
const promise = Fragment.loadFragment(
'core_courseformat',
'section',
@@ -578,8 +612,9 @@ export default class Component extends BaseComponent {
Templates.replaceNode(sectionitem, html, js);
this._indexContents();
pendingReload.resolve();
return;
}).catch();
}).catch(() => {
pendingReload.resolve();
});
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
define("core/utils",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.throttle=_exports.debounce=void 0;_exports.throttle=(func,wait)=>{let onCooldown=!1,runAgain=null;const run=function(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];runAgain=null!==runAgain,onCooldown||(func.apply(this,args),onCooldown=!0,setTimeout((()=>{const recurse=runAgain;onCooldown=!1,runAgain=null,recurse&&run(args)}),wait))};return run};_exports.debounce=(func,wait)=>{let timeout=null;return function(){for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];clearTimeout(timeout),timeout=setTimeout((()=>{func.apply(this,args)}),wait)}}}));
define("core/utils",["exports","core/pending"],(function(_exports,_pending){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.throttle=_exports.debounce=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};_exports.throttle=(func,wait)=>{let onCooldown=!1,runAgain=null;const run=function(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];runAgain=null!==runAgain,onCooldown||(func.apply(this,args),onCooldown=!0,setTimeout((()=>{const recurse=runAgain;onCooldown=!1,runAgain=null,recurse&&run(args)}),wait))};return run};const debounceMap=new Map;_exports.debounce=function(func,wait){let{pending:pending=!1,cancel:cancel=!1}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},timeout=null;const returnedFunction=function(){for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];pending&&!debounceMap.has(returnedFunction)&&debounceMap.set(returnedFunction,new _pending.default("core/utils:debounce")),clearTimeout(timeout),timeout=setTimeout((async()=>{const pendingPromise=debounceMap.get(returnedFunction);debounceMap.delete(returnedFunction),await func.apply(undefined,args),null==pendingPromise||pendingPromise.resolve()}),wait)};return cancel&&(returnedFunction.cancel=()=>{const pendingPromise=debounceMap.get(returnedFunction);null==pendingPromise||pendingPromise.resolve(),clearTimeout(timeout)}),returnedFunction}}));
//# sourceMappingURL=utils.min.js.map
File diff suppressed because one or more lines are too long
+46 -4
View File
@@ -21,6 +21,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Pending from 'core/pending';
/**
* Create a wrapper function to throttle the execution of the given
*
@@ -70,6 +72,11 @@ export const throttle = (func, wait) => {
return run;
};
/**
* @property {Map} debounceMap A map of functions to their debounced pending promises.
*/
const debounceMap = new Map();
/**
* Create a wrapper function to debounce the execution of the given
* function. Each attempt to execute the function will reset the cooldown
@@ -78,14 +85,49 @@ export const throttle = (func, wait) => {
* @method
* @param {Function} func The function to debounce
* @param {Number} wait The number of milliseconds to wait after the final attempt to execute
* @param {Object} [options]
* @param {boolean} [options.pending=false] Whether to wrap the debounced method in a pending promise
* @param {boolean} [options.cancel=false] Whether to add a cancel method to the debounced function
* @return {Function}
*/
export const debounce = (func, wait) => {
export const debounce = (
func,
wait,
{
pending = false,
cancel = false,
} = {},
) => {
let timeout = null;
return function(...args) {
const returnedFunction = (...args) => {
if (pending && !debounceMap.has(returnedFunction)) {
debounceMap.set(returnedFunction, new Pending('core/utils:debounce'));
}
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
timeout = setTimeout(async() => {
// Get the current pending promise and immediately empty it.
// This is important to allow the function to be debounced again as soon as possible.
// We do not resolve it until later - but that's fine because the promise is appropriately scoped.
const pendingPromise = debounceMap.get(returnedFunction);
debounceMap.delete(returnedFunction);
// Allow the debounced function to return a Promise.
// This ensures that Behat will not continue until the function has finished executing.
await func.apply(this, args);
// Resolve the pending promise if it exists.
pendingPromise?.resolve();
}, wait);
};
if (cancel) {
returnedFunction.cancel = () => {
const pendingPromise = debounceMap.get(returnedFunction);
pendingPromise?.resolve();
clearTimeout(timeout);
};
}
return returnedFunction;
};
+5
View File
@@ -1,6 +1,11 @@
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.
=== 4.2.4 ===
* Add a new parameter to the debounce (core/utils) function allow it to create its own own Pending promise.
(via options.pending). This is a backport of patch MDL-78779.
* Add a new parameter to the debounce (core/utils) function to allow for cancellation.
=== 4.2.3 ===
* \moodle_page::set_title() has been updated to append the site name depending on the value of $CFG->sitenameintitle and whether
the site's fullname/shortname has been set. So there's no need to manually add the site name whenever calling $PAGE->set_title().