Skip to content

Commit 0af619a

Browse files
authored
Merge pull request #980 from ita-social-projects/refactor-art-gallery
The long-awaited and desired request for refactoring art-gallery. Meet with applause!
2 parents b0b2d87 + 738d4f1 commit 0af619a

File tree

80 files changed

+2530
-1300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+2530
-1300
lines changed

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"html-react-parser": "^3.0.12",
4141
"html-to-text": "^9.0.5",
4242
"http-status-codes": "^2.2.0",
43+
"install": "^0.13.0",
4344
"jest-canvas-mock": "^2.5.2",
4445
"leaflet": "^1.9.3",
4546
"leaflet-boundary-canvas": "^1.0.0",

src/app/api/media/arts.api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import Art from '@models/media/art.model';
55
const ArtsApi = {
66
getAll: () => Agent.get<Art[]>(`${API_ROUTES.ARTS.GET_ALL}`),
77

8+
getAllByStreetcodeId: (sId: number) => Agent.get<Art[]>(`${API_ROUTES.ARTS.GET_ALL_BY_STREETCODE_ID}/${sId}`),
9+
810
getById: (id: number) => Agent.get<Art>(`${API_ROUTES.ARTS.GET}/${id}`),
911

1012
create: (art: Art) => Agent.post<Art>(`${API_ROUTES.ARTS.CREATE}`, art),

src/app/api/media/streetcode-art.api.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Agent from '@api/agent.api';
22
import { API_ROUTES } from '@constants/api-routes.constants';
33

44
import StreetcodeArt from '@/models/media/streetcode-art.model';
5+
import StreetcodeArtSlide from "@models/media/streetcode-art-slide.model"
56

67
const StreetcodeArtApi = {
78
getAll: () => Agent.get<StreetcodeArt[]>(`${API_ROUTES.STREETCODE_ARTS.GET_ALL}`),
@@ -10,9 +11,9 @@ const StreetcodeArtApi = {
1011
`${API_ROUTES.STREETCODE_ARTS.GET_BY_STREETCODE_ID}/${streetcodeId}`,
1112
),
1213

13-
getPageOfArtsByStreetcodeId: (streetcodeId: number, page: number, pageSize: number) => Agent.get<StreetcodeArt[]>(
14-
`${API_ROUTES.STREETCODE_ARTS.GET_PAGE_BY_STREETCODE_ID}/${streetcodeId}`,
15-
new URLSearchParams(Object.entries({ page: page.toString(), pageSize: pageSize.toString() })),
14+
getArtSlidesByStreetcodeId: (streetcodeId: number, startFromSlide: number, amountOfSlides: number) => Agent.get<StreetcodeArtSlide[]>(
15+
`${API_ROUTES.STREETCODE_ART_SLIDES.GET_SLIDES_BY_STREETCODE_ID}/${streetcodeId}`,
16+
new URLSearchParams(Object.entries({ fromSlideN: startFromSlide.toString(), amountOfSlides: amountOfSlides.toString() })),
1617
),
1718
};
1819

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/* eslint-disable react/jsx-no-bind,@typescript-eslint/no-use-before-define */
2+
import './ArtGalleryBlock.styles.scss';
3+
4+
import { runInAction } from 'mobx';
5+
import { observer } from 'mobx-react-lite';
6+
import { useEffect, useRef, useState } from 'react';
7+
import { useParams } from 'react-router-dom';
8+
import { Settings as SliderSettings } from 'react-slick';
9+
import SLIDER_PROPS from '@components/ArtGallery/constants/sliderProps';
10+
import convertSlidesToTemplates from '@components/ArtGallery/utils/convertSlidesToTemplates';
11+
import SlickSlider from '@features/SlickSlider/SlickSlider.component';
12+
import { useAsync } from '@hooks/stateful/useAsync.hook';
13+
import { ArtCreateUpdate } from '@models/media/art.model';
14+
import StreetcodeArtSlide from '@models/media/streetcode-art-slide.model';
15+
import useMobx, { useStreetcodeDataContext } from '@stores/root-store';
16+
import BlockHeading from '@streetcode/HeadingBlock/BlockHeading.component';
17+
18+
import { Button } from 'antd';
19+
import { useMediaQuery } from 'react-responsive';
20+
21+
const MAX_SLIDES_AMOUNT = 30;
22+
23+
type Props = {
24+
isConfigurationGallery?: boolean
25+
isAdmin?: boolean
26+
isFillArtsStore?: boolean
27+
};
28+
29+
const ArtGallery = ({ isAdmin, isConfigurationGallery, isFillArtsStore } : Props) => {
30+
const { streetcodeArtSlideStore, artGalleryTemplateStore, artStore } = useMobx();
31+
const { streetcodeStore: { getStreetCodeId, errorStreetCodeId } } = useStreetcodeDataContext();
32+
const { fetchNextArtSlidesByStreetcodeId, streetcodeArtSlides, amountOfSlides } = streetcodeArtSlideStore;
33+
const { streetcodeArtSlides: templateArtSlides } = artGalleryTemplateStore;
34+
const [slickProps, setSlickProps] = useState<SliderSettings>(SLIDER_PROPS);
35+
const secondRender = useRef(false);
36+
const isMobile = useMediaQuery({
37+
query: '(max-width: 680px)',
38+
});
39+
40+
const { id } = useParams<any>();
41+
const parseId = id ? +id : errorStreetCodeId;
42+
43+
useAsync(
44+
async () => {
45+
if (streetcodeIdValidAndFetchingRequired()) {
46+
secondRender.current = true;
47+
let currentSlide = 0;
48+
49+
while (currentSlide < MAX_SLIDES_AMOUNT) {
50+
try {
51+
// eslint-disable-next-line no-await-in-loop
52+
await fetchNextArtSlidesByStreetcodeId(getStreetCodeId !== -1 ? getStreetCodeId : parseId);
53+
54+
if (isFillArtsStore) {
55+
copyArtsFromSlidesToStore();
56+
}
57+
58+
currentSlide += amountOfSlides;
59+
} catch (error: unknown) {
60+
currentSlide = MAX_SLIDES_AMOUNT;
61+
}
62+
}
63+
}
64+
},
65+
[getStreetCodeId, parseId],
66+
);
67+
68+
function streetcodeIdValidAndFetchingRequired() {
69+
return (getStreetCodeId !== errorStreetCodeId || parseId !== errorStreetCodeId)
70+
&& !secondRender.current
71+
&& !isConfigurationGallery;
72+
}
73+
74+
function copyArtsFromSlidesToStore() {
75+
runInAction(() => {
76+
const mappedArts = streetcodeArtSlides.map(
77+
(slide) => slide.streetcodeArts.map(
78+
(sArt) => sArt.art,
79+
),
80+
);
81+
82+
artStore.arts = ([] as ArtCreateUpdate[]).concat(...mappedArts);
83+
artStore.toggleMutation();
84+
});
85+
}
86+
87+
useEffect(() => {
88+
if (isConfigurationGallery) {
89+
toggleBlockingOfConfigurationSlider();
90+
}
91+
}, [artGalleryTemplateStore.isEdited]);
92+
93+
function toggleBlockingOfConfigurationSlider() {
94+
setSlickProps((prev) => ({
95+
...prev,
96+
dots: !prev.dots,
97+
arrows: !prev.arrows,
98+
draggable: !prev.draggable,
99+
}));
100+
}
101+
102+
function handleAddNewSlide() {
103+
const newSlide = artGalleryTemplateStore.getEditedSlide() as StreetcodeArtSlide;
104+
105+
if (!newSlide) {
106+
alert('Увага, заповніть усі зображення щоб зберегти слайд');
107+
return;
108+
}
109+
110+
if (newSlide.streetcodeId !== -1) {
111+
runInAction(() => {
112+
const oldSlideIdx = streetcodeArtSlides.findIndex((s) => s.index === newSlide.index);
113+
if (oldSlideIdx !== -1) {
114+
streetcodeArtSlides[oldSlideIdx] = newSlide;
115+
}
116+
});
117+
} else {
118+
newSlide.index = streetcodeArtSlides.length + 1;
119+
newSlide.streetcodeId = parseId ?? -1;
120+
121+
runInAction(() => {
122+
streetcodeArtSlides.push(newSlide);
123+
});
124+
}
125+
}
126+
127+
function handleClearSlideTemplate() {
128+
artGalleryTemplateStore.clearTemplates();
129+
}
130+
131+
return (
132+
<div>
133+
{(streetcodeArtSlides.length > 0 || isConfigurationGallery) && (
134+
<div
135+
id="art-gallery"
136+
className="artGalleryWrapper"
137+
>
138+
<div className="artGalleryContainer container">
139+
<BlockHeading headingText="Арт-галерея" />
140+
<div className="artGalleryContentContainer">
141+
<div className="artGallerySliderContainer">
142+
{isMobile
143+
? isConfigurationGallery
144+
? convertSlidesToTemplates(templateArtSlides as StreetcodeArtSlide[], true)
145+
: convertSlidesToTemplates(
146+
streetcodeArtSlideStore.getVisibleSortedSlides() as StreetcodeArtSlide[],
147+
false,
148+
isAdmin,
149+
)
150+
: <SlickSlider {...slickProps}>
151+
{isConfigurationGallery
152+
? convertSlidesToTemplates(templateArtSlides as StreetcodeArtSlide[], true)
153+
: convertSlidesToTemplates(
154+
streetcodeArtSlideStore.getVisibleSortedSlides() as StreetcodeArtSlide[],
155+
false,
156+
isAdmin,
157+
)}
158+
</SlickSlider>
159+
}
160+
</div>
161+
</div>
162+
</div>
163+
</div>
164+
)}
165+
{artGalleryTemplateStore.isEdited && isConfigurationGallery
166+
? (
167+
<div className="configurationGalleryControls">
168+
<Button type="primary" onClick={handleAddNewSlide}>Додати</Button>
169+
<Button danger onClick={handleClearSlideTemplate}>Скасувати</Button>
170+
</div>
171+
)
172+
: (<></>)}
173+
</div>
174+
);
175+
};
176+
177+
export default observer(ArtGallery);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
@use "@sass/_utils.functions.scss" as *;
2+
@use "@sass/variables/_variables.colors.scss" as c;
3+
@use "@sass/variables/_variables.sizes.scss" as s;
4+
@use "@sass/mixins/_utils.mixins.scss" as mut;
5+
@use "@sass/mixins/_wrapper.mixins.scss" as wr;
6+
@use "@sass/mixins/_vendor.mixins.scss" as vnd;
7+
@use "@features/SlickSlider/SlickSlider.styles.scss" as ss;
8+
9+
.artGalleryContainer .blockHeadingWrapper{
10+
margin-bottom: pxToRem(10px);
11+
}
12+
13+
.artGalleryContainer {
14+
.slick-next:before {
15+
@include ss.slick-arrow-pseudo(Right, -23px)
16+
}
17+
18+
.slick-prev:before {
19+
@include ss.slick-arrow-pseudo(Left, 0px)
20+
}
21+
}
22+
23+
24+
.artGallerySliderContainer .sliderClass .slick-slider {
25+
width: 100%;
26+
.slick-dots {
27+
padding: 0 pxToRem(16px);
28+
bottom: pxToRem(-40px);
29+
}
30+
}
31+
32+
.artGalleryWrapper {
33+
.artGalleryContainer {
34+
.artGalleryContentContainer {
35+
@include mut.flex-centered();
36+
}
37+
}
38+
39+
.artGallerySliderContainer {
40+
width: pxToRem(1260px);
41+
}
42+
}
43+
44+
.configurationGalleryControls{
45+
@include mut.flex-center();
46+
gap: 2rem;
47+
}
48+
49+
@media screen and (max-width:1450px) {
50+
.artGalleryWrapper {
51+
.artGalleryContainer {
52+
.artGalleryContentContainer{
53+
.artGallerySliderContainer{
54+
width: pxToRem(1045.5px);
55+
}
56+
}
57+
58+
}
59+
}
60+
}
61+
@media screen and (max-width:1200px) {
62+
.artGalleryWrapper {
63+
.artGalleryContainer {
64+
.artGalleryContentContainer{
65+
.artGallerySliderContainer{
66+
width: pxToRem(830px);
67+
}
68+
}
69+
}
70+
}
71+
}
72+
73+
@media screen and (max-width:1024px) {
74+
.artGalleryWrapper {
75+
.artGalleryContainer {
76+
.artGalleryContentContainer{
77+
.artGallerySliderContainer{
78+
@include mut.sized(100%, 100%);
79+
}
80+
}
81+
82+
}
83+
}
84+
}
85+
86+
@media screen and (max-width:768px) {
87+
.artGalleryWrapper {
88+
.artGalleryContainer {
89+
.artGalleryContentContainer{
90+
.artGallerySliderContainer{
91+
height: pxToRem(400px);
92+
}
93+
}
94+
95+
}
96+
}
97+
}
98+
99+
@media screen and (max-width:680px) {
100+
.artGalleryWrapper {
101+
.artGalleryContainer {
102+
.artGalleryContentContainer {
103+
.artGallerySliderContainer {
104+
@include mut.rem-padded(0, s.$art-gallery-gap-mobile, 0, 0);
105+
gap: pxToRem(s.$art-gallery-gap-mobile);
106+
height: pxToRem(362px);
107+
108+
overflow-y: hidden;
109+
overflow-x: scroll;
110+
111+
display: flex;
112+
flex-direction: column;
113+
flex-wrap: wrap;
114+
}
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)