perryrperry's picture
Upload 10 files
21678bc verified
/**
* VideoCharm - Main Application
* Core application logic and initialization
*/
const App = (function() {
// DOM Elements
let loadingScreen;
let toast;
let navItems;
let screens;
let historyList;
let favoritesList;
let emptyHistory;
let emptyFavorites;
let clearHistoryBtn;
let clearFavoritesBtn;
let likesCount;
let favoritesCount;
let viewsCount;
let swipeIndicator;
let profileActions;
/**
* Initialize the application
*/
function init() {
// Get DOM elements
loadingScreen = document.getElementById('loadingScreen');
toast = document.getElementById('toast');
navItems = document.querySelectorAll('.nav-item');
screens = document.querySelectorAll('.screen');
historyList = document.getElementById('historyList');
favoritesList = document.getElementById('favoritesList');
emptyHistory = document.getElementById('emptyHistory');
emptyFavorites = document.getElementById('emptyFavorites');
clearHistoryBtn = document.getElementById('clearHistoryBtn');
clearFavoritesBtn = document.getElementById('clearFavoritesBtn');
likesCount = document.getElementById('likesCount');
favoritesCount = document.getElementById('favoritesCount');
viewsCount = document.getElementById('viewsCount');
swipeIndicator = document.getElementById('swipeIndicator');
profileActions = document.querySelectorAll('.action-row');
// Initialize storage
StorageManager.init();
// Initialize video player
VideoPlayer.init();
// Set up event listeners
setupEventListeners();
// Load initial video and start playing immediately
VideoPlayer.loadVideos(1, true).then(() => {
// Show swipe indicator after initial load
setTimeout(() => {
swipeIndicator.style.display = 'flex';
setTimeout(() => {
swipeIndicator.style.opacity = '0';
setTimeout(() => {
swipeIndicator.style.display = 'none';
}, 500);
}, 3000);
}, 500);
// Load more videos in background for smooth experience
setTimeout(() => {
VideoPlayer.loadVideos(3, false);
}, 1000);
});
// Update statistics
updateStats();
}
/**
* Set up event listeners
*/
function setupEventListeners() {
// Navigation
navItems.forEach(item => {
item.addEventListener('click', function() {
const targetScreen = this.getAttribute('data-screen');
switchToScreen(targetScreen);
});
});
// Clear history button
clearHistoryBtn.addEventListener('click', function() {
if (confirm('确定要清空所有观看历史吗?')) {
StorageManager.clearHistory();
loadHistoryList();
updateStats();
showToast('历史记录已清空');
}
});
// Clear favorites button
clearFavoritesBtn.addEventListener('click', function() {
if (confirm('确定要清空所有收藏吗?')) {
StorageManager.clearFavorites();
loadFavoritesList();
updateStats();
showToast('收藏夹已清空');
}
});
// Profile actions
profileActions.forEach(action => {
action.addEventListener('click', function() {
const actionType = this.getAttribute('data-action');
handleProfileAction(actionType);
});
});
// Comment button
document.getElementById('commentButton').addEventListener('click', function() {
showToast('评论功能开发中,敬请期待!');
});
// Share button
document.getElementById('shareButton').addEventListener('click', function() {
showToast('分享功能开发中,敬请期待!');
});
}
/**
* Switch to a different screen
* @param {String} screenId ID of screen to switch to
*/
function switchToScreen(screenId) {
// Update navigation
navItems.forEach(item => {
item.classList.remove('active');
if (item.getAttribute('data-screen') === screenId) {
item.classList.add('active');
}
});
// Update screens
screens.forEach(screen => {
screen.classList.remove('active');
if (screen.id === screenId) {
screen.classList.add('active');
}
});
// Handle specific screen actions
if (screenId === 'historyScreen') {
loadHistoryList();
} else if (screenId === 'favoritesScreen') {
loadFavoritesList();
}
}
/**
* Load history list from storage
*/
function loadHistoryList() {
const history = StorageManager.getHistory();
// Clear list
historyList.innerHTML = '';
if (history.length === 0) {
// Show empty state
emptyHistory.style.display = 'flex';
historyList.style.display = 'none';
} else {
// Hide empty state
emptyHistory.style.display = 'none';
historyList.style.display = 'grid';
// Add history items
history.forEach(item => {
const videoItem = createVideoListItem(item, 'history');
historyList.appendChild(videoItem);
});
}
}
/**
* Load favorites list from storage
*/
function loadFavoritesList() {
const favorites = StorageManager.getFavorites();
// Clear list
favoritesList.innerHTML = '';
if (favorites.length === 0) {
// Show empty state
emptyFavorites.style.display = 'flex';
favoritesList.style.display = 'none';
} else {
// Hide empty state
emptyFavorites.style.display = 'none';
favoritesList.style.display = 'grid';
// Add favorite items
favorites.forEach(item => {
const videoItem = createVideoListItem(item, 'favorite');
favoritesList.appendChild(videoItem);
});
}
}
/**
* Create a video list item for history or favorites
* @param {Object} videoData Video data
* @param {String} type Item type ('history' or 'favorite')
* @returns {HTMLElement} Video list item element
*/
function createVideoListItem(videoData, type) {
const videoItem = document.createElement('div');
videoItem.className = 'video-item';
videoItem.setAttribute('data-id', videoData.id);
// Use thumbnail if available, otherwise use placeholder
let thumbnailUrl;
if (videoData.thumbnail) {
thumbnailUrl = videoData.thumbnail;
} else {
thumbnailUrl = generatePlaceholderImage(videoData.id);
}
videoItem.innerHTML = `
<div class="video-thumbnail">
<img src="${thumbnailUrl}" alt="${videoData.title}" onerror="this.src='${generatePlaceholderImage(videoData.id)}'">
<div class="video-duration">${formatDuration(videoData.duration)}</div>
</div>
<div class="video-item-info">
<div class="video-item-title">${videoData.title}</div>
<div class="video-item-meta">
<div class="time-ago">
<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M11.99 2C6.47 2 2 6.48 2 12C2 17.52 6.47 22 11.99 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 11.99 2ZM12 20C7.58 20 4 16.42 4 12C4 7.58 7.58 4 12 4C16.42 4 20 7.58 20 12C20 16.42 16.42 20 12 20ZM12.5 7H11V13L16.2 16.2L17 14.9L12.5 12.2V7Z"/>
</svg>
${formatTimeAgo(videoData.timestamp)}
</div>
</div>
</div>
`;
// Add click event
videoItem.addEventListener('click', function() {
VideoPlayer.playVideoFromLibrary(videoData);
showToast(`正在播放${type === 'favorite' ? '收藏' : '历史'}视频`);
});
return videoItem;
}
/**
* Update profile statistics
*/
function updateStats() {
const likes = StorageManager.getLikes().length;
const favorites = StorageManager.getFavorites().length;
const views = StorageManager.getViews();
likesCount.textContent = likes;
favoritesCount.textContent = favorites;
viewsCount.textContent = views;
}
/**
* Handle profile action
* @param {String} actionType Type of action
*/
function handleProfileAction(actionType) {
switch (actionType) {
case 'settings':
case 'preferences':
case 'about':
showToast(`${actionType} 功能开发中,敬请期待`);
break;
case 'logout':
if (confirm('确定要退出登录吗?')) {
showToast('退出登录成功');
}
break;
}
}
/**
* Show loading screen
*/
function showLoading() {
loadingScreen.style.display = 'flex';
}
/**
* Hide loading screen
*/
function hideLoading() {
loadingScreen.style.display = 'none';
}
/**
* Show toast message
* @param {String} message Message to show
* @param {Number} duration Duration in ms
*/
function showToast(message, duration = 2000) {
toast.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, duration);
}
/**
* Format duration in seconds to MM:SS
* @param {Number} seconds Duration in seconds
* @returns {String} Formatted duration
*/
function formatDuration(seconds) {
if (!seconds) return '00:00';
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes < 10 ? '0' : ''}${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;
}
/**
* Format timestamp to relative time
* @param {Number} timestamp Timestamp
* @returns {String} Formatted time
*/
function formatTimeAgo(timestamp) {
const now = new Date().getTime();
const diff = now - timestamp;
// Less than 1 minute
if (diff < 60 * 1000) {
return "刚刚";
}
// Less than 1 hour
if (diff < 60 * 60 * 1000) {
return Math.floor(diff / (60 * 1000)) + "分钟前";
}
// Less than 24 hours
if (diff < 24 * 60 * 60 * 1000) {
return Math.floor(diff / (60 * 60 * 1000)) + "小时前";
}
// Less than 7 days
if (diff < 7 * 24 * 60 * 60 * 1000) {
return Math.floor(diff / (24 * 60 * 60 * 1000)) + "天前";
}
// Format as date
const date = new Date(timestamp);
return `${date.getMonth() + 1}${date.getDate()}日`;
}
/**
* Generate consistent placeholder image URL based on ID
* @param {String} id Item ID for consistent image generation
* @returns {String} Image URL
*/
function generatePlaceholderImage(id) {
// Generate a deterministic number from the string ID
let hash = 0;
if (id && id.length > 0) {
for (let i = 0; i < id.length; i++) {
hash = ((hash << 5) - hash) + id.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
}
const randomId = Math.abs(hash % 1000); // Ensure positive
return `https://picsum.photos/seed/${randomId}/300/500`;
}
// Public API
return {
init: init,
showLoading: showLoading,
hideLoading: hideLoading,
showToast: showToast,
switchToScreen: switchToScreen,
updateStats: updateStats
};
})();
// Initialize application when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
App.init();
});