Skip to content

Commit

Permalink
feat: add recommendations
Browse files Browse the repository at this point in the history
  • Loading branch information
sashtje committed Sep 10, 2023
1 parent 231fb9f commit 06a673e
Show file tree
Hide file tree
Showing 23 changed files with 217 additions and 39 deletions.
1 change: 1 addition & 0 deletions extractedTranslations/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
},
"Профиль": "Профиль",
"Редактировать": "Редактировать",
"Рекомендуем": "Рекомендуем",
"Сортировать ПО": "Сортировать ПО",
"Сохранить": "Сохранить",
"Список статей": "Список статей",
Expand Down
1 change: 1 addition & 0 deletions extractedTranslations/ru/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
},
"Профиль": "Профиль",
"Редактировать": "Редактировать",
"Рекомендуем": "Рекомендуем",
"Сортировать ПО": "Сортировать ПО",
"Сохранить": "Сохранить",
"Список статей": "Список статей",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/en/articles.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"IT": "IT",
"Экономика": "Economy",
"Наука": "Science",
"Статьи не найдены": "No articles found"
"Статьи не найдены": "No articles found",
"Рекомендуем": "We recommend"
}
3 changes: 2 additions & 1 deletion public/locales/ru/articles.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"IT": "IT",
"Экономика": "Экономика",
"Наука": "Наука",
"Статьи не найдены": "Статьи не найдены"
"Статьи не найдены": "Статьи не найдены",
"Рекомендуем": "Рекомендуем"
}
6 changes: 4 additions & 2 deletions src/app/providers/StoreProvider/config/StateSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { UserSchema } from 'entities/User';
import { LoginSchema } from 'features/AuthByUsername';
import { ProfileSchema } from 'entities/Profile';
import { ArticleDetailsSchema } from 'entities/Article';
import { ArticleDetailsCommentsSchema } from 'pages/ArticleDetailsPage';
import {
ArticleDetailsPageSchema,
} from 'pages/ArticleDetailsPage';
import { AddCommentFormSchema } from 'features/addCommentForm';
import { ArticlesPageSchema } from 'pages/ArticlesPage';
import { UISchema } from 'features/UI';
Expand All @@ -23,7 +25,7 @@ export interface StateSchema {
loginForm?: LoginSchema,
profile?: ProfileSchema,
articleDetails?: ArticleDetailsSchema,
articleDetailsComments?: ArticleDetailsCommentsSchema,
articleDetailsPage?: ArticleDetailsPageSchema,
addCommentForm?: AddCommentFormSchema,
articlesPage?: ArticlesPageSchema,
}
Expand Down
20 changes: 20 additions & 0 deletions src/app/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ body {
color: var(--primary-color);
}

* {
scrollbar-color: var(--card-bg) var(--inverted-primary-color);
scrollbar-width: 10px;
}

.app {
background: var(--bg-color);
min-height: 100vh;
Expand All @@ -17,3 +22,18 @@ body {
.content-page {
display: flex;
}

*::-webkit-scrollbar {
width: 10px;
height: 8px;
}

*::-webkit-scrollbar-track {
background: var(--card-bg);
}

*::-webkit-scrollbar-thumb {
background-color: var(--inverted-primary-color);
border-radius: 20px;
border: 3px solid var(--primary-color);
}
4 changes: 4 additions & 0 deletions src/app/styles/reset.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ select {
margin: 0;
font: inherit;
}

a {
text-decoration: none;
}
2 changes: 1 addition & 1 deletion src/app/styles/themes/orange.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
--inverted-bg-color: #bd5012;
--primary-color: #9a1a0e;
--secondary-color: #d01f0e;
--inverted-primary-color: #dbd5dc;
--inverted-primary-color: #f7f5dc;
--inverted-secondary-color: #faf4fb;

// skeleton
Expand Down
7 changes: 5 additions & 2 deletions src/entities/Article/ui/ArticleList/ArticleList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback } from 'react';
import { HTMLAttributeAnchorTarget, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';

import { Text, TextSize } from 'shared/ui/Text';
Expand All @@ -14,6 +14,7 @@ interface ArticleListProps {
articles: Article[];
isLoading?: boolean;
view?: ArticleView;
target?: HTMLAttributeAnchorTarget;
}

const getSkeletons = (view: ArticleView) => (new Array(view === ArticleView.SMALL ? 9 : 3)
Expand All @@ -32,6 +33,7 @@ export const ArticleList = memo((props: ArticleListProps) => {
articles,
isLoading,
view = ArticleView.SMALL,
target,
} = props;

const { t } = useTranslation('articles');
Expand All @@ -42,8 +44,9 @@ export const ArticleList = memo((props: ArticleListProps) => {
key={article.id}
article={article}
view={view}
target={target}
/>
), [view]);
), [view, target]);

if (!isLoading && !articles.length) {
return (
Expand Down
32 changes: 18 additions & 14 deletions src/entities/Article/ui/ArticleListItem/ArticleListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { memo, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { HTMLAttributeAnchorTarget, memo } from 'react';
import { useTranslation } from 'react-i18next';

import { classNames } from 'shared/lib/classNames';
Expand All @@ -9,7 +8,8 @@ import { Avatar } from 'shared/ui/Avatar';
import { Button } from 'shared/ui/Button';
import { Icon } from 'shared/ui/Icon';
import EyeIcon from 'shared/assets/icons/eye-20-20.svg';
import { AppRoutes, RoutePath } from 'shared/config/routerConfig/routerConfig';
import { AppLink } from 'shared/ui/AppLink';
import { RoutePath } from 'shared/config/routerConfig/routerConfig';

import {
Article, ArticleBlockType, ArticleTextBlock, ArticleView,
Expand All @@ -21,13 +21,15 @@ interface ArticleListItemProps {
className?: string;
article: Article;
view: ArticleView;
target?: HTMLAttributeAnchorTarget;
}

export const ArticleListItem = memo((props: ArticleListItemProps) => {
const {
className,
article,
view,
target,
} = props;

const { t } = useTranslation('articles');
Expand All @@ -48,11 +50,6 @@ export const ArticleListItem = memo((props: ArticleListItemProps) => {
</>
);

const navigate = useNavigate();
const onOpenArticle = useCallback(() => {
navigate(RoutePath[AppRoutes.ARTICLE_DETAILS] + article.id);
}, [article.id, navigate]);

if (view === ArticleView.BIG) {
const textBlock = article.blocks.find((block) => block.type === ArticleBlockType.TEXT) as ArticleTextBlock;

Expand Down Expand Up @@ -83,9 +80,14 @@ export const ArticleListItem = memo((props: ArticleListItemProps) => {
)}

<div className={cls.footer}>
<Button onClick={onOpenArticle}>
{t('Читать далее')}
</Button>
<AppLink
target={target}
to={RoutePath.article_details + article.id}
>
<Button>
{t('Читать далее')}
</Button>
</AppLink>

{views}
</div>
Expand All @@ -95,10 +97,12 @@ export const ArticleListItem = memo((props: ArticleListItemProps) => {
}

return (
<div
<AppLink
target={target}
to={RoutePath.article_details + article.id}
className={classNames(cls.articleListItem, {}, [className, cls[view]])}
>
<Card className={cls.card} onClick={onOpenArticle}>
<Card className={cls.card}>
<div className={cls.imageWrapper}>
<img
className={cls.img}
Expand All @@ -122,7 +126,7 @@ export const ArticleListItem = memo((props: ArticleListItemProps) => {
text={article.title}
/>
</Card>
</div>
</AppLink>
);
});

Expand Down
2 changes: 1 addition & 1 deletion src/pages/ArticleDetailsPage/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { ArticleDetailsPageAsync as ArticleDetailsPage } from './ui/ArticleDetailsPage/ArticleDetailsPage.async';
export { ArticleDetailsCommentsSchema } from './model/types/ArticleDetailsCommentsSchema';
export { ArticleDetailsPageSchema } from './model/types';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StateSchema } from 'app/providers/StoreProvider';

export const getArticleCommentsIsLoading = (state: StateSchema) => state.articleDetailsComments?.isLoading;
export const getArticleCommentsError = (state: StateSchema) => state.articleDetailsComments?.error;
export const getArticleCommentsIsLoading = (state: StateSchema) => state.articleDetailsPage?.comments.isLoading;
export const getArticleCommentsError = (state: StateSchema) => state.articleDetailsPage?.comments.error;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { StateSchema } from 'app/providers/StoreProvider';

export const getArticleRecommendationsIsLoading = (
state: StateSchema,
) => state.articleDetailsPage?.recommendations.isLoading;

export const getArticleRecommendationsError = (
state: StateSchema,
) => state.articleDetailsPage?.recommendations.error;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createAsyncThunk } from '@reduxjs/toolkit';

import { ThunkConfig } from 'app/providers/StoreProvider';
import { Article } from 'entities/Article';

export const fetchArticleRecommendations = createAsyncThunk<Article[], void, ThunkConfig<string>>(
'articleDetailsPage/fetchArticleRecommendations',
async (args, thunkAPI) => {
const { extra, rejectWithValue } = thunkAPI;

try {
const response = await extra.api.get<Article[]>('/articles', {
params: {
_limit: 4,
},
});

if (!response.data) {
throw new Error();
}

return response.data;
} catch (e) {
return rejectWithValue('error');
}
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { StateSchema } from 'app/providers/StoreProvider';

import {
fetchCommentsByArticleId,
} from 'pages/ArticleDetailsPage/model/services/fetchCommentsByArticleId/fetchCommentsByArticleId';
} from '../../model/services/fetchCommentsByArticleId/fetchCommentsByArticleId';
import { ArticleDetailsCommentsSchema } from '../types/ArticleDetailsCommentsSchema';

const commentsAdapter = createEntityAdapter<Comment>({
selectId: (comment) => comment.id,
});

export const getArticleComments = commentsAdapter.getSelectors<StateSchema>(
(state) => state.articleDetailsComments || commentsAdapter.getInitialState(),
(state) => state.articleDetailsPage?.comments || commentsAdapter.getInitialState(),
);

const articleDetailsCommentsSlice = createSlice({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createEntityAdapter, createSlice, PayloadAction } from '@reduxjs/toolkit';

import { StateSchema } from 'app/providers/StoreProvider';
import { Article } from 'entities/Article';

import {
fetchArticleRecommendations,
} from '../../model/services/fetchArticleRecommendations/fetchArticleRecommendations';
import { ArticleDetailsRecommendationsSchema } from '../types/ArticleDetailsRecommendationsSchema';

const recommendationsAdapter = createEntityAdapter<Article>({
selectId: (article) => article.id,
});

export const getArticleRecommendations = recommendationsAdapter.getSelectors<StateSchema>(
(state) => state.articleDetailsPage?.recommendations || recommendationsAdapter.getInitialState(),
);

const articleDetailsRecommendationsSlice = createSlice({
name: 'articleDetailsRecommendationsSlice',
initialState: recommendationsAdapter.getInitialState<ArticleDetailsRecommendationsSchema>({
isLoading: false,
error: undefined,
ids: [],
entities: {},
}),
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchArticleRecommendations.pending, (state) => {
state.error = undefined;
state.isLoading = true;
})
.addCase(fetchArticleRecommendations.fulfilled, (state, action: PayloadAction<Article[]>) => {
state.isLoading = false;
recommendationsAdapter.setAll(state, action.payload);
})
.addCase(fetchArticleRecommendations.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload;
});
},
});

export const { reducer: articleDetailsRecommendationsReducer } = articleDetailsRecommendationsSlice;
12 changes: 12 additions & 0 deletions src/pages/ArticleDetailsPage/model/slices/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { combineReducers } from '@reduxjs/toolkit';

import { articleDetailsCommentsReducer } from '../../model/slices/articleDetailsCommentsSlice';
import {
articleDetailsRecommendationsReducer,
} from '../../model/slices/articleDetailsRecommendationsSlice';
import { ArticleDetailsPageSchema } from '../types';

export const articleDetailsPageReducer = combineReducers<ArticleDetailsPageSchema>({
comments: articleDetailsCommentsReducer,
recommendations: articleDetailsRecommendationsReducer,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { EntityState } from '@reduxjs/toolkit';

import { Article } from 'entities/Article';

export interface ArticleDetailsRecommendationsSchema extends EntityState<Article>{
isLoading?: boolean;
error?: string;
}
11 changes: 11 additions & 0 deletions src/pages/ArticleDetailsPage/model/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
ArticleDetailsCommentsSchema,
} from '../types/ArticleDetailsCommentsSchema';
import {
ArticleDetailsRecommendationsSchema,
} from '../types/ArticleDetailsRecommendationsSchema';

export interface ArticleDetailsPageSchema {
comments: ArticleDetailsCommentsSchema,
recommendations: ArticleDetailsRecommendationsSchema,
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
.commentTitle {
margin-top: 20px;
}

.recommendations {
margin-top: 20px;
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
justify-content: flex-start;
}
Loading

0 comments on commit 06a673e

Please sign in to comment.