Skip to content

Commit da84346

Browse files
committed
feat: add skeleton loading state for alt-IP section when primary IP is IPv6
Show a shimmer skeleton placeholder while fetching the local IPv4 address via the IPv4-only domain. When the primary IP is already IPv4, no loading state is displayed and the alt section fades in directly.
1 parent d236302 commit da84346

2 files changed

Lines changed: 117 additions & 5 deletions

File tree

static/script.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,37 @@ function initCopyIp(wrap){
272272
return html;
273273
}
274274

275-
function showAltIp(ip,info){
276-
section.innerHTML=buildBlock(ip,info);
275+
function buildSkeleton(){
276+
var html='<div class="skeleton-block">';
277+
html+='<div class="skeleton-bone skeleton-ip"></div>';
278+
html+='</div>';
279+
html+='<div class="skeleton-table">';
280+
for(var i=0;i<8;i++){
281+
html+='<div class="skeleton-row">';
282+
html+='<div class="skeleton-row-label"><div class="skeleton-bone"></div></div>';
283+
html+='<div class="skeleton-row-value"><div class="skeleton-bone"></div></div>';
284+
html+='</div>';
285+
}
286+
html+='</div>';
287+
return html;
288+
}
289+
290+
function showSkeleton(){
291+
section.innerHTML=buildSkeleton();
277292
section.hidden=false;
278-
initCopyIp(section.querySelector(".ip-copy-wrap"));
293+
section.classList.add("alt-visible");
294+
}
295+
296+
function showAltIp(ip,info){
297+
section.classList.remove("alt-visible");
298+
setTimeout(function(){
299+
section.innerHTML=buildBlock(ip,info);
300+
initCopyIp(section.querySelector(".ip-copy-wrap"));
301+
section.hidden=false;
302+
// Force reflow before adding the visible class for transition
303+
void section.offsetHeight;
304+
section.classList.add("alt-visible");
305+
},primaryIsIPv6?300:0);
279306
}
280307

281308
function fetchJson(ip){
@@ -285,8 +312,17 @@ function initCopyIp(wrap){
285312
});
286313
}
287314

315+
function hideSkeleton(){
316+
section.classList.remove("alt-visible");
317+
setTimeout(function(){
318+
section.innerHTML="";
319+
section.hidden=true;
320+
},300);
321+
}
322+
288323
function detectAltIp(){
289324
if(primaryIsIPv6&&ipv4Domain){
325+
showSkeleton();
290326
var controller=new AbortController();
291327
var timeout=setTimeout(function(){controller.abort();},5000);
292328
fetch("https://"+ipv4Domain+"/",{signal:controller.signal})
@@ -297,12 +333,12 @@ function initCopyIp(wrap){
297333
})
298334
.then(function(text){
299335
var ip=text.trim();
300-
if(!ip||ip.indexOf(":")!==-1)return;
336+
if(!ip||ip.indexOf(":")!==-1){hideSkeleton();return;}
301337
return fetchJson(ip).then(function(info){
302338
showAltIp(ip,info);
303339
});
304340
})
305-
.catch(function(){});
341+
.catch(function(){hideSkeleton();});
306342
}else if(!primaryIsIPv6){
307343
fetch("/ip")
308344
.then(function(r){

static/style.css

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,82 @@ main {
477477
padding: 6px 10px 6px 28px;
478478
}
479479

480+
#alt-ip-section {
481+
opacity: 0;
482+
transition: opacity 0.3s ease;
483+
}
484+
485+
#alt-ip-section.alt-visible {
486+
opacity: 1;
487+
}
488+
489+
.skeleton-block {
490+
display: flex;
491+
flex-direction: column;
492+
align-items: center;
493+
}
494+
495+
.skeleton-bone {
496+
background: var(--color-gray5);
497+
border-radius: 6px;
498+
position: relative;
499+
overflow: hidden;
500+
}
501+
502+
.skeleton-bone::after {
503+
content: "";
504+
position: absolute;
505+
inset: 0;
506+
background: linear-gradient(
507+
90deg,
508+
transparent 0%,
509+
var(--color-bg-tertiary) 50%,
510+
transparent 100%
511+
);
512+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
513+
}
514+
515+
@keyframes skeleton-shimmer {
516+
0% { translate: -100% 0; }
517+
100% { translate: 100% 0; }
518+
}
519+
520+
.skeleton-ip {
521+
inline-size: 260px;
522+
block-size: 42px;
523+
margin-block: 28px 20px;
524+
}
525+
526+
.skeleton-table {
527+
inline-size: 100%;
528+
}
529+
530+
.skeleton-row {
531+
display: flex;
532+
align-items: center;
533+
padding-block: 10px;
534+
border-block-start: 1px solid var(--color-separator);
535+
}
536+
537+
.skeleton-row:last-child {
538+
border-block-end: 1px solid var(--color-separator);
539+
}
540+
541+
.skeleton-row-label {
542+
inline-size: 42%;
543+
padding-inline: 35px 32px;
544+
}
545+
546+
.skeleton-row-label .skeleton-bone {
547+
inline-size: 60px;
548+
block-size: 15px;
549+
}
550+
551+
.skeleton-row-value .skeleton-bone {
552+
inline-size: 100px;
553+
block-size: 15px;
554+
}
555+
480556
@media (width <= 480px) {
481557
main {
482558
margin-block-start: 40px;

0 commit comments

Comments
 (0)