Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"babel-loader": "^9.1.3",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.6.0",
"jest": "^29.7.0",
"mini-css-extract-plugin": "^2.9.1",
Expand Down
48 changes: 17 additions & 31 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
class APIService {
/**
* @param services {Services} Менеджер сервисов
* @param config {Object}
*/
constructor(services, config = {}) {
this.services = services;
this.config = config;
Expand All @@ -11,35 +7,25 @@ class APIService {
};
}

/**
* HTTP запрос
* @param url
* @param method
* @param headers
* @param options
* @returns {Promise<{}>}
*/
async request({ url, method = 'GET', headers = {}, ...options }) {
if (!url.match(/^(http|\/\/)/)) url = this.config.baseUrl + url;
const res = await fetch(url, {
method,
headers: { ...this.defaultHeaders, ...headers },
...options,
});
return { data: await res.json(), status: res.status, headers: res.headers };
setHeader(name, value = null) {
if (value != null) this.defaultHeaders[name] = value;
else delete this.defaultHeaders[name];
}

/**
* Установка или сброс заголовка
* @param name {String} Название заголовка
* @param value {String|null} Значение заголовка
*/
setHeader(name, value = null) {
if (value) {
this.defaultHeaders[name] = value;
} else if (this.defaultHeaders[name]) {
delete this.defaultHeaders[name];
}
async request({ url, method='GET', headers={}, ...opts }) {
if (!/^(http|\/\/)/.test(url)) url = this.config.baseUrl + url;

const langHeader = this.services.i18n.lang;
const finalHeaders = {
...this.defaultHeaders,
'Accept-Language': langHeader,
...headers,
};

const res = await fetch(url, { method, headers: finalHeaders, ...opts });
const data = await res.json();

return { data, status: res.status, headers: res.headers };
}
}

Expand Down
103 changes: 55 additions & 48 deletions src/app/article/index.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,71 @@
import { memo, useCallback } from 'react';
import { useParams } from 'react-router-dom';
import useStore from '../../hooks/use-store';
import useTranslate from '../../hooks/use-translate';
import useInit from '../../hooks/use-init';
import PageLayout from '../../components/page-layout';
import Head from '../../components/head';
import Navigation from '../../containers/navigation';
import Spinner from '../../components/spinner';
import ArticleCard from '../../components/article-card';
import LocaleSelect from '../../containers/locale-select';
import TopHead from '../../containers/top-head';
import { useDispatch, useSelector } from 'react-redux';
import shallowequal from 'shallowequal';
import articleActions from '../../store-redux/article/actions';
import HeadLayout from '../../components/head-layout';
import React, { memo, useCallback } from 'react'
import { useParams } from 'react-router-dom'
import useStore from '../../hooks/use-store'
import useTranslate from '../../hooks/use-translate'
import { useArticle } from '../../hooks/use-article'
import { useComments } from '../../hooks/use-comments'
import PageLayout from '../../components/page-layout'
import Head from '../../components/head'
import Spinner from '../../components/spinner'
import ArticleCard from '../../components/article-card'
import LocaleSelect from '../../containers/locale-select'
import TopHead from '../../containers/top-head'
import Navigation from '../../containers/navigation'
import HeadLayout from '../../components/head-layout'
import CommentsSection from '../../components/comments-section'

function Article() {
const store = useStore();
const store = useStore()
const { t, lang } = useTranslate()
const { id } = useParams()
const { article, waiting: artLoading } = useArticle(id)
const {
comments, waiting: comLoading,
replyTo, text, setText,
onReplyClick, onSubmit
} = useComments(id)

const dispatch = useDispatch();
// Параметры из пути /articles/:id

const params = useParams();

useInit(() => {
//store.actions.article.load(params.id);
dispatch(articleActions.load(params.id));
}, [params.id]);

const select = useSelector(
state => ({
article: state.article.data,
waiting: state.article.waiting,
}),
shallowequal,
); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект

const { t } = useTranslate();

const callbacks = {
// Добавление в корзину
addToBasket: useCallback(_id => store.actions.basket.addToBasket(_id), [store]),
};
const isAuth = Boolean(localStorage.getItem('token'))
const addToBasket = useCallback(
id => store.actions.basket.addToBasket(id),
[store]
)

return (
<>
<HeadLayout>
<TopHead />
<TopHead/>
</HeadLayout>
<Head title={select.article.title}>
<LocaleSelect />
<Head title={article.title}>
<LocaleSelect/>
</Head>
<PageLayout>
<Navigation />
<Spinner active={select.waiting}>
<ArticleCard article={select.article} onAdd={callbacks.addToBasket} t={t} />
<Spinner active={artLoading}>
<ArticleCard
article={article}
onAdd={addToBasket}
t={t} lang={lang}
/>

<div className="comments-container">
<h2>{t('comments.heading')} ({comments.length})</h2>
<Spinner active={comLoading}>
<CommentsSection
comments={comments}
replyTo={replyTo}
text={text}
onTextChange={setText}
onReplyClick={onReplyClick}
onSubmit={onSubmit}
isAuthorized={isAuth}
/>
</Spinner>
</div>
</Spinner>
</PageLayout>
</>
);
)
}

export default memo(Article);
export default memo(Article)
7 changes: 3 additions & 4 deletions src/app/basket/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { memo, useCallback } from 'react';
import { useDispatch, useStore as useStoreRedux } from 'react-redux';
import useStore from '../../hooks/use-store';
import useSelector from '../../hooks/use-selector';
import useInit from '../../hooks/use-init';

import useTranslate from '../../hooks/use-translate';
import ItemBasket from '../../components/item-basket';
import List from '../../components/list';
Expand All @@ -21,11 +21,10 @@ function Basket() {
}));

const callbacks = {
// Удаление из корзины

removeFromBasket: useCallback(_id => store.actions.basket.removeFromBasket(_id), [store]),
// Закрытие любой модалки

closeModal: useCallback(() => {
//store.actions.modals.close();
dispatch(modalsActions.close());
}, [store]),
};
Expand Down
3 changes: 1 addition & 2 deletions src/app/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { useCallback, useContext, useEffect, useState } from 'react';
import { Routes, Route } from 'react-router-dom';
import useSelector from '../hooks/use-selector';
import useStore from '../hooks/use-store';
import useInit from '../hooks/use-init';
import Main from './main';
Expand All @@ -21,6 +19,7 @@ function App() {
await store.actions.session.remind();
});


const activeModal = useSelectorRedux(state => state.modals.name);

return (
Expand Down
4 changes: 2 additions & 2 deletions src/app/profile/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Profile() {
waiting: state.profile.waiting,
}));

const { t } = useTranslate();
const { t, lang } = useTranslate();

return (
<>
Expand All @@ -37,7 +37,7 @@ function Profile() {
<PageLayout>
<Navigation />
<Spinner active={select.waiting}>
<ProfileCard data={select.profile} />
<ProfileCard data={select.profile} t={t} lang={lang}/>
</Spinner>
</PageLayout>
</>
Expand Down
Binary file added src/assets/icon/cart_empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading