Skip to content

Commit

Permalink
Fix: Make sendState call async (fixes #134) (#135)
Browse files Browse the repository at this point in the history
* Fix: Make sendState call aysnc

* Promisifying function call

* Minor formatting tidy

* Using Adapt.wait approach

* Reverting to promise approach

* Fix: Apply debounce around sendState calls to avoid concurrency issues

* Reverting earlier Promise approach

* Reverting earlier Promise approach

* Moving debounce to server update part of sendState only

* Splitting sendState so sendStateToServer can be debounced

* Avoiding potential debounce issues from passed arguments

* Object approach to avoid duplicate array items

* Update js/XAPI.js

Co-authored-by: Oliver Foster <[email protected]>

---------

Co-authored-by: Oliver Foster <[email protected]>
  • Loading branch information
cahirodoherty-learningpool and oliverfoster authored Jan 20, 2025
1 parent 267ecbb commit 0fe6ba0
Showing 1 changed file with 38 additions and 24 deletions.
62 changes: 38 additions & 24 deletions js/XAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class XAPI extends Backbone.Model {
this.courseDescription = '';
this.defaultLang = 'en-US';
this.isComplete = false;
this.changedCollectionNames = {};

// Default events to send statements for.
this.coreEvents = {
Expand Down Expand Up @@ -65,6 +66,8 @@ class XAPI extends Backbone.Model {
components: 'component',
offlineStorage: 'offlineStorage'
};

this.sendStateToServer = _.debounce(this.sendStateToServer.bind(this), 500);
}

/** Implementation starts here */
Expand Down Expand Up @@ -1086,23 +1089,18 @@ class XAPI extends Backbone.Model {
* Sends the state to the or the given model to the configured LRS.
* @param {AdaptModel} model - The AdaptModel whose state has changed.
*/
sendState(model, modelState) {
if (this.get('shouldTrackState') !== true || model.get('_isTrackable') === false) {
return;
}
async sendState(model, modelState) {
if (this.get('shouldTrackState') !== true) return;
if (model.get('_isTrackable') === false) return;

const activityId = this.get('activityId');
const actor = this.get('actor');
const type = model.get('_type');
const state = this.get('state');
const registration = this.get('shouldUseRegistration') === true
? this.get('registration')
: null;

const collectionName = _.findKey(this.coreObjects, o => {
return (o === type || o.indexOf(type) > -1);
});
const stateCollection = Array.isArray(state[collectionName]) ? state[collectionName] : [];
let newState;
let newState = modelState;

if (collectionName !== 'course' && collectionName !== 'offlineStorage') {
const index = _.findIndex(stateCollection, { _id: model.get('_id') });
Expand All @@ -1114,24 +1112,44 @@ class XAPI extends Backbone.Model {
}

newState = stateCollection;
} else {
newState = modelState;
}

// Update the locally held state.
this.changedCollectionNames[collectionNames] = true;
state[collectionName] = newState;
this.set({
state
});

// Pass the new state to the LRS.
this.xapiWrapper.sendState(activityId, actor, collectionName, registration, newState, null, null, (error, xhr) => {
if (error) {
Adapt.trigger('xapi:lrs:sendState:error', error);
}
this.sendStateToServer();
}

Adapt.trigger('xapi:lrs:sendState:success', newState);
});
sendStateToServer() {
const activityId = this.get('activityId');
const actor = this.get('actor');
const registration = this.get('shouldUseRegistration') === true
? this.get('registration')
: null;

const changedCollectionNames = Object.keys(this.changedCollectionNames);
this.changedCollectionNames = {};

for (const collectionName of changeCollectionNames) {
const newState = this.get('state')[collectionName];

await new Promise(resolve => {
this.xapiWrapper.sendState(activityId, actor, collectionName, registration, newState, null, null, (error, xhr) => {
if (error) {
Adapt.trigger('xapi:lrs:sendState:error', error);
return resolve();
}

Adapt.trigger('xapi:lrs:sendState:success', newState);
return resolve();
});
});
}
}

/**
Expand Down Expand Up @@ -1360,9 +1378,7 @@ class XAPI extends Backbone.Model {
* @param {array} [attachments] - An array of attachments to pass to the LRS.
*/
async sendStatement(statement, attachments = null) {
if (!statement) {
return;
}
if (!statement) return;

Adapt.trigger('xapi:preSendStatement', statement);

Expand Down Expand Up @@ -1516,9 +1532,7 @@ class XAPI extends Backbone.Model {
* @param {ADL.XAPIStatement[]} statements - An array of valid ADL.XAPIStatement objects.
*/
async sendStatements(statements) {
if (!statements || statements.length === 0) {
return;
}
if (!statements?.length) return;

Adapt.trigger('xapi:preSendStatements', statements);

Expand Down

0 comments on commit 0fe6ba0

Please sign in to comment.