From 312257493a5650f336e1b3551ac6349f92a2573f Mon Sep 17 00:00:00 2001 From: Guy Sandler Date: Sat, 25 Apr 2026 20:03:13 -0700 Subject: [PATCH] better todolist 4 animations, styles, announcement badge, and markings as un/complete --- README.md | 3 +- js/content.js | 126 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 97 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index c0b23d8..5a8612c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ Better Canvas introduces improvements to the Canvas user interface: - Themes created by users (broken due to fork) - Assignments due list - Dashboard notes -- Better todo list - Custom fonts - Condensed cards - Dashboard grades @@ -60,11 +59,11 @@ Better Canvas introduces improvements to the Canvas user interface: - Searching themes (the original didn't actually impliment that) - Card Styles (image size, card roundness, card spacing, width, height, theme compatible) - Custom Background (by URL, theme compatible) +- NEW Better todo list (todo: hover preview, cutoff, maybe fix some missing ones) ## Planned Features (by priority) - popup UI revamp - widgets (music, timer) -- update better todo list (with hover assignment preview) - better sidebar - better notes - auto rotate theme + theme history + fix theme submissions diff --git a/js/content.js b/js/content.js index d094ccd..9bbec9d 100644 --- a/js/content.js +++ b/js/content.js @@ -834,11 +834,23 @@ function convertToDueDate(dueAt) { return final; } function updateIndicator(element) { - const rect = element.getBoundingClientRect(); - const parentRect = element.parentElement.getBoundingClientRect(); const indicator = document.getElementById("better-todo-indicator"); indicator.style.width = `${element.offsetWidth*2}px`; indicator.style.left = `${element.offsetLeft - (element.offsetWidth * .5)}px`; + + const buttons = ["announcement", "assignments", "completed"]; + buttons.forEach(button => { + const btn = document.getElementById(`better-todo-${button}`); + if (btn == element) { + btn.firstElementChild.style.opacity = "1"; + // btn.style.filter = "none"; + } + else { + btn.firstElementChild.style.opacity = ".3"; + // btn.style.filter = "grayscale(100%)"; + } + }) + } // better todo html betterTodoFilter = "tasks"; @@ -858,9 +870,9 @@ async function createTodoSections(location) { let filterControl = makeElement("div", location, { "id": "better-todo-filter" }); filterControl.innerHTML = `
-
+
- + @@ -868,7 +880,7 @@ async function createTodoSections(location) {
- + @@ -876,7 +888,7 @@ async function createTodoSections(location) {
- + @@ -925,8 +937,26 @@ async function createTodoSections(location) { assignmentsDue = data.filter((item) => (item.plannable_type == "assignment" || item.plannable_type == "planner_note") && !item.submissions?.submitted && !item.planner_override?.marked_complete && !item.submissions.graded); completed = data.filter(item => (item.plannable_type == "assignment" || item.plannable_type == "planner_note") && (item.submissions.submitted || item.planner_override?.marked_complete || item.submissions.graded)); }); - console.log(assignmentsDue); - console.log(announcements); + console.log("assignments", assignmentsDue); + console.log("announcements", announcements); + console.log("completed", completed); + + if (!document.getElementById("better-todo-announcement-badge")) { + let isAnnoucementBadge = 0; + announcements.forEach(item => { + if (item.plannable.read_state == "unread") { + isAnnoucementBadge++; + return; + } + }) + if (isAnnoucementBadge > 0) { + makeElement("div", document.getElementById("better-todo-announcement"), { + id: "better-todo-announcement-badge", + style: "background-color:#ff0000;width:15px;height:15px;border-radius:50%;font-size:12px;position:absolute;top:-7px;left:16px;display:flex;justify-content:center;align-items:center;", // TODO: theme compatibility + innerHTML: `${isAnnoucementBadge}` + }) + } + } domContainers = {}; const groupKeys = ["-1", "0", "1", "2", "3", "4", "5", "6", "7", "14", "21", "30", "Later", "New", "Seen", "Ungraded", "Graded"]; @@ -984,16 +1014,20 @@ function populateAssignments(iscompleted = false) { assignments.forEach((item) => { let dueGroup = -1; - let dueDate = new Date(item.plannable_date); - dueDate.setHours(0,0,0,0); - const diffDays = Math.round((dueDate - today) / (1000 * 60 * 60 * 24)); - if (diffDays < 0) {dueGroup = 0;} - else if (diffDays <= 1) { dueGroup = diffDays.toString(); } - else if (diffDays <= 7) { dueGroup = diffDays.toString(); } - else if (diffDays <= 14) {dueGroup = 14;} - else if (diffDays <= 21) {dueGroup = 21;} - else if (diffDays <= 30) {dueGroup = 30;} - else {dueGroup = "Later"}; + if (!iscompleted) { + let dueDate = new Date(item.plannable_date); + dueDate.setHours(0,0,0,0); + const diffDays = Math.round((dueDate - today) / (1000 * 60 * 60 * 24)); + if (diffDays < 0) {dueGroup = 0;} + else if (diffDays <= 1) { dueGroup = diffDays.toString(); } + else if (diffDays <= 7) { dueGroup = diffDays.toString(); } + else if (diffDays <= 14) {dueGroup = 14;} + else if (diffDays <= 21) {dueGroup = 21;} + else if (diffDays <= 30) {dueGroup = 30;} + else {dueGroup = "Later"}; + } else { + dueGroup = item.submissions?.graded ? "Graded" : "Ungraded"; + } let assignment const targetContainer = domContainers[dueGroup]; @@ -1022,15 +1056,26 @@ function populateAssignments(iscompleted = false) {
-
+
${item.context_name} ${item.plannable.title} ${convertToDueDate(item.plannable_date)}
+ + + + + + +
`; + assignment.querySelector(".better-todo-assignment-checkmark").addEventListener("click", () => { + console.log("marking ", item.plannable.title, " as complete"); + markAs(item) + }); }); } @@ -1039,19 +1084,10 @@ function populateAnnouncements() { today.setHours(0,0,0,0); announcements.forEach((item) => { - let dueGroup = -1; - let dueDate = new Date(item.plannable_date); - dueDate.setHours(0,0,0,0); - const diffDays = Math.round((dueDate - today) / (1000 * 60 * 60 * 24)); - if (diffDays < 0) {dueGroup = 0;} - else if (diffDays <= 1) { dueGroup = diffDays.toString(); } - else if (diffDays <= 7) { dueGroup = diffDays.toString(); } - else if (diffDays <= 14) {dueGroup = 14;} - else if (diffDays <= 21) {dueGroup = 21;} - else if (diffDays <= 30) {dueGroup = 30;} - else {dueGroup = "Later"}; + let dueGroup = item.plannable.read_state == "read" ? "Seen" : "New"; let announcement; + // console.log(domContainers) const targetContainer = domContainers[dueGroup]; if (targetContainer) { targetContainer.wrapper.style.display = "block"; @@ -1094,6 +1130,36 @@ function populateAnnouncements() { }); } +function markAs(item) { + const csrfToken = CSRFtoken(); + const completeState = item.planner_override ? !item.planner_override.marked_complete : true; + fetch(domain + "/api/v1/planner/overrides/" + (item.planner_override ? "/" + item.planner_override.id : ""), { + method: item.planner_override ? "PUT" : "POST", + headers: { + "content-type":"application/json", + "accept":"application/json", + "X-CSRF-Token": csrfToken + }, + body: JSON.stringify({ + id: item.planner_override ? item.planner_override.id : null, + marked_complete: completeState, + plannable_id: item.plannable_id, + plannable_type: item.plannable_type + }) + }) + .then(resp => { + if (resp.status == 200 || resp.status == 201) { + console.log("marked as complete"); + item.planner_override = item.planner_override || {}; + item.planner_override.marked_complete = completeState; + clearTodoList(); + createTodoSections(document.querySelector("#bettercanvas-todo-list")); + } + }) + .catch(err => console.error("error marking as complete", err)); + +} + function createTodoViewMore(location, type) { let viewMoreButton = makeElement("button", location, { "className": "bettercanvas-custom-btn bettercanvas-viewmore-btn", "textContent": "View More" }); //viewMoreButton.classList.add("bettercanvas-viewmore-btn");