rss-reader
167 строк · 5.5 Кб
1import onChange from 'on-change';2
3const initialInputState = (input, button) => {4button.disabled = false;5input.disabled = false;6input.value = '';7input.focus();8};9
10const renderState = (input, button, value) => {11if (value === 'processing') {12const oldFeedback = document.querySelector('.feedback');13if (oldFeedback) oldFeedback.remove();14button.disabled = true;15input.disabled = true;16input.classList.remove('is-invalid');17input.classList.remove('is-valid');18}19if (value === 'processed') {20input.classList.add('is-valid');21initialInputState(input, button);22}23if (value === 'failed') {24input.classList.add('is-invalid');25initialInputState(input, button);26}27};28
29const renderFeedback = (state, i18next, rssForm, value) => {30const oldFeedback = rssForm.parentElement.querySelector('.feedback');31if (oldFeedback) oldFeedback.remove();32const newFeedback = document.createElement('p');33newFeedback.classList.add('feedback', 'm-0', 'position-absolute', 'small');34let newTextContent;35if (value === 'waiting') {36newTextContent = i18next.t('feedback.uploadingRss');37newFeedback.classList.add('text-white-50');38}39if (value === 'valid') {40newTextContent = i18next.t('feedback.uploadedRss');41newFeedback.classList.add('text-success');42}43if (value === 'invalid') {44const errorMessage = state.formState.errors.at(-1);45newTextContent = i18next.t(errorMessage);46newFeedback.classList.add('text-danger');47}48newFeedback.textContent = newTextContent;49rssForm.parentElement.append(newFeedback);50};51
52const renderFeeds = (feeds, value) => {53let card;54if (feeds.querySelector('.card')) {55card = feeds.querySelector('.card');56} else {57card = document.createElement('div');58card.classList.add('card', 'border-0');59card.innerHTML = `<div class="card-body"><h2 class="card-title h4">Фиды</h2></div>60<ul class="list-group border-0 rounded-0"></ul>`;61feeds.append(card);62}63const listGroup = feeds.querySelector('.list-group');64listGroup.innerHTML = '';65value.forEach((feed) => {66const li = document.createElement('li');67li.classList.add('list-group-item', 'border-0', 'border-end-0');68const h3 = document.createElement('h3');69h3.classList.add('h6', 'm-0');70h3.textContent = feed.title;71const p = document.createElement('p');72p.classList.add('m-0', 'small', 'text-black-50');73p.textContent = feed.description;74li.append(h3);75li.append(p);76listGroup.prepend(li);77});78};79
80const renderPosts = (posts, value, prevValue) => {81let newPosts;82if (prevValue === undefined) {83newPosts = value;84} else if (prevValue !== undefined) {85const isObjInPrevValue = (newPost) => prevValue.some((prevPost) => prevPost.id === newPost.id);86newPosts = value.filter((newPost) => !isObjInPrevValue(newPost));87}88let card;89if (posts.querySelector('.card')) {90card = posts.querySelector('.card');91} else {92card = document.createElement('div');93card.classList.add('card', 'border-0');94card.innerHTML = `<div class="card-body"><h2 class="card-title h4">Посты</h2></div>95<ul class="list-group border-0 rounded-0"></ul>`;96posts.append(card);97}98const listGroup = posts.querySelector('.list-group');99newPosts.forEach((post) => {100const li = document.createElement('li');101li.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'align-items-start', 'border-0', 'border-end-0');102const link = document.createElement('a');103link.href = post.link;104link.classList.add('fw-bold');105link.setAttribute('data-id', post.id);106link.target = '_blank';107link.rel = 'noopener noreferrer';108link.textContent = post.title;109const button = document.createElement('button');110button.type = 'button';111button.classList.add('btn', 'btn-outline-primary', 'btn-sm');112button.setAttribute('data-id', post.id);113button.setAttribute('data-bs-toggle', 'modal');114button.setAttribute('data-bs-target', '#modal');115button.textContent = 'Просмотр';116li.append(link);117li.append(button);118listGroup.prepend(li);119});120};121
122const renderViewedLink = (value, prevValue) => {123const findNewId = () => Array.from(new Set([...value].filter((item) => !prevValue.has(item))))[0];124const newId = findNewId();125const link = document.querySelector(`a[data-id="${newId}"]`);126link.classList.remove('fw-bold');127link.classList.add('fw-normal', 'link-secondary');128};129
130const renderModal = (modal, value) => {131const modalTitle = modal.querySelector('.modal-title');132const modalBody = modal.querySelector('.modal-body');133const read = modal.querySelector('.modal-footer').querySelector('a');134modalTitle.textContent = value.title;135modalBody.textContent = value.description;136read.href = value.link;137};138
139const render = (state, i18next, elements) => (path, value, prevValue) => {140const {141rssForm, button, input, modal, feeds, posts,142} = elements;143switch (path) {144case 'state':145renderState(input, button, value);146break;147case 'formState.isValid':148renderFeedback(state, i18next, rssForm, value);149break;150case 'data.feeds':151renderFeeds(feeds, value);152break;153case 'data.posts':154renderPosts(posts, value, prevValue);155break;156case 'uiState.viewedPostsId':157renderViewedLink(value, prevValue);158break;159case 'uiState.vievedPost':160renderModal(modal, value);161break;162default:163break;164}165};166
167export default (state, i18next, elements) => onChange(state, render(state, i18next, elements));168