const Auth = {
init: async () => {
let html = await fetch('/lib/pages/auth/index.html').then((response) => response.text());
app.innerHTML = html;
Auth.qr.init();
document.getElementById("page-auth-form").addEventListener("submit", async function (event) {
event.preventDefault();
Auth.submit();
});
},
submit: async () => {
const uuid = document
.getElementById("auth-forms-uuid")
.value
.replace(/^https?:\/\/sheep-service\.com\/?\?(uuid|hash)=/, "");
console.log(uuid);
const URL = `${CONFIG.api}auth`;
USER = await fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json, text/plain, */*",
"Authorization": uuid
}
}).then(response => {
if (response.status === 401) {
localStorage.removeItem("uuid");
Router.navigate(`/auth`);
} else {
Cloud.start();
localStorage.setItem("uuid", uuid);
Router.navigate(`/`, false);
}
return response.json();
});
console.log("USER Info: ", USER);
if (USER.possibilities.can_view_stand) {
newMenuItems({
id: 'menu-stand',
title: 'Графік стенду',
icon: ``,
href: '/stand'
});
}
if (USER.possibilities.can_view_schedule) {
newMenuItems({
id: 'menu-schedule',
title: 'Графіки зібрань',
icon: ``,
href: '/schedule'
});
}
if (USER.possibilities.can_view_sheeps) {
newMenuItems({
id: 'menu-sheeps',
title: 'Вісники',
icon: ``,
href: '/sheeps',
hidden: true
});
await Sheeps.sheeps_list.loadAPI();
}
if (USER.possibilities.can_manager_territory) {
newMenuItems({
id: 'menu-territory',
title: 'Території',
icon: ``,
href: '/territory',
hidden: true
});
}
newMenuItems({
id: 'menu-options',
title: 'Опції',
icon: ``,
href: '/options'
});
},
qr: {
init: () => {
const dialog = document.getElementById('scanner-dialog');
const openBtn = document.getElementById('open-scanner-btn');
const closeBtn = document.getElementById('close-scanner-btn');
const uuidInput = document.getElementById('auth-forms-uuid');
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const loadingStatus = document.getElementById('loading-status');
let streamRef = null;
let isScanning = false;
// --- Логика управления сканером ---
async function startCamera() {
loadingStatus.style.display = 'block';
loadingStatus.innerText = "Запуск камери...";
isScanning = true;
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: { exact: "environment" } }
});
streamRef = stream;
video.srcObject = stream;
video.setAttribute("playsinline", true);
video.play();
video.addEventListener('loadedmetadata', () => {
loadingStatus.style.display = 'none';
requestAnimationFrame(tick);
});
} catch (err) {
// Фолбек, если основная камера недоступна (например, на ПК)
try {
const fallbackStream = await navigator.mediaDevices.getUserMedia({ video: true });
streamRef = fallbackStream;
video.srcObject = fallbackStream;
video.play();
loadingStatus.style.display = 'none';
requestAnimationFrame(tick);
} catch (fallbackErr) {
loadingStatus.innerText = "Помилка доступу до камери";
}
}
}
function stopCamera() {
isScanning = false;
if (streamRef) {
streamRef.getTracks().forEach(track => track.stop());
streamRef = null;
}
video.srcObject = null;
}
function tick() {
if (!isScanning) return;
if (video.readyState === video.HAVE_ENOUGH_DATA) {
canvas.height = video.videoHeight;
canvas.width = video.videoWidth;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert",
});
if (code && code.data.trim() !== "") {
// Успешно считали UUID
uuidInput.value = code.data;
Auth.submit();
// Закрываем модалку (событие close сработает автоматически)
dialog.close();
return;
}
}
requestAnimationFrame(tick);
}
// --- Слушатели событий модального окна ---
// Открыть сканер
openBtn.addEventListener('click', () => {
dialog.showModal(); // Открывает как модальное окно (с бэкдропом)
startCamera();
});
// Закрыть сканер по кнопке крестика
closeBtn.addEventListener('click', () => {
dialog.close();
});
// Важно: событие 'close' срабатывает всегда (и при клике на крестик, и при нажатии Esc)
dialog.addEventListener('close', () => {
stopCamera();
});
},
}
}