Add feed
This commit is contained in:
parent
e27fb1964f
commit
13597cd9da
38
src/feeds.rs
38
src/feeds.rs
@ -4,6 +4,7 @@ use rocket::serde::{json::Json, Deserialize, Serialize};
|
||||
use rocket_db_pools::Connection;
|
||||
use uuid::Uuid;
|
||||
use url::Url;
|
||||
use reqwest;
|
||||
|
||||
use crate::Db;
|
||||
use crate::user::AuthenticatedUser;
|
||||
@ -36,8 +37,7 @@ impl Feed {
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct NewFeed {
|
||||
pub name: String,
|
||||
pub url: Url, // Changed from String to Url
|
||||
pub url: Url, // Only URL is required now
|
||||
}
|
||||
|
||||
#[post("/feeds", data = "<new_feed>")]
|
||||
@ -47,13 +47,41 @@ pub async fn create_feed(
|
||||
user: AuthenticatedUser,
|
||||
) -> Result<Json<Feed>, Status> {
|
||||
let new_feed = new_feed.into_inner();
|
||||
|
||||
// URL validation only needs to check scheme now
|
||||
|
||||
if !new_feed.url.scheme().eq("http") && !new_feed.url.scheme().eq("https") {
|
||||
eprintln!("Invalid URL scheme: {}", new_feed.url.scheme());
|
||||
return Err(Status::UnprocessableEntity);
|
||||
}
|
||||
|
||||
let feed = Feed::new(new_feed.name, new_feed.url, user.user_id);
|
||||
// Fetch the feed content
|
||||
let response = reqwest::get(new_feed.url.as_ref())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed to fetch feed: {}", e);
|
||||
Status::UnprocessableEntity
|
||||
})?;
|
||||
|
||||
let content = response.text()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed to read response body: {}", e);
|
||||
Status::UnprocessableEntity
|
||||
})?;
|
||||
|
||||
|
||||
// Parse the feed
|
||||
let feed_data = feed_rs::parser::parse(content.as_bytes())
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed to parse feed content: {}", e);
|
||||
Status::UnprocessableEntity
|
||||
})?;
|
||||
|
||||
// Use the feed title as the name, or URL if no title is available
|
||||
let name = feed_data.title
|
||||
.map(|t| t.content)
|
||||
.unwrap_or_else(|| new_feed.url.host_str().unwrap_or("Unknown").to_string());
|
||||
|
||||
let feed = Feed::new(name, new_feed.url, user.user_id);
|
||||
|
||||
let query = sqlx::query(
|
||||
"INSERT INTO feeds (feed_id, name, url, user_id, added_time, last_checked_time)
|
||||
|
@ -260,4 +260,30 @@ body.with-sidebar {
|
||||
|
||||
.sidebar {
|
||||
top: 3rem;
|
||||
}
|
||||
|
||||
/* Spinner and loading state */
|
||||
.spinner {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 0 auto 10px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid var(--primary-red);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* Button states */
|
||||
button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
@ -20,22 +20,35 @@ const cancelButton = document.getElementById('cancelAddFeed');
|
||||
const confirmButton = document.getElementById('confirmAddFeed');
|
||||
const addFeedForm = document.getElementById('addFeedForm');
|
||||
const errorMessage = document.getElementById('feedErrorMessage');
|
||||
const loadingMessage = document.getElementById('loadingMessage');
|
||||
|
||||
function showModal() {
|
||||
modal.classList.add('show');
|
||||
errorMessage.style.display = 'none';
|
||||
loadingMessage.style.display = 'none';
|
||||
addFeedForm.reset();
|
||||
confirmButton.disabled = false;
|
||||
}
|
||||
|
||||
function hideModal() {
|
||||
modal.classList.remove('show');
|
||||
errorMessage.style.display = 'none';
|
||||
loadingMessage.style.display = 'none';
|
||||
addFeedForm.reset();
|
||||
confirmButton.disabled = false;
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
errorMessage.textContent = message;
|
||||
errorMessage.style.display = 'block';
|
||||
loadingMessage.style.display = 'none';
|
||||
confirmButton.disabled = false;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
loadingMessage.style.display = 'block';
|
||||
errorMessage.style.display = 'none';
|
||||
confirmButton.disabled = true;
|
||||
}
|
||||
|
||||
addFeedButton.addEventListener('click', showModal);
|
||||
@ -49,21 +62,22 @@ modal.addEventListener('click', (e) => {
|
||||
});
|
||||
|
||||
confirmButton.addEventListener('click', async () => {
|
||||
const name = document.getElementById('feedName').value.trim();
|
||||
const url = document.getElementById('feedUrl').value.trim();
|
||||
|
||||
if (!name || !url) {
|
||||
showError('Please fill in all fields');
|
||||
if (!url) {
|
||||
showError('Please enter a feed URL');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
const response = await fetch('/feeds', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ name, url }),
|
||||
body: JSON.stringify({ url }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@ -72,13 +86,12 @@ confirmButton.addEventListener('click', async () => {
|
||||
// For now, we'll just reload the page
|
||||
window.location.reload();
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
switch (response.status) {
|
||||
case 409:
|
||||
showError('You already have this feed added');
|
||||
break;
|
||||
case 422:
|
||||
showError('Invalid feed URL. Please make sure it starts with http:// or https://');
|
||||
showError('Invalid feed URL. Please make sure it\'s a valid RSS or Atom feed.');
|
||||
break;
|
||||
default:
|
||||
showError('Failed to add feed. Please try again.');
|
||||
|
@ -22,22 +22,21 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addFeedForm">
|
||||
<div class="form-group">
|
||||
<label for="feedName">Feed Name</label>
|
||||
<input type="text" id="feedName" name="name" required
|
||||
placeholder="My Blog Feed">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="feedUrl">Feed URL</label>
|
||||
<input type="url" id="feedUrl" name="url" required
|
||||
placeholder="https://example.com/feed.xml">
|
||||
</div>
|
||||
<div class="error-message" id="feedErrorMessage" style="display: none; color: red; margin-bottom: 10px;"></div>
|
||||
<div class="loading-message" id="loadingMessage" style="display: none; color: #666; margin-bottom: 10px; text-align: center;">
|
||||
<div class="spinner"></div>
|
||||
<div>Loading feed information...</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" id="cancelAddFeed">Cancel</button>
|
||||
<button type="button" class="btn-primary" id="confirmAddFeed">OK</button>
|
||||
<button type="button" class="btn-primary" id="confirmAddFeed">Add Feed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user