mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-11-03 22:07:18 +01:00
custom timeframe FOR FREE
This commit is contained in:
parent
befce19c90
commit
6850b3cb4f
@ -76,16 +76,22 @@ h2 {margin-top: 20px;font-weight:600}
|
|||||||
.custom-card-hide {display: flex; align-items: center;}
|
.custom-card-hide {display: flex; align-items: center;}
|
||||||
.option-name {font-weight: 600}
|
.option-name {font-weight: 600}
|
||||||
.sub-text {font-size: 12px;color: #c7c7c7}
|
.sub-text {font-size: 12px;color: #c7c7c7}
|
||||||
#todo_progress_rings, #anon_grade_preset {font-size: 12px;background: var(--inputbg);color:#e2e2e2;border:none;border-radius:7px;padding:6px 8px;font-family:inherit;width:100%;box-sizing:border-box;margin-top:2px;color-scheme:dark;}
|
#todo_progress_rings, #anon_grade_preset {font-size: 12px;background: var(--inputbg);color:#e2e2e2;border:none;border-radius:7px;padding:6px 8px;font-family:inherit;width:100%;box-sizing:border-box;margin-top:2px;margin-bottom:6px;color-scheme:dark;}
|
||||||
#todo_progress_rings option, #anon_grade_preset option {background:#1e1e1e;color:#e2e2e2;}
|
#todo_progress_rings option, #anon_grade_preset option {background:#1e1e1e;color:#e2e2e2;}
|
||||||
#todo_progress_rings option:checked,
|
#todo_progress_rings option:checked,
|
||||||
#todo_progress_rings option:hover,
|
#todo_progress_rings option:hover,
|
||||||
#anon_grade_preset option:checked,
|
#anon_grade_preset option:checked,
|
||||||
#anon_grade_preset option:hover {background:#2e2e2e;color:#fff;}
|
#anon_grade_preset option:hover {background:#2e2e2e;color:#fff;}
|
||||||
#todo_timeframe {font-size: 12px;background: var(--inputbg);color:#e2e2e2;border:none;border-radius:7px;padding:6px 8px;font-family:inherit;width:100%;box-sizing:border-box;margin-top:2px;color-scheme:dark;}
|
#todo_timeframe {font-size: 12px;background: var(--inputbg);color:#e2e2e2;border:none;border-radius:7px;padding:6px 8px;font-family:inherit;width:100%;box-sizing:border-box;margin-top:2px;margin-bottom:6px;color-scheme:dark;}
|
||||||
#todo_timeframe option {background:#1e1e1e;color:#e2e2e2;}
|
#todo_timeframe option {background:#1e1e1e;color:#e2e2e2;}
|
||||||
#todo_timeframe option:checked,
|
#todo_timeframe option:checked,
|
||||||
#todo_timeframe option:hover {background:#2e2e2e;color:#fff;}
|
#todo_timeframe option:hover {background:#2e2e2e;color:#fff;}
|
||||||
|
/* Custom timeframe inputs, shown under the Time frame dropdown when "Custom" is selected */
|
||||||
|
.custom-tf-wrap {display:none;flex-direction:column;gap:6px;}
|
||||||
|
.custom-tf-wrap.visible {display:flex;}
|
||||||
|
.custom-tf-row {display:flex;align-items:center;gap:6px;}
|
||||||
|
.custom-tf-row input {flex:none;width:58px;font-size:12px;background:var(--inputbg);border:none;padding:6px;color:#e2e2e2;border-radius:6px;font-family:inherit;text-align:right;box-sizing:border-box;color-scheme:dark;}
|
||||||
|
.custom-tf-row input::-webkit-outer-spin-button, .custom-tf-row input::-webkit-inner-spin-button {opacity:1;}
|
||||||
/* Collapsible "More options" section for the Better Todo List sub-options */
|
/* Collapsible "More options" section for the Better Todo List sub-options */
|
||||||
.todo-more-options {margin-top: 7px;}
|
.todo-more-options {margin-top: 7px;}
|
||||||
.todo-more-toggle {display: flex;align-items:center;gap:5px;font-size:12px;color:#a7a7a7;cursor:pointer;user-select:none;padding:3px 0;border-radius:5px;transition:color .1s;}
|
.todo-more-toggle {display: flex;align-items:center;gap:5px;font-size:12px;color:#a7a7a7;cursor:pointer;user-select:none;padding:3px 0;border-radius:5px;transition:color .1s;}
|
||||||
|
|||||||
@ -291,7 +291,20 @@
|
|||||||
<option value="2week">2 weeks</option>
|
<option value="2week">2 weeks</option>
|
||||||
<option value="month">Month</option>
|
<option value="month">Month</option>
|
||||||
<option value="2month">2 months</option>
|
<option value="2month">2 months</option>
|
||||||
|
<option value="custom">Custom</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div id="todo_timeframe_custom" class="custom-tf-wrap">
|
||||||
|
<div class="custom-tf-row">
|
||||||
|
<span class="sub-text">From</span>
|
||||||
|
<input type="number" id="todo_timeframe_custom_past" min="0" max="365">
|
||||||
|
<span class="sub-text">days ago</span>
|
||||||
|
</div>
|
||||||
|
<div class="custom-tf-row">
|
||||||
|
<span class="sub-text">To</span>
|
||||||
|
<input type="number" id="todo_timeframe_custom_future" min="0" max="365">
|
||||||
|
<span class="sub-text">days from now</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-option">
|
<div class="sub-option">
|
||||||
<input type="checkbox" id="hover_preview" name="hover_preview">
|
<input type="checkbox" id="hover_preview" name="hover_preview">
|
||||||
|
|||||||
@ -88,6 +88,9 @@ chrome.runtime.onInstalled.addListener(function () {
|
|||||||
"todo_hide_feedback": false,
|
"todo_hide_feedback": false,
|
||||||
"todo_full_height": true,
|
"todo_full_height": true,
|
||||||
"todo_progress_rings": "rings",
|
"todo_progress_rings": "rings",
|
||||||
|
"todo_timeframe": "all",
|
||||||
|
"todo_timeframe_custom_past": 7,
|
||||||
|
"todo_timeframe_custom_future": 14,
|
||||||
"todo_confetti": true,
|
"todo_confetti": true,
|
||||||
"todo_show_scores": false,
|
"todo_show_scores": false,
|
||||||
"custom_font_skip_p": false,
|
"custom_font_skip_p": false,
|
||||||
|
|||||||
175
js/content.js
175
js/content.js
@ -1080,6 +1080,8 @@ function applyOptionsChanges(changes) {
|
|||||||
case "num_todo_items":
|
case "num_todo_items":
|
||||||
case "hover_preview":
|
case "hover_preview":
|
||||||
case "todo_timeframe":
|
case "todo_timeframe":
|
||||||
|
case "todo_timeframe_custom_past":
|
||||||
|
case "todo_timeframe_custom_future":
|
||||||
// case "todo_overdues":
|
// case "todo_overdues":
|
||||||
case "todo_hide_feedback":
|
case "todo_hide_feedback":
|
||||||
case "todo_hide_read":
|
case "todo_hide_read":
|
||||||
@ -2236,9 +2238,10 @@ function updateIndicator(element) {
|
|||||||
// better todo html
|
// better todo html
|
||||||
betterTodoFilter = "tasks";
|
betterTodoFilter = "tasks";
|
||||||
// Timeframe filter for the upcoming Tasks tab. "all" = no limit; otherwise
|
// Timeframe filter for the upcoming Tasks tab. "all" = no limit; otherwise
|
||||||
// items are limited to those due on/before now+range (which also keeps
|
// items are limited to a window around now: one week of history back through
|
||||||
// overdue items). Only affects the Tasks (upcoming) tab; announcements and
|
// now+range (older overdue backlog is cut). Only affects the Tasks (upcoming)
|
||||||
// completed are unaffected because their dates are in the past.
|
// tab; announcements and completed are unaffected because their dates are in
|
||||||
|
// the past.
|
||||||
let betterTodoTimeframe = "all";
|
let betterTodoTimeframe = "all";
|
||||||
const BETTER_TODO_TIMEFRAME_DAYS = {
|
const BETTER_TODO_TIMEFRAME_DAYS = {
|
||||||
"1week": 7,
|
"1week": 7,
|
||||||
@ -2246,6 +2249,10 @@ const BETTER_TODO_TIMEFRAME_DAYS = {
|
|||||||
"month": 30,
|
"month": 30,
|
||||||
"2month": 60,
|
"2month": 60,
|
||||||
};
|
};
|
||||||
|
// How much history the preset timeframes include: the current window starts
|
||||||
|
// this many days in the past so recently overdue work stays visible while
|
||||||
|
// older backlog is cut. Custom timeframes pick their own "days ago" bound.
|
||||||
|
const TODO_TIMEFRAME_LOOKBACK_DAYS = 4;
|
||||||
// Announcements older than this are never shown in the Better Todo list (and
|
// Announcements older than this are never shown in the Better Todo list (and
|
||||||
// never count toward the unread badge). Unlike assignments, an announcement
|
// never count toward the unread badge). Unlike assignments, an announcement
|
||||||
// stops being an actionable to-do once it's old: the planner cache keeps a
|
// stops being an actionable to-do once it's old: the planner cache keeps a
|
||||||
@ -2255,10 +2262,11 @@ const BETTER_TODO_TIMEFRAME_DAYS = {
|
|||||||
// available on the course's own Announcements page.
|
// available on the course's own Announcements page.
|
||||||
const TODO_ANNOUNCEMENT_MAX_AGE_DAYS = 30;
|
const TODO_ANNOUNCEMENT_MAX_AGE_DAYS = 30;
|
||||||
// Look-ahead paging for the timeframe window (arrow buttons under the Tasks
|
// Look-ahead paging for the timeframe window (arrow buttons under the Tasks
|
||||||
// header). 0 = the current window (now through the timeframe cutoff, with
|
// header). 0 = the current window (from the lookback bound — 1 week back for
|
||||||
// overdue items kept). Each press of the right arrow advances one full
|
// presets, "X days ago" for custom — through the future edge). Each press of
|
||||||
// window further out, e.g. a 1-week timeframe on offset 1 shows only items
|
// the right arrow advances one full window further out, e.g. a 1-week
|
||||||
// due in week 2 (the current week is skipped over).
|
// timeframe on offset 1 shows only items due in week 2 (the current window is
|
||||||
|
// skipped over).
|
||||||
let betterTodoTimeframeOffset = 0;
|
let betterTodoTimeframeOffset = 0;
|
||||||
// null = show every class; a string courseId = only that class's tasks.
|
// null = show every class; a string courseId = only that class's tasks.
|
||||||
let betterTodoProgressFilter = null;
|
let betterTodoProgressFilter = null;
|
||||||
@ -2267,25 +2275,73 @@ let domContainers = {};
|
|||||||
// Resolve the persisted todo_timeframe option to a valid key ("all" when
|
// Resolve the persisted todo_timeframe option to a valid key ("all" when
|
||||||
// unset/unknown).
|
// unset/unknown).
|
||||||
function getTodoTimeframeKey() {
|
function getTodoTimeframeKey() {
|
||||||
return (options.todo_timeframe && Object.prototype.hasOwnProperty.call(BETTER_TODO_TIMEFRAME_DAYS, options.todo_timeframe)) ? options.todo_timeframe : "all";
|
const allowed = ["all", "1week", "2week", "month", "2month", "custom"];
|
||||||
|
return allowed.includes(options.todo_timeframe) ? options.todo_timeframe : "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom timeframe bounds ("custom" option): the window runs from X days ago
|
||||||
|
// to Y days in the future. Clamped to 0-365 days; the defaults match the
|
||||||
|
// popup's initial inputs (1 week of history, 2 weeks ahead).
|
||||||
|
function getTodoCustomTimeframeDays() {
|
||||||
|
const past = parseInt(options.todo_timeframe_custom_past);
|
||||||
|
const future = parseInt(options.todo_timeframe_custom_future);
|
||||||
|
return {
|
||||||
|
past: isNaN(past) ? 7 : Math.max(0, Math.min(365, past)),
|
||||||
|
future: isNaN(future) ? 14 : Math.max(0, Math.min(365, future)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total length of the current timeframe window in days ("all" = 0). Presets
|
||||||
|
// cover their range ahead plus the shared 1-week lookback; custom windows are
|
||||||
|
// as long as their past+future range. Either way the pager tiles windows back
|
||||||
|
// to back.
|
||||||
|
function getTodoTimeframeSpanDays() {
|
||||||
|
const tf = getTodoTimeframeKey();
|
||||||
|
if (tf === "all") return 0;
|
||||||
|
if (tf === "custom") {
|
||||||
|
const c = getTodoCustomTimeframeDays();
|
||||||
|
return Math.max(1, c.past + c.future);
|
||||||
|
}
|
||||||
|
return BETTER_TODO_TIMEFRAME_DAYS[tf] + TODO_TIMEFRAME_LOOKBACK_DAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Days before "now" where the current window starts. Presets start at the
|
||||||
|
// shared 1-week lookback (recently overdue work is kept, older backlog is
|
||||||
|
// cut); custom windows start `past` days in the past per the user's setting.
|
||||||
|
function getTodoTimeframePastDays() {
|
||||||
|
const tf = getTodoTimeframeKey();
|
||||||
|
if (tf === "all") return 0;
|
||||||
|
if (tf === "custom") return getTodoCustomTimeframeDays().past;
|
||||||
|
return TODO_TIMEFRAME_LOOKBACK_DAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Better Todo timeframe filter, shared by the task list and the progress
|
// Better Todo timeframe filter, shared by the task list and the progress
|
||||||
// display so their counts always agree: keeps items due inside the current
|
// display so their counts always agree: keeps items due inside the current
|
||||||
// window. Offset 0 keeps everything on/before now+range (overdue items are
|
// window. Offset 0 runs from the lookback bound (1 week back for presets,
|
||||||
// before now, so they are kept too); offset N>0 shows only the window
|
// "X days ago" for custom) through the future edge, so recently overdue work
|
||||||
// (now+N*range, now+(N+1)*range] so the current timeframe can be paged
|
// stays visible while older backlog is cut; offset N>0 shows only the strict
|
||||||
|
// window (now+N*span, now+(N+1)*span] so the current timeframe can be paged
|
||||||
// through with the look-ahead arrows. "all" keeps everything.
|
// through with the look-ahead arrows. "all" keeps everything.
|
||||||
function applyTodoTimeframe(items) {
|
// `lenientPast` (Announcements / Completed tabs): on the current window those
|
||||||
|
// tabs keep everything on/before the cutoff — completed history and unread
|
||||||
|
// announcements older than the lookback are still meaningful there — while
|
||||||
|
// paging forward uses the same strict windows as Tasks.
|
||||||
|
function applyTodoTimeframe(items, lenientPast) {
|
||||||
betterTodoTimeframe = getTodoTimeframeKey();
|
betterTodoTimeframe = getTodoTimeframeKey();
|
||||||
if (betterTodoTimeframe === "all") return items;
|
if (betterTodoTimeframe === "all") return items;
|
||||||
const days = BETTER_TODO_TIMEFRAME_DAYS[betterTodoTimeframe];
|
const dayMs = 24 * 60 * 60 * 1000;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const cutoff = now + ((betterTodoTimeframeOffset + 1) * days * 24 * 60 * 60 * 1000);
|
const days = getTodoTimeframeSpanDays();
|
||||||
if (betterTodoTimeframeOffset <= 0) {
|
const pastDays = getTodoTimeframePastDays();
|
||||||
|
// End of the current window: the future edge (presets end `days` out,
|
||||||
|
// custom windows end `future` days out; span - past = future in both).
|
||||||
|
const cutoff = now + ((betterTodoTimeframeOffset + 1) * days - pastDays) * dayMs;
|
||||||
|
// Start of the current window: the lookback bound (skipped for the
|
||||||
|
// lenient tabs on the current window).
|
||||||
|
if (lenientPast === true && betterTodoTimeframeOffset <= 0) {
|
||||||
return items.filter(item => new Date(item.plannable_date).getTime() <= cutoff);
|
return items.filter(item => new Date(item.plannable_date).getTime() <= cutoff);
|
||||||
}
|
}
|
||||||
const windowStart = now + (betterTodoTimeframeOffset * days * 24 * 60 * 60 * 1000);
|
const windowStart = now + (betterTodoTimeframeOffset * days - pastDays) * dayMs;
|
||||||
return items.filter(item => {
|
return items.filter(item => {
|
||||||
const t = new Date(item.plannable_date).getTime();
|
const t = new Date(item.plannable_date).getTime();
|
||||||
return t > windowStart && t <= cutoff;
|
return t > windowStart && t <= cutoff;
|
||||||
@ -2297,11 +2353,12 @@ function applyTodoTimeframe(items) {
|
|||||||
// of items the list is currently filtered to.
|
// of items the list is currently filtered to.
|
||||||
function getTodoTimeframeWindow() {
|
function getTodoTimeframeWindow() {
|
||||||
const tf = getTodoTimeframeKey();
|
const tf = getTodoTimeframeKey();
|
||||||
const days = tf === "all" ? 0 : BETTER_TODO_TIMEFRAME_DAYS[tf];
|
const days = getTodoTimeframeSpanDays();
|
||||||
const offset = (tf === "all") ? 0 : betterTodoTimeframeOffset;
|
const offset = (tf === "all") ? 0 : betterTodoTimeframeOffset;
|
||||||
|
const pastDays = getTodoTimeframePastDays();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const start = new Date(now + (offset * days * 24 * 60 * 60 * 1000));
|
const start = new Date(now + (offset * days - pastDays) * 24 * 60 * 60 * 1000);
|
||||||
const end = new Date(now + ((offset + 1) * days * 24 * 60 * 60 * 1000));
|
const end = new Date(start.getTime() + days * 24 * 60 * 60 * 1000);
|
||||||
const fmt = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
const fmt = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
||||||
return { tf, range: fmt(start) + " \u2013 " + fmt(end) };
|
return { tf, range: fmt(start) + " \u2013 " + fmt(end) };
|
||||||
}
|
}
|
||||||
@ -2334,9 +2391,11 @@ function updateTodoTimeframeNav() {
|
|||||||
function getTodoTimeframeBounds() {
|
function getTodoTimeframeBounds() {
|
||||||
const tf = getTodoTimeframeKey();
|
const tf = getTodoTimeframeKey();
|
||||||
if (tf === "all") return null;
|
if (tf === "all") return null;
|
||||||
const span = BETTER_TODO_TIMEFRAME_DAYS[tf] * 24 * 60 * 60 * 1000;
|
const spanDays = getTodoTimeframeSpanDays();
|
||||||
|
const pastDays = getTodoTimeframePastDays();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
return { start: now + (betterTodoTimeframeOffset * span), end: now + ((betterTodoTimeframeOffset + 1) * span) };
|
const start = now + (betterTodoTimeframeOffset * spanDays - pastDays) * 24 * 60 * 60 * 1000;
|
||||||
|
return { start, end: start + spanDays * 24 * 60 * 60 * 1000 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// true when `courseId` is the dimmed-out class because another class is selected.
|
// true when `courseId` is the dimmed-out class because another class is selected.
|
||||||
@ -2936,65 +2995,17 @@ function renderProgressOneLine(wrapper, shown, totalAll, completedAll, percent)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Fake progress display": rewrite the per-course completion entries so the
|
|
||||||
// progress readout shows exactly `anon_progress_done` out of
|
|
||||||
// `anon_progress_total`. Course totals are re-distributed proportionally to
|
|
||||||
// their real share, completion ratios get a small per-course offset (seeded,
|
|
||||||
// so rerolling varies it) so the rings look organic, and rounding drift is
|
|
||||||
// corrected so the displayed sums add up to exactly the chosen numbers.
|
|
||||||
function applyAnonProgressOverride(entries) {
|
|
||||||
const rawDone = parseInt(options.anon_progress_done);
|
|
||||||
const rawTotal = parseInt(options.anon_progress_total);
|
|
||||||
const total = isNaN(rawTotal) ? 18 : Math.max(1, Math.min(999, rawTotal));
|
|
||||||
const done = isNaN(rawDone) ? 12 : Math.max(0, Math.min(total, rawDone));
|
|
||||||
|
|
||||||
// Spread the faked total across courses in proportion to their real
|
|
||||||
// share of the workload (minimum 1 each so every ring stays visible).
|
|
||||||
const realTotal = entries.reduce((s, e) => s + e.total, 0) || 1;
|
|
||||||
let assignedTotal = 0;
|
|
||||||
entries.forEach((e, i) => {
|
|
||||||
if (i === entries.length - 1) {
|
|
||||||
e.total = Math.max(1, total - assignedTotal);
|
|
||||||
} else {
|
|
||||||
e.total = Math.max(1, Math.round((total * e.total) / realTotal));
|
|
||||||
}
|
|
||||||
assignedTotal += e.total;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Completed counts: target ratio plus a seeded per-course offset so the
|
|
||||||
// rings don't all show the same fill.
|
|
||||||
const ratio = done / total;
|
|
||||||
let assignedDone = 0;
|
|
||||||
entries.forEach(e => {
|
|
||||||
const variation = ((anonSeededHash("ring:" + e.courseId) % 100) / 100 - 0.5) * 0.3;
|
|
||||||
e.completed = Math.max(0, Math.min(e.total, Math.round(e.total * (ratio + variation))));
|
|
||||||
assignedDone += e.completed;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Correct rounding drift so the sums match the chosen x/y exactly.
|
|
||||||
let drift = done - assignedDone;
|
|
||||||
while (drift !== 0) {
|
|
||||||
let changed = false;
|
|
||||||
for (const e of entries) {
|
|
||||||
if (drift > 0 && e.completed < e.total) { e.completed++; drift--; changed = true; }
|
|
||||||
else if (drift < 0 && e.completed > 0) { e.completed--; drift++; changed = true; }
|
|
||||||
if (drift === 0) break;
|
|
||||||
}
|
|
||||||
if (!changed) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderProgressRings(container, scopedData) {
|
function renderProgressRings(container, scopedData) {
|
||||||
const mode = getProgressRingMode();
|
const mode = getProgressRingMode();
|
||||||
if (mode === "none") { container.innerHTML = ""; return; }
|
if (mode === "none") { container.innerHTML = ""; return; }
|
||||||
|
|
||||||
// Apply the same timeframe window the list pages through so the counts
|
// Apply the same timeframe window the list pages through so the counts
|
||||||
// in the display match what's shown below it. Unlike the Tasks list —
|
// in the display match what's shown below it. Unlike the Tasks tab —
|
||||||
// which keeps every overdue item — finished work from before the window
|
// which keeps recently overdue work — finished work from before the
|
||||||
// is history, not current workload, so it never counts. Unfinished items
|
// window is history, not current workload, so it never counts. The
|
||||||
// from before the window still count on the current window (offset 0)
|
// window is a strict slice (from the lookback bound through the future
|
||||||
// because the Tasks tab shows them as overdue; later windows are strict
|
// edge), so the counts always match the Tasks tab exactly.
|
||||||
// slices, so the backlog only appears on the current one.
|
|
||||||
const taskItems = scopedData.filter(item => isTodoTaskType(item));
|
const taskItems = scopedData.filter(item => isTodoTaskType(item));
|
||||||
const bounds = getTodoTimeframeBounds();
|
const bounds = getTodoTimeframeBounds();
|
||||||
let allAssignments;
|
let allAssignments;
|
||||||
@ -3004,8 +3015,7 @@ function renderProgressRings(container, scopedData) {
|
|||||||
const isDone = (item) => (item.submissions?.submitted || item.planner_override?.marked_complete === true) && !isPinnedIncomplete(item);
|
const isDone = (item) => (item.submissions?.submitted || item.planner_override?.marked_complete === true) && !isPinnedIncomplete(item);
|
||||||
allAssignments = taskItems.filter(item => {
|
allAssignments = taskItems.filter(item => {
|
||||||
const t = new Date(item.plannable_date).getTime();
|
const t = new Date(item.plannable_date).getTime();
|
||||||
if (t > bounds.start && t <= bounds.end) return true;
|
return t > bounds.start && t <= bounds.end;
|
||||||
return betterTodoTimeframeOffset === 0 && t <= bounds.start && !isDone(item);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3785,15 +3795,16 @@ async function createTodoSections(location) {
|
|||||||
// The timeframe is a persisted Better Todo List sub-option set in the
|
// 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
|
// popup. Read the current value each render so popup changes apply on
|
||||||
// the next render. Every tab is paged through time: Tasks by due
|
// the next render. Every tab is paged through time: Tasks by due
|
||||||
// date, Announcements by posted date, Completed by due date. On the
|
// date, Announcements by posted date, Completed by due date. The
|
||||||
// current window (offset 0) everything on/before the cutoff stays
|
// Tasks tab bounds the past (1 week of history back), while
|
||||||
// visible, so only paging forward narrows these two tabs.
|
// Announcements/Completed keep their older history on the current
|
||||||
|
// window and only narrow when paged forward.
|
||||||
// Pinned items ("sent back" submissions) bypass the window filter so
|
// Pinned items ("sent back" submissions) bypass the window filter so
|
||||||
// an old pinned item still shows up under Tasks.
|
// an old pinned item still shows up under Tasks.
|
||||||
const pinnedItems = assignmentsDue.filter(item => isPinnedIncomplete(item));
|
const pinnedItems = assignmentsDue.filter(item => isPinnedIncomplete(item));
|
||||||
assignmentsDue = applyTodoTimeframe(assignmentsDue.filter(item => !isPinnedIncomplete(item))).concat(pinnedItems);
|
assignmentsDue = applyTodoTimeframe(assignmentsDue.filter(item => !isPinnedIncomplete(item)), false).concat(pinnedItems);
|
||||||
announcements = applyTodoTimeframe(announcements);
|
announcements = applyTodoTimeframe(announcements, true);
|
||||||
completed = applyTodoTimeframe(completed);
|
completed = applyTodoTimeframe(completed, true);
|
||||||
// console.log("assignments", assignmentsDue);
|
// console.log("assignments", assignmentsDue);
|
||||||
// console.log("announcements", announcements);
|
// console.log("announcements", announcements);
|
||||||
// console.log("completed", completed);
|
// console.log("completed", completed);
|
||||||
|
|||||||
44
js/popup.js
44
js/popup.js
@ -8,6 +8,8 @@ const syncedSubOptions = [
|
|||||||
"todo_confetti",
|
"todo_confetti",
|
||||||
"todo_progress_rings",
|
"todo_progress_rings",
|
||||||
"todo_timeframe",
|
"todo_timeframe",
|
||||||
|
"todo_timeframe_custom_past",
|
||||||
|
"todo_timeframe_custom_future",
|
||||||
"device_dark",
|
"device_dark",
|
||||||
"relative_dues",
|
"relative_dues",
|
||||||
"card_overdues",
|
"card_overdues",
|
||||||
@ -74,7 +76,7 @@ const exportCardColorToggles = ["gradient_cards", "disable_color_overlay"];
|
|||||||
const exportCardStyles = ["customCardStyles", "imageSize", "cardRoundness", "imageRoundness", "cardSpacing", "cardWidth", "cardHeight", "cardPadding", "card_grid", "card_grid_columns", "card_grid_rows", "card_grid_column_gap", "card_grid_row_gap", "card_grid_center_rows", "card_grid_flex"];
|
const exportCardStyles = ["customCardStyles", "imageSize", "cardRoundness", "imageRoundness", "cardSpacing", "cardWidth", "cardHeight", "cardPadding", "card_grid", "card_grid_columns", "card_grid_rows", "card_grid_column_gap", "card_grid_row_gap", "card_grid_center_rows", "card_grid_flex"];
|
||||||
const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_height_cards", "remlogo", "hide_new_canvas", "hide_navbar", "tab_icons"];
|
const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_height_cards", "remlogo", "hide_new_canvas", "hide_navbar", "tab_icons"];
|
||||||
const exportSidebar = ["better_sidebar", "sidebar_scale"];
|
const exportSidebar = ["better_sidebar", "sidebar_scale"];
|
||||||
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_hide_read", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_timeframe", "todo_hr24", "todo_separate_scrollbar", "todo_alternate_colors", "todo_ignore_card_colors", "todo_remove_icons", "todo_show_scores", "hover_preview"];
|
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_hide_read", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_timeframe", "todo_timeframe_custom_past", "todo_timeframe_custom_future", "todo_hr24", "todo_separate_scrollbar", "todo_alternate_colors", "todo_ignore_card_colors", "todo_remove_icons", "todo_show_scores", "hover_preview"];
|
||||||
const exportGpa = ["gpa_calc", "gpa_calc_prepend", "gpa_calc_cumulative", "gpa_calc_weighted"];
|
const exportGpa = ["gpa_calc", "gpa_calc_prepend", "gpa_calc_cumulative", "gpa_calc_weighted"];
|
||||||
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen", "card_transparency", "bg_opacity", "sidebar_opacity", "bg_blur", "sidebar_blur", "card_opacity", "card_blur"];
|
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen", "card_transparency", "bg_opacity", "sidebar_opacity", "bg_blur", "sidebar_blur", "card_opacity", "card_blur"];
|
||||||
// Master "On/off toggles" = every visual toggle (no GPA, no dark-mode schedule,
|
// Master "On/off toggles" = every visual toggle (no GPA, no dark-mode schedule,
|
||||||
@ -197,6 +199,8 @@ const defaultOptions = {
|
|||||||
"todo_full_height": false,
|
"todo_full_height": false,
|
||||||
"todo_progress_rings": "rings",
|
"todo_progress_rings": "rings",
|
||||||
"todo_timeframe": "all",
|
"todo_timeframe": "all",
|
||||||
|
"todo_timeframe_custom_past": 7,
|
||||||
|
"todo_timeframe_custom_future": 14,
|
||||||
"todo_more_expanded": false,
|
"todo_more_expanded": false,
|
||||||
"todo_confetti": true,
|
"todo_confetti": true,
|
||||||
"device_dark": false,
|
"device_dark": false,
|
||||||
@ -411,15 +415,39 @@ function setupTodoMoreOptions(initial) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Timeframe dropdown for the upcoming Tasks tab. Persisted so the Better Todo
|
// Timeframe dropdown for the upcoming Tasks tab. Persisted so the Better Todo
|
||||||
// List remembers the selected range across sessions.
|
// List remembers the selected range across sessions. The "custom" option
|
||||||
|
// reveals two number inputs (days ago / days from now) below the dropdown.
|
||||||
|
function setTimeframeCustomVisibility(value) {
|
||||||
|
const wrap = document.getElementById("todo_timeframe_custom");
|
||||||
|
if (wrap) wrap.classList.toggle("visible", value === "custom");
|
||||||
|
}
|
||||||
|
|
||||||
function setupTimeframeSelect(initial) {
|
function setupTimeframeSelect(initial) {
|
||||||
const el = document.querySelector("#todo_timeframe");
|
const el = document.querySelector("#todo_timeframe");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const allowed = ["all", "1week", "2week", "month", "2month"];
|
const allowed = ["all", "1week", "2week", "month", "2month", "custom"];
|
||||||
let value = allowed.includes(initial) ? initial : "all";
|
let value = allowed.includes(initial) ? initial : "all";
|
||||||
el.value = value;
|
el.value = value;
|
||||||
|
setTimeframeCustomVisibility(value);
|
||||||
el.addEventListener("change", function () {
|
el.addEventListener("change", function () {
|
||||||
chrome.storage.sync.set({ "todo_timeframe": this.value });
|
chrome.storage.sync.set({ "todo_timeframe": this.value });
|
||||||
|
setTimeframeCustomVisibility(this.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom timeframe bounds (shown when Time frame is "custom"): the todo list
|
||||||
|
// window runs from X days ago to Y days in the future. Clamped to 0-365 so a
|
||||||
|
// typo can't hide every task.
|
||||||
|
function setupCustomTimeframeInput(key, initial, fallback) {
|
||||||
|
const el = document.querySelector("#" + key);
|
||||||
|
if (!el) return;
|
||||||
|
const clamp = (v) => Math.max(0, Math.min(365, v));
|
||||||
|
const parsed = parseInt(initial);
|
||||||
|
el.value = isNaN(parsed) ? fallback : clamp(parsed);
|
||||||
|
el.addEventListener("change", function () {
|
||||||
|
const v = clamp(parseInt(this.value));
|
||||||
|
this.value = v;
|
||||||
|
chrome.storage.sync.set({ [key]: v });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1405,6 +1433,14 @@ function setup() {
|
|||||||
identifier: "todo_timeframe",
|
identifier: "todo_timeframe",
|
||||||
setup: (initial) => setupTimeframeSelect(initial),
|
setup: (initial) => setupTimeframeSelect(initial),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
identifier: "todo_timeframe_custom_past",
|
||||||
|
setup: (initial) => setupCustomTimeframeInput("todo_timeframe_custom_past", initial, 7),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
identifier: "todo_timeframe_custom_future",
|
||||||
|
setup: (initial) => setupCustomTimeframeInput("todo_timeframe_custom_future", initial, 14),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
identifier: "anon_grade_level",
|
identifier: "anon_grade_level",
|
||||||
setup: (initial) => setupAnonGradeSelect(initial),
|
setup: (initial) => setupAnonGradeSelect(initial),
|
||||||
@ -2291,6 +2327,8 @@ function saveCurrentTheme() {
|
|||||||
"todo_confetti": current["todo_confetti"],
|
"todo_confetti": current["todo_confetti"],
|
||||||
"todo_progress_rings": current["todo_progress_rings"],
|
"todo_progress_rings": current["todo_progress_rings"],
|
||||||
"todo_timeframe": current["todo_timeframe"],
|
"todo_timeframe": current["todo_timeframe"],
|
||||||
|
"todo_timeframe_custom_past": current["todo_timeframe_custom_past"],
|
||||||
|
"todo_timeframe_custom_future": current["todo_timeframe_custom_future"],
|
||||||
"todo_hr24": current["todo_hr24"],
|
"todo_hr24": current["todo_hr24"],
|
||||||
"todo_separate_scrollbar": current["todo_separate_scrollbar"],
|
"todo_separate_scrollbar": current["todo_separate_scrollbar"],
|
||||||
"todo_alternate_colors": current["todo_alternate_colors"],
|
"todo_alternate_colors": current["todo_alternate_colors"],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user