Reputation: 11
I am trying to create a project - RSS reader and I have a problem. After I adding new RSS Feed I don't have a message under URL input about successful RSS load.
I tried everything and I can't find a problem. What can I try next?
app.js:
import i18next from 'i18next';
import { initView } from './view.js';
import { createSchema } from './validate.js';
import { fetchRss } from './rss.js';
import { parseRSS } from './parser.js';
import { checkForUpdates } from './updater.js';
export default () => {
const state = {
urls: [],
isValid: true,
error: '',
feeds: [],
posts: [],
readPostIds: [],
loading: false,
};
i18next.init({
lng: 'ru',
resources: {
ru: {
translation: {
success: 'RSS успешно загружен',
invalidUrl: 'Ссылка должна быть валидным URL',
existingUrl: 'RSS уже существует',
required: 'Не должно быть пустым',
networkError: 'Ошибка сети',
invalidRSS: 'Ресурс не содержит валидный RSS',
},
},
},
});
const watchedState = initView(state, i18next);
const form = document.querySelector('.rss-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const url = formData.get('url').trim();
const schema = createSchema(watchedState.urls);
try {
if (watchedState.urls.includes(url)) {
throw new Error('existingUrl');
}
await schema.validate(url);
watchedState.loading = true;
const rssData = await fetchRss(url);
const { feed, posts } = parseRSS(rssData);
watchedState.feeds.push(feed);
watchedState.posts.push(...posts.map((post, index) => ({
...post,
id: `post-${index}`,
})));
watchedState.urls.push(url);
watchedState.isValid = true;
watchedState.error = '';
watchedState.feeds = [...watchedState.feeds];
watchedState.posts = [...watchedState.posts];
watchedState.urls = [...watchedState.urls];
if (watchedState.urls.length === 1 && !watchedState.loading) {
setTimeout(() => checkForUpdates(watchedState), 1000);
}
} catch (err) {
if (err.name === 'ValidationError') {
watchedState.error = err.message;
} else {
watchedState.error = i18next.t(err.message);
}
watchedState.isValid = false;
} finally {
watchedState.loading = false;
}
});
};
view.js
import onChange from 'on-change';
import { Modal } from 'bootstrap';
export const initView = (state, i18next) => {
const input = document.querySelector('#url-input');
const feedback = document.querySelector('.feedback');
const feedsContainer = document.querySelector('.feeds');
const postsContainer = document.querySelector('.posts');
const modalTitle = document.querySelector('.modal-title');
const modalBody = document.querySelector('.modal-body');
const fullArticleLink = document.querySelector('.full-article');
const modalElement = document.getElementById('modal');
const modal = new Modal(modalElement);
const handlePostClick = (post) => {
if (!post.title || !post.description || !post.link) {
return;
}
modalTitle.textContent = post.title;
modalBody.innerHTML = post.description;
fullArticleLink.href = post.link;
modal.show();
};
const renderFeeds = () => {
feedsContainer.innerHTML = `
<h4 class="${state.feeds.length === 0 ? 'd-none' : ''} mb-4">Фиды</h4>
${state.feeds.map((feed) => `
<div class="feed mb-4">
<h6>${feed.title}</h6>
<p>${feed.description}</p>
</div>
`).join('')}
`;
};
const renderPosts = () => {
postsContainer.innerHTML = `
<h4 class="${state.posts.length === 0 ? 'd-none' : ''} mb-4">Посты</h4>
${state.posts.map((post) => `
<div class="post mb-3 d-flex justify-content-between align-items-center">
<a href="${post.link}" target="_blank" class="post-link ${state.readPostIds.includes(post.id) ? 'text-secondary fw-normal' : 'fw-bold'}" data-post-id="${post.id}">
${post.title}
</a>
<button class="btn btn-outline-primary btn-sm" data-post-id="${post.id}">Просмотр</button>
</div>
`).join('')}
`;
document.querySelectorAll('.post-link').forEach((link) => {
link.addEventListener('click', (e) => {
const postId = e.target.dataset.postId;
if (postId && !state.readPostIds.includes(postId)) {
state.readPostIds.push(postId);
e.target.classList.add('text-muted', 'fw-normal');
e.target.classList.remove('fw-bold')
}
});
});
document.querySelectorAll('.btn-outline-primary').forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault();
const postId = e.target.dataset.postId;
const post = state.posts.find((p) => p.id === postId);
if (post) {
handlePostClick(post);
if (!state.readPostIds.includes(postId)) {
state.readPostIds.push(postId);
const postLink = document.querySelector(`.post-link[data-post-id="${postId}"]`)
if (postLink) {
postLink.classList.add('text-muted', 'fw-normal');
postLink.classList.remove('fw-bold')
}
}
}
});
});
};
return onChange(state, (path) => {
if (path === 'isValid' || path === 'error') {
if (state.isValid) {
feedback.textContent = i18next.t('success');
feedback.classList.replace('text-danger', 'text-success');
input.classList.remove('is-invalid');
input.value = '';
input.focus();
} else {
feedback.textContent = i18next.t(state.error);
feedback.classList.replace('text-success', 'text-danger');
input.classList.add('is-invalid');
}
}
if (path === 'feeds') {
renderFeeds();
}
if (path === 'posts' || path === 'readPostIds') {
renderPosts();
}
});
};
updater.js
import { fetchRss } from "./rss.js";
import { parseRSS } from "./parser.js"
export const checkForUpdates = async (state) => {
try {
for (const url of state.urls) {
const rssData = await fetchRss(url);
const { posts: newPosts } = parseRSS(rssData)
const existingUrl = state.posts.map((post) => post.link);
const uniqueNewPosts = newPosts.filter(
(post) => !existingUrl.includes(post.link)
);
if (uniqueNewPosts.length > 0) {
state.posts.unshift(...uniqueNewPosts);
}
}
} catch (err) {
console.log('Error', err);
} finally {
setTimeout(() => checkForUpdates(state), 5000)
}
}
I tried to change state
Upvotes: 1
Views: 43
Reputation: 3568
Often I use an empty <div>
in combination with css and javascript.
HMTL:
any code
<div id=success></div>
any code
CSS:
#success { /* layout */ }
Javasript:
function printSuccess ( msg ) /* msg may contain HTML tags */
{
var e = document.getElementById("success");
if (e)
e.innerHTML = msg;
}
Upvotes: 0