diff --git a/README.md b/README.md index 4b291a4..df79126 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,9 @@ - [x] 复制代码块内容 - [x] Progress bar - [x] 提高文章页面加载速度 -- [ ] 图片懒加载占位问题 -- [ ] algolia 搜索样式 +- [x] ~~组件懒加载(首页,归档页,文章页)~~(Suspense) +- [x] algolia 搜索样式 - [ ] Use [sharp](https://github.com//lovell/sharp) process images. -- [ ] 组件懒加载(首页,归档页,文章页) ## ❤️ diff --git a/components/NavBar.tsx b/components/NavBar.tsx index afc3290..bff3468 100644 --- a/components/NavBar.tsx +++ b/components/NavBar.tsx @@ -212,13 +212,8 @@ const NavBar: FC = () => { {/* Menu */} - {/* Mobile menu */} - + {/* Mobile menu (controled by collapse) */} + {/* Desktop menu */} diff --git a/components/loading/PostLoadingTOC.tsx b/components/loading/PostLoadingTOC.tsx new file mode 100644 index 0000000..98e7a54 --- /dev/null +++ b/components/loading/PostLoadingTOC.tsx @@ -0,0 +1,26 @@ +import { FC } from 'react'; +import { Box, Skeleton } from '@chakra-ui/react'; + +const PostLoadingTOC: FC = () => { + return ( + <> + + + + + + + + + + + ); +}; + +export default PostLoadingTOC; diff --git a/components/post/PostComment.tsx b/components/post/PostComment.tsx index b17d93a..f79b6c0 100644 --- a/components/post/PostComment.tsx +++ b/components/post/PostComment.tsx @@ -11,9 +11,9 @@ const PostComment: FC = () => { = ({ return ( <> - - {parsedHit.map((part, index) => - part.isHighlighted ? ( - {part.value} - ) : ( - {part.value} - ) - )} - + {parsedHit.map((part, index) => + part.isHighlighted ? ( + {part.value} + ) : ( + {part.value} + ) + )} ); }; diff --git a/components/search/CustomHits.tsx b/components/search/CustomHits.tsx index 72c7877..b9262d7 100644 --- a/components/search/CustomHits.tsx +++ b/components/search/CustomHits.tsx @@ -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> = ({ hits }) => { return ( <> -
    - {hits.map((item) => { - return ( -
  1. - - -
  2. - ); - })} -
+ {hits.map((item) => { + return ; + })} ); }; diff --git a/components/search/HitCard.tsx b/components/search/HitCard.tsx new file mode 100644 index 0000000..f061574 --- /dev/null +++ b/components/search/HitCard.tsx @@ -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 = ({ hit }) => { + const { url } = hit; + const dispatch = useAppDispatch(); + const router = useRouter(); + const { boxBg, headingColor } = useGetColors(); + + const goToPost = ( + e: 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 ( + <> + + goToPost(e, url)} + _focus={{ boxShadow: 'unset' }} + > + + + + + + + + + + + ); +}; + +export default HitCard; diff --git a/components/search/index.tsx b/components/search/index.tsx index e810f4c..43b84a6 100644 --- a/components/search/index.tsx +++ b/components/search/index.tsx @@ -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 = ({ isModalOpen, onModalClose }) => { + const { bgColor } = useGetColors(); + return ( <> - + - - + + + - + + { '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, }; }; diff --git a/lib/hooks/useIntersection.ts b/lib/hooks/useIntersection.ts new file mode 100644 index 0000000..aa09108 --- /dev/null +++ b/lib/hooks/useIntersection.ts @@ -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; diff --git a/lib/hooks/useLazyload.ts b/lib/hooks/useLazyload.ts index b430eca..aa02e9e 100644 --- a/lib/hooks/useLazyload.ts +++ b/lib/hooks/useLazyload.ts @@ -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, diff --git a/pages/_app.tsx b/pages/_app.tsx index 51a388f..86757c1 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -80,6 +80,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { right="0" size="xs" isIndeterminate + zIndex="1984" /> )} {getLayout()} diff --git a/pages/p/[slug].tsx b/pages/p/[slug].tsx index 2b370f5..e77bf0e 100644 --- a/pages/p/[slug].tsx +++ b/pages/p/[slug].tsx @@ -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: () =>

...

, + loading: () => , }); export async function getStaticPaths() { diff --git a/scripts/build-search.mjs b/scripts/build-search.mjs index efa2045..38be198 100644 --- a/scripts/build-search.mjs +++ b/scripts/build-search.mjs @@ -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(