Show status of importing opml

This commit is contained in:
Greg Shuflin 2025-02-16 01:48:38 -08:00
parent c138940250
commit 059fd1a50d
3 changed files with 117 additions and 14 deletions

View File

@ -3,19 +3,76 @@
background-color: var(--topbar-bg); background-color: var(--topbar-bg);
color: white; color: white;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
display: flex; display: grid;
justify-content: space-between; grid-template-columns: auto 1fr auto;
align-items: center; align-items: center;
gap: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.left-controls {
display: flex;
align-items: center;
gap: 0.5rem;
}
.hamburger-menu {
display: none;
background: none;
border: none;
color: var(--text-color);
font-size: 1.2rem;
cursor: pointer;
padding: 0.5rem;
}
.add-feed-button {
background: none;
border: none;
color: var(--text-color);
font-size: 1.2rem;
cursor: pointer;
padding: 0.5rem;
display: flex;
align-items: center;
}
.job-status {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-color);
font-size: 0.9rem;
}
.job-status i {
color: var(--primary-red);
}
.job-status.completed i {
animation: none;
color: #28a745;
}
.job-status-text {
white-space: nowrap;
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.job-status.fade-out {
animation: fadeOut 0.5s ease-out forwards;
}
.top-bar-title { .top-bar-title {
font-size: 1.5rem; font-size: 1.5rem;
margin: 0; margin: 0;
color: var(--primary-red); color: var(--primary-red);
font-weight: 500; font-weight: 500;
text-align: center; text-align: center;
grid-column: 2;
} }
.feed-title-separator { .feed-title-separator {
@ -23,6 +80,24 @@
margin: 0 0.5rem; margin: 0 0.5rem;
} }
@media (max-width: 768px) {
.hamburger-menu {
display: block;
}
.job-status {
display: none !important;
}
.top-bar {
padding: 0.5rem;
}
.top-bar-title {
font-size: 1.2rem;
}
}
/* User menu styles */ /* User menu styles */
.user-menu { .user-menu {
position: relative; position: relative;

View File

@ -87,25 +87,47 @@ document.addEventListener('DOMContentLoaded', function() {
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
showStatusModal(result.message, false); showJobStatus("Validating OPML file...");
if (result.job_id) { if (result.job_id) {
pollJobStatus(result.job_id); pollJobStatus(result.job_id);
} }
} else { } else {
showStatusModal('OPML import failed: ' + result.message, true); showError('OPML import failed: ' + result.message);
} }
} else { } else {
showStatusModal('Failed to import OPML file. Please try again.', true); showError('Failed to import OPML file. Please try again.');
} }
} catch (error) { } catch (error) {
console.error('OPML import failed:', error); console.error('OPML import failed:', error);
showStatusModal('Failed to import OPML file. Please try again.', true); showError('Failed to import OPML file. Please try again.');
} }
} }
// Clear the input so the same file can be selected again // Clear the input so the same file can be selected again
e.target.value = ''; e.target.value = '';
}); });
function showJobStatus(message, isCompleted = false) {
const jobStatus = document.getElementById('jobStatus');
const jobStatusText = jobStatus.querySelector('.job-status-text');
jobStatus.style.display = 'flex';
jobStatusText.textContent = message;
if (isCompleted) {
jobStatus.classList.add('completed');
// Hide after 5 seconds
setTimeout(() => {
jobStatus.classList.add('fade-out');
setTimeout(() => {
jobStatus.style.display = 'none';
jobStatus.classList.remove('completed', 'fade-out');
}, 500);
}, 5000);
} else {
jobStatus.classList.remove('completed');
}
}
async function pollJobStatus(jobId) { async function pollJobStatus(jobId) {
const maxAttempts = 30; // 30 attempts * 2 second delay = 1 minute maximum const maxAttempts = 30; // 30 attempts * 2 second delay = 1 minute maximum
let attempts = 0; let attempts = 0;
@ -116,15 +138,15 @@ document.addEventListener('DOMContentLoaded', function() {
if (response.ok) { if (response.ok) {
const status = await response.json(); const status = await response.json();
if (status.status === 'completed') { if (status.status === 'completed') {
showStatusModal(`Import completed. Successfully imported ${status.completed} feeds.`, false); showJobStatus(`Import completed. Successfully imported ${status.completed} feeds.`, true);
handleFeeds(); handleFeeds();
return; return;
} else if (status.status === 'in_progress') { } else if (status.status === 'in_progress') {
showStatusModal(`Importing feeds... ${status.completed}/${status.total} completed`, false); showJobStatus(`Importing feeds... ${status.completed}/${status.total} completed`);
if (attempts++ < maxAttempts) { if (attempts++ < maxAttempts) {
setTimeout(poll, 2000); // Poll every 2 seconds setTimeout(poll, 2000); // Poll every 2 seconds
} else { } else {
showStatusModal('Import taking longer than expected. Check feeds list in a few minutes.', false); showJobStatus('Import taking longer than expected. Check feeds list in a few minutes.', true);
setTimeout(handleFeeds, 5000); setTimeout(handleFeeds, 5000);
} }
} }
@ -133,7 +155,7 @@ document.addEventListener('DOMContentLoaded', function() {
} }
} catch (error) { } catch (error) {
console.error('Failed to poll job status:', error); console.error('Failed to poll job status:', error);
showStatusModal('Failed to check import status. Please refresh the page.', true); showError('Failed to check import status. Please refresh the page.');
} }
}; };

View File

@ -66,9 +66,15 @@
<button class="hamburger-menu" id="hamburgerMenu"> <button class="hamburger-menu" id="hamburgerMenu">
<i class="fas fa-bars"></i> <i class="fas fa-bars"></i>
</button> </button>
<button class="add-feed-button" id="addFeedButton" title="Add Feed"> <div class="left-controls">
<i class="fas fa-plus"></i> <button class="add-feed-button" id="addFeedButton" title="Add Feed">
</button> <i class="fas fa-plus"></i>
</button>
<div class="job-status" id="jobStatus" style="display: none">
<i class="fas fa-sync fa-spin"></i>
<span class="job-status-text"></span>
</div>
</div>
<h1 class="top-bar-title">RSS Reader</h1> <h1 class="top-bar-title">RSS Reader</h1>
<div class="user-menu"> <div class="user-menu">
<button class="user-menu-button" id="userMenuButton"> <button class="user-menu-button" id="userMenuButton">