<div {{ stimulus_controller('@pwa/network-information') }}>
<div class="media-gallery">
<header class="gallery-header">
<h1>Media Gallery</h1>
<div class="network-indicator" id="network-indicator">
<span class="indicator-dot"></span>
<span class="indicator-text">Detecting...</span>
</div>
</header>
<div class="controls">
<button id="toggle-auto" class="btn-primary">Auto Quality: ON</button>
<select id="manual-quality" disabled>
<option value="low">Low Quality</option>
<option value="medium">Medium Quality</option>
<option value="high">High Quality</option>
</select>
</div>
<div class="gallery-grid" id="gallery"></div>
<div class="stats" id="stats"></div>
</div>
</div>
<script>
const mediaItems = [
{ id: 1, title: 'Sunset Beach', type: 'image' },
{ id: 2, title: 'Mountain Peak', type: 'image' },
{ id: 3, title: 'City Timelapse', type: 'video' },
{ id: 4, title: 'Forest Stream', type: 'image' },
{ id: 5, title: 'Desert Landscape', type: 'image' },
{ id: 6, title: 'Ocean Waves', type: 'video' }
];
let autoQuality = true;
let currentQuality = 'medium';
let connectionInfo = null;
// UI Elements
const gallery = document.getElementById('gallery');
const networkIndicator = document.getElementById('network-indicator');
const statsEl = document.getElementById('stats');
const toggleAutoBtn = document.getElementById('toggle-auto');
const manualQualitySelect = document.getElementById('manual-quality');
// Toggle auto quality
toggleAutoBtn.addEventListener('click', () => {
autoQuality = !autoQuality;
toggleAutoBtn.textContent = `Auto Quality: ${autoQuality ? 'ON' : 'OFF'}`;
manualQualitySelect.disabled = autoQuality;
if (autoQuality && connectionInfo) {
updateQualityFromConnection(connectionInfo);
}
});
// Manual quality selection
manualQualitySelect.addEventListener('change', () => {
if (!autoQuality) {
currentQuality = manualQualitySelect.value;
loadGallery();
}
});
// Network change handler
document.addEventListener('pwa--network-information:change', (event) => {
connectionInfo = event.detail.connection;
updateNetworkIndicator(connectionInfo);
if (autoQuality) {
updateQualityFromConnection(connectionInfo);
}
updateStats(connectionInfo);
});
function updateQualityFromConnection(connection) {
let newQuality = 'medium';
if (connection.saveData) {
newQuality = 'low';
} else {
const effectiveType = connection.effectiveType;
const downlink = connection.downlink || 1;
if (effectiveType === '4g' && downlink > 10) {
newQuality = 'high';
} else if (effectiveType === '4g' || effectiveType === '3g') {
newQuality = 'medium';
} else {
newQuality = 'low';
}
}
if (newQuality !== currentQuality) {
currentQuality = newQuality;
manualQualitySelect.value = currentQuality;
loadGallery();
}
}
function updateNetworkIndicator(connection) {
const dot = networkIndicator.querySelector('.indicator-dot');
const text = networkIndicator.querySelector('.indicator-text');
let color = '#6b7280';
let status = 'Unknown';
if (connection.effectiveType === '4g' && connection.downlink > 10) {
color = '#10b981';
status = 'Excellent';
} else if (connection.effectiveType === '4g' || connection.effectiveType === '3g') {
color = '#3b82f6';
status = 'Good';
} else if (connection.effectiveType === '2g') {
color = '#f59e0b';
status = 'Slow';
} else {
color = '#ef4444';
status = 'Very Slow';
}
if (connection.saveData) {
status += ' (Data Saver)';
}
dot.style.background = color;
text.textContent = status;
}
function loadGallery() {
const items = mediaItems.map(item => {
if (item.type === 'image') {
return `
<div class="gallery-item">
<img src="/media/${item.id}-${currentQuality}.jpg"
alt="${item.title}"
loading="lazy">
<div class="item-title">${item.title}</div>
<div class="quality-badge">${currentQuality}</div>
</div>
`;
} else {
const showVideo = currentQuality !== 'low';
return `
<div class="gallery-item">
${showVideo ? `
<video src="/media/${item.id}-${currentQuality}.mp4"
controls
preload="metadata">
</video>
` : `
<div class="video-placeholder">
<p>📹 Video (disabled on slow connection)</p>
<button onclick="location.href='/media/${item.id}'">
View anyway
</button>
</div>
`}
<div class="item-title">${item.title}</div>
<div class="quality-badge">${currentQuality}</div>
</div>
`;
}
}).join('');
gallery.innerHTML = items;
}
function updateStats(connection) {
const estimatedPageSize = {
low: 2.5,
medium: 8.0,
high: 25.0
}[currentQuality];
const loadTime = connection.downlink > 0
? (estimatedPageSize / connection.downlink).toFixed(1)
: '?';
statsEl.innerHTML = `
<h3>Current Settings</h3>
<div class="stats-grid">
<div>
<strong>Quality:</strong> ${currentQuality}
</div>
<div>
<strong>Connection:</strong> ${connection.effectiveType}
</div>
<div>
<strong>Speed:</strong> ${connection.downlink || 'N/A'} Mbps
</div>
<div>
<strong>Latency:</strong> ${connection.rtt || 'N/A'} ms
</div>
<div>
<strong>Est. Page Size:</strong> ${estimatedPageSize} MB
</div>
<div>
<strong>Est. Load Time:</strong> ${loadTime}s
</div>
</div>
`;
}
document.addEventListener('pwa--network-information:unsupported', () => {
networkIndicator.querySelector('.indicator-text').textContent =
'Network API not supported';
currentQuality = 'medium';
loadGallery();
});
</script>
<style>
.media-gallery {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.gallery-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.network-indicator {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 20px;
background: #f9fafb;
border-radius: 20px;
}
.indicator-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #6b7280;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.controls {
display: flex;
gap: 15px;
margin-bottom: 30px;
}
.btn-primary {
padding: 10px 20px;
background: #3b82f6;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
}
.btn-primary:hover {
background: #2563eb;
}
select {
padding: 10px;
border: 1px solid #e5e7eb;
border-radius: 6px;
font-size: 14px;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.gallery-item {
position: relative;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.gallery-item:hover {
transform: translateY(-4px);
}
.gallery-item img,
.gallery-item video {
width: 100%;
display: block;
}
.video-placeholder {
aspect-ratio: 16/9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #f3f4f6;
padding: 40px;
text-align: center;
}
.video-placeholder button {
margin-top: 10px;
padding: 8px 16px;
background: #3b82f6;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
.item-title {
padding: 15px;
background: white;
font-weight: 500;
}
.quality-badge {
position: absolute;
top: 10px;
right: 10px;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.7);
color: white;
border-radius: 4px;
font-size: 12px;
text-transform: uppercase;
}
.stats {
padding: 20px;
background: #f9fafb;
border-radius: 12px;
}
.stats h3 {
margin: 0 0 15px 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.stats-grid div {
padding: 10px;
background: white;
border-radius: 6px;
}
</style>