feat: add Next Lesson widget and enhance existing widgets

- Implemented a new Next Lesson widget to display the upcoming lesson details on the home screen.
- Updated Today widget to dynamically adjust the number of lessons displayed based on available space.
- Enhanced the Settings screen to allow users to manage hidden lessons more effectively.
- Introduced harmonized color palettes for better visual consistency across themes.
- Updated the timetable API to support a new range parameter for semester-based queries.
- Added tests to ensure the new semester range functionality works as expected.
- Refactored widget styling for improved appearance on devices with different Android versions.
This commit is contained in:
2026-10-18 00:39:32 +03:00
parent 29df0aa61f
commit d900e038b6
52 changed files with 278058 additions and 183859 deletions

View File

@@ -5,6 +5,9 @@ import { lessonKey, subgroupsOf } from "../shared/lessons.js";
import { occurrence, peopleAndRooms, roomsOn } from "./directory.js";
const MAX_DAYS = 62;
// A whole semester with its session fits easily; the cap only guards against abuse.
const MAX_SEMESTER_DAYS = 200;
const DAY_MS = 86_400_000;
function lessonOut(lesson, group, people, date, status, calendar) {
const bell = calendar?.bells.find((b) => b.start === lesson.start);
@@ -33,13 +36,23 @@ function lessonOut(lesson, group, people, date, status, calendar) {
* days: one entry per date from `from`, with the lessons that take place that day.
* weekly: the Word rows by weekday, for when dates cannot be calculated.
*/
export function timetable({ file, schedule, calendar, group, from, days }) {
export function timetable({ file, schedule, calendar, group, from, days, range, today }) {
if (!schedule.groups.includes(group)) {
const error = new Error(`В документе нет группы ${group}.`);
error.status = 404;
throw error;
}
const count = Math.min(Math.max(Number(days) || 7, 1), MAX_DAYS);
let count = Math.min(Math.max(Number(days) || 7, 1), MAX_DAYS);
// range=semester: every day of the semester `today` falls in, so a client can keep
// the whole term offline. Without a known semester it keeps the plain from/days window.
if (range === "semester") {
const current = semesterView(calendar, schedule, today || from);
if (current) {
from = current.semester.start;
const span = Math.round((Date.parse(current.semester.end) - Date.parse(from)) / DAY_MS) + 1;
count = Math.min(Math.max(span, 1), MAX_SEMESTER_DAYS);
}
}
const lessons = schedule.lessons.filter((l) => l.groups.includes(group));
const entries = lessons.map((lesson) => ({
doc: { id: file.id, schedule },