mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
hopefully fix the issues
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
@use "sass:meta";
|
@use "sass:meta";
|
||||||
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
|
// Removed Google Fonts import - Firefox blocks external resources
|
||||||
|
// Using system font stack instead: Rubik -> system-ui -> sans-serif
|
||||||
|
|
||||||
@include meta.load-css("injected/sidebar-animation.scss");
|
@include meta.load-css("injected/sidebar-animation.scss");
|
||||||
@include meta.load-css("injected/theme.scss");
|
@include meta.load-css("injected/theme.scss");
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
background: var(--better-main) !important;
|
background: var(--better-main) !important;
|
||||||
--navy: #1a1a1a !important;
|
--navy: #1a1a1a !important;
|
||||||
--auto-background: var(--better-pale, var(--background-secondary)) !important;
|
--auto-background: var(--better-pale, var(--background-secondary)) !important;
|
||||||
font-family: Rubik, sans-serif !important;
|
font-family: Rubik, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::view-transition-old(root),
|
::view-transition-old(root),
|
||||||
@@ -36,7 +37,7 @@ body,
|
|||||||
.legacy-root option,
|
.legacy-root option,
|
||||||
.legacy-root .input,
|
.legacy-root .input,
|
||||||
html {
|
html {
|
||||||
font-family: Rubik, sans-serif !important;
|
font-family: Rubik, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure native select dropdowns are readable on Windows */
|
/* Ensure native select dropdowns are readable on Windows */
|
||||||
@@ -58,7 +59,7 @@ select {
|
|||||||
background: var(--auto-background) !important;
|
background: var(--auto-background) !important;
|
||||||
}
|
}
|
||||||
:root * {
|
:root * {
|
||||||
font-family: Rubik, sans-serif !important;
|
font-family: Rubik, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
--theme-fg-parts: white;
|
--theme-fg-parts: white;
|
||||||
}
|
}
|
||||||
.extension-editor {
|
.extension-editor {
|
||||||
@@ -307,7 +308,7 @@ select {
|
|||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-size: 0px !important;
|
font-size: 0px !important;
|
||||||
font-family: Rubik, sans-serif !important;
|
font-family: Rubik, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
&::before {
|
&::before {
|
||||||
font-size: 18px !important;
|
font-size: 18px !important;
|
||||||
content: "Search" !important;
|
content: "Search" !important;
|
||||||
@@ -423,7 +424,7 @@ ul.magicDelete > li.deleting {
|
|||||||
background: var(--better-main) !important;
|
background: var(--better-main) !important;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
border-right: none;
|
border-right: none;
|
||||||
font-family: Rubik, sans-serif !important;
|
font-family: Rubik, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
}
|
}
|
||||||
#menu li > label > svg,
|
#menu li > label > svg,
|
||||||
#menu section > label > svg {
|
#menu section > label > svg {
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ export const actionMap: Record<string, ActionHandler<any>> = {
|
|||||||
}) as ActionHandler<any>,
|
}) as ActionHandler<any>,
|
||||||
|
|
||||||
assessment: (async (item: IndexItem & { metadata: AssessmentMetadata }) => {
|
assessment: (async (item: IndexItem & { metadata: AssessmentMetadata }) => {
|
||||||
if (item.metadata.isMessageBased) {
|
console.debug("[Assessment Action] Navigating to assessment:", item.id, item.metadata);
|
||||||
|
|
||||||
|
if (item.metadata?.isMessageBased) {
|
||||||
window.location.hash = `#?page=/messages`;
|
window.location.hash = `#?page=/messages`;
|
||||||
|
|
||||||
await waitForElm('[class*="Viewer__Viewer___"] > div', true, 20);
|
await waitForElm('[class*="Viewer__Viewer___"] > div', true, 20);
|
||||||
@@ -69,7 +71,60 @@ export const actionMap: Record<string, ActionHandler<any>> = {
|
|||||||
selected: new Set([item.metadata.messageId]),
|
selected: new Set([item.metadata.messageId]),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
window.location.hash = `#?page=/assessments&id=${item.metadata.assessmentId}`;
|
// Use the correct URL format: /assessments/{programmeId}:{metaclassId}&item={assessmentId}
|
||||||
|
// Convert to numbers to handle string/number inconsistencies
|
||||||
|
let programmeId = item.metadata?.programmeId;
|
||||||
|
let metaclassId = item.metadata?.metaclassId;
|
||||||
|
let assessmentId = item.metadata?.assessmentId;
|
||||||
|
|
||||||
|
// Fallback: try to extract assessmentId from item ID if metadata is missing
|
||||||
|
if (!assessmentId && item.id && item.id.startsWith('assignment-')) {
|
||||||
|
const extractedId = item.id.replace('assignment-', '');
|
||||||
|
assessmentId = Number(extractedId) || extractedId;
|
||||||
|
console.debug("[Assessment Action] Extracted assessmentId from item ID:", assessmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to numbers for consistency
|
||||||
|
programmeId = Number(programmeId) || programmeId;
|
||||||
|
metaclassId = Number(metaclassId) || metaclassId;
|
||||||
|
assessmentId = Number(assessmentId) || assessmentId;
|
||||||
|
|
||||||
|
// Check if values exist (including 0, which is a valid ID)
|
||||||
|
const hasProgrammeId = programmeId !== undefined && programmeId !== null && programmeId !== '';
|
||||||
|
const hasMetaclassId = metaclassId !== undefined && metaclassId !== null && metaclassId !== '';
|
||||||
|
const hasAssessmentId = assessmentId !== undefined && assessmentId !== null && assessmentId !== '';
|
||||||
|
|
||||||
|
if (hasProgrammeId && hasMetaclassId && hasAssessmentId) {
|
||||||
|
const url = `#?page=/assessments/${programmeId}:${metaclassId}&item=${assessmentId}`;
|
||||||
|
console.debug("[Assessment Action] Navigating to:", url, {
|
||||||
|
programmeId,
|
||||||
|
metaclassId,
|
||||||
|
assessmentId,
|
||||||
|
rawMetadata: item.metadata,
|
||||||
|
});
|
||||||
|
window.location.hash = url;
|
||||||
|
} else {
|
||||||
|
// Fallback: try to navigate to assessments page if metadata is incomplete
|
||||||
|
console.warn("[Assessment Action] Missing required metadata:", {
|
||||||
|
programmeId,
|
||||||
|
metaclassId,
|
||||||
|
assessmentId,
|
||||||
|
hasProgrammeId,
|
||||||
|
hasMetaclassId,
|
||||||
|
hasAssessmentId,
|
||||||
|
fullMetadata: item.metadata,
|
||||||
|
itemId: item.id,
|
||||||
|
itemKeys: Object.keys(item),
|
||||||
|
});
|
||||||
|
// If we at least have an assessmentId, try to navigate to the general assessments page
|
||||||
|
// The user can then find it manually
|
||||||
|
if (hasAssessmentId) {
|
||||||
|
console.info("[Assessment Action] Attempting to navigate to assessments page with item filter");
|
||||||
|
window.location.hash = `#?page=/assessments/upcoming&item=${assessmentId}`;
|
||||||
|
} else {
|
||||||
|
window.location.hash = `#?page=/assessments/upcoming`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}) as ActionHandler<any>,
|
}) as ActionHandler<any>,
|
||||||
|
|
||||||
|
|||||||
@@ -396,18 +396,25 @@ export async function runIndexing(): Promise<void> {
|
|||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
|
|
||||||
allItemsInPrimaryStores = await loadAllStoredItems();
|
allItemsInPrimaryStores = await loadAllStoredItems();
|
||||||
allItemsInPrimaryStores.forEach(item => {
|
// Create new objects to avoid XrayWrapper issues in Firefox
|
||||||
const jobDef = jobs[item.category] || Object.values(jobs).find(j => j.id === item.category) || jobs[item.renderComponentId];
|
const itemsWithComponents = allItemsInPrimaryStores.map(item => {
|
||||||
if (jobDef) {
|
try {
|
||||||
const renderComponent = renderComponentMap[jobDef.renderComponentId];
|
const jobDef = jobs[item.category] || Object.values(jobs).find(j => j.id === item.category) || jobs[item.renderComponentId];
|
||||||
if (renderComponent) {
|
let renderComponent = item.renderComponent;
|
||||||
item.renderComponent = renderComponent;
|
if (jobDef) {
|
||||||
}
|
renderComponent = renderComponentMap[jobDef.renderComponentId] || renderComponent;
|
||||||
} else if (renderComponentMap[item.renderComponentId]) {
|
} else if (renderComponentMap[item.renderComponentId]) {
|
||||||
item.renderComponent = renderComponentMap[item.renderComponentId];
|
renderComponent = renderComponentMap[item.renderComponentId];
|
||||||
|
}
|
||||||
|
// Create a new object instead of modifying the existing one
|
||||||
|
return { ...item, renderComponent };
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback: return item as-is if modification fails (Firefox XrayWrapper)
|
||||||
|
console.warn("[Indexer] Failed to add render component to item (Firefox XrayWrapper):", error);
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loadDynamicItems(allItemsInPrimaryStores);
|
loadDynamicItems(itemsWithComponents);
|
||||||
window.dispatchEvent(new Event("dynamic-items-updated"));
|
window.dispatchEvent(new Event("dynamic-items-updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,10 +46,27 @@ const fetchPastAssessments = async (student: number = 69, subjects: any[]) => {
|
|||||||
programme: subject.programme,
|
programme: subject.programme,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.payload && Array.isArray(res.payload)) {
|
// Past assessments API returns data in payload.tasks, not payload directly
|
||||||
|
if (res.payload && res.payload.tasks && Array.isArray(res.payload.tasks)) {
|
||||||
|
res.payload.tasks.forEach((assessment: any) => {
|
||||||
|
if (assessment && assessment.id) {
|
||||||
|
// Ensure programme and metaclass are included from the subject
|
||||||
|
map[assessment.id] = {
|
||||||
|
...assessment,
|
||||||
|
programme: assessment.programme || assessment.programmeID || subject.programme,
|
||||||
|
metaclass: assessment.metaclass || assessment.metaclassID || subject.metaclass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (res.payload && Array.isArray(res.payload)) {
|
||||||
|
// Fallback: some APIs might return array directly
|
||||||
res.payload.forEach((assessment: any) => {
|
res.payload.forEach((assessment: any) => {
|
||||||
if (assessment && assessment.id) {
|
if (assessment && assessment.id) {
|
||||||
map[assessment.id] = assessment;
|
map[assessment.id] = {
|
||||||
|
...assessment,
|
||||||
|
programme: assessment.programme || assessment.programmeID || subject.programme,
|
||||||
|
metaclass: assessment.metaclass || assessment.metaclassID || subject.metaclass,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -139,7 +156,13 @@ export const assignmentsJob: Job = {
|
|||||||
|
|
||||||
upcoming.forEach((a: any) => {
|
upcoming.forEach((a: any) => {
|
||||||
if (a && a.id) {
|
if (a && a.id) {
|
||||||
allAssessments.set(a.id, { ...a, isUpcoming: true });
|
// Normalize field names - handle both programme/programmeID and metaclass/metaclassID
|
||||||
|
allAssessments.set(a.id, {
|
||||||
|
...a,
|
||||||
|
programme: a.programme || a.programmeID,
|
||||||
|
metaclass: a.metaclass || a.metaclassID,
|
||||||
|
isUpcoming: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -161,22 +184,10 @@ export const assignmentsJob: Job = {
|
|||||||
const assessmentArray = Array.from(allAssessments.values());
|
const assessmentArray = Array.from(allAssessments.values());
|
||||||
const batchSize = 15; // Increased batch size for better performance
|
const batchSize = 15; // Increased batch size for better performance
|
||||||
|
|
||||||
// Only fetch details for upcoming assignments to reduce API calls
|
// Skip fetching assessment details - the API endpoint doesn't exist or returns 404
|
||||||
const upcomingAssessments = assessmentArray.filter(a => a.isUpcoming);
|
// Details are optional and not critical for search functionality
|
||||||
const detailPromises = new Map<string, Promise<string | null>>();
|
const detailPromises = new Map<string, Promise<string | null>>();
|
||||||
|
|
||||||
// Pre-fetch details for upcoming assessments only (most important)
|
|
||||||
for (const assessment of upcomingAssessments.slice(0, 20)) {
|
|
||||||
if (assessment.metaclass && assessment.programme) {
|
|
||||||
const id = `assignment-${assessment.id}`;
|
|
||||||
detailPromises.set(id, fetchAssessmentDetails(
|
|
||||||
assessment.id,
|
|
||||||
assessment.metaclass,
|
|
||||||
assessment.programme,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process all assessments
|
// Process all assessments
|
||||||
for (let i = 0; i < assessmentArray.length; i += batchSize) {
|
for (let i = 0; i < assessmentArray.length; i += batchSize) {
|
||||||
const batch = assessmentArray.slice(i, i + batchSize);
|
const batch = assessmentArray.slice(i, i + batchSize);
|
||||||
@@ -191,16 +202,27 @@ export const assignmentsJob: Job = {
|
|||||||
|
|
||||||
processedIds.add(id);
|
processedIds.add(id);
|
||||||
|
|
||||||
// Only fetch details for upcoming assignments (already pre-fetched)
|
// Skip fetching details - API endpoint doesn't exist
|
||||||
let description = "";
|
const description = "";
|
||||||
const detailPromise = detailPromises.get(id);
|
|
||||||
if (detailPromise) {
|
|
||||||
description = (await detailPromise) || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const subjectName = assessment.subject || assessment.code || "Unknown Subject";
|
const subjectName = assessment.subject || assessment.code || "Unknown Subject";
|
||||||
const dueDate = assessment.due ? new Date(assessment.due).getTime() : null;
|
const dueDate = assessment.due ? new Date(assessment.due).getTime() : null;
|
||||||
|
|
||||||
|
// Normalize programme and metaclass IDs - handle both camelCase and PascalCase
|
||||||
|
const programmeId = assessment.programme || assessment.programmeID;
|
||||||
|
const metaclassId = assessment.metaclass || assessment.metaclassID;
|
||||||
|
|
||||||
|
// Validate that we have the required IDs for navigation
|
||||||
|
if (!programmeId || !metaclassId || !assessment.id) {
|
||||||
|
console.warn(`[Assignments job] Skipping assignment ${assessment.id} - missing required IDs:`, {
|
||||||
|
programmeId,
|
||||||
|
metaclassId,
|
||||||
|
assessmentId: assessment.id,
|
||||||
|
assessment,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const item: IndexItem = {
|
const item: IndexItem = {
|
||||||
id,
|
id,
|
||||||
text: assessment.title || assessment.name || "Untitled Assignment",
|
text: assessment.title || assessment.name || "Untitled Assignment",
|
||||||
@@ -212,11 +234,12 @@ export const assignmentsJob: Job = {
|
|||||||
subject: subjectName,
|
subject: subjectName,
|
||||||
subjectCode: assessment.code,
|
subjectCode: assessment.code,
|
||||||
dueDate: assessment.due,
|
dueDate: assessment.due,
|
||||||
programmeId: assessment.programme,
|
programmeId: Number(programmeId) || programmeId, // Ensure it's a number
|
||||||
metaclassId: assessment.metaclass,
|
metaclassId: Number(metaclassId) || metaclassId, // Ensure it's a number
|
||||||
submitted: assessment.submitted || false,
|
submitted: assessment.submitted || false,
|
||||||
isUpcoming: assessment.isUpcoming || false,
|
isUpcoming: assessment.isUpcoming || false,
|
||||||
term: assessment.term,
|
term: assessment.term,
|
||||||
|
timestamp: assessment.due || new Date().toISOString(), // Required by AssessmentMetadata interface
|
||||||
},
|
},
|
||||||
actionId: "assessment",
|
actionId: "assessment",
|
||||||
renderComponentId: "assessment",
|
renderComponentId: "assessment",
|
||||||
|
|||||||
@@ -604,22 +604,27 @@ export const messagesJob: Job = {
|
|||||||
if (processedItems.length > 0) {
|
if (processedItems.length > 0) {
|
||||||
try {
|
try {
|
||||||
const currentItems = await loadAllStoredItems();
|
const currentItems = await loadAllStoredItems();
|
||||||
currentItems.forEach((item) => {
|
// Create new objects to avoid XrayWrapper issues in Firefox
|
||||||
const jobDef =
|
const itemsWithComponents = currentItems.map((item) => {
|
||||||
jobs[item.category] ||
|
try {
|
||||||
Object.values(jobs).find((j) => j.id === item.category) ||
|
const jobDef =
|
||||||
jobs[item.renderComponentId];
|
jobs[item.category] ||
|
||||||
if (jobDef) {
|
Object.values(jobs).find((j) => j.id === item.category) ||
|
||||||
const renderComponent =
|
jobs[item.renderComponentId];
|
||||||
renderComponentMap[jobDef.renderComponentId];
|
let renderComponent = item.renderComponent;
|
||||||
if (renderComponent) {
|
if (jobDef) {
|
||||||
item.renderComponent = renderComponent;
|
renderComponent = renderComponentMap[jobDef.renderComponentId] || renderComponent;
|
||||||
|
} else if (renderComponentMap[item.renderComponentId]) {
|
||||||
|
renderComponent = renderComponentMap[item.renderComponentId];
|
||||||
}
|
}
|
||||||
} else if (renderComponentMap[item.renderComponentId]) {
|
// Create a new object instead of modifying the existing one
|
||||||
item.renderComponent = renderComponentMap[item.renderComponentId];
|
return { ...item, renderComponent };
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback: return item as-is if modification fails (Firefox XrayWrapper)
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loadDynamicItems(currentItems);
|
loadDynamicItems(itemsWithComponents);
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("dynamic-items-updated", {
|
new CustomEvent("dynamic-items-updated", {
|
||||||
detail: {
|
detail: {
|
||||||
|
|||||||
@@ -372,23 +372,27 @@ export const notificationsJob: Job = {
|
|||||||
if (items.length > 0) {
|
if (items.length > 0) {
|
||||||
try {
|
try {
|
||||||
const currentItems = await loadAllStoredItems();
|
const currentItems = await loadAllStoredItems();
|
||||||
currentItems.forEach((item) => {
|
// Create new objects to avoid XrayWrapper issues in Firefox
|
||||||
const jobDef =
|
const itemsWithComponents = currentItems.map((item) => {
|
||||||
jobs[item.category] ||
|
try {
|
||||||
Object.values(jobs).find((j) => j.id === item.category) ||
|
const jobDef =
|
||||||
jobs[item.renderComponentId];
|
jobs[item.category] ||
|
||||||
if (jobDef) {
|
Object.values(jobs).find((j) => j.id === item.category) ||
|
||||||
const renderComponent =
|
jobs[item.renderComponentId];
|
||||||
renderComponentMap[jobDef.renderComponentId];
|
let renderComponent = item.renderComponent;
|
||||||
if (renderComponent) {
|
if (jobDef) {
|
||||||
item.renderComponent = renderComponent;
|
renderComponent = renderComponentMap[jobDef.renderComponentId] || renderComponent;
|
||||||
|
} else if (renderComponentMap[item.renderComponentId]) {
|
||||||
|
renderComponent = renderComponentMap[item.renderComponentId];
|
||||||
}
|
}
|
||||||
} else if (renderComponentMap[item.renderComponentId]) {
|
// Create a new object instead of modifying the existing one
|
||||||
item.renderComponent =
|
return { ...item, renderComponent };
|
||||||
renderComponentMap[item.renderComponentId];
|
} catch (error) {
|
||||||
|
// Fallback: return item as-is if modification fails (Firefox XrayWrapper)
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loadDynamicItems(currentItems);
|
loadDynamicItems(itemsWithComponents);
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("dynamic-items-updated", {
|
new CustomEvent("dynamic-items-updated", {
|
||||||
detail: {
|
detail: {
|
||||||
|
|||||||
@@ -47,7 +47,17 @@ export function createLazyPlugin<T extends PluginSettings = PluginSettings, S =
|
|||||||
|
|
||||||
// Execute the actual plugin's run function
|
// Execute the actual plugin's run function
|
||||||
return await actualPlugin.run(api);
|
return await actualPlugin.run(api);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
|
// Handle Firefox MIME type errors gracefully
|
||||||
|
if (error?.message?.includes("MIME type") || error?.message?.includes("NS_ERROR_CORRUPTED_CONTENT")) {
|
||||||
|
console.error(
|
||||||
|
`[BetterSEQTA+] Failed to load plugin "${lazyPlugin.id}" due to Firefox module loading restrictions. ` +
|
||||||
|
`This may be a build configuration issue. Error:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
// Don't throw - allow the extension to continue functioning without this plugin
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.error(`[BetterSEQTA+] Failed to dynamically load plugin "${lazyPlugin.id}":`, error);
|
console.error(`[BetterSEQTA+] Failed to dynamically load plugin "${lazyPlugin.id}":`, error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user