mirror of https://github.com/lukechilds/docs.git
Alexander Graebe
4 years ago
committed by
Alexander Graebe
7 changed files with 21 additions and 247 deletions
@ -1,66 +0,0 @@ |
|||
import { convertRemoteDataToMDX } from '@common/data/mdx'; |
|||
import TurndownService from 'turndown'; |
|||
import { slugify } from '@common/utils'; |
|||
import { getBetterNames } from '@common/utils/faqs'; |
|||
|
|||
const fetchSections = async () => { |
|||
const res = await fetch('https://blockstack.zendesk.com/api/v2/help_center/en-us/sections.json'); |
|||
const { sections } = await res.json(); |
|||
return sections; |
|||
}; |
|||
const fetchArticles = async (id: number) => { |
|||
const res = await fetch( |
|||
`https://blockstack.zendesk.com/api/v2/help_center/en-us/sections/${id}/articles.json?per_page=100` |
|||
); |
|||
const { articles } = await res.json(); |
|||
return articles; |
|||
}; |
|||
|
|||
// This function gets called at build time
|
|||
export async function getStaticPaths() { |
|||
const sections = await fetchSections(); |
|||
const paths = sections.map(section => ({ |
|||
params: { slug: slugify(getBetterNames(section.id).title) }, |
|||
})); |
|||
|
|||
return { paths, fallback: false }; |
|||
} |
|||
|
|||
const getSectionBySlug = (sections, slug) => |
|||
sections.find(s => { |
|||
const { title } = getBetterNames(s.id); |
|||
const _slug = slugify(title); |
|||
return _slug === slug; |
|||
}); |
|||
|
|||
export async function getStaticProps(context) { |
|||
const sections = await fetchSections(); |
|||
let articles = []; |
|||
|
|||
if (context?.params?.slug) { |
|||
const section = getSectionBySlug(sections, context.params.slug); |
|||
const _articles = await fetchArticles(section.id); |
|||
const turndownService = new TurndownService(); |
|||
// we convert html to markdown so we can process it with remark/rehype,
|
|||
// eg external links open in new window
|
|||
const md = _articles.map(faq => ({ |
|||
...faq, |
|||
body: turndownService.turndown(faq.body), |
|||
})); |
|||
// convert it to MDX with next-mdx-remote
|
|||
const body = await convertRemoteDataToMDX(md, 'body'); |
|||
articles = _articles.map((faq, index) => ({ |
|||
...faq, |
|||
body: body[index], |
|||
})); |
|||
} |
|||
|
|||
return { |
|||
props: { |
|||
sections, |
|||
articles, |
|||
...context, |
|||
}, |
|||
revalidate: 60 * 60 * 12, // 12 hours
|
|||
}; |
|||
} |
@ -1,61 +0,0 @@ |
|||
import React from 'react'; |
|||
import { Box, space, color, Grid } from '@stacks/ui'; |
|||
import { Text } from '@components/typography'; |
|||
import { slugify } from '@common/utils'; |
|||
import { getCapsizeStyles, getHeadingStyles } from '@components/mdx/typography'; |
|||
import { HoverImage } from '@components/hover-image'; |
|||
import { useTouchable } from '@common/hooks/use-touchable'; |
|||
import Link from 'next/link'; |
|||
import { getBetterNames } from '@common/utils/faqs'; |
|||
|
|||
const FloatingLink = ({ href, ...props }: any) => ( |
|||
<Link href={href} {...props} passHref> |
|||
<Box as="a" position="absolute" size="100%" zIndex={999} left={0} top={0} /> |
|||
</Link> |
|||
); |
|||
const SectionCard = ({ section }) => { |
|||
const { hover, active, bind } = useTouchable({ |
|||
behavior: 'button', |
|||
}); |
|||
const { title, description, img } = getBetterNames(section.id); |
|||
return ( |
|||
<Box |
|||
color={color('text-title')} |
|||
_hover={{ cursor: 'pointer', color: color('accent') }} |
|||
position="relative" |
|||
{...bind} |
|||
> |
|||
<FloatingLink href="/references/faqs/[slug]" as={`/references/faqs/${slugify(title)}`} /> |
|||
<HoverImage isHovered={hover || active} src={img} /> |
|||
<Box> |
|||
<Text color="currentColor" {...getHeadingStyles('h3')}> |
|||
{title} |
|||
</Text> |
|||
<Box> |
|||
<Text |
|||
display={'block'} |
|||
color={color('text-body')} |
|||
mt={space('base-loose')} |
|||
{...getCapsizeStyles(16, 26)} |
|||
> |
|||
{description} |
|||
</Text> |
|||
</Box> |
|||
</Box> |
|||
</Box> |
|||
); |
|||
}; |
|||
export const FAQs = React.memo(({ articles, sections }: any) => { |
|||
return ( |
|||
<Grid |
|||
gridTemplateColumns={['repeat(1, 1fr)', 'repeat(2, 1fr)', 'repeat(2, 1fr)', 'repeat(2, 1fr)']} |
|||
gridColumnGap={space('extra-loose')} |
|||
gridRowGap="64px" |
|||
px={['extra-loose', 'extra-loose', 0, 0]} |
|||
> |
|||
{sections.map(section => { |
|||
return <SectionCard key={section.id} section={section} />; |
|||
})} |
|||
</Grid> |
|||
); |
|||
}); |
@ -1,10 +1,24 @@ |
|||
--- |
|||
title: FAQs |
|||
description: A knowledge base of question and answers related to the Stacks ecosystem. |
|||
duration: '' |
|||
description: Find answers related to the Stacks ecosystem. |
|||
--- |
|||
|
|||
import { FAQs } from '@components/faq' |
|||
export { getStaticProps } from '@common/data/faq' |
|||
## General Information |
|||
|
|||
<FAQs sections={props.sections} /> |
|||
Learn more about the user-owned internet on Bitcoin and the Stacks ecosystem on [stacks.co](https://stacks.co). |
|||
|
|||
## Apps and Smart Contracts |
|||
|
|||
Developers, get started building apps and contracts on the [developer page at stacks.co](https://www.stacks.co/developers). |
|||
|
|||
## Stacks Network |
|||
|
|||
Learn more about the network behind the user-owned internet on Bitcoin in the [Understand Stacks chapter](https://docs.blockstack.org/understand-stacks/overview) in the docs. |
|||
|
|||
## Stacks Token |
|||
|
|||
Stacks fuel apps and smart contracts on Bitcoin. Learn more at [stackstoken.com](https://stackstoken.com/faq). |
|||
|
|||
## Stacks Wallet |
|||
|
|||
Download and find resources about the Stacks Wallet by Hiro at [hiro.so](https://www.hiro.so/wallet). |
|||
|
@ -1,109 +0,0 @@ |
|||
import React from 'react'; |
|||
import { Components } from '@components/mdx'; |
|||
import { Box, Flex, ChevronIcon, space, color, Grid } from '@stacks/ui'; |
|||
import hydrate from 'next-mdx-remote/hydrate'; |
|||
import { Accordion, AccordionItem, AccordionButton, AccordionPanel } from '@reach/accordion'; |
|||
import { border } from '@common/utils'; |
|||
import { useRouter } from 'next/router'; |
|||
import { useActiveHeading } from '@common/hooks/use-active-heading'; |
|||
import { BackButton } from '@components/back-button'; |
|||
import Head from 'next/head'; |
|||
import { MDContents } from '@components/mdx/md-contents'; |
|||
export { getStaticProps, getStaticPaths } from '@common/data/faq'; |
|||
import { slugify, getSlug } from '@common/utils'; |
|||
import { PageTop } from '@components/page-top'; |
|||
import { getBetterNames } from '@common/utils/faqs'; |
|||
|
|||
const FAQItem = React.memo(({ faq, ...rest }: any) => { |
|||
const id = slugify(faq.title); |
|||
const { isActive } = useActiveHeading(id); |
|||
|
|||
return ( |
|||
<Components.section> |
|||
<Box as={AccordionItem} borderBottom={border()} {...rest}> |
|||
<Flex |
|||
as={AccordionButton} |
|||
_hover={{ color: color('accent') }} |
|||
{...{ |
|||
display: 'flex', |
|||
width: '100%', |
|||
outline: 'none', |
|||
bg: 'transparent', |
|||
border: '0', |
|||
alignItems: 'center', |
|||
justifyContent: 'space-between', |
|||
py: space('extra-loose'), |
|||
textAlign: 'left', |
|||
color: isActive ? color('accent') : color('text-title'), |
|||
_hover: { |
|||
cursor: 'pointer', |
|||
color: color('accent'), |
|||
}, |
|||
}} |
|||
> |
|||
<Components.h4 my="0px !important" id={id} color="currentColor"> |
|||
{faq.title} |
|||
</Components.h4> |
|||
<Box color={color('text-caption')} pl={space('base-loose')}> |
|||
<ChevronIcon direction="down" size="22px" /> |
|||
</Box> |
|||
</Flex> |
|||
<Box pb={space('extra-loose')} as={AccordionPanel}> |
|||
{hydrate(faq.body, { components: Components })} |
|||
</Box> |
|||
</Box> |
|||
</Components.section> |
|||
); |
|||
}); |
|||
|
|||
const FaqItems = ({ articles }) => { |
|||
const router = useRouter(); |
|||
const slug = getSlug(router.asPath); |
|||
const slugIndex = articles.findIndex(faq => slugify(faq.title) === slug); |
|||
const [index, setIndex] = React.useState(slugIndex !== -1 ? slugIndex : 0); |
|||
const handleIndexChange = (value: number) => { |
|||
setIndex(value); |
|||
}; |
|||
|
|||
return ( |
|||
<Box |
|||
pr={['extra-loose', 'extra-loose', 'base-loose', 'base-loose']} |
|||
pl={['extra-loose', 'extra-loose', '0', '0']} |
|||
> |
|||
<BackButton href="/references/faqs" mb={0} /> |
|||
<Accordion multiple collapsible defaultIndex={index} onChange={handleIndexChange}> |
|||
{articles |
|||
// @ts-ignore
|
|||
.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)) |
|||
.map((faq, _index) => { |
|||
return <FAQItem faq={faq} key={_index} />; |
|||
})} |
|||
</Accordion> |
|||
</Box> |
|||
); |
|||
}; |
|||
|
|||
const FaqPage = props => { |
|||
const { articles, sections, params } = props; |
|||
|
|||
const section = sections.find(s => { |
|||
const { title } = getBetterNames(s.id); |
|||
const slug = slugify(title); |
|||
return slug === params.slug; |
|||
}); |
|||
|
|||
const { title, description } = getBetterNames(section.id); |
|||
return ( |
|||
<> |
|||
<Head> |
|||
<title>{title} | Stacks</title> |
|||
<meta name="description" content={description} /> |
|||
</Head> |
|||
<MDContents pageTop={() => <PageTop title={title} description={description} />} headings={[]}> |
|||
<FaqItems articles={articles} /> |
|||
</MDContents> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default FaqPage; |
Loading…
Reference in new issue