feat: update language support and enhance API functionality
- Changed the default language in `index.html` from Russian to English, updating the title and button aria-labels for improved accessibility. - Refactored the `buildFetchOptions` function in `api.js` to include an optional external abort signal, enhancing request management. - Updated `fetchDuties` and `fetchCalendarEvents` to support request cancellation using the new abort signal, improving error handling. - Added unit tests for the API functions to ensure proper functionality, including handling of 403 errors and request cancellations. - Enhanced CSS styles for duty markers to improve visual consistency. - Removed unused code and improved the overall structure of the JavaScript files for better maintainability.
This commit is contained in:
@@ -4,8 +4,7 @@
|
||||
|
||||
import { initTheme, applyTheme } from "./theme.js";
|
||||
import { getLang, t, weekdayLabels } from "./i18n.js";
|
||||
import { getInitData } from "./auth.js";
|
||||
import { isLocalhost } from "./auth.js";
|
||||
import { getInitData, isLocalhost } from "./auth.js";
|
||||
import { RETRY_DELAY_MS, RETRY_AFTER_ACCESS_DENIED_MS } from "./constants.js";
|
||||
import {
|
||||
state,
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
renderCalendar
|
||||
} from "./calendar.js";
|
||||
import { initDayDetail } from "./dayDetail.js";
|
||||
import { initHints } from "./hints.js";
|
||||
import { renderDutyList } from "./dutyList.js";
|
||||
import {
|
||||
firstDayOfMonth,
|
||||
@@ -106,10 +106,18 @@ function requireTelegramOrLocalhost(onAllowed) {
|
||||
if (loadingEl) loadingEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
/** AbortController for the in-flight loadMonth request; aborted when a new load starts. */
|
||||
let loadMonthAbortController = null;
|
||||
|
||||
/**
|
||||
* Load current month: fetch duties and events, render calendar and duty list.
|
||||
* Stale requests are cancelled when the user navigates to another month before they complete.
|
||||
*/
|
||||
async function loadMonth() {
|
||||
if (loadMonthAbortController) loadMonthAbortController.abort();
|
||||
loadMonthAbortController = new AbortController();
|
||||
const signal = loadMonthAbortController.signal;
|
||||
|
||||
hideAccessDenied();
|
||||
setNavEnabled(false);
|
||||
if (loadingEl) loadingEl.classList.remove("hidden");
|
||||
@@ -122,8 +130,8 @@ async function loadMonth() {
|
||||
const from = localDateString(start);
|
||||
const to = localDateString(gridEnd);
|
||||
try {
|
||||
const dutiesPromise = fetchDuties(from, to);
|
||||
const eventsPromise = fetchCalendarEvents(from, to);
|
||||
const dutiesPromise = fetchDuties(from, to, signal);
|
||||
const eventsPromise = fetchCalendarEvents(from, to, signal);
|
||||
const duties = await dutiesPromise;
|
||||
const events = await eventsPromise;
|
||||
const byDate = dutiesByDate(duties);
|
||||
@@ -156,15 +164,18 @@ async function loadMonth() {
|
||||
}, 60000);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.name === "AbortError") {
|
||||
return;
|
||||
}
|
||||
if (e.message === "ACCESS_DENIED") {
|
||||
showAccessDenied(e.serverDetail);
|
||||
setNavEnabled(true);
|
||||
if (
|
||||
window.Telegram &&
|
||||
window.Telegram.WebApp &&
|
||||
!window._initDataRetried
|
||||
!state.initDataRetried
|
||||
) {
|
||||
window._initDataRetried = true;
|
||||
state.initDataRetried = true;
|
||||
setTimeout(loadMonth, RETRY_AFTER_ACCESS_DENIED_MS);
|
||||
}
|
||||
return;
|
||||
@@ -204,9 +215,9 @@ if (nextBtn) {
|
||||
"touchstart",
|
||||
(e) => {
|
||||
if (e.changedTouches.length === 0) return;
|
||||
const t = e.changedTouches[0];
|
||||
startX = t.clientX;
|
||||
startY = t.clientY;
|
||||
const touch = e.changedTouches[0];
|
||||
startX = touch.clientX;
|
||||
startY = touch.clientY;
|
||||
},
|
||||
{ passive: true }
|
||||
);
|
||||
@@ -216,9 +227,9 @@ if (nextBtn) {
|
||||
if (e.changedTouches.length === 0) return;
|
||||
if (document.body.classList.contains("day-detail-sheet-open")) return;
|
||||
if (accessDeniedEl && !accessDeniedEl.hidden) return;
|
||||
const t = e.changedTouches[0];
|
||||
const deltaX = t.clientX - startX;
|
||||
const deltaY = t.clientY - startY;
|
||||
const touch = e.changedTouches[0];
|
||||
const deltaX = touch.clientX - startX;
|
||||
const deltaY = touch.clientY - startY;
|
||||
if (Math.abs(deltaX) <= SWIPE_THRESHOLD) return;
|
||||
if (Math.abs(deltaY) > Math.abs(deltaX)) return;
|
||||
if (deltaX > SWIPE_THRESHOLD) {
|
||||
@@ -237,8 +248,8 @@ if (nextBtn) {
|
||||
|
||||
function bindStickyScrollShadow() {
|
||||
const stickyEl = document.getElementById("calendarSticky");
|
||||
if (!stickyEl || document._stickyScrollBound) return;
|
||||
document._stickyScrollBound = true;
|
||||
if (!stickyEl || state.stickyScrollBound) return;
|
||||
state.stickyScrollBound = true;
|
||||
function updateScrolled() {
|
||||
stickyEl.classList.toggle("is-scrolled", window.scrollY > 0);
|
||||
}
|
||||
@@ -250,6 +261,7 @@ runWhenReady(() => {
|
||||
requireTelegramOrLocalhost(() => {
|
||||
bindStickyScrollShadow();
|
||||
initDayDetail();
|
||||
initHints();
|
||||
loadMonth();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user