diff --git a/static/css/style.css b/static/css/style.css
index 55482f0..e9c8cb8 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -286,4 +286,102 @@ body.with-sidebar {
button:disabled {
opacity: 0.6;
cursor: not-allowed;
+}
+
+/* Feed list styles */
+#feedList {
+ margin-bottom: 2rem;
+}
+
+.feed-name {
+ display: block;
+ padding: 0.75rem;
+ margin: 0.25rem 0;
+ color: var(--text-color);
+ text-decoration: none;
+ border-radius: 4px;
+ transition: background-color 0.2s ease;
+ cursor: pointer;
+ position: relative;
+ padding-right: 2.5rem;
+}
+
+.feed-name:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+/* Dropdown styles */
+.feed-menu-button {
+ position: absolute;
+ right: 0.5rem;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ color: var(--text-muted);
+ padding: 0.2rem 0.5rem;
+ cursor: pointer;
+ font-size: 1.2rem;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.feed-name:hover .feed-menu-button {
+ opacity: 1;
+}
+
+.feed-menu-button:hover {
+ color: var(--text-color);
+}
+
+.feed-menu {
+ position: absolute;
+ right: 0.5rem;
+ top: 100%;
+ background-color: var(--sidebar-bg);
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+ display: none;
+ z-index: 100;
+}
+
+.feed-menu.show {
+ display: block;
+}
+
+.feed-menu-item {
+ display: block;
+ padding: 0.5rem 1rem;
+ color: var(--text-color);
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+.feed-menu-item:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+.feed-menu-item.delete {
+ color: var(--primary-red);
+}
+
+.feed-menu-item.delete:hover {
+ background-color: rgba(244, 63, 63, 0.1);
+}
+
+.feed-empty {
+ color: var(--text-muted);
+ text-align: center;
+ padding: 1rem;
+ font-style: italic;
+}
+
+.feed-error {
+ color: var(--primary-red);
+ text-align: center;
+ padding: 1rem;
+ background-color: rgba(244, 63, 63, 0.1);
+ border-radius: 4px;
}
\ No newline at end of file
diff --git a/static/js/app.js b/static/js/app.js
index 73ece8f..8d13130 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -4,17 +4,109 @@ async function fetchFeeds() {
const response = await fetch('/feeds');
if (response.ok) {
const feeds = await response.json();
- console.log('Feeds loaded:', feeds);
+ return feeds;
} else {
console.error('Failed to load feeds:', response.status);
+ return null;
}
} catch (error) {
console.error('Error loading feeds:', error);
+ return null;
+ }
+}
+
+// Close any open feed menus
+function closeAllFeedMenus() {
+ document.querySelectorAll('.feed-menu.show').forEach(menu => {
+ menu.classList.remove('show');
+ });
+}
+
+// Add click handler to close menus when clicking outside
+document.addEventListener('click', (e) => {
+ if (!e.target.closest('.feed-menu') && !e.target.closest('.feed-menu-button')) {
+ closeAllFeedMenus();
+ }
+});
+
+function renderFeedItem(feed) {
+ const container = document.createElement('div');
+ container.style.position = 'relative';
+
+ const name = document.createElement('span');
+ name.className = 'feed-name';
+ name.textContent = feed.name;
+ name.onclick = () => {
+ // TODO: Handle feed click
+ console.log('Feed clicked:', feed);
+ };
+
+ const menuButton = document.createElement('button');
+ menuButton.className = 'feed-menu-button';
+ menuButton.innerHTML = '⋮';
+ menuButton.title = 'Feed options';
+ menuButton.onclick = (e) => {
+ e.stopPropagation();
+ closeAllFeedMenus();
+ menu.classList.toggle('show');
+ };
+
+ const menu = document.createElement('div');
+ menu.className = 'feed-menu';
+
+ const deleteItem = document.createElement('a');
+ deleteItem.className = 'feed-menu-item delete';
+ deleteItem.textContent = 'Remove Feed';
+ deleteItem.onclick = async (e) => {
+ e.stopPropagation();
+ if (confirm(`Are you sure you want to delete "${feed.name}"?`)) {
+ try {
+ const response = await fetch(`/feeds/${feed.feed_id}`, {
+ method: 'DELETE',
+ });
+ if (response.ok) {
+ handleFeeds();
+ } else {
+ console.error('Failed to delete feed:', response.status);
+ }
+ } catch (error) {
+ console.error('Error deleting feed:', error);
+ }
+ }
+ menu.classList.remove('show');
+ };
+
+ menu.appendChild(deleteItem);
+ name.appendChild(menuButton);
+ name.appendChild(menu);
+ container.appendChild(name);
+ return container;
+}
+
+async function handleFeeds() {
+ const feeds = await fetchFeeds();
+ const feedList = document.getElementById('feedList');
+
+ if (feeds) {
+ feedList.innerHTML = '';
+
+ if (feeds.length === 0) {
+ const emptyMessage = document.createElement('div');
+ emptyMessage.className = 'feed-empty';
+ emptyMessage.textContent = 'No feeds added yet';
+ feedList.appendChild(emptyMessage);
+ } else {
+ feeds.forEach(feed => {
+ feedList.appendChild(renderFeedItem(feed));
+ });
+ }
+ } else {
+ feedList.innerHTML = '
Failed to load feeds
';
}
}
// Load feeds when page loads
-document.addEventListener('DOMContentLoaded', fetchFeeds);
+document.addEventListener('DOMContentLoaded', handleFeeds);
// Logout functionality
document.getElementById('logoutButton').addEventListener('click', async () => {
@@ -101,7 +193,7 @@ confirmButton.addEventListener('click', async () => {
if (response.ok) {
hideModal();
// Refresh the feed list
- fetchFeeds();
+ handleFeeds();
} else {
switch (response.status) {
case 409:
diff --git a/templates/index.html.tera b/templates/index.html.tera
index 3d1d34e..40626c3 100644
--- a/templates/index.html.tera
+++ b/templates/index.html.tera
@@ -51,9 +51,7 @@
-
Welcome to RSS Reader
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
RSS Reader