diff --git a/README.md b/README.md index 817ba66..c38dbd6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Colors course site tabs according to the assignment due date. Due date within 14 Days ## Notification Badge -Tells your unchecked latest assignments. +Tells your **unchecked** latest assignments. Notification badge will appear in the upper left-hand side of a course site tab. If you open a course site with the notification badge on, the badge will disappear. @@ -50,8 +50,8 @@ You can add your custom assignment to miniSakai as `memo` with PLUS button locat Also check box is available for you to distinguish completed assignments from working assignments. ## Cache -In order to reduce the network load on Sakai LMS, we have implemented a cache function for getting assignments and quizzes from REST API. -The default cache interval is as follows +In order to reduce the network load on Sakai LMS, we have implemented a cache function for fetching assignments and quizzes from REST API. +The default cache interval is as follows: - Assignment fetching --- 2 minutes - Quiz fetching --- 10 minutes diff --git a/manifest.json b/manifest.json index ba3e257..0dfcad6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Comfortable Sakai", "description": "__MSG_EXTENSION_DESCRIPTION__", - "version": "1.2.0", + "version": "1.2.1", "manifest_version": 2, "default_locale": "en", "icons": { @@ -36,7 +36,7 @@ "web_accessible_resources": [ "css/comfortable-sakai.css", "img/logo.png", - "img/relaxPanda.png", + "img/noAssignment.png", "img/miniSakaiBtn.png", "views/minisakai.mustache" ] diff --git a/package.json b/package.json index ca76a43..217275b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "comfortable-sakai", "version": "1.1.0", - "description": "Comfortable Sakai is a browser extension for managing assignments and quizzes on Sakai LMS.", + "description": "Comfortable Sakai is a Web browser extension for managing assignments and quizzes on Sakai LMS.", "main": "index.ts", "scripts": { "test": "jest", diff --git a/public/img/relaxPanda.png b/public/img/noAssignment.png similarity index 100% rename from public/img/relaxPanda.png rename to public/img/noAssignment.png diff --git a/public/views/minisakai.mustache b/public/views/minisakai.mustache index 6315391..cd78b91 100644 --- a/public/views/minisakai.mustache +++ b/public/views/minisakai.mustache @@ -14,7 +14,7 @@

{{titles.quizFetchedTime}}

-

{{fetchedTime.assignment}}

+

{{fetchedTime.quiz}}

{{/subset}} @@ -213,12 +213,12 @@ {{/display.lateSubmit}} - {{#showRelaxPandA}} + {{#noAssignment}}
-

{{titles.relaxPandA}}

+

{{titles.noAssignment}}

logo
- {{/showRelaxPandA}} + {{/noAssignment}} {{^subset}} diff --git a/src/content_script.ts b/src/content_script.ts index b625ae4..e817e9b 100644 --- a/src/content_script.ts +++ b/src/content_script.ts @@ -1,6 +1,6 @@ import { loadFromLocalStorage, saveToLocalStorage } from "./storage"; import { Assignment, CourseSiteInfo } from "./model"; -import { getCourseIDList, getBaseURL, getAssignmentByCourseID, getQuizFromCourseID } from "./network"; +import { getCourseIDList, getAssignmentByCourseID, getQuizFromCourseID } from "./network"; import { createMiniSakaiBtn, createFavoritesBarNotification, displayMiniSakai } from "./minisakai"; import { @@ -14,26 +14,20 @@ import { updateIsReadFlag, useCache, } from "./utils"; -import { Settings, loadSettings } from "./settings"; +import { Config, loadConfigs } from "./settings"; -export const baseURL = getBaseURL(); -export const VERSION = chrome.runtime.getManifest().version; -export let assignmentCacheInterval: number; -export let quizCacheInterval: number; -export let assignmentFetchedTime: number | undefined; -export let quizFetchedTime: number | undefined; export let courseIDList: Array; export let mergedAssignmentList: Array; export let mergedAssignmentListNoMemo: Array; -export let CPsettings: Settings; /** * Load old assignments/quizzes from storage and fetch new assignments/quizzes from Sakai. + * @param {Config} config * @param {CourseSiteInfo[]} courseSiteInfos * @param {boolean} useAssignmentCache * @param {boolean} useQuizCache */ -export async function loadAndMergeAssignmentList(courseSiteInfos: Array, useAssignmentCache: boolean, useQuizCache: boolean): Promise> { +export async function loadAndMergeAssignmentList(config: Config ,courseSiteInfos: Array, useAssignmentCache: boolean, useQuizCache: boolean): Promise> { // Load old assignments and quizzes from local storage const oldAssignmentList = convertArrayToAssignment(await loadFromLocalStorage("CS_AssignmentList")); const oldQuizList = convertArrayToAssignment(await loadFromLocalStorage("CS_QuizList")); @@ -48,7 +42,7 @@ export async function loadAndMergeAssignmentList(courseSiteInfos: Array { */ async function updateSettings(event: any, type: string): Promise { const settingsID = event.target.id; - let settingsValue = event.currentTarget.value; + let settingsValue = event.target.value; + const config = await loadConfigs(); + const CSsettings = config.CSsettings; // Type of Settings switch (type) { case "check": - settingsValue = event.currentTarget.checked; + settingsValue = event.target.checked; break; case "number": - settingsValue = parseInt(event.currentTarget.value); + settingsValue = parseInt(event.target.value); break; case "string": break; @@ -146,7 +148,7 @@ async function updateSettings(event: any, type: string): Promise { ]; for (const k of colorList) { // @ts-ignore - CPsettings[k] = DefaultSettings[k]; + CSsettings[k] = DefaultSettings[k]; const q = document.getElementById(k); if (q) { // @ts-ignore @@ -155,10 +157,10 @@ async function updateSettings(event: any, type: string): Promise { } } else { // @ts-ignore - CPsettings[settingsID] = settingsValue; + CSsettings[settingsID] = settingsValue; } - saveToLocalStorage("CS_Settings", CPsettings); + await saveToLocalStorage("CS_Settings", CSsettings); await redrawFavoritesBar(courseIDList, true); } @@ -253,8 +255,9 @@ async function editFavoritesMessage(): Promise { */ async function redrawFavoritesBar(courseIDList: Array, useCache: boolean): Promise { deleteFavoritesBarNotification(); - const newAssignmentList = await loadAndMergeAssignmentList(courseIDList, useCache, useCache); - createFavoritesBarNotification(courseIDList, newAssignmentList); + const config = await loadConfigs(); + const newAssignmentList = await loadAndMergeAssignmentList(config, courseIDList, useCache, useCache); + await createFavoritesBarNotification(courseIDList, newAssignmentList); } /** diff --git a/src/minisakai.ts b/src/minisakai.ts index 2fa94c3..122c2c9 100644 --- a/src/minisakai.ts +++ b/src/minisakai.ts @@ -1,7 +1,6 @@ import { Assignment, CourseSiteInfo, DisplayAssignment, DisplayAssignmentEntry } from "./model"; import { createCourseIDMap, getDaysUntil, formatTimestamp, nowTime } from "./utils"; import { appendChildAll, cloneElem, hamburger, miniSakai, SettingsDom } from "./dom"; -import { CPsettings, assignmentFetchedTime, quizFetchedTime, VERSION } from "./content_script"; import { addMemo, deleteMemo, @@ -14,6 +13,7 @@ import { } from "./eventListener"; // @ts-ignore import Mustache = require("mustache"); +import { loadConfigs } from "./settings"; /** * Create a button to open miniSakai @@ -30,10 +30,10 @@ function createMiniSakaiBtn(): void { /** * Using template engine to generate miniSakai list. */ -export function createMiniSakaiGeneralized(root: Element, assignmentList: Array, courseSiteInfos: Array, subset: boolean, insertionProcess: (rendered: string) => void): void { - const assignmentFetchedTimeString = formatTimestamp(assignmentFetchedTime); - const quizFetchedTimeString = formatTimestamp(quizFetchedTime); - +export async function createMiniSakaiGeneralized(root: Element, assignmentList: Array, courseSiteInfos: Array, subset: boolean, insertionProcess: (rendered: string) => void): Promise { + const config = await loadConfigs(); + const assignmentFetchedTimeString = formatTimestamp(config.fetchedTime.assignment); + const quizFetchedTimeString = formatTimestamp(config.fetchedTime.quiz); const courseSiteList: Array = []; const courseIDMap = createCourseIDMap(courseSiteInfos); @@ -90,7 +90,7 @@ export function createMiniSakaiGeneralized(root: Element, assignmentList: Array< appendElement(courseName, otherElements); break; case "duePassed": - const showLateSubmitAssignment = CPsettings ? CPsettings.getDisplayLateSubmitAssignment : false; + const showLateSubmitAssignment = config.CSsettings ? config.CSsettings.getDisplayLateSubmitAssignment : false; if (showLateSubmitAssignment && getDaysUntil(nowTime, assignmentEntry.getCloseDateTimestamp * 1000) !== "duePassed") { appendElement(courseName, lateSubmitElements); } @@ -114,7 +114,7 @@ export function createMiniSakaiGeneralized(root: Element, assignmentList: Array< return elements; }; - let relaxPandA = null; + let noAssignmentImg = null; let assignmentCnt = 0; if (assignmentList.length !== 0) { for (const assignment of assignmentList) { @@ -122,20 +122,21 @@ export function createMiniSakaiGeneralized(root: Element, assignmentList: Array< } } if (assignmentList.length === 0 || assignmentCnt === 0) { - relaxPandA = { - img: chrome.extension.getURL("img/relaxPanda.png"), + noAssignmentImg = { + img: chrome.extension.getURL("img/noAssignment.png"), }; } + // Create dict of data for miniSakai const templateVars = { fetchedTime: { assignment: assignmentFetchedTimeString, quiz: quizFetchedTimeString, }, miniSakaiLogo: chrome.extension.getURL("img/logo.png"), - VERSION: VERSION, + VERSION: config.version, subset: subset, - showRelaxPandA: relaxPandA, + noAssignment: noAssignmentImg, elements: { danger: sortElements(dangerElements), warning: sortElements(warningElements), @@ -160,7 +161,7 @@ export function createMiniSakaiGeneralized(root: Element, assignmentList: Array< due14d: chrome.i18n.getMessage("due14d"), dueOver14d: chrome.i18n.getMessage("dueOver14d"), duePassed: chrome.i18n.getMessage("duePassed"), - relaxPandA: chrome.i18n.getMessage("no_available_assignments"), + noAssignment: chrome.i18n.getMessage("no_available_assignments"), }, todoBox: { courseName: chrome.i18n.getMessage("todo_box_course_name"), @@ -190,8 +191,8 @@ export function createMiniSakaiGeneralized(root: Element, assignmentList: Array< /** * Insert miniSakai into Sakai. */ -function createMiniSakai(assignmentList: Array, courseSiteInfos: Array): void { - createMiniSakaiGeneralized(miniSakai, assignmentList, courseSiteInfos, false, (rendered) => { +async function createMiniSakai(assignmentList: Array, courseSiteInfos: Array): Promise { + await createMiniSakaiGeneralized(miniSakai, assignmentList, courseSiteInfos, false, (rendered) => { miniSakai.innerHTML = rendered; const parent = document.getElementById("pageBody"); const ref = document.getElementById("toolMenuWrap"); @@ -202,19 +203,20 @@ function createMiniSakai(assignmentList: Array, courseSiteInfos: Arr /** * Initialize Settings tab. */ -async function createSettingsTab(root: Element): Promise { - createSettingItem(root, chrome.i18n.getMessage('settings_color_checked_item'), CPsettings.getDisplayCheckedAssignment, "displayCheckedAssignment"); - createSettingItem(root, chrome.i18n.getMessage('settings_display_late_submit_assignment'), CPsettings.getDisplayLateSubmitAssignment, "displayLateSubmitAssignment"); - createSettingItem(root, chrome.i18n.getMessage('settings_assignment_cache'), CPsettings.getAssignmentCacheInterval, "assignmentCacheInterval"); - createSettingItem(root, chrome.i18n.getMessage('settings_quizzes_cache'), CPsettings.getQuizCacheInterval, "quizCacheInterval"); +async function createSettingsTab(root: Element) { + const config = await loadConfigs(); + createSettingItem(root, chrome.i18n.getMessage('settings_color_checked_item'), config.CSsettings.getDisplayCheckedAssignment, "displayCheckedAssignment"); + createSettingItem(root, chrome.i18n.getMessage('settings_display_late_submit_assignment'), config.CSsettings.getDisplayLateSubmitAssignment, "displayLateSubmitAssignment"); + createSettingItem(root, chrome.i18n.getMessage('settings_assignment_cache'), config.CSsettings.getAssignmentCacheInterval, "assignmentCacheInterval"); + createSettingItem(root, chrome.i18n.getMessage('settings_quizzes_cache'), config.CSsettings.getQuizCacheInterval, "quizCacheInterval"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_hour', ['1', 24]), CPsettings.getTopColorDanger, "topColorDanger"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['1', 5]), CPsettings.getTopColorWarning, "topColorWarning"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['1', 14]), CPsettings.getTopColorSuccess, "topColorSuccess"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_hour', ['1', 24]), config.CSsettings.getTopColorDanger, "topColorDanger"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['1', 5]), config.CSsettings.getTopColorWarning, "topColorWarning"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['1', 14]), config.CSsettings.getTopColorSuccess, "topColorSuccess"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_hour', ['2', 24]), CPsettings.getMiniColorDanger, "miniColorDanger"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['2', 5]), CPsettings.getMiniColorWarning, "miniColorWarning"); - createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['2', 14]), CPsettings.getMiniColorSuccess, "miniColorSuccess"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_hour', ['2', 24]), config.CSsettings.getMiniColorDanger, "miniColorDanger"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['2', 5]), config.CSsettings.getMiniColorWarning, "miniColorWarning"); + createSettingItem(root, chrome.i18n.getMessage('settings_colors_day', ['2', 14]), config.CSsettings.getMiniColorSuccess, "miniColorSuccess"); createSettingItem(root, chrome.i18n.getMessage("settings_reset_colors"), "reset", "reset"); // @ts-ignore @@ -292,13 +294,14 @@ function initState(root: Element) { * Display miniSakai */ async function displayMiniSakai(mergedAssignmentList: Array, courseSiteInfos: Array): Promise{ - createMiniSakai(mergedAssignmentList, courseSiteInfos); + await createMiniSakai(mergedAssignmentList, courseSiteInfos); } /** * Add notification badge for new Assignment/Quiz */ -function createFavoritesBarNotification(courseSiteInfos: Array, assignmentList: Array): void { +async function createFavoritesBarNotification(courseSiteInfos: Array, assignmentList: Array): Promise { + const config = await loadConfigs(); const defaultTab = document.querySelectorAll(".Mrphs-sitesNav__menuitem"); const defaultTabCount = Object.keys(defaultTab).length; @@ -311,7 +314,7 @@ function createFavoritesBarNotification(courseSiteInfos: Array, return assignment.courseSiteInfo.courseID === courseID; }); if (q !== -1) { - const closestTime = (CPsettings.displayCheckedAssignment) ? assignmentList[q].closestDueDateTimestamp : assignmentList[q].closestDueDateTimestampExcludeFinished; + const closestTime = (config.CSsettings.displayCheckedAssignment) ? assignmentList[q].closestDueDateTimestamp : assignmentList[q].closestDueDateTimestampExcludeFinished; if (!assignmentList[q].isRead && closestTime !== -1) { defaultTab[j].classList.add("cs-notification-badge"); } @@ -347,7 +350,7 @@ function createFavoritesBarNotification(courseSiteInfos: Array, } } } - overrideCSSColor(); + await overrideCSSColor(); } /** @@ -368,11 +371,12 @@ function deleteFavoritesBarNotification(): void { /** * Override CSS of favorites bar and miniSakai. */ -function overrideCSSColor() { +async function overrideCSSColor() { + const config = await loadConfigs(); const overwriteborder = function (className: string, color: string | undefined) { - const dangerelem = document.getElementsByClassName(className); - for (let i = 0; i < dangerelem.length; i++) { - const elem = dangerelem[i] as HTMLElement; + const element = document.getElementsByClassName(className); + for (let i = 0; i < element.length; i++) { + const elem = element[i] as HTMLElement; const attr = "solid 2px " + color; (elem.style)["border-top"] = attr; (elem.style)["border-left"] = attr; @@ -381,25 +385,26 @@ function overrideCSSColor() { } }; const overwritebackground = function (className: string, color: string | undefined) { - const dangerelem = document.getElementsByClassName(className); - for (let i = 0; i < dangerelem.length; i++) { - const elem = dangerelem[i] as HTMLElement; + const element = document.getElementsByClassName(className); + for (let i = 0; i < element.length; i++) { + const elem = element[i] as HTMLElement; elem.setAttribute("style", "background:" + color + "!important"); } }; - overwriteborder("cs-assignment-danger", CPsettings.getMiniColorDanger); - overwriteborder("cs-assignment-success", CPsettings.getMiniColorSuccess); - overwriteborder("cs-assignment-warning", CPsettings.getMiniColorWarning); - overwritebackground("cs-course-danger", CPsettings.getMiniColorDanger); - overwritebackground("cs-course-success", CPsettings.getMiniColorSuccess); - overwritebackground("cs-course-warning", CPsettings.getMiniColorWarning); - overwritebackground("cs-tab-danger", CPsettings.getTopColorDanger); - overwritebackground("cs-tab-success", CPsettings.getTopColorSuccess); - overwritebackground("cs-tab-warning", CPsettings.getTopColorWarning); - overwriteborder("cs-tab-danger", CPsettings.getTopColorDanger); - overwriteborder("cs-tab-success", CPsettings.getTopColorSuccess); - overwriteborder("cs-tab-warning", CPsettings.getTopColorWarning); + // Overwrite colors + overwritebackground("cs-course-danger", config.CSsettings.getMiniColorDanger); + overwritebackground("cs-course-warning", config.CSsettings.getMiniColorWarning); + overwritebackground("cs-course-success", config.CSsettings.getMiniColorSuccess); + overwritebackground("cs-tab-danger", config.CSsettings.getTopColorDanger); + overwritebackground("cs-tab-warning", config.CSsettings.getTopColorWarning); + overwritebackground("cs-tab-success", config.CSsettings.getTopColorSuccess); + overwriteborder("cs-assignment-danger", config.CSsettings.getMiniColorDanger); + overwriteborder("cs-assignment-warning", config.CSsettings.getMiniColorWarning); + overwriteborder("cs-assignment-success", config.CSsettings.getMiniColorSuccess); + overwriteborder("cs-tab-danger", config.CSsettings.getTopColorDanger); + overwriteborder("cs-tab-warning", config.CSsettings.getTopColorWarning); + overwriteborder("cs-tab-success", config.CSsettings.getTopColorSuccess); } export { diff --git a/src/settings.ts b/src/settings.ts index 3ec9482..c17f1a5 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,5 +1,6 @@ import { loadFromLocalStorage } from "./storage"; import { convertArrayToSettings } from "./utils"; +import { getBaseURL } from "./network"; export class Settings { assignmentCacheInterval?: number; @@ -67,7 +68,48 @@ export class DefaultSettings extends Settings { export async function loadSettings(): Promise { const settingsArr = await loadFromLocalStorage("CS_Settings"); - const CPsettings = convertArrayToSettings(settingsArr); - CPsettings.displayCheckedAssignment = CPsettings.getDisplayCheckedAssignment; - return CPsettings; + const CSsettings = convertArrayToSettings(settingsArr); + CSsettings.displayCheckedAssignment = CSsettings.getDisplayCheckedAssignment; + return CSsettings; +} + +export interface Config { + baseURL: string; + version: string; + CSsettings: Settings; + fetchedTime: { + assignment: number; + quiz: number; + }; + cacheInterval: { + assignment: number; + quiz: number; + }; +} + +/** + * Load configurations from local storage + */ +export async function loadConfigs(): Promise { + const baseURL = getBaseURL(); + const VERSION = chrome.runtime.getManifest().version; + const CSsettings = await loadSettings(); + CSsettings.displayCheckedAssignment = CSsettings.getDisplayCheckedAssignment; + const assignmentCacheInterval = CSsettings.getAssignmentCacheInterval; + const quizCacheInterval = CSsettings.getQuizCacheInterval; + const assignmentFetchedTime = await loadFromLocalStorage("CS_AssignmentFetchTime", "undefined"); + const quizFetchedTime = await loadFromLocalStorage("CS_QuizFetchTime", "undefined"); + return { + baseURL: baseURL, + version: VERSION, + CSsettings: CSsettings, + cacheInterval: { + assignment: assignmentCacheInterval, + quiz: quizCacheInterval, + }, + fetchedTime: { + assignment: assignmentFetchedTime, + quiz: quizFetchedTime, + }, + }; } diff --git a/src/subsakai.ts b/src/subsakai.ts index b86dbee..58cf4eb 100644 --- a/src/subsakai.ts +++ b/src/subsakai.ts @@ -20,7 +20,7 @@ async function updateSubSakai(root: Element) { mergedAssignmentList = mergeIntoAssignmentList(mergedAssignmentList, assignmentMemoList); mergedAssignmentList = sortAssignmentList(mergedAssignmentList); - createMiniSakaiGeneralized(root, mergedAssignmentList, courseIDs, true, (rendered) => { + await createMiniSakaiGeneralized(root, mergedAssignmentList, courseIDs, true, (rendered) => { console.log(rendered); root.innerHTML = rendered; });