diff --git a/js/api.js b/js/api.js new file mode 100644 index 0000000..a9b5f53 --- /dev/null +++ b/js/api.js @@ -0,0 +1,27 @@ +const BASE_URL = 'https://31.javascript.htmlacademy.pro/kekstagram'; +const Route = { + GET_DATA: '/data', + SEND_DATA: '/', +}; +const Method = { + GET: 'GET', + POST: 'POST', +}; + +const load = (route, method = Method.GET, body = null) => + fetch(`${BASE_URL}${route}`, { method, body }) + .then((response) => { + if (!response.ok) { + throw new Error(); + } + + return response.json(); + }) + .catch(() => { + throw new Error(); + }); + +const fetchPhotos = () => load(Route.GET_DATA); +const sendPhoto = (body) => load(Route.SEND_DATA, Method.POST, body); + +export { fetchPhotos, sendPhoto }; diff --git a/js/components/alerts.js b/js/components/alerts.js new file mode 100644 index 0000000..b26920e --- /dev/null +++ b/js/components/alerts.js @@ -0,0 +1,58 @@ +import { isEscapeKey } from '../util.js'; + +const bodyElement = document.body; +const uploadErrorTemplate = document + .querySelector('#error') + .content.querySelector('.error'); +const uploadSuccessTemplate = document + .querySelector('#success') + .content.querySelector('.success'); +const dataLoadErrorTemplate = document + .querySelector('#data-error') + .content.querySelector('.data-error'); + +const Alerts = { + SUCCESS: uploadSuccessTemplate, + ERROR: uploadErrorTemplate, + DATA_ERROR: dataLoadErrorTemplate, +}; + +const onDocumentKeydown = (event) => { + if (isEscapeKey(event)) { + event.preventDefault(); + closeAlert(); + } +}; + +const onDocumentClick = (event) => { + const alertElement = bodyElement.querySelector('.alert'); + + if (!alertElement.querySelector('div').contains(event.target)) { + closeAlert(); + } +}; + +function closeAlert() { + bodyElement.querySelector('.alert').remove(); + + document.removeEventListener('keydown', onDocumentKeydown); + document.removeEventListener('click', onDocumentClick); +} + +const showAlert = (type = 'SUCCESS') => { + const alertElement = Alerts[type].cloneNode(true); + alertElement.classList.add('alert'); + bodyElement.append(alertElement); + + if (alertElement.classList.contains('data-error')) { + setTimeout(() => { + alertElement.remove(); + }, 5000); + } else { + alertElement.querySelector('button').addEventListener('click', closeAlert); + document.addEventListener('keydown', onDocumentKeydown); + document.addEventListener('click', onDocumentClick); + } +}; + +export { showAlert }; diff --git a/js/components/upload-modal.js b/js/components/upload-modal.js index bc88ed7..230251b 100644 --- a/js/components/upload-modal.js +++ b/js/components/upload-modal.js @@ -1,12 +1,11 @@ import { isEscapeKey } from '../util.js'; -import { onUploadFormSubmit } from '../upload-validation.js'; import { createOnEffectChange, clearEffects, createOnScaleButtonClick, } from '../effects.js'; -const bodyElement = document.querySelector('body'); +const bodyElement = document.body; const formElement = document.querySelector('#upload-select-image'); const hashtagsElement = formElement.querySelector('input[name="hashtags"]'); const descriptionElement = formElement.querySelector('.text__description'); @@ -22,7 +21,7 @@ const uploadedPreviewElement = formElement.querySelector( ); const onDocumentKeydown = (event) => { - if (isEscapeKey(event)) { + if (isEscapeKey(event) && !bodyElement.querySelector('.alert')) { event.preventDefault(); closeUploadModal(); } @@ -58,7 +57,6 @@ function closeUploadModal() { ); formElement.removeEventListener('change', onEffectChange); - formElement.removeEventListener('submit', onUploadFormSubmit); document.removeEventListener('keydown', onDocumentKeydown); [hashtagsElement, descriptionElement].forEach((input) => { @@ -77,7 +75,6 @@ const renderUploadModal = () => { ); formElement.addEventListener('change', onEffectChange); - formElement.addEventListener('submit', onUploadFormSubmit); overlayCloseElement.addEventListener('click', closeUploadModal); document.addEventListener('keydown', onDocumentKeydown); @@ -88,4 +85,4 @@ const renderUploadModal = () => { }); }; -export { renderUploadModal }; +export { closeUploadModal, renderUploadModal }; diff --git a/js/data.js b/js/data.js deleted file mode 100644 index 7923b59..0000000 --- a/js/data.js +++ /dev/null @@ -1,73 +0,0 @@ -import { getRandomNumber, getRandomItemFrom } from './util.js'; - -const NAMES = [ - 'Александр', - 'Дмитрий', - 'Иван', - 'Сергей', - 'Андрей', - 'Михаил', - 'Николай', - 'Евгений', - 'Владимир', - 'Алексей', - 'Анна', - 'Мария', - 'Екатерина', - 'Ольга', - 'Наталья' -]; - -const COMMENTS = [ - 'Всё отлично!', - 'В целом всё неплохо. Но не всё.', - 'Когда вы делаете фотографию, хорошо бы убирать палец из кадра. В конце концов это просто непрофессионально.', - 'Моя бабушка случайно чихнула с фотоаппаратом в руках и у неё получилась фотография лучше.', - 'Я поскользнулся на банановой кожуре и уронил фотоаппарат на кота и у меня получилась фотография лучше.', - 'Лица у людей на фотке перекошены, как будто их избивают. Как можно было поймать такой неудачный момент?!' -]; - -const PHOTOS_AMOUNT = 25; - -const createRandomIdGenerator = (min, max) => { - const usedIds = []; - - return () => { - if (usedIds.length >= (max - min + 1)) { - return null; - } - - let currentId = getRandomNumber(min, max); - while (usedIds.includes(currentId)) { - currentId = getRandomNumber(min, max); - } - - usedIds.push(currentId); - return currentId; - }; -}; - -const getPhotoId = createRandomIdGenerator(1, PHOTOS_AMOUNT); - -const genereteComment = function () { - return { - id: crypto.randomUUID(), - avatar: `img/avatar-${getRandomNumber(1, 6)}.svg`, - message: getRandomItemFrom(COMMENTS), - name: getRandomItemFrom(NAMES) - }; -}; - -const generetePhoto = function () { - return { - id: crypto.randomUUID(), - url: `photos/${getPhotoId()}.jpg`, - description: 'user photo', - likes: getRandomNumber(15, 200), - comments: Array.from({length: getRandomNumber(0, 30)}, genereteComment) - }; -}; - -const genereteData = () => Array.from({length: PHOTOS_AMOUNT}, generetePhoto); - -export { genereteData }; diff --git a/js/main.js b/js/main.js index 61aa577..ea2f65e 100644 --- a/js/main.js +++ b/js/main.js @@ -1,11 +1,21 @@ -import { genereteData } from './data.js'; +import { fetchPhotos } from './api.js'; import { renderGalley } from './components/gallery.js'; import { renderPhotoModal } from './components/photo-modal.js'; -import { renderUploadModal } from './components/upload-modal.js'; +import { + renderUploadModal, + closeUploadModal, +} from './components/upload-modal.js'; +import { setUploadFormSubmit } from './upload-validation.js'; +import { showAlert } from './components/alerts.js'; -const data = genereteData(); const picturesElement = document.querySelector('.pictures'); -renderGalley(data, picturesElement); -renderPhotoModal(data, picturesElement); +fetchPhotos() + .then((data) => { + renderGalley(data, picturesElement); + renderPhotoModal(data, picturesElement); + }) + .catch(() => showAlert('DATA_ERROR')); + renderUploadModal(); +setUploadFormSubmit(closeUploadModal); diff --git a/js/upload-validation.js b/js/upload-validation.js index c569ce9..dafc232 100644 --- a/js/upload-validation.js +++ b/js/upload-validation.js @@ -1,4 +1,8 @@ +import { sendPhoto } from './api.js'; +import { showAlert } from './components/alerts.js'; + const formElement = document.querySelector('#upload-select-image'); +const submitElement = formElement.querySelector('button[type="submit"]'); const hashtagsElement = formElement.querySelector('input[name="hashtags"]'); const descriptionElement = formElement.querySelector('.text__description'); const hashtagRegex = /^#[a-zа-яё0-9]{1,19}$/i; @@ -18,7 +22,7 @@ const validateHashtagsFormat = (value) => { return hashtags.every((hashtag) => hashtagRegex.test(hashtag)); }; -const validatekHashtagsAmount = (value) => { +const validateHashtagsAmount = (value) => { if (value.length === 0) { return true; } @@ -55,7 +59,7 @@ pristine.addValidator( ); pristine.addValidator( hashtagsElement, - validatekHashtagsAmount, + validateHashtagsAmount, 'Максимум 5 хэштегов', ); pristine.addValidator( @@ -69,12 +73,23 @@ pristine.addValidator( 'Максимум 140 символов', ); -const onUploadFormSubmit = (event) => { - event.preventDefault(); +const setUploadFormSubmit = (onSuccess) => { + formElement.addEventListener('submit', (event) => { + event.preventDefault(); - if (pristine.validate()) { - formElement.submit(); - } + if (pristine.validate()) { + submitElement.disabled = true; + sendPhoto(new FormData(event.target)) + .then(onSuccess) + .then(showAlert) + .catch(() => { + showAlert('ERROR'); + }) + .finally(() => { + submitElement.disabled = false; + }); + } + }); }; -export { onUploadFormSubmit }; +export { setUploadFormSubmit };