walolorj's picture
tamam şimdi bu uygulamaya üyelik, profil, anasayfa, shoot (kısa video sekmesi), içerik paylaşım sayfalarını oluştur lütfen
463b1f0 verified
// FlowFlex - Main JavaScript
class FlowFlexApp {
constructor() {
this.currentFeed = 'trending';
this.posts = [];
this.seenPosts = new Set();
this.userPreferences = this.getUserPreferences();
this.isLoading = false;
this.page = 1;
this.init();
}
init() {
this.bindEvents();
this.loadInitialFeed();
this.setupIntersectionObserver();
this.checkAuthStatus();
}
bindEvents() {
// Feed filter buttons
document.querySelectorAll('.feed-filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.switchFeed(e.target.closest('.feed-filter-btn').dataset.filter);
});
// Like button functionality
document.addEventListener('click', (e) => {
if (e.target.closest('.like-btn')) {
this.handleLike(e.target.closest('.like-btn'));
});
// Save post functionality
document.addEventListener('click', (e) => {
if (e.target.closest('.save-btn')) {
this.handleSave(e.target.closest('.save-btn'));
}
// Post interaction tracking
document.addEventListener('click', (e) => {
const postCard = e.target.closest('.post-card');
if (postCard) {
this.trackPostInteraction(postCard.dataset.postId);
}
// Auth modal triggers
document.addEventListener('click', (e) => {
if (e.target.closest('[data-auth-trigger]')) {
this.showAuthModal();
}
});
}
switchFeed(feedType) {
// Update active button
document.querySelectorAll('.feed-filter-btn').forEach(btn => {
btn.classList.remove('active', 'bg-primary-500', 'text-white');
btn.classList.add('bg-gray-200', 'text-gray-700');
});
const activeBtn = document.querySelector(`[data-filter="${feedType}"]`);
activeBtn.classList.remove('bg-gray-200', 'text-gray-700');
activeBtn.classList.add('active', 'bg-primary-500', 'text-white');
this.currentFeed = feedType;
this.page = 1;
this.posts = [];
this.loadFeed();
}
async loadInitialFeed() {
await this.loadFeed();
}
async loadFeed() {
if (this.isLoading) return;
this.isLoading = true;
this.showLoading();
try {
// Simulate API call with timeout
await new Promise(resolve => setTimeout(resolve, 1000));
const newPosts = await this.fetchPosts(this.currentFeed, this.page);
const filteredPosts = this.filterDuplicatePosts(newPosts);
if (filteredPosts.length > 0) {
this.posts = [...this.posts, ...filteredPosts];
this.renderPosts(filteredPosts);
this.page++;
} else {
this.showEmptyState();
}
} catch (error) {
console.error('Error loading feed:', error);
this.showError('Failed to load posts. Please try again.');
} finally {
this.isLoading = false;
this.hideLoading();
}
}
async fetchPosts(feedType, page = 1) {
// Using Unsplash API for demo posts (real implementation would use your backend)
const accessKey = 'YOUR_UNSPLASH_ACCESS_KEY'; // Replace with actual key
const endpoints = {
trending: `https://api.unsplash.com/photos?page=${page}&per_page=9&order_by=popular`,
following: `https://api.unsplash.com/photos?page=${page}&per_page=9&order_by=latest`,
discover: `https://api.unsplash.com/photos/random?count=9`,
personalized: `https://api.unsplash.com/photos?page=${page}&per_page=9`
};
// For demo purposes, we'll use mock data
return this.generateMockPosts(9);
}
generateMockPosts(count) {
const categories = ['nature', 'technology', 'travel', 'food', 'architecture', 'people'];
const users = [
{ name: 'Alex Johnson', username: 'alexj', followers: 1243 },
{ name: 'Sarah Miller', username: 'sarahm', followers: 856 },
{ name: 'Mike Chen', username: 'mikec', followers: 2107 },
{ name: 'Emma Davis', username: 'emmad', followers: 932 },
{ name: 'James Wilson', username: 'jamesw', followers: 1541 }
];
return Array.from({ length: count }, (_, i) => {
const user = users[Math.floor(Math.random() * users.length)];
const category = categories[Math.floor(Math.random() * categories.length)];
const postId = `post_${Date.now()}_${i}`;
return {
id: postId,
user: user,
image: `http://static.photos/${category}/640x360/${i + 1}`,
caption: this.generateMockCaption(category),
likes: Math.floor(Math.random() * 1000),
comments: Math.floor(Math.random() * 50),
shares: Math.floor(Math.random() * 20),
timestamp: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000), // Within last 7 days
category: category,
engagement: Math.random() * 100
};
});
}
generateMockCaption(category) {
const captions = {
nature: ['Beautiful sunset at the beach! 🌅', 'Morning hike in the mountains 🏔️', 'Peaceful forest walk 🌲'],
technology: ['Working on some exciting new projects! 💻', 'Tech conference was amazing! 🚀', 'Latest gadget unboxing 📱'],
travel: ['Exploring new places! ✈️', 'Cultural experience of a lifetime 🌍', 'Travel dreams coming true 🗺️'],
food: ['Delicious homemade meal! 🍽️', 'Food photography session 📸', 'Trying out new recipes 👨‍🍳'],
architecture: ['Modern architecture never fails to impress! 🏛️', 'Historical building tour 🏰', 'Architectural marvels 🏗️'],
people: ['Great time with friends! 👥', 'Community event was fantastic! 🎉', 'Networking and making connections 🤝']
};
const categoryCaptions = captions[category] || ['Great day! 😊'];
return categoryCaptions[Math.floor(Math.random() * categoryCaptions.length)];
}
filterDuplicatePosts(newPosts) {
return newPosts.filter(post => !this.seenPosts.has(post.id));
}
renderPosts(posts) {
const feedContainer = document.getElementById('feed-container');
const emptyState = document.getElementById('empty-state');
if (posts.length === 0 && this.posts.length === 0) {
this.showEmptyState();
return;
}
emptyState.classList.add('hidden');
posts.forEach(post => {
this.seenPosts.add(post.id);
const postElement = this.createPostElement(post);
feedContainer.appendChild(postElement);
});
}
createPostElement(post) {
const postDiv = document.createElement('div');
postDiv.className = 'post-card bg-white rounded-xl shadow-sm overflow-hidden fade-in';
postDiv.dataset.postId = post.id;
const timeAgo = this.getTimeAgo(post.timestamp);
postDiv.innerHTML = `
<div class="relative">
<img src="${post.image}" alt="Post image" class="w-full h-48 object-cover">
<button class="save-btn absolute top-3 right-3 p-2 bg-white/80 backdrop-blur-sm rounded-full hover:bg-white transition-colors duration-200">
<i data-feather="bookmark" class="w-4 h-4 text-gray-600"></i>
</button>
</div>
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center space-x-3">
<img src="http://static.photos/people/40x40/${Math.floor(Math.random() * 100)}" alt="${post.user.name}" class="w-8 h-8 rounded-full">
<div>
<h4 class="font-semibold text-gray-900 text-sm">${post.user.name}</h4>
<p class="text-gray-500 text-xs">@${post.user.username}</p>
</div>
</div>
<span class="text-xs text-gray-400">${timeAgo}</span>
</div>
<p class="text-gray-700 mb-4 text-sm">${post.caption}</p>
<div class="flex items-center justify-between text-gray-500">
<div class="flex items-center space-x-4">
<button class="like-btn flex items-center space-x-1 text-sm hover:text-red-500 transition-colors duration-200">
<i data-feather="heart" class="w-4 h-4"></i>
<span>${post.likes}</span>
</button>
<button class="flex items-center space-x-1 text-sm hover:text-blue-500 transition-colors duration-200">
<i data-feather="message-circle" class="w-4 h-4"></i>
<span>${post.comments}</span>
</button>
<button class="flex items-center space-x-1 text-sm hover:text-green-500 transition-colors duration-200">
<i data-feather="share-2" class="w-4 h-4"></i>
<span>${post.shares}</span>
</button>
</div>
</div>
`;
return postDiv;
}
handleLike(likeBtn) {
const heartIcon = likeBtn.querySelector('i');
const likesCount = likeBtn.querySelector('span');
likeBtn.classList.add('like-animation');
setTimeout(() => likeBtn.classList.remove('like-animation'), 600);
const isLiked = heartIcon.style.fill === 'currentColor';
const currentLikes = parseInt(likesCount.textContent);
if (isLiked) {
heartIcon.style.fill = 'none';
likesCount.textContent = currentLikes - 1;
likesCount.style.color = '';
} else {
heartIcon.style.fill = 'currentColor';
likesCount.textContent = currentLikes + 1;
likesCount.style.color = '#ef4444';
}
}
handleSave(saveBtn) {
const bookmarkIcon = saveBtn.querySelector('i');
const isSaved = bookmarkIcon.style.fill === 'currentColor';
if (isSaved) {
bookmarkIcon.style.fill = 'none';
} else {
bookmarkIcon.style.fill = 'currentColor';
}
}
trackPostInteraction(postId) {
// Track user engagement for personalization
const post = this.posts.find(p => p.id === postId);
if (post) {
this.updateUserPreferences(post);
}
}
updateUserPreferences(post) {
if (!this.userPreferences.engagedCategories) {
this.userPreferences.engagedCategories = {};
}
this.userPreferences.engagedCategories[post.category] =
(this.userPreferences.engagedCategories[post.category] || 0) + 1;
localStorage.setItem('flowflex_preferences', JSON.stringify(this.userPreferences));
}
getUserPreferences() {
const stored = localStorage.getItem('flowflex_preferences');
return stored ? JSON.parse(stored) : {
engagedCategories: {},
preferredFeed: 'trending',
blockedUsers: []
};
}
getTimeAgo(timestamp) {
const now = new Date();
const diff = now - new Date(timestamp);
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (days > 0) return `${days}d ago`;
if (hours > 0) return `${hours}h ago`;
return `${minutes}m ago`;
}
showLoading() {
document.getElementById('loading').classList.remove('hidden');
}
hideLoading() {
document.getElementById('loading').classList.add('hidden');
}
showEmptyState() {
document.getElementById('feed-container').innerHTML = '';
document.getElementById('empty-state').classList.remove('hidden');
}
showError(message) {
// Simple error notification
const errorDiv = document.createElement('div');
errorDiv.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50';
errorDiv.textContent = message;
document.body.appendChild(errorDiv);
setTimeout(() => {
errorDiv.remove();
}, 3000);
}
setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.isLoading) {
this.loadFeed();
}
}, {
rootMargin: '100px'
});
observer.observe(document.getElementById('loading'));
}
checkAuthStatus() {
const isLoggedIn = localStorage.getItem('flowflex_user');
if (!isLoggedIn) {
// Show login prompt or redirect
console.log('User not logged in');
}
}
showAuthModal() {
const authModal = document.querySelector('auth-modal');
if (authModal) {
authModal.show();
}
}
// Profile page specific methods
loadProfilePosts() {
// Load user's posts for profile page
const posts = this.generateMockPosts(12);
this.renderProfilePosts(posts);
}
renderProfilePosts(posts) {
const profileContent = document.getElementById('profile-content');
if (!profileContent) return;
posts.forEach(post => {
const postElement = this.createPostElement(post);
profileContent.appendChild(postElement);
});
}
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new FlowFlexApp();
});
// Utility function for API calls
async function apiCall(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Authorization': 'Client-ID YOUR_UNSPLASH_ACCESS_KEY',
'Accept-Version': 'v1'
},
...options
});
if (!response.ok) {
throw new Error(`API call failed: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('API call error:', error);
throw error;
}
}