Profile

NBTV
0
NBTV APP - My Profile

My Profile

...

Loading profile data...

`; claimBtn.style.display = 'none'; timerContainer.style.display = 'block'; const totalWaitTime = 10; // User must wait 10 seconds viewing the ad modal let timeLeft = totalWaitTime; countdownSpan.textContent = timeLeft; progressBar.style.width = '0%'; if (adUnlockTimer) clearInterval(adUnlockTimer); adUnlockTimer = setInterval(() => { timeLeft--; countdownSpan.textContent = timeLeft; const percentage = ((totalWaitTime - timeLeft) / totalWaitTime) * 100; progressBar.style.width = `${percentage}%`; if (timeLeft <= 0) { clearInterval(adUnlockTimer); timerContainer.style.display = 'none'; claimBtn.style.display = 'block'; claimBtn.onclick = () => claimAdUnlock(episodeId, episodeTitle); } }, 1000); } /** * NEW: Grants access to the episode after the Ad Timer completes. */ async function claimAdUnlock(episodeId, episodeTitle) { const userUid = auth.currentUser.uid; const unlocksRef = db.ref(`users/${userUid}/unlocked_episodes/${episodeId}`); const unlockExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); try { await unlocksRef.set(unlockExpiry); showToast(`"${episodeTitle}" unlocked for 24 hours!`); if (currentUserData) { if (!currentUserData.unlocked_episodes) currentUserData.unlocked_episodes = {}; currentUserData.unlocked_episodes[episodeId] = unlockExpiry; } toggleModal('adUnlockModal', false); renderEpisodeButtons(); // Refresh buttons to remove lock icon } catch (error) { console.error("Error unlocking via ad:", error); showToast("Failed to unlock episode. Please try again.", true); } } /** * Deducts cost from user's wallet and unlocks the episode for 24 hours. */ async function deductAndUnlockEpisode(episodeId, episodeTitle, cost) { const userUid = auth.currentUser.uid; const walletPKRRef = db.ref(`users/${userUid}/wallet/PKR`); const unlocksRef = db.ref(`users/${userUid}/unlocked_episodes/${episodeId}`); try { await walletPKRRef.transaction(currentBalance => { const balance = currentBalance !== null && typeof currentBalance === 'number' ? currentBalance : 0; if (balance >= cost) { return balance - cost; } return undefined; // Abort transaction }, async (error, committed, snapshot) => { if (error) { showToast("Failed to unlock episode. Please try again.", true); } else if (committed) { const unlockExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); await unlocksRef.set(unlockExpiry); await db.ref(`transactions/${userUid}`).push({ amount: cost, type: 'debit', currency: 'PKR', description: `Unlocked: ${currentSeriesTitle} - ${episodeTitle} (24h)`, created_at: new Date().toISOString() }); showToast(`"${episodeTitle}" unlocked for 24 hours!`); if (currentUserData) { currentUserData.wallet.PKR = snapshot.val(); if (!currentUserData.unlocked_episodes) currentUserData.unlocked_episodes = {}; currentUserData.unlocked_episodes[episodeId] = unlockExpiry; } renderEpisodeButtons(); } else { showToast("Transaction aborted: Insufficient funds.", true); } }); } catch (error) { console.error("Error during episode unlock by payment:", error); showToast("An error occurred during unlock.", true); } } /** * Opens the external unlock link. */ async function openUnlockLink(episodeId, episodeTitle, unlockLink) { if (!unlockLink) { return showToast("No unlock link provided.", true); } // Smart link already opened in unlockEpisodePrompt window.open(unlockLink, '_blank'); showToast("Opening unlock link... Follow instructions on the page.", false); setTimeout(async () => { const userUid = auth.currentUser.uid; const unlocksRef = db.ref(`users/${userUid}/unlocked_episodes/${episodeId}`); const unlockExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); try { await unlocksRef.set(unlockExpiry); showToast(`"${episodeTitle}" should now be unlocked for 24 hours!`); if (currentUserData) { if (!currentUserData.unlocked_episodes) currentUserData.unlocked_episodes = {}; currentUserData.unlocked_episodes[episodeId] = unlockExpiry; } renderEpisodeButtons(); } catch (error) { console.error("Error setting episode unlock status after link:", error); showToast("Failed to update unlock status. Please try again.", true); } }, 3000); } /** * Helper function to convert a standard YouTube URL to an embeddable URL. * Also handles Google Drive and direct video links. * @param {string} url - The original video URL. * @returns {string} The embeddable URL or direct video URL. */ function convertVideoUrlToEmbed(url) { if (!url) return ''; let youtubeId = getYoutubeVideoId(url); if (youtubeId) { return `https://www.youtube.com/embed/${youtubeId}?autoplay=1&rel=0`; } if (url.includes('drive.google.com')) { const match = url.match(/\/d\/([a-zA-Z0-9_-]+)/); if (match && match[1]) { return `https://drive.google.com/file/d/${match[1]}/preview`; } } return url; } /** * Renders the Video Player Page dynamically. * @param {HTMLElement} container - The container element. * @param {object} data - The video section data. Expected to have `youtubeUrl` (now generic `video_url`), `title`, `description`, `watchTimeSeconds`, `rewardPkr`. */ async function renderVideoPlayerPage(container, data) { // This function is included but would typically not be called in a profile-only standalone file. // It's here for completeness based on the provided script, but its UI elements are not in the HTML. console.warn("renderVideoPlayerPage called in profile-only view. This should not display content here."); showToast("Video player functionality is not available in this profile-only view.", true); return; } /** * Handles the countdown timer for video rewards. (Not directly used in profile-only HTML but part of original script). * @param {number} totalSeconds - Total time to watch. * @param {number} rewardPkr - Reward amount. * @param {string} videoId - Database ID of the video section. * @param {string} videoTitle - Title of the video. * @param {string} sectionTitle - Section title (often same as video title in this structure). */ function startVideoRewardTimer(totalSeconds, rewardPkr, videoId, videoTitle, sectionTitle) { console.warn("startVideoRewardTimer called in profile-only view. Functionality not fully supported here."); showToast("Video reward timer functionality is not fully available in this profile-only view.", true); // Minimal implementation to prevent errors if accidentally called: const statusContainer = document.getElementById('video-reward-status'); if (statusContainer) statusContainer.innerHTML = `

Video rewards not active in this view.

`; } /** * Claims the reward after the video timer finishes. (Not directly used in profile-only HTML but part of original script). */ async function claimVideoReward(rewardPkr, videoId, videoTitle, sectionTitle, statusContainer) { console.warn("claimVideoReward called in profile-only view. Functionality not fully supported here."); showToast("Video reward claim functionality is not fully available in this profile-only view.", true); if (statusContainer) statusContainer.innerHTML = `

Video rewards not active in this view.

`; } /** * Initiates playing a game URL, optionally as part of a tournament, and handles play count logic for regular games. * @param {string} gameId - The ID of the game from the database (relevant for non-tournament games). * @param {string} url - The URL of the game. * @param {string} [tournamentId=null] - The ID of the tournament, if applicable. */ async function playGameUrl(gameId, url, tournamentId = null) { if (!auth.currentUser) { return showToast('Login required to play!', true); } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } // Logic for non-tournament, limited-play games if (gameId && !tournamentId) { const gameRef = db.ref(`games/${gameId}`); try { const { committed } = await gameRef.transaction(gameData => { if (gameData && gameData.status === 'approved' && typeof gameData.play_limit === 'number') { gameData.play_count = (gameData.play_count || 0) + 1; if (gameData.play_limit !== 0 && gameData.play_count >= gameData.play_limit) { gameData.status = 'completed'; gameData.notified_play_limit_reached = false; } return gameData; } return; }); if (!committed) { showToast("This game is no longer available or its play limit has been reached.", true); // In a profile-only view, we can't re-render the homepage, just inform. return; } localStorage.setItem('game_played_pending', 'true'); } catch (error) { console.error("Error updating play count for game:", error); showToast("Could not record your play due to an error. Please try again.", true); return; } } if (tournamentId) { localStorage.setItem('active_tournament_id', tournamentId); localStorage.setItem('game_start_time', Date.now()); } // Smart link already opened by checkLoginAndAct window.location.href = url; // Redirect to the game URL } /** * NEW: Opens a banner link. * @param {string} url - The URL to open. */ function openBannerLink(url) { if (!url) { showToast("Link is missing!", true); return; } // Smart link already opened by checkLoginAndAct window.open(url, '_blank'); // Open in a new tab/window } /** * Renders the Wallet Page content dynamically. (Not directly used in profile-only HTML). * @param {HTMLElement} container - The container element for the wallet page. */ async function renderWalletPage(container) { console.warn("renderWalletPage called in profile-only view. This should not display content here."); showToast("Wallet functionality is not available in this profile-only view.", true); } /** * Renders the My Tournaments Page content dynamically. (Not directly used in profile-only HTML). * @param {HTMLElement} container - The container element for the My Tournaments page. */ async function renderMyTournamentsPage(container) { console.warn("renderMyTournamentsPage called in profile-only view. This should not display content here."); showToast("My Matches functionality is not available in this profile-only view.", true); } /** * Renders the Notifications Page content dynamically. (Not directly used in profile-only HTML). * @param {HTMLElement} container - The container element for the notifications page. */ async function renderNotificationsPage(container) { console.warn("renderNotificationsPage called in profile-only view. This should not display content here."); showToast("Notifications functionality is not available in this profile-only view.", true); } /** * Formats a timestamp into a human-readable "time ago" string. * @param {string} timestamp - The ISO string timestamp. * @returns {string} The formatted time ago string. */ function formatTimeAgo(timestamp) { const now = new Date(); const past = new Date(timestamp); const diffSeconds = Math.round((now.getTime() - past.getTime()) / 1000); const minutes = Math.round(diffSeconds / 60); const hours = Math.round(diffSeconds / 3600); const days = Math.round(diffSeconds / (3600 * 24)); if (diffSeconds < 60) return `${diffSeconds} sec ago`; if (minutes < 60) return `${minutes} min ago`; if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`; return `${days} day${days > 1 ? 's' : ''} ago`; } /** * Marks a single notification as read in Firebase. * @param {string} notificationId - The ID of the notification to mark as read. */ async function markNotificationAsRead(notificationId) { if (!auth.currentUser || !notificationId) return; try { await db.ref(`notifications/${auth.currentUser.uid}/${notificationId}`).update({ status: 'read', read_at: new Date().toISOString() }); showToast('Notification marked as read!'); // Re-render profile if support messages are open to reflect changes if (document.getElementById('mySupportMessagesSection').style.display === 'block') { showMySupportMessages(); } } catch (error) { console.error("Error marking notification as read:", error); showToast('Failed to mark notification as read.', true); } } /** * Marks all unread notifications for the current user as read. */ async function markAllNotificationsAsRead() { if (!auth.currentUser) return; try { const unreadSnap = await db.ref(`notifications/${auth.currentUser.uid}`).orderByChild('status').equalTo('unread').once('value'); const updates = {}; unreadSnap.forEach(childSnap => { updates[`notifications/${auth.currentUser.uid}/${childSnap.key}/status`] = 'read'; updates[`notifications/${auth.currentUser.uid}/${childSnap.key}/read_at`] = new Date().toISOString(); }); if (Object.keys(updates).length > 0) { await db.ref().update(updates); showToast('All notifications marked as read!'); } else { showToast('No unread notifications to mark.', false); } // Re-render profile if support messages are open to reflect changes if (document.getElementById('mySupportMessagesSection').style.display === 'block') { showMySupportMessages(); } } catch (error) { console.error("Error marking all notifications as read:", error); showToast('Failed to mark all notifications as read.', true); } } /** * Renders the Profile Page content dynamically. * @param {HTMLElement} container - The container element for the profile page. */ function renderProfilePage(container) { if (!auth.currentUser) { // Display Login Form when not logged in container.innerHTML = `

Login to view and manage your profile.

Welcome!

Login or create an account to manage your profile.

`; // Attach listeners for the dynamically added login/signup forms attachLoginSignupFormListeners('Page'); // Use 'Page' suffix for these specific elements return; } // Display Profile Details when logged in const userReferralCode = currentUserData?.username || ''; const referralsEarned = currentUserData?.referrals_earned_count || 0; const preferredCurrency = currentUserData?.preferred_currency || 'PKR'; container.innerHTML = `

Profile & Settings

${currentUserData?.username ? currentUserData.username[0].toUpperCase() : 'U'}

${escapeJsStringForHtmlAttribute(currentUserData?.username || 'User')}

${escapeJsStringForHtmlAttribute(currentUserData?.email || auth.currentUser?.email || 'N/A')}

Referrals Joined: ${referralsEarned}

My Wallet Settings

This sets the default currency displayed in your wallet and header.

Invite Friends & Earn!

Share your username as referral code. You get PKR ${referralBonusAmount} for every friend who signs up!

Friends must enter your username during signup to count as your referral.

Get the Full App Data delete !

Fill this form to delete your account permanently..

Account Delete Form

Claim PKR 100 - 1000 daily!
Withdrawal requires 20 referrals who each deposited PKR 300.

`; // Ensure event listeners are re-attached for dynamically loaded elements document.getElementById('preferred-currency-select')?.addEventListener('change', updateUserPreferredCurrency); updateProfileContent(); // Call to populate specific elements like username, email, etc. renderMySubmittedGames(); // Call the function to populate the submitted games list updateFeatureButtonStates(); // Update button states after rendering profile content } /** * Renders the list of games submitted by the current user with their play counts. */ function renderMySubmittedGames() { const listEl = document.getElementById('my-submitted-games-list'); if (!auth.currentUser || !listEl) { listEl.innerHTML = `

Login to view your submitted games.

`; return; } const userId = auth.currentUser.uid; db.ref('games').orderByChild('created_by').equalTo(userId).on('value', snapshot => { const gamesData = snapshot.val(); if (!gamesData) { listEl.innerHTML = `

You have not submitted any games yet.

`; return; } const submittedGames = []; for (const gameId in gamesData) { const game = gamesData[gameId]; if (game.status === 'approved' || game.status === 'completed' || game.status === 'pending') { submittedGames.push({ id: gameId, ...game }); } } if (submittedGames.length === 0) { listEl.innerHTML = `

You have no active games. Games appear here once approved by admin.

`; return; } submittedGames.sort((a, b) => a.title.localeCompare(b.title)); listEl.innerHTML = submittedGames.map(game => { const currentPlays = game.play_count || 0; const playLimit = game.play_limit || 0; const progressPercentage = playLimit > 0 ? Math.min((currentPlays / playLimit) * 100, 100) : 0; let gameStatusText; let statusColorClass; switch (game.status) { case 'approved': gameStatusText = 'Live'; statusColorClass = 'text-green-600 bg-green-100'; break; case 'completed': gameStatusText = 'Limit Reached'; statusColorClass = 'text-red-600 bg-red-100'; break; case 'pending': gameStatusText = 'Pending Approval'; statusColorClass = 'text-yellow-600 bg-yellow-100'; break; case 'rejected': statusColorClass = 'text-red-800 bg-gray-200'; // Corrected variable name gameStatusText = 'Rejected (Refunded)'; break; default: gameStatusText = 'Unknown'; statusColorClass = 'text-gray-600 bg-gray-100'; } const reAddButton = game.status === 'completed' ? `` : ''; return `

${escapeJsStringForHtmlAttribute(game.title)}

${gameStatusText}

Plays: ${currentPlays} / ${playLimit === 0 ? 'Unlimited' : playLimit}

${reAddButton}
`; }).join(''); }, (error) => { console.error("Error loading user's submitted games:", error); listEl.innerHTML = `

Error loading your games.

`; }); } /** * NEW FEATURE: Updates the disabled state and styling of feature buttons based on `appFeatureControls`. */ function updateFeatureButtonStates() { const addMoneyBtn = document.getElementById('addMoneyBtn'); const withdrawMoneyBtn = document.getElementById('withdrawMoneyBtn'); const addGameBtn = document.getElementById('addGameBtn'); // This is from modals, so relevant const applyState = (button, featureKey) => { if (!button) return; const control = appFeatureControls[featureKey] || { locked: false, reason: '' }; if (control.locked) { button.disabled = true; button.classList.add('opacity-50', 'cursor-not-allowed'); button.classList.remove('hover:from-green-600', 'hover:to-green-600', 'hover:from-blue-600', 'hover:to-blue-600', 'hover:from-orange-600', 'hover:to-yellow-600'); } else { button.disabled = false; button.classList.remove('opacity-50', 'cursor-not-allowed'); // Restore hover effects based on original colors (can be more specific if needed) // Note: These hover classes are generic; actual classes from Tailwind might be more specific. if (featureKey === 'add_money') button.classList.add('hover:bg-green-600'); // Example, adjust as per actual button styles else if (featureKey === 'withdraw_money') button.classList.add('hover:bg-blue-600'); // Example else if (featureKey === 'add_game') button.classList.add('hover:from-orange-600', 'hover:to-yellow-600'); // Example } }; // These buttons are in the modals, so they will be updated when modals are rendered or shown applyState(addMoneyBtn, 'add_money'); applyState(withdrawMoneyBtn, 'withdraw_money'); applyState(addGameBtn, 'add_game'); // For the button in profile section } /** * NEW FEATURE: Handles click on a locked feature button. * Displays a toast with the lock reason. * @param {string} featureName - The name of the feature (e.g., 'add_money'). * @returns {boolean} True if the feature is unlocked, false if locked. */ function checkFeatureLock(featureName) { const control = appFeatureControls[featureName]; if (control && control.locked) { showToast(control.reason || `This feature (${featureName.replace(/_/g, ' ')}) is currently locked by the admin.`, true); return false; } return true; } function updateProfileContent() { if (currentUserData) { const usernameEl = document.getElementById('profile-username'); if (usernameEl) usernameEl.textContent = currentUserData.username || 'User'; const emailEl = document.getElementById('profile-email'); if (emailEl) emailEl.textContent = currentUserData.email || auth.currentUser?.email || 'N/A'; const referralsEarnedEl = document.getElementById('profile-referrals-count'); if (referralsEarnedEl) { const countToDisplay = currentUserData.referrals_earned_count || 0; referralsEarnedEl.textContent = countToDisplay; } const referralLinkInput = document.getElementById('referralLinkInput'); if (referralLinkInput) { referralLinkInput.value = currentUserData.username || ''; } const referralBonusTextEl = document.getElementById('referral-bonus-text'); if (referralBonusTextEl) { referralBonusTextEl.textContent = referralBonusAmount; } // Note: withdraw-amount and admin-deposit-number elements are within modals, // so they are usually updated when the modal is opened, not just on profile content update. // Keeping these lines for completeness but they might not affect visible profile directly. const withdrawAmountInput = document.getElementById('withdraw-amount'); if (withdrawAmountInput) { withdrawAmountInput.placeholder = `Min ${formatCurrency(minWithdrawalAmount, 'PKR')}`; } const adminDepositNumEl = document.getElementById('admin-deposit-number'); if (adminDepositNumEl) { adminDepositNumEl.textContent = adminDepositNumber; } } else { console.warn("DEBUG: updateProfileContent called but currentUserData is null."); // If currentUserData is null, ensure profile details are cleared or show default. document.getElementById('profile-username').textContent = 'User'; document.getElementById('profile-email').textContent = 'N/A'; document.getElementById('profile-referrals-count').textContent = '0'; document.getElementById('referralLinkInput').value = ''; } } /** * Copies the referral username to the clipboard. */ function copyReferralLink() { const referralLinkInput = document.getElementById('referralLinkInput'); if (referralLinkInput) { referralLinkInput.select(); referralLinkInput.setSelectionRange(0, 99999); document.execCommand('copy'); showToast('Referral username copied!'); } } /** * Attaches event listeners for the login/signup tabs and forms. * @param {string} suffix - Suffix for element IDs (e.g., 'Modal' or 'Page'). */ function attachLoginSignupFormListeners(suffix) { const loginTab = document.getElementById('loginTabBtn' + suffix); const signupTab = document.getElementById('signupTabBtn' + suffix); const loginForm = document.getElementById('loginForm' + suffix); const signupForm = document.getElementById('signupForm' + suffix); const signupUsernameInput = document.getElementById('signupUsername' + suffix); const usernameAvailabilityEl = document.getElementById('usernameAvailability' + suffix); const signupSubmitBtn = document.getElementById('signupSubmitBtn' + suffix); const signupReferralCodeInput = document.getElementById('signupReferralCode' + suffix); // Defensive checks if (!loginTab || !signupTab || !loginForm || !signupForm || !signupUsernameInput || !usernameAvailabilityEl || !signupSubmitBtn || !signupReferralCodeInput) { console.warn(`Login/Signup form elements with suffix '${suffix}' not found, skipping attaching listeners.`); return; } // Initialize signup button state signupSubmitBtn.disabled = true; loginTab.onclick = () => { // Use onclick instead of addEventListener for dynamically added elements to prevent multiple attachments loginTab.className = "flex-1 py-2 text-center font-bold border-b-4 border-red-500 text-red-600"; signupTab.className = "flex-1 py-2 text-center font-bold text-gray-400 border-b-4 border-transparent"; loginForm.style.display = 'block'; signupForm.style.display = 'none'; signupSubmitBtn.disabled = true; usernameAvailabilityEl.textContent = ''; signupUsernameInput.value = ''; signupReferralCodeInput.value = ''; }; signupTab.onclick = () => { // Use onclick signupTab.className = "flex-1 py-2 text-center font-bold border-b-4 border-red-500 text-red-600"; loginTab.className = "flex-1 py-2 text-center font-bold text-gray-400 border-b-4 border-transparent"; signupForm.style.display = 'block'; loginForm.style.display = 'none'; signupSubmitBtn.disabled = true; usernameAvailabilityEl.textContent = ''; signupUsernameInput.value = ''; signupReferralCodeInput.value = ''; }; let usernameTimer; signupUsernameInput.oninput = () => { // Use oninput clearTimeout(usernameTimer); const username = signupUsernameInput.value.trim(); if (username.length < 3) { usernameAvailabilityEl.textContent = 'Username must be at least 3 characters.'; usernameAvailabilityEl.className = 'text-xs mt-1 text-red-500'; signupSubmitBtn.disabled = true; return; } if (!/^[a-zA-Z0-9_.-]+$/.test(username)) { usernameAvailabilityEl.textContent = 'Invalid characters. Use letters, numbers, _, ., -'; usernameAvailabilityEl.className = 'text-xs mt-1 text-red-500'; signupSubmitBtn.disabled = true; return; } usernameAvailabilityEl.textContent = 'Checking availability...'; usernameAvailabilityEl.className = 'text-xs mt-1 text-gray-500'; signupSubmitBtn.disabled = true; usernameTimer = setTimeout(async () => { try { const snap = await db.ref('usernames/' + username.toLowerCase()).once('value'); if (snap.exists()) { usernameAvailabilityEl.textContent = 'Username is already taken.'; usernameAvailabilityEl.className = 'text-xs mt-1 text-red-500'; signupSubmitBtn.disabled = true; } else { usernameAvailabilityEl.textContent = 'Username is available!'; usernameAvailabilityEl.className = 'text-xs mt-1 text-green-500'; signupSubmitBtn.disabled = false; } } catch (error) { console.error("Error checking username availability:", error); usernameAvailabilityEl.textContent = 'Error checking username.'; usernameAvailabilityEl.className = 'text-xs mt-1 text-red-500'; signupSubmitBtn.disabled = true; } }, 500); }; loginForm.onsubmit = async e => { // Use onsubmit e.preventDefault(); try { await auth.signInWithEmailAndPassword(e.target['loginEmail' + suffix].value, e.target['loginPassword' + suffix].value); showToast('Login successful!'); // If login form is in a modal, close the modal. If on the page, re-render profile. if (suffix === 'Modal') toggleModal('authModal', false); e.target.reset(); renderProfilePage(document.getElementById('profilePageContainer')); // Re-render profile content } catch (err) { showToast(err.message, true); } }; signupForm.onsubmit = async e => { // Use onsubmit e.preventDefault(); const emailInput = e.target['signupEmail' + suffix]; const passwordInput = e.target['signupPassword' + suffix]; const usernameInput = e.target['signupUsername' + suffix]; const referralCodeInput = e.target['signupReferralCode' + suffix]; const enteredReferralCode = referralCodeInput.value.trim().toLowerCase(); const username = usernameInput.value.trim(); try { const finalCheckSnap = await db.ref('usernames/' + username.toLowerCase()).once('value'); if (finalCheckSnap.exists()) { showToast('Username is already taken. Please choose another.', true); usernameInput.focus(); return; } const cred = await auth.createUserWithEmailAndPassword(emailInput.value, passwordInput.value); const newUserId = cred.user.uid; const initialSignupBonus = signupBonusAmount; const referralBonus = referralBonusAmount; let newUserData = { username: username, email: emailInput.value, wallet: { PKR: initialSignupBonus, INR: 0, USD: 0 }, preferred_currency: 'PKR', referrals_earned_count: 0, created_at: new Date().toISOString(), locked: false, lockReason: null }; newUserData.referral_code = username.toLowerCase(); let feedbackMessage = `Signup successful! You got ${formatCurrency(initialSignupBonus, 'PKR')} 🎉`; if (enteredReferralCode && enteredReferralCode !== username.toLowerCase()) { const refSnap = await db.ref('usernames/' + enteredReferralCode).once('value'); if (refSnap.exists()) { const referrerUid = refSnap.val(); await db.ref(`users/${referrerUid}`).transaction((data) => { if (data) { if (!data.wallet) data.wallet = { PKR: data.wallet_balance || 0, INR: 0, USD: 0 }; data.wallet.PKR = (data.wallet.PKR !== null && typeof data.wallet.PKR === 'number' ? data.wallet.PKR : 0) + referralBonus; data.referrals_earned_count = (data.referrals_earned_count || 0) + 1; } return data; }); await db.ref(`transactions/${referrerUid}`).push({ amount: referralBonus, type: "credit", currency: 'PKR', description: `Referral bonus from ${username}`, created_at: new Date().toISOString() }); newUserData.referred_by_username = enteredReferralCode; feedbackMessage = `Signup successful! You got ${formatCurrency(initialSignupBonus, 'PKR')} & referrer rewarded 🎉`; } else { feedbackMessage = `Signup successful! You got ${formatCurrency(initialSignupBonus, 'PKR')} (Invalid referral code)`; } } else if (enteredReferralCode === username.toLowerCase()) { feedbackMessage = `Signup successful! You got ${formatCurrency(initialSignupBonus, 'PKR')} (Cannot refer yourself)`; } const userRef = db.ref('users/' + newUserId); const snap = await userRef.once('value'); if (!snap.exists()) { await userRef.set(newUserData); } else { await userRef.update({ wallet: newUserData.wallet, preferred_currency: newUserData.preferred_currency, username: newUserData.username, email: newUserData.email, referral_code: newUserData.referral_code, referred_by_username: newUserData.referred_by_username || null, created_at: newUserData.created_at, locked: newUserData.locked, lockReason: newUserData.lockReason }); } await db.ref('usernames/' + username.toLowerCase()).set(newUserId); await db.ref(`transactions/${newUserId}`).push({ amount: initialSignupBonus, type: "credit", currency: 'PKR', description: "Signup Bonus", created_at: new Date().toISOString() }); showToast(feedbackMessage); // If signup form is in a modal, close the modal. If on the page, re-render profile. if (suffix === 'Modal') toggleModal('authModal', false); signupForm.reset(); referralCodeInput.value = ''; usernameAvailabilityEl.textContent = ''; renderProfilePage(document.getElementById('profilePageContainer')); // Re-render profile content } catch (err) { console.error("Signup Error:", err); showToast(err.message, true); } }; } /** * Handles claiming daily bonus. */ async function claimDailyBonus() { if (!auth.currentUser) { showToast('Login required to claim bonus!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } const userUid = auth.currentUser.uid; const userRef = db.ref(`users/${userUid}`); try { const snap = await userRef.once('value'); const userData = snap.val(); if (!userData) { showToast('User data not found. Please try logging in again.', true); toggleModal('authModal', true); // Open login modal return; } const lastClaimTimestamp = userData.last_daily_bonus_claim_timestamp || 0; const twentyFourHours = 0.10 * 60 * 60 * 1000; // 6 minutes for 0.1 hour test, adjust to 24 * 60 * 60 * 1000 for actual 24 hours if (Date.now() - lastClaimTimestamp < twentyFourHours) { const timeLeft = twentyFourHours - (Date.now() - lastClaimTimestamp); const hours = Math.floor(timeLeft / (1000 * 60 * 60)); const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60)); showToast(`You can claim your next daily bonus in ${hours}h ${minutes}m.`, true); window.open(ADSTERRA_SMART_LINK, '_blank'); // Open smart link only if logged in but on cooldown return; } const randomBonus = Math.floor(Math.random() * 200) + 100; let committed = false; const walletRef = db.ref(`users/${userUid}/wallet/PKR`); await walletRef.transaction(data => { const balance = data !== null && typeof data === 'number' ? data : 0; return balance + randomBonus; }, (error, _committed, snapshot) => { if (error) { console.error("Daily bonus transaction failed: ", error); showToast("Failed to claim daily bonus. Please try again.", true); } else if (_committed) { committed = true; db.ref(`transactions/${userUid}`).push({ amount: randomBonus, type: 'credit', currency: 'PKR', description: 'Daily Bonus', created_at: new Date().toISOString() }); userRef.update({ last_daily_bonus_claim_timestamp: Date.now(), daily_bonus_withdrawal_condition_active: true }).then(() => { showToast(`💰 You claimed ${formatCurrency(randomBonus, 'PKR')} daily bonus!`, false); window.open(ADSTERRA_SMART_LINK, '_blank'); // Open smart link after successful claim updateProfileContent(); // Update profile to show new balance }).catch(updateError => { console.error("Failed to update daily bonus timestamp:", updateError); showToast('Claimed bonus but failed to record timestamp.', true); }); } else { console.log("Daily bonus transaction aborted."); } }); } catch (error) { console.error("Error claiming daily bonus:", error); showToast('An error occurred while claiming bonus.', true); window.open(ADSTERRA_SMART_LINK, '_blank'); // Open smart link on unexpected error } } /** * Attaches event listeners for the tabs on the My Tournaments page. (Not directly used in profile-only HTML). */ function attachMyTournamentsListeners() { console.warn("attachMyTournamentsListeners called in profile-only view. Functionality not fully supported here."); } /** * Handles joining a tournament. Deducts entry fee and adds user as participant. * @param {Event} event - The form submission event. * @param {string} tournamentId - The ID of the tournament. * @param {number} entryFee - The entry fee for the tournament. */ async function joinTournament(event, tournamentId, entryFee) { event.preventDefault(); const user = auth.currentUser; if (!user) { showToast('Login required!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } // Tournaments entry fee is always PKR if ((currentUserData.wallet.PKR || 0) < entryFee) return showToast('Insufficient PKR balance!', true); try { const tournamentSnap = await db.ref(`tournaments/${tournamentId}/title`).once('value'); const tournamentTitle = tournamentSnap.val() || 'Unknown Tournament'; const newTransactionKey = db.ref().child('transactions').push().key; const updates = { [`/users/${user.uid}/wallet/PKR`]: (currentUserData.wallet.PKR || 0) - entryFee, [`/participants/${tournamentId}/${user.uid}`]: { status: 'Participated', joined_at: new Date().toISOString() }, [`/transactions/${user.uid}/${newTransactionKey}`]: { amount: entryFee, type: 'debit', currency: 'PKR', description: `Entry: ${tournamentTitle}`, created_at: new Date().toISOString() } }; await db.ref().update(updates); showToast('Joined successfully!'); // Update profile content to reflect balance change updateProfileContent(); } catch (error) { console.error("Error joining tournament:", error); showToast('Failed to join tournament. ' + error.message, true); } } /** * Handles adding money to the user's account via deposit request. * @param {Event} event - The form submission event. */ async function addMoney(event) { event.preventDefault(); const amount = Number(document.getElementById('add-amount').value); const tid = document.getElementById('deposit-tid').value.trim(); const sourceType = document.getElementById('deposit-source-type').value.trim(); const acceptRulesCheckbox = document.getElementById('acceptDepositRules'); if (amount <= 0) { return showToast('Amount must be positive!', true); } if (!tid) { return showToast('Please enter the Transaction ID (TID)!', true); } if (!sourceType) { return showToast('Please specify EasyPaisa or JazzCash!', true); } if (!acceptRulesCheckbox.checked) { return showToast('Please accept the Deposit Rules to proceed.', true); } const user = auth.currentUser; if (!user) { showToast('Login required!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } try { await db.ref(`pending_deposits/${user.uid}`).push({ amount: amount, tid: tid, source_details: sourceType, status: 'pending', currency: 'PKR', created_at: new Date().toISOString(), user_email: currentUserData.email || user.email, user_username: currentUserData.username || 'N/A' }); showToast('Deposit request submitted! Awaiting verification.'); toggleModal('addMoneyModal', false); event.target.reset(); acceptRulesCheckbox.checked = false; } catch (error) { console.error("Error submitting deposit request:", error); showToast('Failed to submit deposit request. ' + error.message, true); } } /** * Handles withdrawal requests from the user's account. * @param {Event} event - The form submission event. */ async function withdrawMoney(event) { event.preventDefault(); const amount = Number(document.getElementById('withdraw-amount').value); const currency = document.getElementById('withdraw-currency').value; const withdrawNumber = document.getElementById('withdraw-number').value.trim(); const ownerName = document.getElementById('withdraw-owner-name').value.trim(); const accountType = document.getElementById('withdraw-account-type').value; const acceptRulesCheckbox = document.getElementById('acceptWithdrawalRules'); let minWithdraw = minWithdrawalAmount; if(currency !== 'PKR') { const rate = getExchangeRate('PKR', currency); minWithdraw = minWithdrawalAmount * rate; } if (amount < minWithdraw) { return showToast(`Minimum withdrawal is ${formatCurrency(minWithdraw, currency)}`, true); } if (!withdrawNumber || !ownerName || !accountType) { return showToast('Please fill all withdrawal details!', true); } if (!acceptRulesCheckbox.checked) { return showToast('Please accept the Withdrawal Rules to proceed.', true); } const user = auth.currentUser; if (!user) { showToast('Login required!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } if (amount > (currentUserData.wallet[currency] || 0)) { return showToast(`Insufficient ${currency} funds!`, true); } if (currency === 'PKR' && currentUserData.daily_bonus_withdrawal_condition_active) { const requiredReferrals = 10; if ((currentUserData.referrals_earned_count || 0) < requiredReferrals) { showToast(`Withdrawal from PKR requires at least ${requiredReferrals} referrals for bonus funds. You have ${currentUserData.referrals_earned_count || 0}.`, true); return; } } const uid = user.uid; try { const walletCurrencyRef = db.ref(`users/${uid}/wallet/${currency}`); let committed = false; await walletCurrencyRef.transaction(currentBalance => { const balance = currentBalance !== null && typeof currentBalance === 'number' ? currentBalance : 0; if (balance >= amount) { return balance - amount; } return undefined; }, async (error, _committed, snapshot) => { if (error) { console.error("Withdrawal deduction failed: ", error); showToast("Withdrawal failed: Could not deduct funds.", true); } else if (_committed) { committed = true; const transactionRef = db.ref(`transactions/${uid}`).push(); const transactionId = transactionRef.key; await transactionRef.set({ amount: amount, type: 'debit', currency: currency, description: `Withdrawal request for ${accountType} (${withdrawNumber})`, status: 'pending', created_at: new Date().toISOString() }); const withdrawalRequestKey = db.ref("pending_withdrawals/" + uid).push().key; await db.ref("pending_withdrawals/" + uid + "/" + withdrawalRequestKey).set({ amount: amount, currency: currency, status: "pending", withdrawal_account: withdrawNumber, withdrawal_owner_name: ownerName, withdrawal_account_type: accountType, created_at: new Date().toISOString(), user_uid: uid, user_email: currentUserData.email || user.email, user_username: currentUserData.username || "N/A", transaction_id_ref: transactionId }); showToast("Withdrawal request sent! Amount deducted and awaiting admin approval."); toggleModal("withdrawMoneyModal", false); event.target.reset(); acceptRulesCheckbox.checked = false; updateProfileContent(); // Update profile to reflect balance change } else { showToast("Withdrawal aborted: Insufficient funds or another operation occurred.", true); } }); } catch (error) { console.error("Error during withdrawal request:", error); showToast("Withdrawal failed. Please try again.", true); } } /** * Updates the user's preferred currency in Firebase. * @param {Event} e - The change event from the select element. */ async function updateUserPreferredCurrency(e) { const newCurrency = e.target.value; if (!auth.currentUser || !currentUserData) return; try { await db.ref(`users/${auth.currentUser.uid}`).update({ preferred_currency: newCurrency }); showToast(`Preferred currency set to ${newCurrency}!`); updateProfileContent(); // Update header balance display } catch (error) { console.error("Error updating preferred currency:", error); showToast("Failed to update preference.", true); } } /** * Handles adding a new game submitted by a user. * Deducts cost from wallet based on play limit and adds game to Firebase `pending_games` node. * @param {Event} event - The form submission event. */ async function addNewGame(event) { event.preventDefault(); const user = auth.currentUser; if (!user) { showToast('Login required to add games!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } const gameTitle = document.getElementById('gameTitleInput').value.trim(); const gameCategory = document.getElementById('gameCategoryInput').value.trim(); const playLimit = parseInt(document.getElementById('gamePlayLimitInput').value, 10); const thumbnailSource = document.querySelector('input[name="thumbnailSource"]:checked').value; let gameImageUrl = ''; const gameImageFile = document.getElementById('gameImageFileInput').files[0]; const gameSource = document.querySelector('input[name="gameSource"]:checked').value; let gameContent = ''; // This will hold either URL or HTML if (thumbnailSource === 'url') { gameImageUrl = document.getElementById('gameImageUrlInput').value.trim(); if (!gameImageUrl) { showToast('Thumbnail URL is required!', true); return; } } else { // 'upload' if (!gameImageFile) { showToast('Thumbnail image file is required!', true); return; } } if (gameSource === 'url') { gameContent = document.getElementById('gameUrlInput').value.trim(); if (!gameContent) { showToast('Game/Website URL is required!', true); return; } } else { // 'html' gameContent = document.getElementById('gameHtmlCodeInput').value.trim(); if (!gameContent) { showToast('HTML Code is required!', true); return; } } if (!gameTitle || !gameCategory || isNaN(playLimit) || playLimit <= 0) { showToast('Title, Category, and Play Limit (positive number) are required!', true); return; } // NEW FEATURE: Category Restriction Validation try { const allApprovedGamesSnapshot = await db.ref('games').orderByChild('status').equalTo('approved').once('value'); const existingCategories = new Set(); allApprovedGamesSnapshot.forEach(childSnap => { const game = childSnap.val(); if (game.category) { existingCategories.add(game.category.trim().toLowerCase()); } }); // If the submitted category is not 'Uncategorized' and doesn't exist among approved categories if (gameCategory.toLowerCase() !== 'uncategorized' && !existingCategories.has(gameCategory.toLowerCase())) { const existingCategoriesArray = Array.from(existingCategories); const categoryList = existingCategoriesArray.length > 0 ? `(e.g., ${existingCategoriesArray.join(', ')})` : ''; showToast(`You must use an existing game category ${categoryList}. "Uncategorized" is also an option if applicable.`, true); return; } } catch (error) { console.error("Error validating game category:", error); showToast('Failed to validate game category. Please try again.', true); return; } const gameCost = playLimit; if (!currentUserData || (currentUserData.wallet.PKR || 0) < gameCost) { showToast(`Insufficient balance. You need ${formatCurrency(gameCost, 'PKR')} to add this game.`, true); return; } try { // Upload image to Firebase Storage if selected if (thumbnailSource === 'upload' && gameImageFile) { showToast('Uploading thumbnail image...', false); const storageRef = storage.ref(`game_thumbnails/${user.uid}/${Date.now()}_${gameImageFile.name}`); const uploadTask = storageRef.put(gameImageFile); await new Promise((resolve, reject) => { uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log('Upload is ' + progress + '% done'); }, (error) => { console.error("Image upload failed:", error); showToast("Image upload failed! " + error.message, true); reject(error); }, async () => { gameImageUrl = await uploadTask.snapshot.ref.getDownloadURL(); resolve(); } ); }); } const userWalletPKRRef = db.ref(`users/${user.uid}/wallet/PKR`); const pendingGameRef = db.ref('pending_games').push(); const transactionRef = db.ref(`transactions/${user.uid}`).push(); let committed = false; await userWalletPKRRef.transaction(currentBalance => { if (currentBalance !== null && currentBalance >= gameCost) { return currentBalance - gameCost; } return undefined; }, async (error, _committed, snapshot) => { if (error) { console.error("Transaction failed: ", error); showToast("Failed to deduct game cost. Please try again.", true); } else if (_committed) { committed = true; await pendingGameRef.set({ title: gameTitle, image_url: gameImageUrl, game_url: (gameSource === 'url' ? gameContent : null), // Store URL if source is URL game_html_code: (gameSource === 'html' ? gameContent : null), // Store HTML if source is HTML content_type: gameSource, // 'url' or 'html' created_by: user.uid, created_by_username: currentUserData.username || 'N/A', created_at: new Date().toISOString(), status: 'pending', // Mark as pending for admin approval play_limit: playLimit, play_count: 0, notified_play_limit_reached: false // Flag to track if user has been notified }); await transactionRef.set({ amount: gameCost, type: 'debit', currency: 'PKR', description: `Cost for game submission (${playLimit} plays): ${gameTitle}`, created_at: new Date().toISOString() }); showToast('Game submitted successfully! It will appear on the homepage after admin approval.'); toggleModal('addGameModal', false); event.target.reset(); document.getElementById('gameSubmissionCost').textContent = 'Cost: PKR 0'; updateProfileContent(); // Update profile content (e.g., submitted games list) } else { showToast(`Transaction aborted: Insufficient balance. You need ${formatCurrency(gameCost, 'PKR')} to add a game.`, true); } }); } catch (error) { console.error("Error adding game or deducting balance:", error); showToast('Failed to submit game. Please try again.', true); } } /** * Handles sending a contact message from the user to admin. * @param {Event} event - The form submission event. */ async function sendContactMessage(event) { event.preventDefault(); const user = auth.currentUser; if (!user) { showToast('Login required to send a message!', true); toggleModal('authModal', true); // Open login modal return; } if (!currentUserData || currentUserData.locked) { auth.signOut(); const lockReason = currentUserData?.lockReason ? `Reason: ${currentUserData.lockReason}` : ''; return showToast(`Your account is locked. Please contact support. ${lockReason}`, true); } const subject = document.getElementById('contactSubject').value.trim(); const message = document.getElementById('contactMessage').value.trim(); if (!subject || !message) { showToast('Subject and Message cannot be empty!', true); return; } try { await db.ref('contact_messages').push({ userId: user.uid, username: currentUserData.username || 'N/A', email: currentUserData.email || user.email, subject: subject, message: message, timestamp: new Date().toISOString(), status: 'pending' }); showToast('Message sent successfully!'); toggleModal('contactUsModal', false); event.target.reset(); if (document.getElementById('mySupportMessagesSection').style.display === 'block') { showMySupportMessages(); // Refresh support messages if visible } } catch (error) { console.error("Error sending contact message:", error); showToast('Failed to send message. ' + error.message, true); } } /** * Opens the currency exchange modal and populates it with current user balances. */ function openExchangeCurrencyModal() { if (!currentUserData || !currentUserData.wallet) { showToast('Please login to exchange currency.', true); toggleModal('authModal', true); // Open login modal return; } const balanceInfoEl = document.getElementById('current-balance-exchange-info'); let balancesHtml = 'Your balances: '; ['PKR', 'INR', 'USD'].forEach(currency => { const balance = currentUserData.wallet[currency] || 0; balancesHtml += `${formatCurrency(balance, currency)}`; }); balanceInfoEl.innerHTML = balancesHtml; document.getElementById('exchange-amount').value = ''; document.getElementById('exchange-from-currency').value = currentUserData.preferred_currency || 'PKR'; document.getElementById('exchange-to-currency').value = (currentUserData.preferred_currency === 'PKR' ? 'INR' : 'PKR'); document.getElementById('exchange-error-message').style.display = 'none'; document.getElementById('exchange-result-message').style.display = 'none'; toggleModal('exchangeCurrencyModal', true); } /** * Calculates the exchange rate between two currencies. * @param {string} fromCurrency - The currency to convert from. * @param {string} toCurrency - The currency to convert to. * @returns {number} The exchange rate, or 0 if not found/invalid. */ function getExchangeRate(fromCurrency, toCurrency) { if (fromCurrency === toCurrency) { return 1; } const key = `${fromCurrency}_to_${toCurrency}`; return exchangeRates[key] || 0; } /** * Handles the currency exchange process. * @param {Event} e - The form submission event. */ async function exchangeCurrency(e) { e.preventDefault(); const amountToExchange = Number(document.getElementById('exchange-amount').value); const fromCurrency = document.getElementById('exchange-from-currency').value; const toCurrency = document.getElementById('exchange-to-currency').value; const errorMessageEl = document.getElementById('exchange-error-message'); const resultMessageEl = document.getElementById('exchange-result-message'); errorMessageEl.style.display = 'none'; resultMessageEl.style.display = 'none'; if (!amountToExchange || amountToExchange <= 0) { errorMessageEl.textContent = 'Please enter a valid amount to exchange.'; errorMessageEl.style.display = 'block'; return; } if (fromCurrency === toCurrency) { errorMessageEl.textContent = 'Cannot exchange to the same currency.'; errorMessageEl.style.display = 'block'; return; } if (!auth.currentUser || !currentUserData || !currentUserData.wallet) { errorMessageEl.textContent = 'User data not loaded. Please re-login.'; errorMessageEl.style.display = 'block'; return; } const availableAmount = currentUserData.wallet[fromCurrency] || 0; if (availableAmount < amountToExchange) { errorMessageEl.textContent = `Insufficient ${fromCurrency} balance. You have ${formatCurrency(availableAmount, fromCurrency)}.`; errorMessageEl.style.display = 'block'; return; } const rate = getExchangeRate(fromCurrency, toCurrency); if (rate === 0) { errorMessageEl.textContent = `Exchange rate for ${fromCurrency} to ${toCurrency} not found.`; errorMessageEl.style.display = 'block'; return; } const convertedAmount = amountToExchange * rate; const userId = auth.currentUser.uid; let shouldChangePreferredCurrency = false; try { await db.ref(`users/${userId}/wallet`).transaction(currentWallet => { if (currentWallet) { const fromBalance = currentWallet[fromCurrency] || 0; if (fromBalance >= amountToExchange) { currentWallet[fromCurrency] = fromBalance - amountToExchange; currentWallet[toCurrency] = (currentWallet[toCurrency] || 0) + convertedAmount; if (currentUserData.preferred_currency === fromCurrency && currentWallet[fromCurrency] < 0.01) { shouldChangePreferredCurrency = true; } return currentWallet; } } return undefined; }, async (error, committed, snapshot) => { if (error) { console.error('Currency exchange transaction failed:', error); errorMessageEl.textContent = `Exchange failed: ${error.message}`; errorMessageEl.style.display = 'block'; } else if (committed) { if (shouldChangePreferredCurrency) { await db.ref(`users/${userId}`).update({ preferred_currency: toCurrency }); } await db.ref(`transactions/${userId}`).push({ amount: amountToExchange, type: 'exchange', currency: fromCurrency, description: `Exchanged ${formatCurrency(amountToExchange, fromCurrency)} to ${formatCurrency(convertedAmount, toCurrency)}`, exchange_from_currency: fromCurrency, exchange_to_currency: toCurrency, exchanged_amount: convertedAmount, created_at: new Date().toISOString() }); resultMessageEl.textContent = `Successfully exchanged ${formatCurrency(amountToExchange, fromCurrency)} for ${formatCurrency(convertedAmount, toCurrency)}!`; resultMessageEl.style.display = 'block'; showToast(resultMessageEl.textContent); // Re-open modal to show updated balances, then close after a short delay setTimeout(() => openExchangeCurrencyModal(), 100); } else { errorMessageEl.textContent = 'Exchange aborted: Insufficient funds or another operation occurred.'; errorMessageEl.style.display = 'block'; } }); } catch (error) { console.error("Error during exchange:", error); errorMessageEl.textContent = `An unexpected error occurred: ${error.message}`; errorMessageEl.style.display = 'block'; } } /** * Toggles the visibility of the main profile view vs. policy content area. */ function showMainProfileView() { document.getElementById('mainProfileView').style.display = 'block'; document.getElementById('policyContentArea').style.display = 'none'; document.getElementById('policy-content-display').style.display = 'none'; document.getElementById('mySupportMessagesSection').style.display = 'none'; } /** * Displays a specific policy section or the contact messages. * @param {string} sectionKey - The key for the policy (e.g., 'privacy_policy') or 'my_support_messages'. */ async function showPolicySection(sectionKey) { document.getElementById('mainProfileView').style.display = 'none'; document.getElementById('policyContentArea').style.display = 'block'; document.getElementById('mySupportMessagesSection').style.display = 'none'; // Hide support messages by default const policyDisplay = document.getElementById('policy-content-display'); const policyTitle = document.getElementById('policy-display-title'); const policyBody = document.getElementById('policy-display-body'); if (!policyDisplay || !policyTitle || !policyBody) { console.warn("Policy display elements not found."); showToast("Error: Policy display elements missing.", true); return; } policyDisplay.style.display = 'block'; policyTitle.textContent = 'Loading...'; policyBody.innerHTML = '

Loading policy content...

'; try { const policySnap = await db.ref(`app_content/${sectionKey}`).once('value'); const policyData = policySnap.val(); if (policyData) { policyTitle.textContent = policyData.displayTitle || sectionKey.replace(/_/g, ' ').toUpperCase(); policyBody.innerHTML = policyData.body || '

Content not available.

'; } else { policyTitle.textContent = 'Content Not Found'; policyBody.innerHTML = '

The requested policy content could not be loaded.

'; } } catch (error) { console.error("Error loading policy:", error); policyTitle.textContent = 'Error'; policyBody.innerHTML = '

Failed to load policy content due to an error.

'; } } /** * Displays the user's support messages. */ async function showMySupportMessages() { if (!auth.currentUser) { showToast('Login required to view support messages!', true); toggleModal('authModal', true); // Open login modal return; } document.getElementById('mainProfileView').style.display = 'none'; document.getElementById('policyContentArea').style.display = 'block'; document.getElementById('policy-content-display').style.display = 'none'; // Hide general policy display document.getElementById('mySupportMessagesSection').style.display = 'block'; const listEl = document.getElementById('user-contact-messages-list'); if (!listEl) { console.warn("user-contact-messages-list not found."); return; } listEl.innerHTML = '

Loading your messages...

'; const userId = auth.currentUser.uid; try { const messagesSnap = await db.ref('contact_messages').orderByChild('userId').equalTo(userId).once('value'); const messagesData = messagesSnap.val(); if (!messagesData) { listEl.innerHTML = '

You have not sent any support messages yet.

'; return; } const messagesArray = []; for (const id in messagesData) { messagesArray.push({ id, ...messagesData[id] }); } messagesArray.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); listEl.innerHTML = messagesArray.map(msg => { const statusColor = msg.status === 'resolved' ? 'text-green-600' : (msg.status === 'replied' ? 'text-blue-600' : 'text-orange-500'); return `

${escapeJsStringForHtmlAttribute(msg.subject)}

${escapeJsStringForHtmlAttribute(msg.status)}

${escapeJsStringForHtmlAttribute(msg.message)}

${msg.adminReply ? `

Admin Reply: ${escapeJsStringForHtmlAttribute(msg.adminReply)}

` : ''}

${new Date(msg.timestamp).toLocaleString()}

`; }).join(''); } catch (error) { console.error("Error loading support messages:", error); listEl.innerHTML = '

Error loading your support messages.

'; } } /** * Logs the current user out of the application. */ function logout() { auth.signOut(); // After logout, re-render the profile page to show the login form renderProfilePage(document.getElementById('profilePageContainer')); } /** * Sends a password reset email to the current user. */ function changePassword() { const user = auth.currentUser; if (user && user.email) { auth.sendPasswordResetEmail(user.email) .then(() => showToast(`Password reset link sent to ${user.email}.`)) .catch(err => showToast(err.message, true)); } else { showToast("No active user or email found.", true); toggleModal('authModal', true); // Open login modal } } /** * Initializes the application once the DOM is fully loaded. */ document.addEventListener('DOMContentLoaded', async () => { // Attach event listeners for the modal login/signup forms (if they exist) attachLoginSignupFormListeners('Modal'); // Attach event listeners for forms and buttons in profile document.getElementById('addMoneyForm')?.addEventListener('submit', addMoney); document.getElementById('withdrawMoneyForm')?.addEventListener('submit', withdrawMoney); document.getElementById('addGameForm')?.addEventListener('submit', addNewGame); document.getElementById('contactUsForm')?.addEventListener('submit', sendContactMessage); document.getElementById('exchangeCurrencyForm')?.addEventListener('submit', exchangeCurrency); // AddGameModal specific listeners for toggling input fields const thumbnailSourceRadios = document.querySelectorAll('input[name="thumbnailSource"]'); const gameImageUrlInput = document.getElementById('gameImageUrlInput'); const gameImageFileInput = document.getElementById('gameImageFileInput'); thumbnailSourceRadios.forEach(radio => { radio.addEventListener('change', (event) => { if (event.target.value === 'url') { gameImageUrlInput.classList.remove('hidden'); gameImageUrlInput.setAttribute('required', 'required'); gameImageFileInput.classList.add('hidden'); gameImageFileInput.removeAttribute('required'); gameImageFileInput.value = ''; // Clear file input } else { gameImageUrlInput.classList.add('hidden'); gameImageUrlInput.removeAttribute('required'); gameImageUrlInput.value = ''; // Clear URL input gameImageFileInput.classList.remove('hidden'); gameImageFileInput.setAttribute('required', 'required'); } }); }); const gameSourceRadios = document.querySelectorAll('input[name="gameSource"]'); const gameUrlInput = document.getElementById('gameUrlInput'); const gameHtmlCodeInput = document.getElementById('gameHtmlCodeInput'); gameSourceRadios.forEach(radio => { radio.addEventListener('change', (event) => { if (event.target.value === 'url') { gameUrlInput.classList.remove('hidden'); gameUrlInput.setAttribute('required', 'required'); gameHtmlCodeInput.classList.add('hidden'); gameHtmlCodeInput.removeAttribute('required'); gameHtmlCodeInput.value = ''; // Clear HTML input } else { gameUrlInput.classList.add('hidden'); gameUrlInput.removeAttribute('required'); gameUrlInput.value = ''; // Clear URL input gameHtmlCodeInput.classList.remove('hidden'); gameHtmlCodeInput.setAttribute('required', 'required'); } }); }); // End AddGameModal specific listeners const gamePlayLimitInput = document.getElementById('gamePlayLimitInput'); const gameSubmissionCostDisplay = document.getElementById('gameSubmissionCost'); if (gamePlayLimitInput && gameSubmissionCostDisplay) { gamePlayLimitInput.addEventListener('input', () => { const playLimit = parseInt(gamePlayLimitInput.value, 10); if (!isNaN(playLimit) && playLimit > 0) { gameSubmissionCostDisplay.textContent = `Cost: ${formatCurrency(playLimit, 'PKR')}`; } else { gameSubmissionCostDisplay.textContent = 'Cost: PKR 0'; } }); } // Initially render the profile page content based on login status // The onAuthStateChanged listener will call renderProfilePage again once Firebase auth is initialized. renderProfilePage(document.getElementById('profilePageContainer')); });

Post a Comment

0 Comments

Post a Comment (0)

Copy right

© All Rights Reserved | Designed & Managed by NAZIM MUSTAFA
Ok, Go it!