mirror of
https://github.com/moodle/moodle.git
synced 2025-03-18 22:50:19 +01:00
Merge branch 'MDL-79194-401' of https://github.com/laurentdavid/moodle into MOODLE_401_STABLE
This commit is contained in:
commit
e146a9d8cd
4
course/format/amd/build/local/content.min.js
vendored
4
course/format/amd/build/local/content.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
import {BaseComponent} from 'core/reactive';
|
||||
import {debounce} from 'core/utils';
|
||||
import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
|
||||
import inplaceeditable from 'core/inplace_editable';
|
||||
import Section from 'core_courseformat/local/content/section';
|
||||
@ -33,6 +34,8 @@ import DispatchActions from 'core_courseformat/local/content/actions';
|
||||
import * as CourseEvents from 'core_course/events';
|
||||
// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.
|
||||
import jQuery from 'jquery';
|
||||
import Pending from 'core/pending';
|
||||
import log from 'core/log';
|
||||
|
||||
export default class Component extends BaseComponent {
|
||||
|
||||
@ -75,6 +78,7 @@ export default class Component extends BaseComponent {
|
||||
this.cms = {};
|
||||
// The page section return.
|
||||
this.sectionReturn = descriptor.sectionReturn ?? 0;
|
||||
this.debouncedReloads = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,6 +234,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},
|
||||
// State changes thaty require to reload course modules.
|
||||
@ -506,14 +512,65 @@ export default class Component extends BaseComponent {
|
||||
* @param {object} param0.element the state object
|
||||
*/
|
||||
_reloadCm({element}) {
|
||||
const cmitem = this.getElement(this.selectors.CM, element.id);
|
||||
if (cmitem) {
|
||||
const promise = courseActions.refreshModule(cmitem, element.id);
|
||||
if (!this.getElement(this.selectors.CM, element.id)) {
|
||||
return;
|
||||
}
|
||||
const debouncedReload = this._getDebouncedReloadCm(element.id);
|
||||
debouncedReload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate or get a reload CM debounced function.
|
||||
* @param {Number} cmId
|
||||
* @returns {Function} the debounced reload function
|
||||
*/
|
||||
_getDebouncedReloadCm(cmId) {
|
||||
const pendingKey = `courseformat/content:reloadCm_${cmId}`;
|
||||
let debouncedReload = this.debouncedReloads.get(pendingKey);
|
||||
if (debouncedReload) {
|
||||
return debouncedReload;
|
||||
}
|
||||
const reload = () => {
|
||||
const pendingReload = new Pending(pendingKey);
|
||||
this.debouncedReloads.delete(pendingKey);
|
||||
const cmitem = this.getElement(this.selectors.CM, cmId);
|
||||
if (!cmitem) {
|
||||
return pendingReload.resolve();
|
||||
}
|
||||
const promise = courseActions.refreshModule(cmitem, cmId);
|
||||
promise.then(() => {
|
||||
this._indexContents();
|
||||
return;
|
||||
}).catch();
|
||||
return true;
|
||||
}).catch((error) => {
|
||||
log.debug(error);
|
||||
}).finally(() => {
|
||||
pendingReload.resolve();
|
||||
});
|
||||
return pendingReload;
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -526,13 +583,22 @@ export default class Component extends BaseComponent {
|
||||
* @param {object} param0.element the state object
|
||||
*/
|
||||
_reloadSection({element}) {
|
||||
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 = courseActions.refreshSection(sectionitem, element.id);
|
||||
promise.then(() => {
|
||||
this._indexContents();
|
||||
return;
|
||||
}).catch();
|
||||
return true;
|
||||
}).catch((error) => {
|
||||
log.debug(error);
|
||||
}).finally(() => {
|
||||
pendingReload.resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
2
lib/amd/build/utils.min.js
vendored
2
lib/amd/build/utils.min.js
vendored
@ -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
@ -20,6 +20,7 @@
|
||||
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
|
||||
* @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 +71,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 +84,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;
|
||||
};
|
||||
|
@ -1,6 +1,11 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 4.1.7 ===
|
||||
* 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.1.6 ===
|
||||
* \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().
|
||||
|
Loading…
x
Reference in New Issue
Block a user