diff --git a/js/content.js b/js/content.js
index 2ed885d..dde20a5 100644
--- a/js/content.js
+++ b/js/content.js
@@ -2515,7 +2515,7 @@ function renderProgressRings(container, scopedData) {
const entries = Object.keys(groups).map(cid => {
const arr = groups[cid];
- const completed = arr.filter(it => (it.submissions?.submitted || it.planner_override?.marked_complete)).length;
+ const completed = arr.filter(it => (it.submissions?.submitted || it.planner_override?.marked_complete) && !isPinnedIncomplete(it)).length;
return { courseId: cid, total: arr.length, completed };
}).filter(e => e.total > 0);
@@ -3168,13 +3168,20 @@ async function createTodoSections(location) {
});
announcements = displayData.filter(item => item.plannable_type == "announcement");
- assignmentsDue = displayData.filter(item => isTodoTaskType(item) && !item.submissions?.submitted && !item.planner_override?.marked_complete);
- completed = displayData.filter(item => isTodoTaskType(item) && (item.submissions?.submitted || item.planner_override?.marked_complete));
+ // Pinned items (locally forced incomplete, e.g. a submitted assignment
+ // the user sent back to Tasks) always count as due; everything else is
+ // due only when neither submitted nor marked complete.
+ assignmentsDue = displayData.filter(item => isTodoTaskType(item) && ((!item.submissions?.submitted && !item.planner_override?.marked_complete) || isPinnedIncomplete(item)));
+ completed = displayData.filter(item => isTodoTaskType(item) && (item.submissions?.submitted || item.planner_override?.marked_complete) && !isPinnedIncomplete(item));
// The timeframe is a persisted Better Todo List sub-option set in the
// popup. Read the current value each render so popup changes apply on
// the next render. Only the Tasks tab is affected (announcements and
// the completed tab always show everything).
- assignmentsDue = applyTodoTimeframe(assignmentsDue);
+ // The timeframe filter only applies to the Tasks tab, but a pinned item
+ // must never be dropped from both tabs: exclude pins from the cutoff
+ // filter so a "sent back" old item still shows up under Tasks.
+ const pinnedItems = assignmentsDue.filter(item => isPinnedIncomplete(item));
+ assignmentsDue = applyTodoTimeframe(assignmentsDue.filter(item => !isPinnedIncomplete(item))).concat(pinnedItems);
// console.log("assignments", assignmentsDue);
// console.log("announcements", announcements);
// console.log("completed", completed);
@@ -3478,6 +3485,11 @@ function populateAssignments(iscompleted = false) {
}
return new Date(b.plannable_date) - new Date(a.plannable_date);
});
+ } else {
+ // Keep a stable chronological order (overdue first) so the visible
+ // item budget below counts from due today onward. The planner API
+ // normally returns ascending order already; this makes it guaranteed.
+ assignments.sort((a, b) => new Date(a.plannable_date) - new Date(b.plannable_date));
}
let assignmentCount = 0;
@@ -3502,8 +3514,12 @@ function populateAssignments(iscompleted = false) {
let assignment
const targetContainer = domContainers[dueGroup];
- assignmentCount++;
- let isHidden = assignmentCount > maxElements;
+ // Overdue items are always shown and don't consume the visible-item
+ // budget: the count starts at items due today. (On the Completed tab,
+ // whose groups are Graded/Ungraded, every item still counts.)
+ const isOverdue = !iscompleted && dueGroup === -1;
+ if (!isOverdue) assignmentCount++;
+ let isHidden = !isOverdue && assignmentCount > maxElements;
if (targetContainer) {
if (!isHidden) {
@@ -3545,6 +3561,10 @@ function populateAssignments(iscompleted = false) {
const removeIcons = options.todo_remove_icons === true;
const isCustomTask = item.plannable_type == "planner_note" || item.planner_override?.custom === true;
+ // True when Canvas has a real submission for this item: the Completed
+ // list shows it regardless of the checkmark state, so the toggle is
+ // disabled for it (see the checkmark listener below).
+ const wasSubmitted = item.submissions?.submitted === true;
const taskHref = isCustomTask ? customTaskHref(item) : (domain + item.html_url);
const editButtonSvg = isCustomTask
? ``
@@ -3577,7 +3597,7 @@ function populateAssignments(iscompleted = false) {
${convertToDueDate(item.plannable_date)}
${editButtonSvg}
-