Update search

* update algolia replace all objects
* add table of content skeleton
* add bg color
* move comment info to .env
This commit is contained in:
DefectingCat
2021-12-05 00:34:14 +08:00
parent 2f4b6d5ce7
commit 6a329d01b5
14 changed files with 186 additions and 48 deletions

View File

@ -13,10 +13,9 @@
- [x] 复制代码块内容
- [x] Progress bar
- [x] 提高文章页面加载速度
- [ ] 图片懒加载占位问题
- [ ] algolia 搜索样式
- [x] ~~组件懒加载(首页,归档页,文章页)~~Suspense
- [x] algolia 搜索样式
- [ ] Use [sharp](https://github.com//lovell/sharp) process images.
- [ ] 组件懒加载(首页,归档页,文章页)
## ❤️

View File

@ -212,13 +212,8 @@ const NavBar: FC = () => {
{/* Menu */}
<Box mx={['-2rem', '-4rem', 'unset']}>
{/* Mobile menu */}
<Box
as={Collapse}
in={isOpen}
animateOpacity
display={[null, null, 'none']}
>
{/* Mobile menu (controled by collapse) */}
<Box as={Collapse} in={isOpen} animateOpacity display="none">
<MenuList boxBg={boxBg} handleMenuClick={handleMenuClick} />
</Box>
{/* Desktop menu */}

View File

@ -0,0 +1,26 @@
import { FC } from 'react';
import { Box, Skeleton } from '@chakra-ui/react';
const PostLoadingTOC: FC = () => {
return (
<>
<Box
display={['none', 'none', 'none', 'block']}
position="sticky"
top="3rem"
mt="3rem"
w="280px"
>
<Skeleton height="2rem" my="1rem" />
<Skeleton height="1rem" w="80%" mb="1rem" />
<Skeleton height="1rem" w="80%" ml="1rem" mb="1rem" />
<Skeleton height="1rem" w="80%" ml="1rem" mb="1rem" />
<Skeleton height="1rem" w="80%" mb="1rem" />
<Skeleton height="1rem" w="80%" ml="1rem" mb="1rem" />
<Skeleton height="1rem" w="80%" ml="1rem" mb="1rem" />
</Box>
</>
);
};
export default PostLoadingTOC;

View File

@ -11,9 +11,9 @@ const PostComment: FC = () => {
<Box mt="2rem">
<Giscus
repo="DefectingCat/DefectingCat.github.io"
repoId="MDEwOlJlcG9zaXRvcnkyMzk5MTUyNzk="
repoId={process.env.REPO_ID ?? ''}
categoryId={process.env.CATEGORY_ID ?? ''}
category="Announcements"
categoryId="DIC_kwDODkzRD84B_43T"
mapping="title"
reactionsEnabled="1"
emitMetadata="0"

View File

@ -21,15 +21,13 @@ const Highlight: FC<HighlightProvided & Props> = ({
return (
<>
<span>
{parsedHit.map((part, index) =>
part.isHighlighted ? (
<mark key={index}>{part.value}</mark>
) : (
<span key={index}>{part.value}</span>
)
)}
</span>
{parsedHit.map((part, index) =>
part.isHighlighted ? (
<mark key={index}>{part.value}</mark>
) : (
<span key={index}>{part.value}</span>
)
)}
</>
);
};

View File

@ -1,7 +1,9 @@
import { FC } from 'react';
import { HitsProvided } from 'react-instantsearch-core';
import { connectHits } from 'react-instantsearch-dom';
import CustomHighlight from './CustomHighlight';
import dynamic from 'next/dynamic';
const HitCard = dynamic(() => import('./HitCard'));
export interface PostHits {
objectID: string;
@ -18,16 +20,9 @@ export interface PostHits {
const Hits: FC<HitsProvided<PostHits>> = ({ hits }) => {
return (
<>
<ol>
{hits.map((item) => {
return (
<li key={item.objectID}>
<CustomHighlight attribute="title" hit={item} />
<CustomHighlight attribute="desc" hit={item} />
</li>
);
})}
</ol>
{hits.map((item) => {
return <HitCard key={item.objectID} hit={item} />;
})}
</>
);
};

View File

@ -0,0 +1,65 @@
import { FC, MouseEvent, useEffect } from 'react';
import { setFromPath } from 'features/router/routerSlice';
import useGetColors from 'lib/hooks/useGetColors';
import { useRouter } from 'next/router';
import { useAppDispatch } from 'app/hooks';
import { Box, Heading, Link } from '@chakra-ui/react';
import type { PostHits } from 'components/search/CustomHits';
import dynamic from 'next/dynamic';
const CustomHighlight = dynamic(() => import('./CustomHighlight'));
interface Props {
hit: PostHits;
}
const HitCard: FC<Props> = ({ hit }) => {
const { url } = hit;
const dispatch = useAppDispatch();
const router = useRouter();
const { boxBg, headingColor } = useGetColors();
const goToPost = (
e: MouseEvent<HTMLAnchorElement, globalThis.MouseEvent>,
url: string
) => {
e.preventDefault();
dispatch(setFromPath(location.pathname));
router.push(`/p/${url}`);
};
useEffect(() => {
router.prefetch(`/p/${url}`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);
return (
<>
<Box
as="article"
borderRadius="10px"
bg={boxBg}
overflow="hidden"
boxShadow="card"
mb="1rem"
p="1rem"
>
<Link
href={`/p/${url}`}
onClick={(e) => goToPost(e, url)}
_focus={{ boxShadow: 'unset' }}
>
<Heading color={headingColor} mb="0.6rem" size="md">
<CustomHighlight attribute="title" hit={hit} />
</Heading>
<Box>
<CustomHighlight attribute="desc" hit={hit} />
</Box>
</Link>
</Box>
</>
);
};
export default HitCard;

View File

@ -9,12 +9,16 @@ import {
ModalContent,
ModalOverlay,
} from '@chakra-ui/react';
import CustomSearchBox from './CustomSearchBox';
import CustomHits from './CustomHits';
import useGetColors from 'lib/hooks/useGetColors';
import dynamic from 'next/dynamic';
const CustomSearchBox = dynamic(() => import('./CustomSearchBox'));
const CustomHits = dynamic(() => import('./CustomHits'));
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID ?? '',
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY ?? ''
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY ?? '',
{}
);
interface Props {
@ -23,15 +27,31 @@ interface Props {
}
const Search: FC<Props> = ({ isModalOpen, onModalClose }) => {
const { bgColor } = useGetColors();
return (
<>
<Modal size="xl" isOpen={isModalOpen} onClose={onModalClose}>
<Modal
size="3xl"
isOpen={isModalOpen}
onClose={onModalClose}
scrollBehavior="inside"
>
<ModalOverlay />
<ModalContent>
<ModalBody p="0.5rem">
<ModalContent
my={['unset', '3.75rem']}
maxH={['100vh', 'calc(100% - 7.5rem)']}
>
<ModalBody p="1rem" bg={bgColor} rounded="lg">
<InstantSearch searchClient={searchClient} indexName="rua">
<Flex justifyContent="space-between" alignItems="center">
<Flex
justifyContent="space-between"
alignItems="center"
mb="1rem"
>
<CustomSearchBox />
<ModalCloseButton
position="unset"
display={[null, null, 'none']}

View File

@ -5,6 +5,7 @@ interface UseGetColors {
textColor: string;
bioColor: string;
headingColor: string;
bgColor: string;
giscusColor: 'light' | 'dark';
borderColor: string;
}
@ -18,6 +19,8 @@ const useGetColors = (): UseGetColors => {
'whiteAlpha.800'
);
const borderColor = useColorModeValue('gray.300', 'whiteAlpha.300');
// Body and search background color
const bgColor = useColorModeValue('home.bg', 'gray.800');
// comment
const giscusColor = useColorModeValue('light', 'dark');
@ -28,6 +31,7 @@ const useGetColors = (): UseGetColors => {
bioColor,
headingColor,
giscusColor,
bgColor,
borderColor,
};
};

View File

@ -0,0 +1,29 @@
import { useEffect, useRef, useState } from 'react';
const useIntersection = () => {
const targetRef = useRef(null);
const [intersect, setIntersect] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (targetRef.current) {
setIntersect(true);
observer.unobserve(targetRef.current);
}
}
});
});
targetRef.current && observer.observe(targetRef.current);
}, []);
return {
targetRef,
intersect,
};
};
export default useIntersection;

View File

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
/**
* Use IntersectionObserver API to lazy load taget DOM.
@ -18,10 +18,10 @@ const useLazyLoad = (src: string, blurPx = '10px') => {
setBlur('unset');
};
useEffect(() => {
// Lazy load images when they entry the view port.
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
const load = useCallback(
(entries, observer) => {
// Lazy load images when they entry the view port.
entries.forEach((entry: IntersectionObserverEntry) => {
if (entry.isIntersecting) {
if (targetRef.current) {
setInitSrc(src);
@ -33,7 +33,12 @@ const useLazyLoad = (src: string, blurPx = '10px') => {
}
}
});
});
},
[src]
);
useEffect(() => {
const observer = new IntersectionObserver(load);
targetRef.current && observer.observe(targetRef.current);
@ -43,7 +48,7 @@ const useLazyLoad = (src: string, blurPx = '10px') => {
cleanRef &&
(cleanRef as HTMLElement).removeEventListener('load', cleanBlur);
};
}, [src]);
}, [load, src]);
return {
initSrc,

View File

@ -80,6 +80,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
right="0"
size="xs"
isIndeterminate
zIndex="1984"
/>
)}
{getLayout(<Component {...pageProps} />)}

View File

@ -22,6 +22,7 @@ import { RootState } from 'app/store';
import { cleanFromPath } from 'features/router/routerSlice';
import useGetColors from 'lib/hooks/useGetColors';
import { useAppSelector, useAppDispatch } from 'app/hooks';
import PostLoadingTOC from 'components/loading/PostLoadingTOC';
const CopyButton = dynamic(() => import('components/post/CopyButton'));
const Footer = dynamic(() => import('components/Footer'));
@ -31,7 +32,7 @@ const PostImage = dynamic(() => import('components/post/PostImage'));
const PostComment = dynamic(() => import('components/post/PostComment'));
const PostHead = dynamic(() => import('components/post/PostHead'));
const PostTOC = dynamic(() => import('components/post/PostTOC'), {
loading: () => <p>...</p>,
loading: () => <PostLoadingTOC />,
});
export async function getStaticPaths() {

View File

@ -82,7 +82,7 @@ async function getSortedPostsData() {
const index = client.initIndex('rua');
// save the objects!
const algoliaResponse = await index.saveObjects(posts);
const algoliaResponse = await index.replaceAllObjects(posts);
// check the output of the response in the console
console.log(