Skip to content

Commit

Permalink
Fixed page ids not saving, added quality of life features
Browse files Browse the repository at this point in the history
- Fixed page id not saving correctly
- Fixed minor issue with getting assignment/page function
- Added stylesheet injection
- Updated inline styles to classes in new injected stylesheet
- Fixed checked todo name truncation
- Added link to assignment/page from checked todos
- Removed redundant function arguments
- Added state to checked todos list to remain open/closed on reload or DOM change
  • Loading branch information
da-stoi committed Nov 22, 2023
1 parent c8e2428 commit ffa7b75
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 85 deletions.
2 changes: 1 addition & 1 deletion public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Canvas Strikethrough",
"description": "Mark Canvas calendar events without deliverables as complete.",
"author": "Daniel Stoiber",
"version": "1.5",
"version": "1.6",
"manifest_version": 3,
"permissions": [
"storage"
Expand Down
36 changes: 6 additions & 30 deletions src/content.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
import { getEvents, getCalendarView } from "./getCalendarEvents";
import { getCheckedCourses } from "./getCheckedCourses";
import { getCheckedTodos } from "./storage";
import { Todo, CalendarEvent } from "./types";
import { checkEvent } from "./updateCalendarEvent";
import { displayCheckButton } from "./displayCheckButton";
import { displayCheckedTodos } from "./displayCheckedTodos";
import { injectCss } from "./injectCss";
import { calendar } from "./updateCalendar";

export async function calendar() {

// Get checked courses
const courseList: number[] = getCheckedCourses();

// Get all checked assignments
const checkedTodos: Todo[] = getCheckedTodos(courseList);

// Display checked todos list
displayCheckedTodos();

// Get all calendar events
const calendarView = getCalendarView();
const calendarEvents: CalendarEvent[] = getEvents(calendarView);

calendarEvents.forEach((event: CalendarEvent) => {
const { name, element } = event;

// If assignment is checked
if (checkedTodos.find((todo: Todo) => todo.name.trim() === name.trim())) {
checkEvent(element);
}
});
}
// Inject CSS
injectCss();

// Get path
const path = window.location.pathname;

if (path.includes('calendar')) {

// Keep track of last time DOM was modified
Expand Down
46 changes: 35 additions & 11 deletions src/displayCheckButton.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { getAssignment, getPage } from "./getTodos";
import { markCompleteOnclick, markIncompleteOnclick } from "./markTodos";
import { markCompleteOnclick, markIncompleteOnclick } from "./updateTodos";
import { isTodoChecked } from "./storage";
import { Todo } from "./types";

function checkButtonHtml(courseId: number, todo: Todo, isChecked: boolean, isSubmitted: boolean): HTMLButtonElement {
function checkButtonHtml(todo: Todo, isChecked: boolean, isSubmitted: boolean): HTMLButtonElement {

const checkButton: HTMLButtonElement = document.createElement('button');
checkButton.innerHTML = isChecked ? 'Mark as incomplete' : 'Mark as complete';
checkButton.id = 'canvas-strikethrough-check-button';
checkButton.classList.add('Button', 'Button--primary');

if (isChecked) {
checkButton.onclick = () => {
markIncompleteOnclick(courseId, todo, checkButton, true);
markIncompleteOnclick(todo, checkButton, true);
};
} else {
checkButton.onclick = () => {
markCompleteOnclick(courseId, todo, checkButton, true);
markCompleteOnclick(todo, checkButton, true);
};
}

Expand All @@ -26,7 +27,7 @@ function checkButtonHtml(courseId: number, todo: Todo, isChecked: boolean, isSub
function assignmentDisclaimer(): HTMLSpanElement {
const disclaimer: HTMLSpanElement = document.createElement('span');
disclaimer.innerHTML = 'Note: Marking an assignment as complete is only for your own reference. It does not affect your submission/grade, nor does it notify your instructor.';
disclaimer.style.fontStyle = 'italic';
disclaimer.classList.add('disclaimer-text');

return disclaimer;
}
Expand All @@ -35,7 +36,7 @@ function assignmentDisclaimer(): HTMLSpanElement {
function gradedDisclaimer(): HTMLSpanElement {
const disclaimer: HTMLSpanElement = document.createElement('span');
disclaimer.innerHTML = 'Note: This assignment has already been submitted.';
disclaimer.style.fontStyle = 'italic';
disclaimer.classList.add('disclaimer-text');
disclaimer.style.color = 'var(--ic-link-color)';

return disclaimer;
Expand Down Expand Up @@ -68,7 +69,7 @@ async function handleAssignment(courseId: number, assignmentId: number) {
header.innerHTML = 'Canvas Strikethrough';

// Create check button
const checkButton = checkButtonHtml(courseId, todo, isChecked, isSubmitted);
const checkButton = checkButtonHtml(todo, isChecked, isSubmitted);

buttonDiv.appendChild(header);
buttonDiv.appendChild(checkButton);
Expand Down Expand Up @@ -107,7 +108,7 @@ async function handlePage(courseId: number, pageUrl: string) {
const isChecked = isTodoChecked(courseId, todo.id);

// Create check button
const checkButton = checkButtonHtml(courseId, todo, isChecked, false);
const checkButton = checkButtonHtml(todo, isChecked, false);

buttonDiv.appendChild(document.createElement('br'));
buttonDiv.appendChild(checkButton);
Expand All @@ -119,18 +120,31 @@ async function handleEvent(courseId: number, todoId: number | string, eventType:
let todo: Todo | undefined = undefined;
let isSubmitted = false;

if (typeof todoId === 'string') {
if (eventType === 'page') {

const pageData = await getPage(courseId, todoId);

if (!pageData) {
return;
}

todo = pageData.todo;

// Get check status
isChecked = isTodoChecked(courseId, todo.id);
} else if (typeof todoId === 'number') {
} else if (eventType === 'assignment') {

// Check if todoId is a number
if (typeof todoId !== 'number') {
return;
}

const assignmentData = await getAssignment(courseId, todoId);

if (!assignmentData) {
return;
}

isSubmitted = assignmentData.isSubmitted
todo = assignmentData.todo;

Expand All @@ -144,7 +158,7 @@ async function handleEvent(courseId: number, todoId: number | string, eventType:
return;
}

const checkButton = checkButtonHtml(courseId, todo, isChecked, isSubmitted)
const checkButton = checkButtonHtml(todo, isChecked, isSubmitted)

header.appendChild(checkButton);

Expand All @@ -171,13 +185,22 @@ export async function displayCheckButton() {
const courseId = parseInt(path.split('/')[2]);
const assignmentId = parseInt(path.split('/')[4]);

if (Number.isNaN(courseId) || Number.isNaN(assignmentId)) {
return;
}

handleAssignment(courseId, assignmentId);
return;
}

if (path.includes('pages')) {
const courseId = parseInt(path.split('/')[2]);
const pageUrl = path.split('/')[4];

if (Number.isNaN(courseId) || !pageUrl) {
return;
}

handlePage(courseId, pageUrl);
return;
}
Expand All @@ -198,6 +221,7 @@ export async function displayCheckButton() {
const url = eventUrl.split('/courses/')[1];

const courseId = parseInt(url.split('/')[0]);
console.log(url.split('/'), courseId);
const eventType: 'assignment' | 'page' = url.split('/')[1] === 'assignments' ? 'assignment' : 'page';
let todoId: string | number = url.split('/')[2];

Expand Down
67 changes: 29 additions & 38 deletions src/displayCheckedTodos.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getCheckedCourses } from "./getCheckedCourses";
import { markIncompleteOnclick } from "./markTodos";
import { getCheckedTodos } from "./storage";
import { markIncompleteOnclick } from "./updateTodos";
import { getCheckedTodos, getCheckedTodosListState, toggleCheckedTodosListState } from "./storage";
import { Todo } from "./types";

export async function displayCheckedTodos() {
Expand All @@ -25,20 +25,26 @@ export async function displayCheckedTodos() {
const h2 = document.createElement('h2');
h2.tabIndex = -1;

// Get checked todos list state
const checkedTodoListOpen = getCheckedTodosListState();

// Header button
const span = document.createElement('span');
span.role = 'button';
span.id = 'checked-todos-button';
span.classList.add('element_toggler');
span.setAttribute('aria-controls', 'checked-todos');
span.setAttribute('aria-expanded', 'false');
span.setAttribute('aria-expanded', checkedTodoListOpen ? 'true' : 'false');
span.setAttribute('aria-label', 'Undated items toggle list visibility');
span.tabIndex = 0;
span.onclick = () => {
toggleCheckedTodosListState();
}

// Header button icon
const i = document.createElement('i');
i.classList.add('auto_rotate');
i.classList.add('icon-mini-arrow-right');
i.classList.add(checkedTodoListOpen ? 'icon-mini-arrow-down' : 'icon-mini-arrow-right');

span.appendChild(i);
span.appendChild(document.createTextNode(' Canvas Strikethrough Checked'));
Expand All @@ -48,60 +54,45 @@ export async function displayCheckedTodos() {
// List of checked todos
const div = document.createElement('div');
div.id = 'checked-todos';
div.style.display = 'none';
div.style.opacity = '1';
// Hide list by default
div.style.display = checkedTodoListOpen ? 'block' : 'none';

const ul = document.createElement('ul');
ul.style.listStyleType = 'none';
ul.style.padding = '0';
ul.style.margin = '0';
ul.id = 'checked-todos-list';

checkedTodos.forEach((todo, index) => {

const event = document.createElement('div');
event.classList.add('checked-todo');
event.classList.add('event');
event.classList.add(`group_course_${todo.courseId}`);
event.id = `checked-todo-${index}`;
event.style.borderRadius = '3px';
event.style.marginBottom = '3px';
event.style.padding = '3px';
event.style.color = 'white';
event.setAttribute('data-course-id', `${todo.courseId}`);
event.setAttribute('data-todo-id', `${todo.id}`);

const spanInner = document.createElement('a');
spanInner.innerHTML = todo.name.length > 25 ? todo.name.substring(0, 25) + '...' : todo.name;
spanInner.classList.add('icon-calendar-month');
spanInner.style.color = 'white';
spanInner.style.textDecoration = 'none';
spanInner.style.cursor = 'pointer';
const todoParent = document.createElement('div');
todoParent.classList.add('checked-todo');
todoParent.classList.add('event');
todoParent.classList.add(`group_course_${todo.courseId}`);
todoParent.id = `checked-todo-${index}`;
todoParent.setAttribute('data-course-id', `${todo.courseId}`);
todoParent.setAttribute('data-todo-id', `${todo.id}`);

const aInner = document.createElement('a');
aInner.innerHTML = todo.name;
aInner.classList.add('icon-calendar-month');
aInner.href = `/courses/${todo.courseId}/${todo.type}s/${todo.id}`;

const markAsIncompleteButton = document.createElement('button');
markAsIncompleteButton.classList.add('Button');
markAsIncompleteButton.classList.add('Button--link');
markAsIncompleteButton.innerText = 'Mark as incomplete';
markAsIncompleteButton.style.padding = '3px';
markAsIncompleteButton.style.margin = '3px';
markAsIncompleteButton.style.backgroundColor = 'white';
markAsIncompleteButton.style.color = 'black';
markAsIncompleteButton.style.borderRadius = '3px';
markAsIncompleteButton.style.cursor = 'pointer';
markAsIncompleteButton.style.border = '1px solid black';

markAsIncompleteButton.onclick = () => {
markIncompleteOnclick(todo.courseId, todo, markAsIncompleteButton, true);
markIncompleteOnclick(todo, markAsIncompleteButton, true);

// Remove checked todo from list
const checkedTodo = document.querySelector(`#checked-todo-${index}`);
checkedTodo?.remove();
};

event.appendChild(spanInner);
event.appendChild(document.createElement('br'));
event.appendChild(markAsIncompleteButton);
ul.appendChild(event);
todoParent.appendChild(aInner);
// todoParent.appendChild(document.createElement('br'));
todoParent.appendChild(markAsIncompleteButton);
ul.appendChild(todoParent);
});

div.appendChild(ul);
Expand Down
12 changes: 11 additions & 1 deletion src/getTodos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Todo } from "./types";

export async function getAssignment(courseId: number, assignmentId: number) {

if (!courseId || !assignmentId) {
return null;
}

// Get assignment data
const assignmentReq = await fetch(`/api/v1/courses/${courseId}/assignments/${assignmentId}`);
// Get submission data
Expand Down Expand Up @@ -30,18 +34,24 @@ export async function getAssignment(courseId: number, assignmentId: number) {

export async function getPage(courseId: number, pageId: number | string) {

if (!courseId || !pageId) {
return null;
}

// Check if pageId is a number or a string
const pageIdType = typeof pageId === 'number' ? 'pageId' : 'pageUrl';

// Get page data
const pageReq = await fetch(`/api/v1/courses/${courseId}/pages/${pageId}`);
const pageData = await pageReq.json();

console.log(pageData);

// Create todo object
const page: Todo = {
type: 'page',
courseId,
id: pageIdType === 'pageId' ? pageId : pageData.id,
id: typeof pageId === 'number' ? pageId : pageData.page_id,
name: pageData.title
};

Expand Down
Loading

0 comments on commit ffa7b75

Please sign in to comment.