import { showToast, updateImageInfo } from './utils.js'; import SwipeCard from '../components/swipe-card.js'; document.addEventListener('DOMContentLoaded', () => { // Track total images and processed count for progress bar const progressState = { totalImages: 0, processedImages: 0 }; // Global state const state = { currentImageInfo: null, currentOrientation: 'all', isLoading: false }; // DOM elements const lastActionText = document.getElementById('last-action'); const orientationFilters = document.querySelector('.orientation-filters'); const modal = document.getElementById('fullscreen-modal'); const fullscreenImage = document.getElementById('fullscreen-image'); const closeModal = document.querySelector('.close-modal'); const progressBar = document.getElementById('progress-bar'); // Initialize the enhanced swipe card const swipeCard = new SwipeCard({ container: document.querySelector('.swipe-container'), onSwipe: performSwipe, threshold: 100 }); // Make state available to window for debugging and other components window.state = state; window.performSwipe = performSwipe; function performSwipe(direction) { if (!state.currentImageInfo) return; // Update last action text with the action name instead of direction const actionMap = { left: 'Discarded', right: 'Kept', up: 'Favorited', down: 'Marked for review' }; lastActionText.textContent = `Last action: ${actionMap[direction] || 'Unknown'}`; // Show toast notification const toastMap = { left: 'Discarded', right: 'Kept', up: 'Favorited', down: 'Marked for review' }; showToast(toastMap[direction] || 'Action'); // Record the selection recordSelection(state.currentImageInfo, direction); // Update progress progressState.processedImages++; updateProgressBar(); // Load new image after animation completes setTimeout(() => { loadNewImage(); }, 500); } function updateProgressBar() { if (progressBar && progressState.totalImages > 0) { const percentage = (progressState.processedImages / progressState.totalImages) * 100; progressBar.style.width = `${Math.min(percentage, 100)}%`; } } function recordSelection(imageInfo, action) { fetch('/selection', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_path: imageInfo.path, resolution: imageInfo.resolution, action, }), }).catch(error => console.error('Error recording selection:', error)); } function loadNewImage() { if (state.isLoading) return; state.isLoading = true; swipeCard.showLoading(); // First, get the total count if we don't have it yet if (progressState.totalImages === 0) { fetch('/image-count') .then(response => response.json()) .catch(() => ({ count: 100 })) // Fallback if endpoint doesn't exist .then(data => { progressState.totalImages = data.count || 100; updateProgressBar(); }); } fetch(`/random-image?orientation=${state.currentOrientation}&t=${new Date().getTime()}`) .then(response => response.json()) .then(data => { state.isLoading = false; swipeCard.hideLoading(); if (data && data.path) { state.currentImageInfo = data; swipeCard.setImage(data); updateImageInfo(data); adjustContainerToImage(data.orientation); } else { swipeCard.card.innerHTML = `
`; state.currentImageInfo = null; } }) .catch(error => { console.error('Error fetching image:', error); state.isLoading = false; swipeCard.hideLoading(); swipeCard.card.innerHTML = ''; }); } function adjustContainerToImage(orientation) { const container = document.querySelector('.swipe-container'); if (window.innerWidth < 992) { // Only on desktop container.style.transition = 'all 0.5s ease-in-out'; if (orientation === 'landscape') { container.style.flex = '4'; } else { container.style.flex = '2'; } } } // Button event listeners document.getElementById('btn-left').addEventListener('click', () => performSwipe('left')); document.getElementById('btn-right').addEventListener('click', () => performSwipe('right')); document.getElementById('btn-up').addEventListener('click', () => performSwipe('up')); document.getElementById('btn-down').addEventListener('click', () => performSwipe('down')); // Orientation filter event listeners orientationFilters.addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON' && !e.target.classList.contains('active')) { orientationFilters.querySelector('.active').classList.remove('active'); e.target.classList.add('active'); state.currentOrientation = e.target.dataset.orientation; loadNewImage(); } }); // Modal event listeners swipeCard.card.addEventListener('click', () => { if (!swipeCard.state.hasMoved && state.currentImageInfo) { fullscreenImage.src = state.currentImageInfo.path; document.getElementById('modal-resolution').textContent = `Resolution: ${state.currentImageInfo.resolution}`; document.getElementById('modal-filename').textContent = `Filename: ${state.currentImageInfo.filename || 'N/A'}`; document.getElementById('modal-creation-date').textContent = `Creation Date: ${state.currentImageInfo.creation_date || 'N/A'}`; modal.style.display = 'flex'; // Add animation classes setTimeout(() => { modal.classList.add('show'); }, 10); } }); closeModal.addEventListener('click', () => { modal.classList.remove('show'); setTimeout(() => { modal.style.display = 'none'; }, 400); }); modal.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('show'); setTimeout(() => { modal.style.display = 'none'; }, 400); } }); // Keyboard event listeners document.addEventListener('keydown', (e) => { if (modal.style.display === 'flex' && e.key === 'Escape') { modal.classList.remove('show'); setTimeout(() => { modal.style.display = 'none'; }, 400); return; } if (modal.style.display !== 'flex') { switch (e.key) { case 'ArrowLeft': performSwipe('left'); break; case 'ArrowRight': performSwipe('right'); break; case 'ArrowUp': performSwipe('up'); break; case 'ArrowDown': performSwipe('down'); break; } } }); // Add ripple effect to action buttons document.querySelectorAll('.action-btn').forEach(button => { button.addEventListener('click', function(e) { const ripple = document.createElement('span'); ripple.classList.add('ripple'); this.appendChild(ripple); const rect = button.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${e.clientX - rect.left - size/2}px`; ripple.style.top = `${e.clientY - rect.top - size/2}px`; setTimeout(() => { ripple.remove(); }, 600); }); }); // Initialize by loading the first image loadNewImage(); });