// Twitter API base URL // const API_BASE = 'http://localhost:3343/api' // const API_BASE = 'https://tweets.nunosempere.com/api'; // DOM elements const healthCheckBtn = document.getElementById('health-check'); const healthResultDiv = document.getElementById('health-result'); const addUsernameInput = document.getElementById('add-username'); const addAccountBtn = document.getElementById('add-account'); const bulkUsernamesInput = document.getElementById('bulk-usernames'); const bulkAddAccountsBtn = document.getElementById('bulk-add-accounts'); const bulkResultDiv = document.getElementById('bulk-result'); const showAccountsBtn = document.getElementById('show-accounts'); const hideAccountsBtn = document.getElementById('hide-accounts'); const accountResultDiv = document.getElementById('account-result'); const monitoredAccountsResultDiv = document.getElementById('monitored-accounts-result'); const showListsBtn = document.getElementById('show-lists'); const hideListsBtn = document.getElementById('hide-lists'); const listsResultDiv = document.getElementById('lists-result'); const newListNameInput = document.getElementById('new-list-name'); const listUsernamesInput = document.getElementById('list-usernames'); const createListBtn = document.getElementById('create-list'); const createListResultDiv = document.getElementById('create-list-result'); const editListNameInput = document.getElementById('edit-list-name'); const editListUsernamesInput = document.getElementById('edit-list-usernames'); const editListPasswordInput = document.getElementById('edit-list-password'); const editListBtn = document.getElementById('edit-list'); const editListResultDiv = document.getElementById('edit-list-result'); const tweetsLimitInput = document.getElementById('tweets-limit'); const tweetsListInput = document.getElementById('tweets-list'); const getAllTweetsBtn = document.getElementById('get-all-tweets'); const userTweetsUsernameInput = document.getElementById('user-tweets-username'); const userTweetsLimitInput = document.getElementById('user-tweets-limit'); const getUserTweetsBtn = document.getElementById('get-user-tweets'); const tweetsResultDiv = document.getElementById('tweets-result'); const userTweetsResultDiv = document.getElementById('user-tweets-result'); const hideAllTweetsBtn = document.getElementById('hide-all-tweets'); const hideUserTweetsBtn = document.getElementById('hide-user-tweets'); const filterQuestionInput = document.getElementById('filter-question'); const summarizationQuestionInput = document.getElementById('summarization-question'); const filterListInput = document.getElementById('filter-list'); const filterUsersInput = document.getElementById('filter-users'); const filterTweetsBtn = document.getElementById('filter-tweets'); const hideFilterResultsBtn = document.getElementById('hide-filter-results'); const filterResultDiv = document.getElementById('filter-result'); // Helper functions function showError(container, message) { container.innerHTML = `
${message}
`; container.classList.add('show'); } function showSuccess(container, message) { container.innerHTML = `
${message}
`; container.classList.add('show'); } function showResults(container, html) { container.innerHTML = html; container.classList.add('show'); } function formatDate(dateString) { return new Date(dateString).toLocaleString(); } function truncateText(text, maxLength = 100) { return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; } // Parse markdown to HTML using marked library function parseMarkdown(text) { if (!text || typeof marked === 'undefined') return text; return marked.parse(text); } // API request helper async function apiRequest(endpoint, options = {}) { try { const controller = new AbortController(); // const timeoutId = setTimeout(() => controller.abort(), options.timeout || 30000); const response = await fetch(`${API_BASE}${endpoint}`, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options, signal: controller.signal }); // clearTimeout(timeoutId); console.log(response) const data = await response.json(); if (!response.ok) { throw new Error(data.message || `HTTP error! status: ${response.status}`); } console.log(data) return data; } catch (error) { console.error('API Error:', error); throw error; } } // Health Check Handler healthCheckBtn.addEventListener('click', async () => { healthCheckBtn.disabled = true; healthCheckBtn.textContent = 'Checking...'; try { const result = await apiRequest('/health'); console.log(result) showSuccess(healthResultDiv, `Server is healthy; status: ${result.status}`); } catch (error) { showError(healthResultDiv, `Health check failed: ${error.message}`); } finally { healthCheckBtn.disabled = false; healthCheckBtn.textContent = 'Check Server Health'; } }); // Add Account Handler addAccountBtn.addEventListener('click', async () => { const username = addUsernameInput.value.trim(); if (!username) { showError(accountResultDiv, 'Please enter a username.'); return; } addAccountBtn.disabled = true; addAccountBtn.textContent = 'Adding...'; try { const body = { username }; const result = await apiRequest('/accounts', { method: 'POST', body: JSON.stringify(body) }); showSuccess(accountResultDiv, `${result.message}`); addUsernameInput.value = ''; } catch (error) { showError(accountResultDiv, `Failed to add account: ${error.message}`); } finally { addAccountBtn.disabled = false; addAccountBtn.textContent = 'Add Account'; } }); // Bulk Add Accounts Handler bulkAddAccountsBtn.addEventListener('click', async () => { const usernamesText = bulkUsernamesInput.value.trim(); if (!usernamesText) { showError(bulkResultDiv, 'Please enter at least one username.'); return; } const usernames = usernamesText.split('\n') .map(u => u.trim()) .filter(u => u && !u.startsWith('@')); // Remove empty lines and @ symbols if (usernames.length === 0) { showError(bulkResultDiv, 'Please enter valid usernames.'); return; } bulkAddAccountsBtn.disabled = true; bulkAddAccountsBtn.textContent = `Adding ${usernames.length} accounts...`; const results = { successful: [], failed: [] }; // Show initial progress showResults(bulkResultDiv, `
Processing ${usernames.length} accounts...
`); for (let i = 0; i < usernames.length; i++) { const username = usernames[i]; try { const result = await apiRequest('/accounts', { method: 'POST', body: JSON.stringify({ username }) }); results.successful.push({ username, message: result.message }); } catch (error) { results.failed.push({ username, error: error.message }); } // Update progress const progress = Math.round(((i + 1) / usernames.length) * 100); showResults(bulkResultDiv, `
Processing ${usernames.length} accounts... ${progress}% complete (${i + 1}/${usernames.length})
`); } // Show final results let html = `

Bulk Import Results

`; html += `

Total: ${usernames.length} accounts processed

`; html += `

Successful: ${results.successful.length}

`; html += `

Failed: ${results.failed.length}

`; if (results.successful.length > 0) { html += '

Successfully Added:

'; html += ''; } if (results.failed.length > 0) { html += '

Failed to Add:

'; html += ''; } showResults(bulkResultDiv, html); // Clear input if all successful if (results.failed.length === 0) { bulkUsernamesInput.value = ''; } bulkAddAccountsBtn.disabled = false; bulkAddAccountsBtn.textContent = 'Add All Accounts'; }); // Show Lists Handler showListsBtn.addEventListener('click', async () => { showListsBtn.disabled = true; showListsBtn.textContent = 'Loading...'; try { const result = await apiRequest('/lists'); if (result.data && result.data.length > 0) { let html = '

All Lists

'; html += '
'; html += '
List Name
Accounts
Actions
'; result.data.forEach((list, index) => { const accountCount = list.count || 0; const listId = `list-${index}`; html += `
${list.name}
${accountCount}
Accounts:
`; }); html += '
'; showResults(listsResultDiv, html); showListsBtn.style.display = 'none'; hideListsBtn.style.display = 'inline-block'; } else { showResults(listsResultDiv, '

All Lists

No lists found in database.

'); showListsBtn.style.display = 'none'; hideListsBtn.style.display = 'inline-block'; } } catch (error) { showError(listsResultDiv, `Failed to get lists: ${error.message}`); } finally { showListsBtn.disabled = false; showListsBtn.textContent = 'Show All Lists'; } }); // Hide Lists Handler hideListsBtn.addEventListener('click', () => { listsResultDiv.classList.remove('show'); listsResultDiv.innerHTML = ''; showListsBtn.style.display = 'inline-block'; hideListsBtn.style.display = 'none'; }); // Create List Handler createListBtn.addEventListener('click', async () => { const listName = newListNameInput.value.trim(); const usernamesText = listUsernamesInput.value.trim(); if (!listName) { showError(createListResultDiv, 'Please enter a list name.'); return; } if (!usernamesText) { showError(createListResultDiv, 'Please enter at least one username.'); return; } const usernames = usernamesText.split('\n') .map(u => u.trim()) .filter(u => u && !u.startsWith('@')); // Remove empty lines and @ symbols if (usernames.length === 0) { showError(createListResultDiv, 'Please enter valid usernames.'); return; } createListBtn.disabled = true; createListBtn.textContent = 'Creating List...'; try { const body = { name: listName, usernames: usernames }; const result = await apiRequest('/lists', { method: 'POST', body: JSON.stringify(body) }); showSuccess(createListResultDiv, `List "${listName}" created successfully with ${usernames.length} accounts.`); newListNameInput.value = ''; listUsernamesInput.value = ''; } catch (error) { showError(createListResultDiv, `Failed to create list: ${error.message}`); } finally { createListBtn.disabled = false; createListBtn.textContent = 'Create List'; } }); // Event delegation for list account toggles and copy functionality listsResultDiv.addEventListener('click', (event) => { if (event.target.classList.contains('toggle-list-btn')) { const listId = event.target.dataset.listid; const accountsDiv = document.getElementById(listId); if (accountsDiv.classList.contains('show')) { accountsDiv.classList.remove('show'); event.target.textContent = 'Show Accounts'; } else { accountsDiv.classList.add('show'); event.target.textContent = 'Hide Accounts'; } } else if (event.target.classList.contains('copy-list-btn')) { const listName = event.target.dataset.listname; const usernames = JSON.parse(atob(event.target.dataset.usernames || btoa('[]'))); if (usernames.length === 0) { showError(listsResultDiv, `List "${listName}" has no accounts to copy.`); return; } const textToCopy = usernames.join('\n'); // Try to copy to clipboard if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(textToCopy) .then(() => { // Temporarily change button text to show success const originalText = event.target.textContent; event.target.textContent = 'Copied!'; event.target.style.backgroundColor = '#4caf50'; setTimeout(() => { event.target.textContent = originalText; event.target.style.backgroundColor = ''; }, 2000); }) .catch(err => { console.error('Failed to copy to clipboard:', err); fallbackCopy(textToCopy, event.target, listName); }); } else { // Fallback for older browsers or non-secure contexts fallbackCopy(textToCopy, event.target, listName); } } }); // Edit List Handler editListBtn.addEventListener('click', async () => { const listName = editListNameInput.value.trim(); const usernamesText = editListUsernamesInput.value.trim(); const password = editListPasswordInput.value.trim(); if (!listName) { showError(editListResultDiv, 'Please enter the name of the list to edit.'); return; } if (!usernamesText) { showError(editListResultDiv, 'Please enter at least one username.'); return; } if (!password) { showError(editListResultDiv, 'Password is required to edit lists.'); return; } const usernames = usernamesText.split('\n') .map(u => u.trim()) .filter(u => u && !u.startsWith('@')); // Remove empty lines and @ symbols if (usernames.length === 0) { showError(editListResultDiv, 'Please enter valid usernames.'); return; } editListBtn.disabled = true; editListBtn.textContent = 'Editing List...'; try { const body = { usernames: usernames, password: password }; const result = await apiRequest(`/lists/${encodeURIComponent(listName)}/edit`, { method: 'PUT', body: JSON.stringify(body) }); showSuccess(editListResultDiv, `List "${listName}" updated successfully with ${usernames.length} accounts.`); editListNameInput.value = ''; editListUsernamesInput.value = ''; editListPasswordInput.value = ''; } catch (error) { if (error.message.includes('401')) { showError(editListResultDiv, 'Incorrect password. Please try again.'); } else if (error.message.includes('404')) { showError(editListResultDiv, `List "${listName}" not found. Please check the list name.`); } else { showError(editListResultDiv, `Failed to edit list: ${error.message}`); } } finally { editListBtn.disabled = false; editListBtn.textContent = 'Edit List'; } }); // Fallback copy function for older browsers function fallbackCopy(text, button, listName) { const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { const successful = document.execCommand('copy'); if (successful) { // Temporarily change button text to show success const originalText = button.textContent; button.textContent = 'Copied!'; button.style.backgroundColor = '#4caf50'; setTimeout(() => { button.textContent = originalText; button.style.backgroundColor = ''; }, 2000); } else { showError(listsResultDiv, `Failed to copy list "${listName}". Please copy manually: ${text}`); } } catch (err) { console.error('Fallback copy failed:', err); showError(listsResultDiv, `Copy not supported in this browser. List "${listName}" usernames: ${text}`); } finally { document.body.removeChild(textArea); } } // Show Monitored Accounts Handler showAccountsBtn.addEventListener('click', async () => { showAccountsBtn.disabled = true; showAccountsBtn.textContent = 'Loading...'; try { const result = await apiRequest('/accounts'); if (result.data && result.data.length > 0) { let html = '

Monitored Accounts

'; showResults(monitoredAccountsResultDiv, html); showAccountsBtn.style.display = 'none'; hideAccountsBtn.style.display = 'inline-block'; } else { showResults(monitoredAccountsResultDiv, '

Monitored Accounts

No accounts found in database.

'); showAccountsBtn.style.display = 'none'; hideAccountsBtn.style.display = 'inline-block'; } } catch (error) { showError(monitoredAccountsResultDiv, `Failed to get accounts: ${error.message}`); } finally { showAccountsBtn.disabled = false; showAccountsBtn.textContent = 'Show Monitored Accounts'; } }); // Hide Accounts Handler hideAccountsBtn.addEventListener('click', () => { monitoredAccountsResultDiv.classList.remove('show'); monitoredAccountsResultDiv.innerHTML = ''; showAccountsBtn.style.display = 'inline-block'; hideAccountsBtn.style.display = 'none'; }); // Get All Tweets Handler getAllTweetsBtn.addEventListener('click', async () => { const limit = parseInt(tweetsLimitInput.value) || 100; const list = tweetsListInput.value.trim(); getAllTweetsBtn.disabled = true; getAllTweetsBtn.textContent = 'Loading...'; try { let endpoint = `/tweets?limit=${limit}`; if (list) endpoint += `&list=${encodeURIComponent(list)}`; const result = await apiRequest(endpoint); console.log(result) if (result.data.tweets && result.data.tweets.length > 0) { let html = `

Tweets (${result.data.tweets.length})

`; html += '
'; result.data.tweets.forEach(tweet => { html += `
@${tweet.username}
${tweet.text}
${formatDate(tweet.created_at)}
`; }); html += '
'; showResults(tweetsResultDiv, html); hideAllTweetsBtn.style.display = 'inline-block'; hideAllTweetsBtn.textContent = 'Hide Results'; } else { showSuccess(tweetsResultDiv, 'No tweets found.'); } } catch (error) { showError(tweetsResultDiv, `Failed to get tweets: ${error.message}`); } finally { getAllTweetsBtn.disabled = false; getAllTweetsBtn.textContent = 'Get All Tweets'; } }); // Get User Tweets Handler getUserTweetsBtn.addEventListener('click', async () => { const username = userTweetsUsernameInput.value.trim(); const limit = parseInt(userTweetsLimitInput.value) || 50; if (!username) { showError(userTweetsResultDiv, 'Please enter a username.'); return; } getUserTweetsBtn.disabled = true; getUserTweetsBtn.textContent = 'Loading...'; try { const result = await apiRequest(`/tweets/${username}?limit=${limit}`); if (result.data.tweets && result.data.tweets.length > 0) { let html = `

Tweets from @${username} (${result.data.tweets.length})

`; html += '
'; result.data.tweets.forEach(tweet => { html += `
${tweet.text}
${formatDate(tweet.created_at)}
`; }); html += '
'; showResults(userTweetsResultDiv, html); hideUserTweetsBtn.style.display = 'inline-block'; hideUserTweetsBtn.textContent = 'Hide Results'; } else { showSuccess(userTweetsResultDiv, `No tweets found for @${username}.`); } } catch (error) { showError(userTweetsResultDiv, `Failed to get tweets for @${username}: ${error.message}`); } finally { getUserTweetsBtn.disabled = false; getUserTweetsBtn.textContent = 'Get User Tweets'; } }); // Filter Tweets Handler (Polling) filterTweetsBtn.addEventListener('click', async () => { const filterQuestion = filterQuestionInput.value.trim(); const summarizationQuestion = summarizationQuestionInput.value.trim(); const list = filterListInput.value.trim(); const usersText = filterUsersInput.value.trim(); if (!filterQuestion) { showError(filterResultDiv, 'Please enter a filter question.'); return; } if (!summarizationQuestion) { showError(filterResultDiv, 'Please enter a summarization question.'); return; } if (!list && !usersText) { showError(filterResultDiv, 'Please enter either a list name or usernames.'); return; } if (list && usersText) { showError(filterResultDiv, 'Please provide either a list name OR usernames, not both.'); return; } filterTweetsBtn.disabled = true; filterTweetsBtn.textContent = 'Starting...'; // Show initial progress showResults(filterResultDiv, '

Creating filter job...

'); try { const requestBody = { filter_question: filterQuestion, summarization_question: summarizationQuestion }; if (list) { requestBody.list = list; } else { const users = usersText.split('\n').map(u => u.trim()).filter(u => u); if (users.length === 0) { showError(filterResultDiv, 'Please enter valid usernames.'); filterTweetsBtn.disabled = false; filterTweetsBtn.textContent = 'Filter Tweets'; return; } requestBody.users = users; } // Create filter job const jobResponse = await apiRequest('/filter-job', { method: 'POST', body: JSON.stringify(requestBody), timeout: 60000 // 1 minute timeout for job creation }); const jobId = jobResponse.data.job_id; filterTweetsBtn.textContent = 'Filtering...'; showResults(filterResultDiv, '

Job created, starting polling...

'); // Initialize results container window.currentFilterResults = null; // Start polling for job status await pollFilterJob(jobId); } catch (error) { showError(filterResultDiv, `Failed to start filtering: ${error.message}`); filterTweetsBtn.disabled = false; filterTweetsBtn.textContent = 'Filter Tweets'; } }); // Function to poll filter job status async function pollFilterJob(jobId, retryCount = 0) { const maxRetries = 3; const maxAttempts = 300; // 5 minute timeout let attempts = 0; while (attempts < maxAttempts) { try { const statusResponse = await apiRequest(`/filter-job/${jobId}/status`, { timeout: 200 // 200ms timeout for status checks }); const status = statusResponse.data; // Update progress display and show partial results if available if (status.progress) { const progressHtml = `

${status.progress.message || 'Processing tweets'}

Status: ${status.status}

`; showResults(filterResultDiv, progressHtml); } // Show partial results while running if (status.status === 'running' && status.partial_results && status.partial_results.partial_tweets) { const partialResult = { filtered_tweets: status.partial_results.partial_tweets, summary: null // No summary yet while running }; // Update stored results with partial data window.currentFilterResults = partialResult; // Display partial results with progress indicator displayPartialFilterResults(partialResult, status.progress); } if (status.status === 'completed') { // Job completed, get final results const resultsResponse = await apiRequest(`/filter-job/${jobId}/results`); if (resultsResponse.data && resultsResponse.data.results) { window.currentFilterResults = resultsResponse.data.results; // Display final results (replacing any partial results) displayFilterResults(window.currentFilterResults); } else { throw new Error('Job completed but no results available'); } filterTweetsBtn.disabled = false; filterTweetsBtn.textContent = 'Filter Tweets'; return; } else if (status.status === 'failed') { throw new Error(status.error_message || 'Job failed'); } // Job still in progress, wait before next poll // const delay = Math.min(1000 * Math.pow(1.5, attempts), 5000); // Exponential backoff up to 5s const delay = 200 await new Promise(resolve => setTimeout(resolve, delay)); attempts++; } catch (networkError) { console.warn(`Network error during polling (attempt ${retryCount + 1}):`, networkError); if (retryCount < maxRetries) { await new Promise(resolve => setTimeout(resolve, 1000)); return pollFilterJob(jobId, retryCount + 1); } else { throw new Error(`Network error after ${maxRetries} retries: ${networkError.message}`); } } } throw new Error('Job timeout after 5 minutes'); } // Helper function to display partial filter results while running function displayPartialFilterResults(result, progress) { if (result && result.filtered_tweets) { const filtered = result.filtered_tweets; const passing = filtered.filter(item => item.pass); const failing = filtered.filter(item => !item.pass); let html = `

Filter Results (Processing...)

`; // Show progress bar if (progress) { html += `

${progress.message || 'Processing tweets'}: ${progress.current || 0}/${progress.total || 0}

`; } html += `

Passing tweets so far: ${passing.length}

`; if (passing.length > 0) { html += '

Passing Tweets (Partial)

'; html += '
'; passing.forEach(item => { html += `
@${item.tweet.username}
${item.tweet.text}
${formatDate(item.tweet.created_at)}
Reasoning: ${item.reasoning}
`; }); html += '
'; } // Show sample of failing tweets if any if (failing.length > 0) { html += '

Non-Passing Tweets (Partial Sample)

'; html += '
'; failing.slice(0, 3).forEach(item => { html += `
@${item.tweet.username}
${item.tweet.text}
Reasoning: ${item.reasoning}
`; }); html += '
'; } showResults(filterResultDiv, html); hideFilterResultsBtn.style.display = 'inline-block'; hideFilterResultsBtn.textContent = 'Hide Results'; } } // Helper function to display filter results function displayFilterResults(result) { if (result && result.filtered_tweets) { const filtered = result.filtered_tweets; const passing = filtered.filter(item => item.pass); const failing = filtered.filter(item => !item.pass); let html = `

Filter Results

`; html += `

Passing tweets: ${passing.length}

`; if (passing.length > 0) { html += '

Passing Tweets

'; html += '
'; passing.forEach(item => { html += `
@${item.tweet.username}
${item.tweet.text}
${formatDate(item.tweet.created_at)}
Reasoning: ${item.reasoning}
`; }); html += '
'; } if (failing.length > 0) { html += '

Non-Passing Tweets

'; html += '
'; failing.forEach(item => { html += `
@${item.tweet.username}
${item.tweet.text}
Reasoning: ${item.reasoning}
`; }); html += '
'; } // Show sample of failing tweets if any if (failing.length > 0) { html += '

Non-Passing Tweets (Partial Sample)

'; html += '
'; failing.slice(0, 3).forEach(item => { html += `
@${item.tweet.username}
${item.tweet.text}
Reasoning: ${item.reasoning}
`; }); html += '
'; } // Show summary if available if (result.summary) { html += `
`; html += `

Summary

`; html += `
${parseMarkdown(result.summary)}
`; html += `
`; } showResults(filterResultDiv, html); hideFilterResultsBtn.style.display = 'inline-block'; hideFilterResultsBtn.textContent = 'Hide Results'; } else { showSuccess(filterResultDiv, 'No tweets found to filter.'); } } // Keyboard shortcuts addUsernameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { addAccountBtn.click(); } }); bulkUsernamesInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); bulkAddAccountsBtn.click(); } }); newListNameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { createListBtn.click(); } }); listUsernamesInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); createListBtn.click(); } }); editListNameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { editListBtn.click(); } }); editListUsernamesInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); editListBtn.click(); } }); editListPasswordInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { editListBtn.click(); } }); userTweetsUsernameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { getUserTweetsBtn.click(); } }); filterQuestionInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); filterTweetsBtn.click(); } }); summarizationQuestionInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); filterTweetsBtn.click(); } }); filterUsersInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); filterTweetsBtn.click(); } }); // Hide/Show All Tweets Handler hideAllTweetsBtn.addEventListener('click', () => { if (tweetsResultDiv.classList.contains('show')) { tweetsResultDiv.classList.remove('show'); hideAllTweetsBtn.textContent = 'Show Results'; } else { tweetsResultDiv.classList.add('show'); hideAllTweetsBtn.textContent = 'Hide Results'; } }); // Hide/Show User Tweets Handler hideUserTweetsBtn.addEventListener('click', () => { if (userTweetsResultDiv.classList.contains('show')) { userTweetsResultDiv.classList.remove('show'); hideUserTweetsBtn.textContent = 'Show Results'; } else { userTweetsResultDiv.classList.add('show'); hideUserTweetsBtn.textContent = 'Hide Results'; } }); // Hide/Show Filter Results Handler hideFilterResultsBtn.addEventListener('click', () => { if (filterResultDiv.classList.contains('show')) { filterResultDiv.classList.remove('show'); hideFilterResultsBtn.textContent = 'Show Results'; } else { filterResultDiv.classList.add('show'); hideFilterResultsBtn.textContent = 'Hide Results'; } });