Header loading optimization #43
@@ -6,6 +6,8 @@
|
|||||||
<title>Pool Oscars - Checklist</title>
|
<title>Pool Oscars - Checklist</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
|
<script src="./js/app_header_boot.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -65,7 +67,7 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" style="margin-left: -7px;">
|
<a class="navbar-brand" style="margin-left: -7px;">
|
||||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,8 @@
|
|||||||
<title>Pool Oscars - Admin</title>
|
<title>Pool Oscars - Admin</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
|
<script src="./js/app_header_boot.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -31,7 +33,7 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" style="margin-left: -7px;">
|
<a class="navbar-brand" style="margin-left: -7px;">
|
||||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
@@ -74,6 +74,30 @@ footer {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Avoid initial navbar "blur flash" before JS computes the exact height. */
|
||||||
|
body.page-loading .page-loader-overlay {
|
||||||
|
top: var(--page-loader-top, 72px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reserve space for username so it doesn't "pop" the layout. */
|
||||||
|
#user-name {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 8ch;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reserve space for the Admin button even when hidden.
|
||||||
|
This prevents a visible re-layout when we discover admin status from the token.
|
||||||
|
*/
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
nav.navbar #admin-control-link.d-none {
|
||||||
|
display: inline-block !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.page-loader-overlay.page-loader-hide {
|
.page-loader-overlay.page-loader-hide {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Runs early (defer) to populate the navbar before page-specific JS.
|
||||||
|
// Goal: avoid "late" header updates that look like flicker.
|
||||||
|
(function () {
|
||||||
|
function base64UrlToJson(b64url) {
|
||||||
|
const s = String(b64url || '')
|
||||||
|
.replace(/-/g, '+')
|
||||||
|
.replace(/_/g, '/');
|
||||||
|
const padded = s + '='.repeat((4 - (s.length % 4)) % 4);
|
||||||
|
return JSON.parse(atob(padded));
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeJwtPayload(token) {
|
||||||
|
const parts = String(token || '').split('.');
|
||||||
|
if (parts.length < 2) return null;
|
||||||
|
try { return base64UrlToJson(parts[1]); } catch (_) { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExpired(decoded) {
|
||||||
|
const exp = Number(decoded?.exp);
|
||||||
|
if (!Number.isFinite(exp) || exp <= 0) return false;
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
return exp < now;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = localStorage.getItem('auth_token');
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
const decoded = decodeJwtPayload(token);
|
||||||
|
if (!decoded) return;
|
||||||
|
|
||||||
|
// Fast-path redirect checks (match page scripts behavior).
|
||||||
|
if (decoded.mustChangePassword && !/\/change-password\.html(\?|#|$)/.test(window.location.pathname)) {
|
||||||
|
window.location.href = '/change-password.html';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isExpired(decoded)) {
|
||||||
|
try { localStorage.removeItem('auth_token'); } catch (_) {}
|
||||||
|
if (!/\/index\.html$/.test(window.location.pathname) && window.location.pathname !== '/') {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameEl = document.getElementById('user-name');
|
||||||
|
if (nameEl && !nameEl.textContent) {
|
||||||
|
nameEl.textContent = decoded.name || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminLink = document.getElementById('admin-control-link');
|
||||||
|
if (adminLink && decoded.admin) {
|
||||||
|
adminLink.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
+3
-1
@@ -6,6 +6,8 @@
|
|||||||
<title>Pool Oscars - Films</title>
|
<title>Pool Oscars - Films</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
|
<script src="./js/app_header_boot.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -80,7 +82,7 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" style="margin-left: -7px;">
|
<a class="navbar-brand" style="margin-left: -7px;">
|
||||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,8 @@
|
|||||||
<title>Player</title>
|
<title>Player</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
|
<script src="./js/app_header_boot.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -65,7 +67,7 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" style="margin-left: -7px;" href="movies.html">
|
<a class="navbar-brand" style="margin-left: -7px;" href="movies.html">
|
||||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
<title>Pool Oscars - Statistiques des utilisateurs</title>
|
<title>Pool Oscars - Statistiques des utilisateurs</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
|
<script src="./js/app_header_boot.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -53,7 +55,7 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" style="margin-left: -7px;">
|
<a class="navbar-brand" style="margin-left: -7px;">
|
||||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
Reference in New Issue
Block a user