Merge pull request #37 from quoije/cursor/page-load-indicators-ea0b
Page load indicators
This commit was merged in pull request #37.
This commit is contained in:
@@ -50,3 +50,72 @@ footer {
|
|||||||
0%, 80%, 100% { opacity: 0.2; }
|
0%, 80%, 100% { opacity: 0.2; }
|
||||||
40% { opacity: 1; }
|
40% { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Full-page loader overlay (blur + progress bar) */
|
||||||
|
body.page-loading {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px;
|
||||||
|
background: rgba(255, 255, 255, 0.55);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-overlay.page-loader-hide {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-card {
|
||||||
|
width: min(520px, 92vw);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 18px 18px 14px;
|
||||||
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-title {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-subtitle {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-progress {
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-bar {
|
||||||
|
height: 100%;
|
||||||
|
width: 0%;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: linear-gradient(90deg, #ff4d4d 0%, #ffb200 50%, #4cd964 100%);
|
||||||
|
transition: width 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loader-actions {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
+104
-25
@@ -1,30 +1,100 @@
|
|||||||
|
function createPageLoader(options = {}) {
|
||||||
|
const title = String(options.title || 'Chargement…');
|
||||||
|
const subtitle = String(options.subtitle || 'Préparation de la page…');
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
let removed = false;
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'page-loader-overlay';
|
||||||
|
overlay.setAttribute('role', 'status');
|
||||||
|
overlay.setAttribute('aria-live', 'polite');
|
||||||
|
overlay.setAttribute('aria-busy', 'true');
|
||||||
|
|
||||||
|
overlay.innerHTML = `
|
||||||
|
<div class="page-loader-card">
|
||||||
|
<p class="page-loader-title">${title}</p>
|
||||||
|
<p class="page-loader-subtitle" id="page-loader-subtitle">${subtitle}</p>
|
||||||
|
<div class="page-loader-progress" aria-hidden="true">
|
||||||
|
<div class="page-loader-bar" id="page-loader-bar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="page-loader-actions d-none" id="page-loader-actions">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-dark" id="page-loader-retry">Réessayer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const barEl = () => overlay.querySelector('#page-loader-bar');
|
||||||
|
const subtitleEl = () => overlay.querySelector('#page-loader-subtitle');
|
||||||
|
const actionsEl = () => overlay.querySelector('#page-loader-actions');
|
||||||
|
const retryBtnEl = () => overlay.querySelector('#page-loader-retry');
|
||||||
|
|
||||||
|
function clampPct(p) {
|
||||||
|
const n = Number(p);
|
||||||
|
if (!Number.isFinite(n)) return 0;
|
||||||
|
return Math.max(0, Math.min(100, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgress(p) {
|
||||||
|
progress = clampPct(p);
|
||||||
|
const bar = barEl();
|
||||||
|
if (bar) bar.style.width = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSubtitle(text) {
|
||||||
|
const el = subtitleEl();
|
||||||
|
if (el) el.textContent = String(text || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureMounted() {
|
||||||
|
if (removed) return;
|
||||||
|
if (!overlay.isConnected) {
|
||||||
|
document.body.classList.add('page-loading');
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAndRemoveSoon() {
|
||||||
|
if (removed) return;
|
||||||
|
overlay.classList.add('page-loader-hide');
|
||||||
|
overlay.setAttribute('aria-busy', 'false');
|
||||||
|
document.body.classList.remove('page-loading');
|
||||||
|
window.setTimeout(() => {
|
||||||
|
removed = true;
|
||||||
|
try { overlay.remove(); } catch (_) {}
|
||||||
|
}, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
setProgress(100);
|
||||||
|
hideAndRemoveSoon();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail(message) {
|
||||||
|
setSubtitle(message || 'Erreur lors du chargement.');
|
||||||
|
setProgress(Math.max(progress, 90));
|
||||||
|
const actions = actionsEl();
|
||||||
|
if (actions) actions.classList.remove('d-none');
|
||||||
|
const btn = retryBtnEl();
|
||||||
|
if (btn) btn.onclick = () => window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureMounted();
|
||||||
|
setProgress(8);
|
||||||
|
|
||||||
|
return { setProgress, setSubtitle, done, fail };
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = async function () {
|
window.onload = async function () {
|
||||||
|
const pageLoader = createPageLoader({
|
||||||
|
title: 'Chargement de la checklist',
|
||||||
|
subtitle: 'Récupération des données…'
|
||||||
|
});
|
||||||
|
|
||||||
const token = localStorage.getItem('auth_token');
|
const token = localStorage.getItem('auth_token');
|
||||||
const movieTableBody = document.getElementById('movie-table-body');
|
const movieTableBody = document.getElementById('movie-table-body');
|
||||||
const watchedRatioEl = document.getElementById('watched-ratio');
|
const watchedRatioEl = document.getElementById('watched-ratio');
|
||||||
|
|
||||||
function showTableLoading() {
|
|
||||||
if (!movieTableBody) return;
|
|
||||||
movieTableBody.innerHTML = `
|
|
||||||
<tr>
|
|
||||||
<td colspan="3" class="text-center py-4" aria-live="polite" aria-busy="true">
|
|
||||||
<div class="d-inline-flex align-items-center gap-2" role="status" aria-label="Chargement">
|
|
||||||
<span class="text-muted">Chargement</span>
|
|
||||||
<span class="loading-dots" aria-hidden="true"><span>.</span><span>.</span><span>.</span></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInlineLoading(el, label = 'Chargement…') {
|
|
||||||
// Keep the header clean: no inline spinners, just text.
|
|
||||||
if (!el) return;
|
|
||||||
el.textContent = label;
|
|
||||||
el.setAttribute('aria-live', 'polite');
|
|
||||||
el.setAttribute('aria-busy', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
function showTableError(message) {
|
function showTableError(message) {
|
||||||
if (!movieTableBody) return;
|
if (!movieTableBody) return;
|
||||||
movieTableBody.innerHTML = `
|
movieTableBody.innerHTML = `
|
||||||
@@ -215,10 +285,9 @@ window.onload = async function () {
|
|||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
showTableLoading();
|
if (watchedRatioEl) watchedRatioEl.textContent = '—';
|
||||||
setInlineLoading(watchedRatioEl);
|
|
||||||
const progressBarEl = document.getElementById('progress-bar');
|
const progressBarEl = document.getElementById('progress-bar');
|
||||||
// Avoid extra "loading" animations; the table loader is enough.
|
pageLoader.setProgress(18);
|
||||||
|
|
||||||
let movies = [];
|
let movies = [];
|
||||||
let watchedMovies = [];
|
let watchedMovies = [];
|
||||||
@@ -233,11 +302,14 @@ window.onload = async function () {
|
|||||||
const ratioPct = totalCount > 0 ? ((watchedCount / totalCount) * 100).toFixed(1) : '0.0';
|
const ratioPct = totalCount > 0 ? ((watchedCount / totalCount) * 100).toFixed(1) : '0.0';
|
||||||
if (watchedRatioEl) watchedRatioEl.innerText = `Vu: ${watchedCount} / ${totalCount} (${ratioPct}%)`;
|
if (watchedRatioEl) watchedRatioEl.innerText = `Vu: ${watchedCount} / ${totalCount} (${ratioPct}%)`;
|
||||||
updateProgressBar(watchedCount, totalCount, false);
|
updateProgressBar(watchedCount, totalCount, false);
|
||||||
|
pageLoader.setProgress(32);
|
||||||
return summary;
|
return summary;
|
||||||
})
|
})
|
||||||
.catch(() => null);
|
.catch(() => null);
|
||||||
|
|
||||||
// Fetch remaining data in parallel.
|
// Fetch remaining data in parallel.
|
||||||
|
pageLoader.setSubtitle('Chargement de la liste des films…');
|
||||||
|
pageLoader.setProgress(26);
|
||||||
const [completionModalContent, moviesRes] = await Promise.all([
|
const [completionModalContent, moviesRes] = await Promise.all([
|
||||||
fetchCompletionModalContent(),
|
fetchCompletionModalContent(),
|
||||||
fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}&view=checklist`, {
|
fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}&view=checklist`, {
|
||||||
@@ -247,6 +319,7 @@ window.onload = async function () {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
applyCompletionModalContent(completionModalContent);
|
applyCompletionModalContent(completionModalContent);
|
||||||
|
pageLoader.setProgress(55);
|
||||||
|
|
||||||
if (!moviesRes.ok) {
|
if (!moviesRes.ok) {
|
||||||
localStorage.removeItem('auth_token');
|
localStorage.removeItem('auth_token');
|
||||||
@@ -255,6 +328,7 @@ window.onload = async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
movies = await moviesRes.json();
|
movies = await moviesRes.json();
|
||||||
|
pageLoader.setProgress(62);
|
||||||
|
|
||||||
// Sort movies alphabetically by title
|
// Sort movies alphabetically by title
|
||||||
movies.sort((a, b) => a.title.localeCompare(b.title, 'fr', { sensitivity: 'base' }));
|
movies.sort((a, b) => a.title.localeCompare(b.title, 'fr', { sensitivity: 'base' }));
|
||||||
@@ -265,6 +339,7 @@ window.onload = async function () {
|
|||||||
if (progressBarEl) progressBarEl.removeAttribute('aria-busy');
|
if (progressBarEl) progressBarEl.removeAttribute('aria-busy');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showTableError('Impossible de charger la checklist. Réessaie dans quelques instants.');
|
showTableError('Impossible de charger la checklist. Réessaie dans quelques instants.');
|
||||||
|
pageLoader.fail('Impossible de charger la checklist. Vérifie ta connexion puis réessaie.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,6 +486,10 @@ window.onload = async function () {
|
|||||||
const moviesPerDay = daysLeft > 0 ? (moviesLeft / daysLeft).toFixed(2) : moviesLeft;
|
const moviesPerDay = daysLeft > 0 ? (moviesLeft / daysLeft).toFixed(2) : moviesLeft;
|
||||||
document.getElementById('movies-per-day').textContent = `À voir par jour: ${moviesPerDay} films`;
|
document.getElementById('movies-per-day').textContent = `À voir par jour: ${moviesPerDay} films`;
|
||||||
|
|
||||||
|
pageLoader.setSubtitle('Finalisation de l’affichage…');
|
||||||
|
pageLoader.setProgress(92);
|
||||||
|
pageLoader.done();
|
||||||
|
|
||||||
document.getElementById('log-off').addEventListener('click', () => {
|
document.getElementById('log-off').addEventListener('click', () => {
|
||||||
localStorage.removeItem('auth_token');
|
localStorage.removeItem('auth_token');
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
|
|||||||
+133
-21
@@ -1,21 +1,123 @@
|
|||||||
window.onload = async function () {
|
function createPageLoader(options = {}) {
|
||||||
function setLoading(el) {
|
const title = String(options.title || 'Chargement…');
|
||||||
if (!el) return;
|
const subtitle = String(options.subtitle || 'Préparation de la page…');
|
||||||
el.innerHTML = `
|
|
||||||
<div class="loading-indicator" role="status" aria-live="polite" aria-busy="true" aria-label="Chargement">
|
|
||||||
<span class="text-muted">Chargement</span>
|
|
||||||
<span class="loading-dots" aria-hidden="true"><span>.</span><span>.</span><span>.</span></span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInlineLoading(el, label = 'Chargement…') {
|
let progress = 0;
|
||||||
// Keep the header clean: no inline spinners, just text.
|
let removed = false;
|
||||||
if (!el) return;
|
|
||||||
el.textContent = label;
|
const overlay = document.createElement('div');
|
||||||
el.setAttribute('aria-live', 'polite');
|
overlay.className = 'page-loader-overlay';
|
||||||
el.setAttribute('aria-busy', 'true');
|
overlay.setAttribute('role', 'status');
|
||||||
|
overlay.setAttribute('aria-live', 'polite');
|
||||||
|
overlay.setAttribute('aria-busy', 'true');
|
||||||
|
|
||||||
|
overlay.innerHTML = `
|
||||||
|
<div class="page-loader-card">
|
||||||
|
<p class="page-loader-title">${title}</p>
|
||||||
|
<p class="page-loader-subtitle" id="page-loader-subtitle">${subtitle}</p>
|
||||||
|
<div class="page-loader-progress" aria-hidden="true">
|
||||||
|
<div class="page-loader-bar" id="page-loader-bar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="page-loader-actions d-none" id="page-loader-actions">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-dark" id="page-loader-retry">Réessayer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const barEl = () => overlay.querySelector('#page-loader-bar');
|
||||||
|
const subtitleEl = () => overlay.querySelector('#page-loader-subtitle');
|
||||||
|
const actionsEl = () => overlay.querySelector('#page-loader-actions');
|
||||||
|
const retryBtnEl = () => overlay.querySelector('#page-loader-retry');
|
||||||
|
|
||||||
|
function clampPct(p) {
|
||||||
|
const n = Number(p);
|
||||||
|
if (!Number.isFinite(n)) return 0;
|
||||||
|
return Math.max(0, Math.min(100, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgress(p) {
|
||||||
|
progress = clampPct(p);
|
||||||
|
const bar = barEl();
|
||||||
|
if (bar) bar.style.width = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSubtitle(text) {
|
||||||
|
const el = subtitleEl();
|
||||||
|
if (el) el.textContent = String(text || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureMounted() {
|
||||||
|
if (removed) return;
|
||||||
|
if (!overlay.isConnected) {
|
||||||
|
document.body.classList.add('page-loading');
|
||||||
|
document.body.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAndRemoveSoon() {
|
||||||
|
if (removed) return;
|
||||||
|
overlay.classList.add('page-loader-hide');
|
||||||
|
overlay.setAttribute('aria-busy', 'false');
|
||||||
|
document.body.classList.remove('page-loading');
|
||||||
|
window.setTimeout(() => {
|
||||||
|
removed = true;
|
||||||
|
try { overlay.remove(); } catch (_) {}
|
||||||
|
}, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
setProgress(100);
|
||||||
|
hideAndRemoveSoon();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail(message) {
|
||||||
|
setSubtitle(message || 'Erreur lors du chargement.');
|
||||||
|
setProgress(Math.max(progress, 90));
|
||||||
|
const actions = actionsEl();
|
||||||
|
if (actions) actions.classList.remove('d-none');
|
||||||
|
const btn = retryBtnEl();
|
||||||
|
if (btn) btn.onclick = () => window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureMounted();
|
||||||
|
setProgress(8);
|
||||||
|
|
||||||
|
return { setProgress, setSubtitle, done, fail };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForImagesIn(containerEl, onProgress) {
|
||||||
|
if (!containerEl) return;
|
||||||
|
const imgs = Array.from(containerEl.querySelectorAll('img'));
|
||||||
|
if (imgs.length === 0) return;
|
||||||
|
|
||||||
|
let resolved = 0;
|
||||||
|
const total = imgs.length;
|
||||||
|
const notify = () => {
|
||||||
|
if (typeof onProgress === 'function') onProgress(resolved, total);
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.race([
|
||||||
|
Promise.all(
|
||||||
|
imgs.map((img) => new Promise((resolve) => {
|
||||||
|
const finish = () => {
|
||||||
|
resolved += 1;
|
||||||
|
notify();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
if (img.complete) return finish();
|
||||||
|
img.addEventListener('load', finish, { once: true });
|
||||||
|
img.addEventListener('error', finish, { once: true });
|
||||||
|
}))
|
||||||
|
),
|
||||||
|
new Promise((resolve) => window.setTimeout(resolve, 7000)), // safety timeout
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = async function () {
|
||||||
|
const pageLoader = createPageLoader({
|
||||||
|
title: 'Chargement des films',
|
||||||
|
subtitle: 'Récupération des données…'
|
||||||
|
});
|
||||||
|
|
||||||
function setLoadError(el, message) {
|
function setLoadError(el, message) {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -159,11 +261,7 @@ window.onload = async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const moviesList = document.getElementById('movies-list');
|
const moviesList = document.getElementById('movies-list');
|
||||||
setLoading(moviesList);
|
pageLoader.setProgress(18);
|
||||||
|
|
||||||
// Make header stats feel responsive (these used to wait for the heavy movies payload).
|
|
||||||
setInlineLoading(document.getElementById('movies-last-updated'));
|
|
||||||
setInlineLoading(document.getElementById('watched-ratio'));
|
|
||||||
|
|
||||||
let watchedMoviesInYear = [];
|
let watchedMoviesInYear = [];
|
||||||
|
|
||||||
@@ -173,6 +271,7 @@ window.onload = async function () {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
watchedMoviesInYear = Array.isArray(data?.watchedMovies) ? data.watchedMovies : [];
|
watchedMoviesInYear = Array.isArray(data?.watchedMovies) ? data.watchedMovies : [];
|
||||||
applySummaryToHeader(data);
|
applySummaryToHeader(data);
|
||||||
|
pageLoader.setProgress(32);
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -184,6 +283,7 @@ window.onload = async function () {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pageLoader.setProgress(28);
|
||||||
const res = await fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}`, {
|
const res = await fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { 'Authorization': `Bearer ${token}` },
|
headers: { 'Authorization': `Bearer ${token}` },
|
||||||
@@ -196,8 +296,10 @@ window.onload = async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const movies = await res.json();
|
const movies = await res.json();
|
||||||
|
pageLoader.setProgress(55);
|
||||||
// If summary is still in flight, wait for it before rendering watched banners.
|
// If summary is still in flight, wait for it before rendering watched banners.
|
||||||
await summaryPromise;
|
await summaryPromise;
|
||||||
|
pageLoader.setProgress(62);
|
||||||
|
|
||||||
// Sort movies alphabetically by title (server also sorts, but keep client as a fallback)
|
// Sort movies alphabetically by title (server also sorts, but keep client as a fallback)
|
||||||
movies.sort((a, b) => a.title.localeCompare(b.title, 'fr', { sensitivity: 'base' }));
|
movies.sort((a, b) => a.title.localeCompare(b.title, 'fr', { sensitivity: 'base' }));
|
||||||
@@ -245,8 +347,18 @@ window.onload = async function () {
|
|||||||
</div>`;
|
</div>`;
|
||||||
moviesList.appendChild(movieDiv);
|
moviesList.appendChild(movieDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pageLoader.setSubtitle('Finalisation de l’affichage…');
|
||||||
|
pageLoader.setProgress(80);
|
||||||
|
await waitForImagesIn(moviesList, (loaded, total) => {
|
||||||
|
// Move 80% -> 98% as posters resolve
|
||||||
|
const pct = 80 + (loaded / Math.max(1, total)) * 18;
|
||||||
|
pageLoader.setProgress(pct);
|
||||||
|
});
|
||||||
|
pageLoader.done();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoadError(moviesList, 'Impossible de charger la liste des films. Réessaie dans quelques instants.');
|
setLoadError(moviesList, 'Impossible de charger la liste des films. Réessaie dans quelques instants.');
|
||||||
|
pageLoader.fail('Impossible de charger les films. Vérifie ta connexion puis réessaie.');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('log-off').addEventListener('click', function () {
|
document.getElementById('log-off').addEventListener('click', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user