mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-09-22 13:04:53 +02:00
fixed return to assignment button
This commit is contained in:
parent
11bdfb0b3b
commit
97b2612062
117
js/content.js
117
js/content.js
@ -81,29 +81,60 @@ function quizSafeModeActive() {
|
|||||||
return isQuizPage() && options.quiz_safe_mode === true;
|
return isQuizPage() && options.quiz_safe_mode === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the URL live: Canvas' "New Canvas" UI navigates client-side via
|
||||||
|
// history.pushState/replaceState, and current_page (captured at document_start)
|
||||||
|
// can be stale when the user clicks into a submission page. Reading
|
||||||
|
// window.location.pathname at check time makes this correct regardless.
|
||||||
function getSubmissionAssignmentLink() {
|
function getSubmissionAssignmentLink() {
|
||||||
const match = current_page.match(/^\/courses\/(\d+)\/assignments\/(\d+)\/submissions\/(\d+)(?:\/|$)/);
|
const match = window.location.pathname.match(/^\/courses\/(\d+)\/assignments\/(\d+)\/submissions\/(\d+)(?:\/|$)/);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
return `${domain}/courses/${match[1]}/assignments/${match[2]}/`;
|
return `${domain}/courses/${match[1]}/assignments/${match[2]}/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The content container isn't always #content on every Canvas layout (submission
|
||||||
|
// pages in particular may render into .ic-Layout-contentMain or #main). Match the
|
||||||
|
// quiz-safe-mode banner's container finder so the button lands in the visible
|
||||||
|
// content area; fall back to <body> so injection never silently no-ops.
|
||||||
|
function findContentContainer() {
|
||||||
|
return document.querySelector(".ic-Layout-contentMain")
|
||||||
|
|| document.getElementById("content")
|
||||||
|
|| document.querySelector("#main")
|
||||||
|
|| document.body;
|
||||||
|
}
|
||||||
|
|
||||||
let submissionPageButtonObserver = null;
|
let submissionPageButtonObserver = null;
|
||||||
|
let submissionButtonScheduled = false;
|
||||||
let profileLogoutButtonObserver = null;
|
let profileLogoutButtonObserver = null;
|
||||||
let newCanvasButtonObserver = null;
|
let newCanvasButtonObserver = null;
|
||||||
|
|
||||||
function addSubmissionPageButton() {
|
function addSubmissionPageButton() {
|
||||||
const assignmentLink = getSubmissionAssignmentLink();
|
const assignmentLink = getSubmissionAssignmentLink();
|
||||||
if (!assignmentLink) return;
|
if (!assignmentLink) return;
|
||||||
const content = document.getElementById("content");
|
// Place the button inline with the "Submission Details" heading and the
|
||||||
if (!content || content.querySelector("#canvasrefined-assignment-return")) return;
|
// grade-values table, inside the .submission-details-header__heading-and-grades
|
||||||
|
// flex row (appended so it sits to the right of the grade summary). Only inject
|
||||||
|
// once that row exists; if it's not there yet the persistent MutationObserver
|
||||||
|
// re-tries on the next DOM change so we never fall back to body/#content
|
||||||
|
// (which would put the button at the bottom of the page).
|
||||||
|
const row = document.querySelector(".submission-details-header__heading-and-grades")
|
||||||
|
|| document.querySelector(".submission-details-header")
|
||||||
|
|| document.querySelector(".submission_details");
|
||||||
|
if (!row || row.querySelector("#canvasrefined-assignment-return")) return;
|
||||||
|
|
||||||
makeElement("a", content, {
|
// Insert between the h1 heading and the grade-summary div so it reads
|
||||||
|
// [Heading] [Back to Assignment] [Grade]. Falls back to appending if the
|
||||||
|
// grade-summary div isn't found for some reason.
|
||||||
|
const gradeSummary = row.querySelector(".submission-details-header__grade-summary");
|
||||||
|
const btn = makeElement("a", row, {
|
||||||
id: "canvasrefined-assignment-return",
|
id: "canvasrefined-assignment-return",
|
||||||
className: "canvasrefined-custom-btn",
|
className: "canvasrefined-custom-btn",
|
||||||
href: assignmentLink,
|
href: assignmentLink,
|
||||||
textContent: "Back to Assignment",
|
textContent: "Back to Assignment",
|
||||||
style: "display:inline-flex;align-items:center;justify-content:center;align-self:flex-start;margin:0 0 12px 0;padding:10px 14px;text-decoration:none;font-weight:700;",
|
style: "display:inline-flex;align-items:center;justify-content:center;align-self:center;margin-left:auto;margin-right:12px;padding:6px 12px;text-decoration:none;font-weight:700;color:inherit!important;",
|
||||||
}, true);
|
});
|
||||||
|
if (gradeSummary && gradeSummary.parentNode === row) {
|
||||||
|
row.insertBefore(btn, gradeSummary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addProfileLogoutPageButton() {
|
function addProfileLogoutPageButton() {
|
||||||
@ -153,19 +184,26 @@ function watchProfileLogoutPageButton() {
|
|||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureSubmissionPageButton() {
|
// Reconcile the button against the current page on a rAF-throttled schedule.
|
||||||
const assignmentLink = getSubmissionAssignmentLink();
|
// Canvas (React-based "New Canvas" UI) re-renders the content area on SPA
|
||||||
if (!assignmentLink) return false;
|
// navigation and can wipe our injected node; a persistent observer re-adds it.
|
||||||
const content = document.getElementById("content");
|
function maintainSubmissionPageButton() {
|
||||||
if (!content) return false;
|
if (submissionButtonScheduled) return;
|
||||||
const existing = content.querySelector("#canvasrefined-assignment-return");
|
submissionButtonScheduled = true;
|
||||||
if (existing) {
|
requestAnimationFrame(() => {
|
||||||
// Keep the href in sync when navigating between submission pages via SPA routing.
|
submissionButtonScheduled = false;
|
||||||
existing.href = assignmentLink;
|
const link = getSubmissionAssignmentLink();
|
||||||
return true;
|
const existing = document.getElementById("canvasrefined-assignment-return");
|
||||||
}
|
if (!link) {
|
||||||
addSubmissionPageButton();
|
existing?.remove();
|
||||||
return Boolean(content.querySelector("#canvasrefined-assignment-return"));
|
return;
|
||||||
|
}
|
||||||
|
if (existing) {
|
||||||
|
if (existing.href !== link) existing.href = link;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addSubmissionPageButton();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAssignmentPage() {
|
function isAssignmentPage() {
|
||||||
@ -201,29 +239,19 @@ function watchSequenceFooter() {
|
|||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// One persistent, rAF-throttled observer that keeps the button present whenever
|
||||||
|
// we're on a submission page. Unlike the old 10s-disconnecting observer, this
|
||||||
|
// survives Canvas' post-navigation re-renders that remove injected nodes. The
|
||||||
|
// extra delayed checks cover React hydration that wipes the button after our
|
||||||
|
// first add without emitting any later mutation for the observer to catch.
|
||||||
function watchSubmissionPageButton() {
|
function watchSubmissionPageButton() {
|
||||||
if (!getSubmissionAssignmentLink()) {
|
|
||||||
// Navigated away from a submission page; remove any stale button left in #content.
|
|
||||||
document.getElementById("canvasrefined-assignment-return")?.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ensureSubmissionPageButton()) return;
|
|
||||||
if (submissionPageButtonObserver) return;
|
if (submissionPageButtonObserver) return;
|
||||||
|
maintainSubmissionPageButton();
|
||||||
submissionPageButtonObserver = new MutationObserver(() => {
|
submissionPageButtonObserver = new MutationObserver(maintainSubmissionPageButton);
|
||||||
if (ensureSubmissionPageButton() && submissionPageButtonObserver) {
|
|
||||||
submissionPageButtonObserver.disconnect();
|
|
||||||
submissionPageButtonObserver = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
submissionPageButtonObserver.observe(document.documentElement, { childList: true, subtree: true });
|
submissionPageButtonObserver.observe(document.documentElement, { childList: true, subtree: true });
|
||||||
setTimeout(() => {
|
for (const ms of [300, 800, 1600, 3000, 5000]) {
|
||||||
if (submissionPageButtonObserver) {
|
setTimeout(maintainSubmissionPageButton, ms);
|
||||||
submissionPageButtonObserver.disconnect();
|
}
|
||||||
submissionPageButtonObserver = null;
|
|
||||||
}
|
|
||||||
}, 10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeNewCanvasButton() {
|
function removeNewCanvasButton() {
|
||||||
@ -619,6 +647,15 @@ function startExtension() {
|
|||||||
});
|
});
|
||||||
footerObserver.observe(document.documentElement, { childList: true, subtree: true });
|
footerObserver.observe(document.documentElement, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// Start the submission-page "Back to Assignment" button watcher and the SPA
|
||||||
|
// navigation hook immediately, before the async storage callbacks below.
|
||||||
|
// These don't depend on `options`, and running them first means a throw in
|
||||||
|
// any later init step can't prevent the button from appearing. The watcher
|
||||||
|
// reads window.location.pathname live and uses a persistent MutationObserver
|
||||||
|
// to (re)inject the button once the content container exists.
|
||||||
|
setupNavigationListener();
|
||||||
|
watchSubmissionPageButton();
|
||||||
|
|
||||||
toggleDarkMode();
|
toggleDarkMode();
|
||||||
|
|
||||||
// Include bg_opacity/bg_blur so setupBetterSidebar (called below) tints the
|
// Include bg_opacity/bg_blur so setupBetterSidebar (called below) tints the
|
||||||
@ -644,9 +681,7 @@ function startExtension() {
|
|||||||
applyCustomBackground();
|
applyCustomBackground();
|
||||||
ensureBetterSidebar();
|
ensureBetterSidebar();
|
||||||
watchSequenceFooter();
|
watchSequenceFooter();
|
||||||
watchSubmissionPageButton();
|
|
||||||
watchProfileLogoutPageButton();
|
watchProfileLogoutPageButton();
|
||||||
setupNavigationListener();
|
|
||||||
|
|
||||||
setupQuizSafeModeBanner();
|
setupQuizSafeModeBanner();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user