mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 16:51:37 +00:00
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:
@ -13,10 +13,9 @@
|
||||
- [x] 复制代码块内容
|
||||
- [x] Progress bar
|
||||
- [x] 提高文章页面加载速度
|
||||
- [ ] 图片懒加载占位问题
|
||||
- [ ] algolia 搜索样式
|
||||
- [x] ~~组件懒加载(首页,归档页,文章页)~~(Suspense)
|
||||
- [x] algolia 搜索样式
|
||||
- [ ] Use [sharp](https://github.com//lovell/sharp) process images.
|
||||
- [ ] 组件懒加载(首页,归档页,文章页)
|
||||
|
||||
## ❤️
|
||||
|
||||
|
@ -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 */}
|
||||
|
26
components/loading/PostLoadingTOC.tsx
Normal file
26
components/loading/PostLoadingTOC.tsx
Normal 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;
|
@ -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"
|
||||
|
@ -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>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
65
components/search/HitCard.tsx
Normal file
65
components/search/HitCard.tsx
Normal 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;
|
@ -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']}
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
29
lib/hooks/useIntersection.ts
Normal file
29
lib/hooks/useIntersection.ts
Normal 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;
|
@ -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,
|
||||
|
@ -80,6 +80,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
right="0"
|
||||
size="xs"
|
||||
isIndeterminate
|
||||
zIndex="1984"
|
||||
/>
|
||||
)}
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
|
@ -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() {
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user