Skip to content

Commit

Permalink
Feat article modals (#104)
Browse files Browse the repository at this point in the history
* Feat: desktop articles in modals

* remove node-fetch import

* sashachabin review

* fix closing modals

* move on close to props
  • Loading branch information
hiba9201 authored Dec 10, 2023
1 parent 2714482 commit da867ee
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 53 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ common/utils/*.js*
schedulers/masstrans/*.js*
schedulers/transphoto/*.js*
schedulers/utils/*.js*
api/build

# misc
.DS_Store
Expand Down
10 changes: 4 additions & 6 deletions client/api/articles/articles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export const articlesApi = {
dataField: 'data',
}),
getArticle: (slug: string) =>
fetchApi(
`${STRAPI_URL}/api/articles?filters[slug][$eq]=${encodeURIComponent(
slug,
)}`,
{ dataField: 'data' },
),
fetchApi(`${STRAPI_URL}/api/articles?filters[slug][$eq]=${encodeURIComponent(slug)}`, {
dataField: 'data',
}),
getAllArticles: () => fetchApi(`${STRAPI_URL}/api/articles`, { dataField: 'data' }),
};
2 changes: 1 addition & 1 deletion client/api/articles/articles.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export interface Article {
createdAt: Date;
updatedAt: Date;
publishedAt: Date;
url: string;
slug: string;
};
}
2 changes: 2 additions & 0 deletions client/components/MainPage/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function Card({
dynamicContent,
headerCaption,
footerCaption,
onClick,
}: CardProps) {
const cardRef = useRef<HTMLAnchorElement>(null);
const cardTitleRef = useRef<HTMLDivElement>(null);
Expand All @@ -40,6 +41,7 @@ export function Card({
<a
className={cn(styles.Card, CARD_TYPES_CLASSNAMES[type])}
href={url}
onClick={onClick}
ref={cardRef}
style={
{
Expand Down
6 changes: 4 additions & 2 deletions client/components/MainPage/Card/Card.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ReactElement } from "react";
import {TCardType} from "api/main-page/main-page.types";
import React, { ReactElement } from 'react';

import { TCardType } from 'api/main-page/main-page.types';

export type CardProps = {
type: TCardType | undefined;
Expand All @@ -12,4 +13,5 @@ export type CardProps = {
footerCaption: string;
dynamicContent: ReactElement;
key: number;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
};
116 changes: 94 additions & 22 deletions client/components/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */

import React from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import classNames from 'classnames/bind';

import { Article as ArticleType } from 'api/articles/articles.types';
import { Article } from 'components/Articles/Article/Article';
import { Modal } from 'components/UI/Modal/Modal';

import { MainPageTypes } from './MainPage.types';
import { Card } from './Card/Card';
import { CardTrafficJams } from './Card/CardDynamic/CardTrafficJams';
Expand All @@ -15,49 +20,116 @@ import { Marquee } from './Marquee/Marquee';

const cn = classNames.bind(styles);

export function MainPage({ cards, cardsDynamicData, marqueeItems }: MainPageTypes) {
const marqueeItemsData = marqueeItems.map(({ id, attributes: { message } }) => ({ id, message }))

const MOBILE_WIDTH = 768;

export function MainPage({ cards, cardsDynamicData, marqueeItems, articles }: MainPageTypes) {
const marqueeItemsData = marqueeItems.map(({ id, attributes: { message } }) => ({
id,
message,
}));
const [openedArticle, setOpenedArticle] = useState<ArticleType['attributes'] | null>(null);

useEffect(() => {
const handlePopState = (e: PopStateEvent) => {
if (e.state.url === '/') {
setOpenedArticle(null);
}
};

window.addEventListener('popstate', handlePopState);

return () => window.removeEventListener('popstate', handlePopState);
}, [setOpenedArticle]);

const handleCardClick = useCallback(
(url: string) => (e: React.MouseEvent<HTMLAnchorElement>) => {
if (window.innerWidth <= MOBILE_WIDTH) {
return;
}

const cardSlug = url.slice(1);
const article = articles.find((a) => a.slug === cardSlug);

if (!article) {
return;
}

e.preventDefault();
setOpenedArticle(article);
window.history.pushState({ articleSlug: cardSlug }, '', `/${cardSlug}`);
},
[articles, setOpenedArticle],
);

const handleCloseModal = useCallback(() => {
setOpenedArticle(null);
window.history.pushState({ articleSlug: null }, '', `/`);
}, [setOpenedArticle]);

const getDynamicContent = (dynamicId) => {
switch (dynamicId) {
case 'a11y-transport':
return <CardTransportA11y {...cardsDynamicData.a11yTransportCounters} />
return <CardTransportA11y {...cardsDynamicData.a11yTransportCounters} />;
case 'traffic-jams':
return <CardTrafficJams score={cardsDynamicData.trafficJams} />
return <CardTrafficJams score={cardsDynamicData.trafficJams} />;
case 'map':
return <CardMap />
return <CardMap />;
}
}
};

return (
<div className={cn(styles.MainPage)}>
<div className={cn(styles.MainPageInner)}>
<div className={cn(styles.MainPageLogo)}>
<Logo />
<h1 className={cn(styles.MainPageTitle)}>Транспорт<br />Екатеринбурга</h1>
<h1 className={cn(styles.MainPageTitle)}>
Транспорт
<br />
Екатеринбурга
</h1>
</div>

<div className={styles.MainPageCardGrid}>
{cards
.sort((a, b) => a.attributes.priority - b.attributes.priority)
.map(({ id, attributes }) => {
return <Card
type={attributes.type}
title={attributes.title}
titleBackgroundColor={attributes.titleBackgroundColor}
url={attributes.url}
backgroundImage={attributes?.backgroundImage?.data?.attributes?.url}
backgroundImageHover={attributes?.backgroundImageHover?.data?.attributes?.url}
headerCaption={attributes.headerCaption}
footerCaption={attributes.footerCaption}
dynamicContent={getDynamicContent(attributes.dynamicId)}
key={id}
/>
return (
<Card
type={attributes.type}
title={attributes.title}
titleBackgroundColor={attributes.titleBackgroundColor}
url={attributes.url}
backgroundImage={
attributes?.backgroundImage?.data?.attributes?.url
}
backgroundImageHover={
attributes?.backgroundImageHover?.data?.attributes?.url
}
headerCaption={attributes.headerCaption}
footerCaption={attributes.footerCaption}
dynamicContent={getDynamicContent(attributes.dynamicId)}
key={id}
onClick={
attributes.url.startsWith('/')
? handleCardClick(attributes.url)
: null
}
/>
);
})}
</div>

<Marquee items={marqueeItemsData} />
{openedArticle && (
<Modal onClose={handleCloseModal}>
<Article
title={openedArticle.title}
description={openedArticle.description}
external
/>
</Modal>
)}
</div>
</div>
);
}
}
6 changes: 4 additions & 2 deletions client/components/MainPage/MainPage.types.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Article } from 'api/articles/articles.types';
import type {
Card,
AccessibilityTransportCounters,
Marquee,
Notification
Notification,
} from 'api/main-page/main-page.types';

export type MainPageTypes = {
cards: Card[];
cardsDynamicData: {
trafficJams: number;
a11yTransportCounters: AccessibilityTransportCounters,
a11yTransportCounters: AccessibilityTransportCounters;
};
notifications: Notification[];
marqueeItems: Marquee[];
articles: Article['attributes'][];
};
2 changes: 2 additions & 0 deletions client/components/UI/Modal/Modal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
color: rgba(0, 0, 0, 0.5);
cursor: pointer;
transition: .15s ease;
border-radius: 50%;
}

@media (hover) {
Expand All @@ -56,6 +57,7 @@
.Modal {
max-width: 800px;
margin: auto;
max-height: calc(100vh - 92px);
}

.ModalClose {
Expand Down
16 changes: 6 additions & 10 deletions client/components/UI/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,28 @@ import styles from './Modal.module.css';

const cn = classNames.bind(styles);

export function Modal({ title, children }) {
export function Modal({ title = null, children, onClose = () => {} }) {
const ref = useRef<HTMLDialogElement>(null);

useEffect(() => {
ref.current.showModal();
// Remove focus after open
(document.activeElement as HTMLElement).blur();
}, [])
}, []);

const close = () => {
ref.current.close();
}
};

return (
<dialog className={cn(styles.Modal)} ref={ref}>
<dialog className={cn(styles.Modal)} ref={ref} onClose={onClose}>
<button className={cn(styles.ModalClose)} onClick={close}>
<Close />
</button>

<h1 className={cn(styles.ModalTitle)}>
{t(title)}
</h1>
{title && <h1 className={cn(styles.ModalTitle)}>{t(title)}</h1>}

<div className={cn(styles.ModalInner)}>
{children}
</div>
<div className={cn(styles.ModalInner)}>{children}</div>
</dialog>
);
}
15 changes: 8 additions & 7 deletions client/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import Head from 'next/head';
import { MainPageApi } from 'api/main-page/main-page';
import { articlesApi } from 'api/articles/articles';
import { MainPage } from 'components/MainPage/MainPage';

export default function Home(props) {

return (
<>
<Head>
Expand All @@ -15,16 +15,17 @@ export default function Home(props) {
);
}

export async function getStaticProps() {
export async function getServerSideProps() {
return {
props: {
cards: await MainPageApi.getCards() || [],
cards: (await MainPageApi.getCards()) || [],
cardsDynamicData: {
trafficJams: await MainPageApi.getTrafficJamsCounter() || null,
a11yTransportCounters: await MainPageApi.getA11yTransportCounters() || {},
trafficJams: (await MainPageApi.getTrafficJamsCounter()) || null,
a11yTransportCounters: (await MainPageApi.getA11yTransportCounters()) || {},
},
marqueeItems: await MainPageApi.getMarqueeItems() || [],
marqueeItems: (await MainPageApi.getMarqueeItems()) || [],
articles:
(await articlesApi.getAllArticles()).map((article) => article.attributes) || [],
},
revalidate: 60,
};
}
4 changes: 1 addition & 3 deletions server/model/ekaterinburg-rf/ekaterinburg-rf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import fetch from 'node-fetch';
import _ from 'lodash';

import {
Expand Down Expand Up @@ -156,7 +155,7 @@ export class EkaterinburgRfModel {
jsonrpc,
method: JsonRpcMethods.StartSession,
params: {},
ts: getCurrentTimestamp()
ts: getCurrentTimestamp(),
}),
});

Expand Down Expand Up @@ -184,7 +183,6 @@ export class EkaterinburgRfModel {
magic: token.magic,
},
};


const fetchOptions = {
...fetchCommonOptions,
Expand Down

0 comments on commit da867ee

Please sign in to comment.