Skip to content

Commit

Permalink
Merge pull request #548 from matematikk-mooc/aj/DIT-34
Browse files Browse the repository at this point in the history
DIT-34: Implement SWR caching strategy to improve performance for frontpage
  • Loading branch information
madsenandreas authored Sep 5, 2024
2 parents 0d72722 + 3571510 commit c15b410
Show file tree
Hide file tree
Showing 7 changed files with 387 additions and 418 deletions.
4 changes: 2 additions & 2 deletions src/js/3party/messagehandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default (function() {
return {
init: function() {
window.addEventListener('message', function(e) {
console.log("FRONTEND_LTI_MESSAGE_RECEIVED", e)
// console.log("FRONTEND_LTI_MESSAGE_RECEIVED", e)

const error = error => console.error('error calling api', error);
try {
Expand All @@ -40,7 +40,7 @@ export default (function() {
message = JSON.parse(e.data);
}

console.log("FRONTEND_LTI_MESSAGE_DATA", message)
// console.log("FRONTEND_LTI_MESSAGE_DATA", message)
if(message.subject == "kpas-lti.connect") {
const connectedMsg = {
subject: 'kpas-lti.ltiparentready'
Expand Down
10 changes: 10 additions & 0 deletions src/js/api/kpas-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ export default (function() {
uri: '/settings/highlighted',
params: {}
});
},
getCoursesForFrontpage: function(callback, error) {
this._get({
callback: function(course) {
callback(course);
},
error: error,
uri: '/bff/frontpage/courses',
params: {}
});
}
}
})();
Expand Down
71 changes: 38 additions & 33 deletions src/js/modules/courselist.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import api from "../api/api";
import { createApp } from "vue/dist/vue.runtime.esm-bundler.js";
import kpasApi from '../api/kpas-api';
import { renderPrivacyPolicyLink } from "../../vue/pages/courselist-page";
import enroll from './enroll.js';
import util from "./util";

export default (function () {
Expand All @@ -18,42 +19,46 @@ export default (function () {
header.insertAdjacentElement('afterend', loader);
loaderComponent.mount("#loader");

api.getEnrolledCourses((courses) => {
kpasApi.getAllCourseSettings(function (allCoursesSettings) {
var myCoursesWithSettings = util.mapCourseSettings(courses, allCoursesSettings.result);

if (courses.length == 0) {
window.location.href = "/search/all_courses";
} else {


let wrapper = document.getElementById("application");
try {
if(wrapper != null){
myCoursesWithSettings.forEach(course => {
course.enrolled = true;
});
const app = createApp(MyCoursesPage, {courses: myCoursesWithSettings});

let myCourses = wrapper.appendChild(document.createElement("div"));
myCourses.setAttribute("id", "my-courses-container");
myCourses.setAttribute("style", "width: 100%; justify-content: center; display: flex; min-height: 85vh;");
let footerNode = document.getElementById("wrapper");
footerNode.parentNode.insertBefore(myCourses, footerNode);
document.getElementById('wrapper').innerHTML = ''
$('#wrapper').remove();
document.getElementById('loader').remove();
app.mount("#my-courses-container");
util.fetchWithSwr("frontpage_courses", enroll.fetchFrontpageCourses, function(frontpageData) {
if (frontpageData && frontpageData.result) {
enroll.withEnrolledCourses(enroll.setCourseEnrolledStatus, frontpageData.result, function(dataWithEnrolled) {
let allCoursesSettings = dataWithEnrolled.settings;
let courses = enroll.filterOnlyEnrolledCourses(dataWithEnrolled.courses);
let myCoursesWithSettings = util.mapCourseSettings(courses, allCoursesSettings);

if (courses.length == 0) {
window.location.href = "/search/all_courses";
} else {


let wrapper = document.getElementById("application");
try {
if(wrapper != null){
myCoursesWithSettings.forEach(course => {
course.enrolled = true;
});
const app = createApp(MyCoursesPage, {courses: myCoursesWithSettings});

let myCourses = wrapper.appendChild(document.createElement("div"));
myCourses.setAttribute("id", "my-courses-container");
myCourses.setAttribute("style", "width: 100%; justify-content: center; display: flex; min-height: 85vh;");
let footerNode = document.getElementById("wrapper");
footerNode.parentNode.insertBefore(myCourses, footerNode);
document.getElementById('wrapper').innerHTML = ''
$('#wrapper').remove();
document.getElementById('loader').remove();
app.mount("#my-courses-container");

}
}
catch(err) {
console.error(err);
}

}
catch(err) {
console.error(err);
}

}
$.isFunction(callback) && callback();
});
$.isFunction(callback) && callback();
}, true);
}
});
} else {
renderPrivacyPolicyLink('terms_of_service_link');
Expand Down
168 changes: 110 additions & 58 deletions src/js/modules/enroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,87 @@ export default (function () {
this.addForgotPasswordLink();
this.updateGotoDashboardButton();
},
setCourseEnrolledStatus: function (allCourses, enrolledCourses) {
setCourseEnrolledStatus: function (allCourses, enrolledCourses, merge = false) {
var allCoursesWithStatus = [];
for (var i = 0; i < allCourses.length; i++) {
allCourses[i].course.enrolled = false;
allCourses[i].course.enrolled_status = null;
allCourses[i].course.course_progress = null;
for (var j = 0; j < enrolledCourses.length; j++) {
if (allCourses[i].course.id == enrolledCourses[j].id) {
let foundCourse = allCourses[i].course.id == enrolledCourses[j].id;
if (foundCourse) {
allCourses[i].course.enrolled_status = enrolledCourses[j].enrollments[0]?.enrollment_state;
allCourses[i].course.enrolled = true;
allCourses[i].course.course_progress = enrolledCourses[j].course_progress;
}
}

allCoursesWithStatus.push(allCourses[i].course);
}

//merge courses from enrolled that does not exist in the public all courses listing
if (merge) {
for (var j = 0; j < enrolledCourses.length; j++) {
let enrolledCourseItem = enrolledCourses[j];
let courseAlreadyExists = false;

for (var i = 0; i < allCoursesWithStatus.length; i++) {
let allCoursesWithStatusItem = allCoursesWithStatus[i];
if (enrolledCourseItem.id == allCoursesWithStatusItem.id) {
courseAlreadyExists = true;
}
}

if (!courseAlreadyExists) {
enrolledCourseItem.enrolled = true;
enrolledCourseItem.enrolled_status = enrolledCourseItem.enrollments[0]?.enrollment_state;
allCoursesWithStatus.unshift(enrolledCourseItem);
}
}
}

return allCoursesWithStatus;
},
printAllCourses: function () {
var self = this;
filterOnlyEnrolledCourses: function(courses) {
let enrolledCourses = [];
for (var j = 0; j < courses.length; j++) {
let courseItem = courses[j];
let isEnrolled = courseItem.enrolled == true;

if (isEnrolled) {
enrolledCourses.push(courseItem);
}
}

return enrolledCourses;
},
withEnrolledCourses: function(setCourseEnrolledStatus, frontpageData, callback, merge = false) {
let isAuthenticated = util.isAuthenticated();
if (!isAuthenticated) {
frontpageData.courses = setCourseEnrolledStatus(frontpageData.courses, [], merge);
callback(frontpageData);
} else {
api.getEnrolledCourses(function (enrolledCourses) {
frontpageData.courses = setCourseEnrolledStatus(frontpageData.courses, enrolledCourses, merge);
callback(frontpageData);
});
}
},
fetchFrontpageCourses: function() {
return new Promise(async (resolve, reject) => {
kpasApi.getCoursesForFrontpage(function (result, error) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
},
printAllCourses: async function () {
let withEnrolledCourses = this.withEnrolledCourses;
let setCourseEnrolledStatus = this.setCourseEnrolledStatus;

document.getElementById('content').innerHTML = "";
let loader = document.createElement('div');
loader.id = 'loader';
Expand All @@ -101,31 +165,29 @@ export default (function () {
header.insertAdjacentElement('afterend', loader);
loaderComponent.mount("#loader");

api.getAllPublicCourses(function (allCourses) {
api.getEnrolledCourses(function (enrolledCourses) {
var allCoursesWithStatus = self.setCourseEnrolledStatus(
allCourses,
enrolledCourses
);

kpasApi.getAllCourseSettings(function (allCoursesSettings) {
kpasApi.getAllFilters(function (allFilters) {
var allFiltersList = allFilters.result;
var allCoursesWithSettings = util.mapCourseSettings(allCoursesWithStatus, allCoursesSettings.result);
//Reverse list to show newest courses first
allCoursesWithSettings = allCoursesWithSettings.reverse();
var isAuthenticated = util.isAuthenticated();
util.fetchWithSwr("frontpage_courses", this.fetchFrontpageCourses, function(frontpageData) {
if (frontpageData && frontpageData.result) {
withEnrolledCourses(setCourseEnrolledStatus, frontpageData.result, function(dataWithEnrolled) {
let courses = dataWithEnrolled.courses;
let settings = dataWithEnrolled.settings;
let filters = dataWithEnrolled.filters;
let highlightedCourseId = dataWithEnrolled.highligthed != null ? dataWithEnrolled.highligthed.course_id : null;

var allCoursesWithSettings = util.mapCourseSettings(courses, settings);
allCoursesWithSettings = allCoursesWithSettings.reverse(); //Reverse list to show newest courses first
var isAuthenticated = util.isAuthenticated();
let wrapper = document.getElementById("content");

if(wrapper != null){
if (isAuthenticated) {
document.getElementById('content').innerHTML = "";
let wrapper = document.getElementById("content");
try {
if (wrapper != null) {
document.getElementById('content').innerHTML = "";
const customContent = document.createElement("div");
var mobiletablet = util.isMobileOrTablet();
let props = {
courses: allCoursesWithSettings,
filterData: allFiltersList,
mobiletablet: mobiletablet
courses : allCoursesWithSettings,
filterData : filters,
mobiletablet : mobiletablet
};
let page = createApp(LoggedInLandingPage, props);
customContent.setAttribute("id", "loggedInLandingPage");
Expand All @@ -136,49 +198,39 @@ export default (function () {
$('#wrapper').remove();
document.getElementById('loader').remove();
page.mount("#loggedInLandingPage");
}
} catch (e) {
console.log(e);
}
}
else {
} else {
try {
if (wrapper != null) {
kpasApi.getHighlightedCourse(function (highlightedCourse) {
var highlightedCourseId = highlightedCourse.result.course_id;
var allFiltersList = allFilters.result;
var allCoursesWithSettings = util.mapCourseSettings(allCoursesWithStatus, allCoursesSettings.result);

const customContent = document.createElement("div");
var highlightedCourse = allCoursesWithSettings.find(course => course.id == highlightedCourseId);
if (highlightedCourse == null || highlightedCourse == undefined) {
highlightedCourse = allCoursesWithSettings[0];
}
var mobiletablet = util.isMobileOrTablet();
let props = {
courses: allCoursesWithSettings,
filterData: allFiltersList,
highlightedCourse: highlightedCourse,
mobiletablet: mobiletablet
};
let page = createApp(NotLoggedInPage, props);
customContent.setAttribute("id", "notLoggedInPage");
customContent.setAttribute("style", "width: 100%; justify-content: center; display: flex;");
let footerNode = document.getElementById("wrapper");
footerNode.parentNode.insertBefore(customContent, footerNode)
document.getElementById('wrapper').innerHTML = '';
$('#wrapper').remove();
document.getElementById('loader').remove();
page.mount("#notLoggedInPage");
});
}
const customContent = document.createElement("div");
var highlightedCourse = allCoursesWithSettings.find(course => course.id == highlightedCourseId);
if(highlightedCourse == null || highlightedCourse == undefined) {
highlightedCourse = allCoursesWithSettings[0];
}
var mobiletablet = util.isMobileOrTablet();
let props = {
courses : allCoursesWithSettings,
filterData : filters,
highlightedCourse : highlightedCourse,
mobiletablet : mobiletablet
};
let page = createApp(NotLoggedInPage, props);
customContent.setAttribute("id", "notLoggedInPage");
customContent.setAttribute("style", "width: 100%; justify-content: center; display: flex;");
let footerNode = document.getElementById("wrapper");
footerNode.parentNode.insertBefore(customContent, footerNode)
document.getElementById('wrapper').innerHTML = '';
$('#wrapper').remove();
document.getElementById('loader').remove();
page.mount("#notLoggedInPage");
} catch (e) {
console.log(e);
}
}
});
}
});
});
}
});
},
};
Expand Down
39 changes: 39 additions & 0 deletions src/js/modules/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,45 @@ export default (function () {
}
}
}, 1000);
},

// V2: Add timeout support for revalidation, so that we can for example limit it to only call the API every 15 minutes
fetchWithSwr: function (key, fetcher, callback) {
//return stale content while data is revalidating in the background
//clear cache if data is not valid json/corrupted
let storageKey = `swr_${key}`
let initStorageDataIsSent = false;
let storageData = window.localStorage.getItem(storageKey);
if (storageData != null) {
try {
let parsedJsonData = JSON.parse(storageData);
callback(parsedJsonData);
initStorageDataIsSent = true;
} catch (error) {
window.localStorage.removeItem(storageKey);
}
}

//revalidate data when everything else has already loaded
//execute fetch data call right away if there is no cache response in storage
if (window.document.readyState != 'complete' && initStorageDataIsSent) {
window.addEventListener('load', () => {
this.swrFetcher(storageKey, fetcher, callback, storageData, initStorageDataIsSent);
});
} else {
this.swrFetcher(storageKey, fetcher, callback, storageData, initStorageDataIsSent);
}
},
swrFetcher: function (key, fetcher, callback, stringData, init) {
fetcher().then(function (res) {
let newStorageDataString = JSON.stringify(res);
let diffInData = stringData != newStorageDataString;

if (!init || diffInData) {
callback(res);
window.localStorage.setItem(key, newStorageDataString);
}
});
}
};
})();
Loading

0 comments on commit c15b410

Please sign in to comment.