Skip to content

Commit

Permalink
Redesign article blocks, articles list
Browse files Browse the repository at this point in the history
  • Loading branch information
nothing9537 committed Oct 30, 2023
1 parent 99d8412 commit 6ab3a98
Show file tree
Hide file tree
Showing 41 changed files with 386 additions and 164 deletions.
2 changes: 1 addition & 1 deletion json-server/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@
"isAppRedesigned": true
},
"jsonSettings": {
"theme": "app_light_theme",
"theme": "app_sand_theme",
"isArticlesPageWasOpenedOnce": true
},
"avatar": "https://source.boringavatars.com/pixel/120/Stefan?colors=26a653,2a1d8f,79646a"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, memo } from 'react';
import { classNames } from '@/shared/lib/classNames/classNames';
import { Code } from '@/shared/ui/deprecated/Code';
import { Code } from '@/shared/ui/redesigned/Code';
import { ArticleCodeBlock } from '../../model/types/article';
import cls from './ArticleCodeBlockComponent.module.scss';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,3 @@
margin: 0 auto;
}

.article {
// width: 100%;
// height: 100%;
}
97 changes: 28 additions & 69 deletions src/entities/Article/ui/ArticleDetails/ArticleDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { FC, memo, ReactNode, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { FC, memo, ReactNode } from 'react';
import { useAppSelector } from '@/shared/lib/hooks/useAppSelector';
import { DynamicModuleWrapper, ReducersList } from '@/shared/lib/components/DynamicModuleWrapper';
import { classNames } from '@/shared/lib/classNames/classNames';
import { useAppDispatch } from '@/shared/lib/hooks/useAppDispatch';
import { useInitialEffect } from '@/shared/lib/hooks/useInitialEffect';
import { CreatedAtIcon, ViewsIcon } from '@/shared/assets/deprecated-icons';
import { Skeleton } from '@/shared/ui/deprecated/Skeleton';
import { Avatar } from '@/shared/ui/deprecated/Avatar';
import { Icon } from '@/shared/ui/deprecated/Icon';
import { HStack, VStack } from '@/shared/ui/redesigned/Stack';
import { Text, TextAlign, TextSize, TextTheme } from '@/shared/ui/deprecated/Text';
import { ToggleFeatures } from '@/shared/lib/features';

import { articleDetailsReducer } from '../../model/slices/articleDetailsSlice';
import { fetchArticleById } from '../../model/services/fetchArticleById/fetchArticleById';
import { ArticleBlock } from '../../model/types/article';
import { ArticleCodeBlockComponent } from '../ArticleCodeBlockComponent';
import { ArticleImageBlockComponent } from '../ArticleImageBlockComponent';
import { ArticleTextBlockComponent } from '../ArticleTextBlockComponent';
import { ArticleBlockType } from '../../model/consts';
import {
getArticleDetailsData,
getArticleDetailsError,
getArticleDetailsIsLoading,
} from '../../model/selectors/articleDetails';
import {
ArticleDetailsRedesignedContent,
ArticleDetailsRedesignedError,
ArticleDetailsRedesignedSkeletons,
} from './ArticleDetailsRedesigned';
import {
ArticleDetailsDeprecatedContent,
ArticleDetailsDeprecatedError,
ArticleDetailsDeprecatedSkeletons,
} from './ArticleDetailsDeprecated';

import cls from './ArticleDetails.module.scss';

const reducers: ReducersList = {
Expand All @@ -34,27 +35,12 @@ interface ArticleDetailsProps {
}

export const ArticleDetails: FC<ArticleDetailsProps> = memo(({ className, id }) => {
const { t } = useTranslation('articles');

const dispatch = useAppDispatch();

const data = useAppSelector(getArticleDetailsData);
const error = useAppSelector(getArticleDetailsError);
const isLoading = useAppSelector(getArticleDetailsIsLoading);

const renderBlock = useCallback((block: ArticleBlock) => {
switch (block.type) {
case ArticleBlockType.CODE:
return <ArticleCodeBlockComponent key={block.id} block={block} />;
case ArticleBlockType.IMAGE:
return <ArticleImageBlockComponent key={block.id} block={block} />;
case ArticleBlockType.TEXT:
return <ArticleTextBlockComponent key={block.id} block={block} />;
default:
return null;
}
}, []);

useInitialEffect(() => {
dispatch(fetchArticleById(id));
}, [dispatch, id]);
Expand All @@ -63,54 +49,27 @@ export const ArticleDetails: FC<ArticleDetailsProps> = memo(({ className, id })

if (isLoading) {
content = (
<div className={classNames(cls.ArticleDetails, { [cls.loading]: isLoading }, [className])}>
<Skeleton width={200} height={200} borderRadius="50%" className={cls.avatar} />
<Skeleton width={700} height={30} />
<Skeleton width={400} height={30} />
<Skeleton width={300} height={16} />
<Skeleton height={200} />
<Skeleton height={200} />
<Skeleton height={200} />
</div>
<ToggleFeatures
name="isAppRedesigned"
on={<ArticleDetailsRedesignedSkeletons isLoading={isLoading} className={className} />}
off={<ArticleDetailsDeprecatedSkeletons isLoading={isLoading} className={className} />}
/>
);
} else if (error) {
content = (
<Text
align={TextAlign.CENTER}
theme={TextTheme.ERROR}
title={t('error-fetching-article')}
<ToggleFeatures
name="isAppRedesigned"
on={<ArticleDetailsRedesignedError />}
off={<ArticleDetailsDeprecatedError />}
/>
);
} else {
content = (
<article className={cls.article}>
<VStack justify="center" align="center" gap={12}>
<Avatar
width={200}
height={200}
src={data?.img}
alt="Avatar"
/>
</VStack>
<VStack gap={4} data-testid="ArticleDetailsInfo">
<Text
title={data?.title}
text={data?.subtitle}
size={TextSize.L}
/>
<HStack gap={12}>
<Icon SVG={<ViewsIcon />} />
<Text text={data?.views.toString()} />
</HStack>
<HStack gap={12}>
<Icon SVG={<CreatedAtIcon />} />
<Text text={new Date(data?.createdAt as number).toLocaleString()} />
</HStack>
</VStack>
<VStack gap={16} justify="center" align="center">
{data?.blocks.map(renderBlock)}
</VStack>
</article>
<ToggleFeatures
name="isAppRedesigned"
on={<ArticleDetailsRedesignedContent data={data} />}
off={<ArticleDetailsDeprecatedContent data={data} />}
/>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { FC } from 'react';
import { Avatar } from '@/shared/ui/deprecated/Avatar';
import { HStack, VStack } from '@/shared/ui/redesigned/Stack';
import { Text, TextSize } from '@/shared/ui/deprecated/Text';
import { Icon } from '@/shared/ui/deprecated/Icon';
import { CreatedAtIcon, ViewsIcon } from '@/shared/assets/deprecated-icons';

import { renderBlock } from '../renderBlock';
import { Article } from '../../../model/types/article';

interface ArticleDetailsDeprecatedContentProps {
data?: Article
}

/**
* @deprecated
*/
export const ArticleDetailsDeprecatedContent: FC<ArticleDetailsDeprecatedContentProps> = (props) => {
const { data } = props;

return (
<article>
<VStack justify="center" align="center" gap={12}>
<Avatar
width={200}
height={200}
src={data?.img}
alt="Avatar"
/>
</VStack>
<VStack gap={4} data-testid="ArticleDetailsInfo">
<Text
title={data?.title}
text={data?.subtitle}
size={TextSize.L}
/>
<HStack gap={12}>
<Icon SVG={<ViewsIcon />} />
<Text text={data?.views.toString()} />
</HStack>
<HStack gap={12}>
<Icon SVG={<CreatedAtIcon />} />
<Text text={new Date(data?.createdAt as number).toLocaleString()} />
</HStack>
</VStack>
<VStack gap={16} justify="center" align="center">
{data?.blocks.map(renderBlock)}
</VStack>
</article>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FC } from 'react';
import { useAppTranslation } from '@/shared/lib/hooks/useAppTranslation';
import { Text, TextAlign, TextTheme } from '@/shared/ui/deprecated/Text';

/**
* @deprecated
*/
export const ArticleDetailsDeprecatedError: FC = () => {
const { t } = useAppTranslation('articles');

return (
<Text
align={TextAlign.CENTER}
theme={TextTheme.ERROR}
title={t('error-fetching-article')}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { FC } from 'react';
import { classNames } from '@/shared/lib/classNames/classNames';
import { Skeleton } from '@/shared/ui/deprecated/Skeleton';

import cls from '../ArticleDetails.module.scss';

interface ArticleDetailsRedesignedSkeletonsProps {
isLoading: boolean;
className?: string;
}

/**
* @deprecated
*/
export const ArticleDetailsDeprecatedSkeletons: FC<ArticleDetailsRedesignedSkeletonsProps> = ({ isLoading, className }) => {
return (
<div className={classNames(cls.ArticleDetails, { [cls.loading]: isLoading }, [className])}>
<Skeleton width={200} height={200} borderRadius="50%" className={cls.avatar} />
<Skeleton width={700} height={30} />
<Skeleton width={400} height={30} />
<Skeleton width={300} height={16} />
<Skeleton height={200} />
<Skeleton height={200} />
<Skeleton height={200} />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { ArticleDetailsDeprecatedContent } from './ArticleDetailsDeprecatedContent';
export { ArticleDetailsDeprecatedError } from './ArticleDetailsDeprecatedError';
export { ArticleDetailsDeprecatedSkeletons } from './ArticleDetailsDeprecatedSkeletons';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { FC } from 'react';
import { VStack } from '@/shared/ui/redesigned/Stack';
import { Text } from '@/shared/ui/redesigned/Text';
import { AppImage } from '@/shared/ui/redesigned/AppImage';
import { Skeleton } from '@/shared/ui/redesigned/Skeleton';

import { renderBlock } from '../renderBlock';
import { Article } from '../../../model/types/article';

interface ArticleDetailsRedesignedContentProps {
data?: Article
}

export const ArticleDetailsRedesignedContent: FC<ArticleDetailsRedesignedContentProps> = (props) => {
const { data } = props;

return (
<VStack component="article" gap={16}>
<Text
bold={{ title: true, text: false }}
title={data?.title}
text={data?.subtitle}
size="l"
/>
<VStack justify="center" align="center">
<AppImage
width={200}
height={200}
src={data?.img}
alt="Avatar"
fallback={<Skeleton width={200} height={200} borderRadius="50%" />}
/>
</VStack>
<VStack gap={16} justify="center" align="center">
{data?.blocks.map(renderBlock)}
</VStack>
</VStack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FC } from 'react';
import { useAppTranslation } from '@/shared/lib/hooks/useAppTranslation';
import { Text } from '@/shared/ui/redesigned/Text';

export const ArticleDetailsRedesignedError: FC = () => {
const { t } = useAppTranslation('articles');

return (
<Text
align="center"
variant="error"
title={t('error-fetching-article')}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FC } from 'react';
import { classNames } from '@/shared/lib/classNames/classNames';
import { Skeleton } from '@/shared/ui/redesigned/Skeleton';

import cls from '../ArticleDetails.module.scss';

interface ArticleDetailsRedesignedSkeletonsProps {
isLoading: boolean;
className?: string;
}

export const ArticleDetailsRedesignedSkeletons: FC<ArticleDetailsRedesignedSkeletonsProps> = ({ isLoading, className }) => {
return (
<div className={classNames(cls.ArticleDetails, { [cls.loading]: isLoading }, [className])}>
<Skeleton width={700} height={30} />
<Skeleton width={400} height={30} />
<Skeleton width={200} height={200} borderRadius="50%" className={cls.avatar} />
<Skeleton width={300} height={16} />
<Skeleton height={200} />
<Skeleton height={200} />
<Skeleton height={200} />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { ArticleDetailsRedesignedContent } from './ArticleDetailsRedesignedContent';
export { ArticleDetailsRedesignedError } from './ArticleDetailsRedesignedError';
export { ArticleDetailsRedesignedSkeletons } from './ArticleDetailsRedesignedSkeletons';
18 changes: 18 additions & 0 deletions src/entities/Article/ui/ArticleDetails/renderBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ArticleBlockType } from '../../model/consts';
import { ArticleBlock } from '../../model/types/article';
import { ArticleCodeBlockComponent } from '../ArticleCodeBlockComponent';
import { ArticleImageBlockComponent } from '../ArticleImageBlockComponent';
import { ArticleTextBlockComponent } from '../ArticleTextBlockComponent';

export const renderBlock = (block: ArticleBlock) => {
switch (block.type) {
case ArticleBlockType.CODE:
return <ArticleCodeBlockComponent key={block.id} block={block} />;
case ArticleBlockType.IMAGE:
return <ArticleImageBlockComponent key={block.id} block={block} />;
case ArticleBlockType.TEXT:
return <ArticleTextBlockComponent key={block.id} block={block} />;
default:
return null;
}
};
Loading

0 comments on commit 6ab3a98

Please sign in to comment.