Skip to content

Commit

Permalink
feat: Dodano wyszukiwarkę Algolia do strony (#124)
Browse files Browse the repository at this point in the history
* Add algolia search

* Code review

* Code review

* Simple version of hits

* Create custom algolia components

* Code review

* Code review

* Change fading behaviour of hits

* Add optional chaining operator

* Fix

* Quickfixes

* Code review

* Code review

* Delete unused code

* Add translation

* Code review

* CSS changes

* Fix

* Remove unused css classes

* Change article reference

Co-authored-by: Michał Miszczyszyn <mmiszy@users.noreply.github.com>
Co-authored-by: Adam Siekierski <a@siekierski.ml>
  • Loading branch information
3 people committed Jan 20, 2021
1 parent 211fb77 commit 352a60e
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 14 deletions.
5 changes: 3 additions & 2 deletions .env-sample
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SECRET="anothersecret"
JWT_SECRET="hmmmmmm"
NEXTAUTH_URL="http://localhost:3000"

ALGOLIA_APP_ID=""
NEXT_PUBLIC_ALGOLIA_API_KEY=""
NEXT_PUBLIC_ALGOLIA_APP_ID=""
NEXT_PUBLIC_ALGOLIA_INDEX_NAME=""
ALGOLIA_API_SECRET=""
ALGOLIA_INDEX_NAME=""
11 changes: 11 additions & 0 deletions components/AlgoliaSearch/AlgoliaHit.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.heading {
margin-top: 0;
}

.favicon {
margin-right: 0.5rem;
}

.articleRef {
display: block;
}
48 changes: 48 additions & 0 deletions components/AlgoliaSearch/AlgoliaHit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Link from 'next/link';

import styles from './AlgoliaHit.module.css';

export type Hit = {
readonly objectID: string;
readonly href: string;
readonly publishedAt: string;
readonly slug?: string;
readonly description?: string;
readonly title: string;
readonly blog: {
readonly name: string;
readonly href: string;
readonly favicon?: string;
};
};

type HitProps = {
readonly hit: Hit;
};

export const AlgoliaHit = ({ hit }: HitProps) => (
<>
<h3 className={styles.heading}>{hit.title}</h3>
<span>
<img
loading="lazy"
src={hit?.blog?.favicon || undefined}
alt=""
className={styles.favicon}
height={16}
width={16}
/>
{hit?.blog?.name}
</span>
{hit.slug ? (
<Link href={`/artykuly/${hit.slug}`}>
<a className={styles.articleRef}>Przejdź do artykułu</a>
</Link>
) : (
<a className={styles.articleRef} href={hit.href} target="_blank" rel="noreferrer noopener">
Przejdź do artykułu
</a>
)}
</>
);
AlgoliaHit.displayName = 'AlogliaHit';
19 changes: 19 additions & 0 deletions components/AlgoliaSearch/AlgoliaHits.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.list {
list-style: none;
width: 100%;
padding: 0;
}

.listItem {
width: 100%;
padding: 1rem;
margin-bottom: 0.35rem;
border: 1px solid var(--brand-color-main);
box-shadow: 0 2px 5px 0 var(--gray-shadow);
background: var(--white);
border-radius: 0.4rem;
}

.listItem:hover {
background: var(--gray-border);
}
21 changes: 21 additions & 0 deletions components/AlgoliaSearch/AlgoliaHits.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { connectHits } from 'react-instantsearch-dom';

import type { Hit } from './AlgoliaHit';
import { AlgoliaHit } from './AlgoliaHit';
import styles from './AlgoliaHits.module.css';

type HitsProps = {
readonly hits: readonly Hit[];
};

const Hits = ({ hits }: HitsProps) => (
<ol className={styles.list}>
{hits.map((hit) => (
<li className={styles.listItem} key={hit.objectID}>
<AlgoliaHit hit={hit} />
</li>
))}
</ol>
);

export const AlgoliaHits = connectHits(Hits);
47 changes: 47 additions & 0 deletions components/AlgoliaSearch/AlgoliaSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import algoliasearch from 'algoliasearch/lite';
import { memo, useEffect } from 'react';
import { InstantSearch, SearchBox } from 'react-instantsearch-dom';

import { AlgoliaHits } from './AlgoliaHits';

const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID as string,
process.env.NEXT_PUBLIC_ALGOLIA_API_KEY as string,
);

export type SearchState = { readonly query: string };

export type AlgoliaSearchProps = {
readonly searchState: SearchState;
readonly setSearchState: (searchState: SearchState) => void;
};

export const AlogliaSearch = memo<AlgoliaSearchProps>(({ searchState, setSearchState }) => {
const showHits = searchState.query !== '';

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) =>
event.key === 'Escape' && setSearchState({ query: '' });
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [setSearchState]);

return (
<InstantSearch
searchState={searchState}
onSearchStateChange={setSearchState}
searchClient={searchClient}
indexName={process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME as string}
>
<SearchBox
translations={{
submitTitle: 'Zatwierdź frazę wyszukiwania.',
resetTitle: 'Wyczyść frazę wyszukiwania.',
placeholder: 'Szukaj...',
}}
/>
{showHits && <AlgoliaHits />}
</InstantSearch>
);
});
AlogliaSearch.displayName = 'AlogliaSearch';
15 changes: 15 additions & 0 deletions components/Main/Main.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.section {
max-width: 73rem;
margin: 0 auto;
}

.searchWrapper {
min-height: 5rem;
padding: 1rem 1rem;
margin-top: 3rem;
padding: 1rem;

@media screen and (min-width: 45em) {
padding: 1rem 2rem;
}
}
33 changes: 33 additions & 0 deletions components/Main/Main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import dynamic from 'next/dynamic';
import { memo, useState } from 'react';

import type { HomePageProps } from '../../pages/[displayStyle]/[cursor]';
import type { AlgoliaSearchProps, SearchState } from '../AlgoliaSearch/AlgoliaSearch';
import { MainTiles } from '../MainTiles/MainTiles';

import styles from './Main.module.scss';

type MainProps = HomePageProps;

const AlogliaSearch = dynamic<AlgoliaSearchProps>(
() =>
import(
/* webpackChunkName: "AlgoliaSearch" */
'../AlgoliaSearch/AlgoliaSearch'
).then((mod) => mod.AlogliaSearch),
{ ssr: false },
);

export const Main = memo<MainProps>((props) => {
const [searchState, setSearchState] = useState<SearchState>({ query: '' });

return (
<section className={styles.section}>
<div className={styles.searchWrapper}>
<AlogliaSearch searchState={searchState} setSearchState={setSearchState} />
</div>
{!searchState.query && <MainTiles {...props} />}
</section>
);
});
Main.displayName = 'Main';
4 changes: 2 additions & 2 deletions components/MainTiles/MainTiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const MainTiles = memo<MainTilesProps>((props) => {
});

return (
<section className={styles.section}>
<>
<h2 className={styles.heading}>Wszystkie artykuły</h2>
<div className={styles.buttons}>
<Link href="/zglos-serwis" passHref>
Expand Down Expand Up @@ -62,7 +62,7 @@ export const MainTiles = memo<MainTilesProps>((props) => {
</Link>
)}
</div>
</section>
</>
);
});

Expand Down
5 changes: 0 additions & 5 deletions components/MainTiles/mainTiles.module.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
.section {
max-width: 73rem;
margin: 0 auto;
}

.heading {
padding: 2rem 0 1rem;
border-bottom: 2px solid var(--gray-border);
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@sentry/tracing": "5.29.2",
"@sentry/webpack-plugin": "1.14.0",
"@zeit/next-source-maps": "0.0.4-canary.1",
"algoliasearch": "4.8.3",
"cheerio": "1.0.0-rc.5",
"clsx": "1.1.1",
"feed": "4.2.1",
Expand All @@ -62,6 +63,7 @@
"ramda": "0.27.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-instantsearch-dom": "6.8.2",
"rxjs": "6.6.3",
"slugify": "1.4.6",
"superjson": "1.4.1",
Expand Down Expand Up @@ -90,6 +92,7 @@
"@types/pino": "6.3.4",
"@types/react": "17.0.0",
"@types/react-dom": "17.0.0",
"@types/react-instantsearch-dom": "6.8.0",
"@types/yup": "0.29.11",
"@types/zeit__next-source-maps": "0.0.2",
"@typescript-eslint/eslint-plugin": "4.12.0",
Expand Down
4 changes: 2 additions & 2 deletions pages/[displayStyle]/[cursor].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { closeConnection, openConnection } from '../../api-helpers/db';
import { HTTPNotFound } from '../../api-helpers/errors';
import { Layout } from '../../components/Layout';
import { MainTiles } from '../../components/MainTiles/MainTiles';
import { Main } from '../../components/Main/Main';
import type { InferGetStaticPropsContext, InferGetStaticPropsType2 } from '../../types';
import { addExcerptToArticle } from '../../utils/excerpt-utils';

Expand All @@ -26,7 +26,7 @@ export default function HomePage(props: HomePageProps) {
} artykułów z polskich blogów frontendowych`}
titleTemplate=""
>
<MainTiles {...props} />
<Main {...props} />
</Layout>
);
}
Expand Down
12 changes: 12 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ export default function MyApp({
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="alternate" href="/feed" type="application/rss+xml" title="Polski Frontend RSS" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.3.1/themes/reset-min.css"
integrity="sha256-t2ATOGCtAIZNnzER679jwcFcKYfLlw01gli6F6oszk8="
crossOrigin="anonymous"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.3.1/themes/algolia-min.css"
integrity="sha256-HB49n/BZjuqiCtQQf49OdZn63XuKFaxcIHWf0HNKte8="
crossOrigin="anonymous"
/>
</Head>
<Component {...pageProps} err={err} />
</>
Expand Down

0 comments on commit 352a60e

Please sign in to comment.