Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: download snippet generation #7351

Merged
merged 27 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aa3f53f
feat: download snippet generation
ovflowd Dec 22, 2024
125e1bb
feat: changed approach
ovflowd Dec 22, 2024
e946778
chore: simplify and reduce bundle size
ovflowd Dec 22, 2024
9fa2e99
chore: reduce bundle
ovflowd Dec 22, 2024
0837e6b
chore: minor code fixes
ovflowd Dec 22, 2024
701ed84
chore: more code review
ovflowd Dec 22, 2024
6526100
feat: introduced skeleton and animation
ovflowd Dec 22, 2024
0e354da
chore: synchronous shiki
ovflowd Dec 22, 2024
c21ef03
chore: prevent internal errors
ovflowd Dec 22, 2024
6faed96
chore: attempt to handle null situations
ovflowd Dec 22, 2024
e63c68e
fix: attempt to fix vercel env
ovflowd Dec 22, 2024
0633c19
fix: erroneous env usage for fetch
ovflowd Dec 22, 2024
ca565c6
chore: fix select styles
ovflowd Dec 22, 2024
c757334
chore: updated allowlist for env vars
ovflowd Dec 22, 2024
69433d0
feat: streamline detectos (only run query once)
ovflowd Dec 23, 2024
cc7574d
fix: avatar rendering and avatar group margins
ovflowd Dec 23, 2024
60a478c
fix: pass ref
ovflowd Dec 23, 2024
5901572
chore: optimized release provider memo
ovflowd Dec 23, 2024
d84772a
chore: next-image mock
ovflowd Dec 23, 2024
cab9da9
Update crowdin.yml (#7353)
Andrulko Dec 23, 2024
85366e6
chore: improved loading state
ovflowd Dec 23, 2024
e1150e0
Apply suggestions from code review
ovflowd Dec 23, 2024
957bb6a
chore: correct fixes
ovflowd Dec 23, 2024
021cd2f
fix: unit tests
ovflowd Dec 23, 2024
ff76711
chore: final code review
ovflowd Dec 24, 2024
957b902
fix: server component test
ovflowd Dec 24, 2024
8747ab1
fix: rsc still risky for storybook
ovflowd Dec 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/translations-pr-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ on:
- '!apps/site/pages/en/**/*.mdx'
- 'packages/i18n/locales/*.json'
- '!packages/i18n/locales/en.json'
- 'apps/site/snippets/**/*.bash'
- '!apps/site/snippets/en/**/*.bash'

# Cancel any runs on the same branch
concurrency:
Expand Down
1 change: 1 addition & 0 deletions apps/site/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const config: StorybookConfig = {
resolve: {
...config.resolve,
alias: {
'next/image': join(mocksFolder, './next-image.mjs'),
'next-intl/navigation': join(mocksFolder, './next-intl.mjs'),
'@/client-context': join(mocksFolder, './client-context.mjs'),
'@': join(__dirname, '../'),
Expand Down
13 changes: 7 additions & 6 deletions apps/site/app/[locale]/feed/[feed]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import provideWebsiteFeeds from '@/next-data/providers/websiteFeeds';
import { siteConfig } from '@/next.json.mjs';
import { defaultLocale } from '@/next.locales.mjs';

// We only support fetching these pages from the /en/ locale code
const locale = defaultLocale.code;

type StaticParams = { params: Promise<{ feed: string; locale: string }> };
type DynamicStaticPaths = { locale: string; feed: string };
type StaticParams = { params: Promise<DynamicStaticPaths> };

// This is the Route Handler for the `GET` method which handles the request
// for the Node.js Website Blog Feeds (RSS)
Expand All @@ -20,15 +18,18 @@ export const GET = async (_: Request, props: StaticParams) => {

return new NextResponse(websiteFeed, {
headers: { 'Content-Type': 'application/xml' },
status: websiteFeed ? 200 : 404,
status: websiteFeed !== undefined ? 200 : 404,
});
};

// This function generates the static paths that come from the dynamic segments
// `[locale]/feeds/[feed]` and returns an array of all available static paths
// This is used for ISR static validation and generation
export const generateStaticParams = async () =>
siteConfig.rssFeeds.map(feed => ({ feed: feed.file, locale }));
siteConfig.rssFeeds.map(feed => ({
locale: defaultLocale.code,
feed: feed.file,
}));

// Enforces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {
} from '@/next-data/providers/blogData';
import { defaultLocale } from '@/next.locales.mjs';

type StaticParams = {
params: Promise<{ locale: string; category: string; page: string }>;
};
type DynamicStaticPaths = { locale: string; category: string; page: string };
type StaticParams = { params: Promise<DynamicStaticPaths> };

// This is the Route Handler for the `GET` method which handles the request
// for providing Blog Posts for Blog Categories and Pagination Metadata
Expand Down
37 changes: 37 additions & 0 deletions apps/site/app/[locale]/next-data/download-snippets/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import provideDownloadSnippets from '@/next-data/providers/downloadSnippets';
import { defaultLocale } from '@/next.locales.mjs';

type DynamicStaticPaths = { locale: string };
type StaticParams = { params: Promise<DynamicStaticPaths> };

// This is the Route Handler for the `GET` method which handles the request
// for generating JSON data for Download Snippets
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
export const GET = async (_: Request, props: StaticParams) => {
const params = await props.params;

// Retrieve all available Download snippets for a given locale if available
const snippets = provideDownloadSnippets(params.locale);

// We append always the default/fallback snippets when a result is found
return Response.json(snippets, {
status: snippets !== undefined ? 200 : 404,
});
};

// This function generates the static paths that come from the dynamic segments
// `[locale]/next-data/download-snippets/` this will return a default value as we don't want to
// statically generate this route as it is compute-expensive.
// Hence we generate a fake route just to satisfy Next.js requirements.
export const generateStaticParams = async () => [
{ locale: defaultLocale.code },
];

// Enforces that this route is cached and static as much as possible
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
export const dynamic = 'force-static';

// Ensures that this endpoint is invalidated and re-executed every X minutes
// so that when new deployments happen, the data is refreshed
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate
export const revalidate = 300;
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,10 @@ const CATEGORY_TO_THEME_COLOUR_MAP = {
vulnerability: tailwindConfig.theme.colors.warning['600'],
};

type StaticParams = {
params: Promise<{
locale: string;
category: keyof typeof CATEGORY_TO_THEME_COLOUR_MAP;
title: string;
}>;
};
type Category = keyof typeof CATEGORY_TO_THEME_COLOUR_MAP;

type DynamicStaticPaths = { locale: string; category: Category; title: string };
type StaticParams = { params: Promise<DynamicStaticPaths> };

// This is the Route Handler for the `GET` method which handles the request
// for generating OpenGraph images for Blog Posts and Pages
Expand Down
12 changes: 4 additions & 8 deletions apps/site/components/Blog/BlogHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import { RssIcon } from '@heroicons/react/24/solid';
import { useTranslations } from 'next-intl';
import type { FC } from 'react';
Expand All @@ -9,15 +7,13 @@ import { siteConfig } from '@/next.json.mjs';

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

type BlogHeaderProps = {
category: string;
};
type BlogHeaderProps = { category: string };

const BlogHeader: FC<BlogHeaderProps> = ({ category }) => {
const t = useTranslations();
const currentFile =
siteConfig.rssFeeds.find(item => item.category === category)?.file ??
'blog.xml';

const feed = siteConfig.rssFeeds.find(item => item.category === category);
const currentFile = feed ? feed.file : 'blog.xml';

return (
<header className={styles.blogHeader}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
justify-center
rounded-full
border-2
border-white
border-transparent
bg-neutral-100
object-cover
text-xs
text-neutral-800
dark:border-neutral-950
dark:bg-neutral-900
dark:text-neutral-300;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,4 @@ export const NoSquare: Story = {
},
};

export const FallBack: Story = {
args: {
image: 'https://avatars.githubusercontent.com/u/',
nickname: 'John Doe',
fallback: 'JD',
},
};

export default { component: Avatar } as Meta;
46 changes: 27 additions & 19 deletions apps/site/components/Common/AvatarGroup/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as RadixAvatar from '@radix-ui/react-avatar';
import classNames from 'classnames';
import type { ComponentPropsWithoutRef, ElementRef } from 'react';
import Image from 'next/image';
import type { HTMLAttributes } from 'react';
import { forwardRef } from 'react';

import Link from '@/components/Link';
Expand All @@ -16,37 +16,45 @@ export type AvatarProps = {
url?: string;
};

// @TODO: We temporarily removed the Avatar Radix UI primitive, since it was causing flashing
// during initial load and not being able to render nicely when images are already cached.
// @see https://github.com/radix-ui/primitives/pull/3008
const Avatar = forwardRef<
ElementRef<typeof RadixAvatar.Root>,
ComponentPropsWithoutRef<typeof RadixAvatar.Root> & AvatarProps
HTMLSpanElement,
HTMLAttributes<HTMLSpanElement> & AvatarProps
>(({ image, nickname, name, fallback, url, size = 'small', ...props }, ref) => {
const Wrapper = url ? Link : 'div';

return (
<RadixAvatar.Root
<span
{...props}
className={classNames(styles.avatar, styles[size], props.className)}
ref={ref}
className={classNames(styles.avatar, styles[size], props.className)}
>
<Wrapper
href={url || undefined}
target={url ? '_blank' : undefined}
className={styles.wrapper}
>
<RadixAvatar.Image
loading="lazy"
src={image}
alt={name || nickname}
className={styles.item}
/>
<RadixAvatar.Fallback
delayMs={500}
className={classNames(styles.item, styles[size])}
>
{fallback}
</RadixAvatar.Fallback>
{image && (
<Image
width={40}
height={40}
loading="lazy"
decoding="async"
src={image}
alt={name || nickname}
className={styles.item}
/>
)}

{!image && (
<span className={classNames(styles.item, styles[size])}>
{fallback}
</span>
)}
</Wrapper>
</RadixAvatar.Root>
</span>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const AvatarOverlay: FC<AvatarOverlayProps> = ({
fallback={fallback}
size="medium"
/>

<div className={styles.user}>
{name && <div className={styles.name}>{name}</div>}
{nickname && <div className={styles.nickname}>{nickname}</div>}
Expand Down
15 changes: 12 additions & 3 deletions apps/site/components/Common/AvatarGroup/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@
@apply flex
flex-wrap
items-center;

&:not(.expandable) {
> span {
@apply ml-0;
}
}
}

.small {
@apply xs:-space-x-2
space-x-0.5;
> span {
@apply -ml-2;
}
}

.medium {
@apply -space-x-2.5;
> span {
@apply -ml-2.5;
}
}
8 changes: 1 addition & 7 deletions apps/site/components/Common/AvatarGroup/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,8 @@ const names = [
'araujogui',
];

const unknownAvatar = {
image: 'https://avatars.githubusercontent.com/u/',
nickname: 'unknown-avatar',
fallback: 'UA',
};

const defaultProps = {
avatars: [unknownAvatar, ...getAuthorWithId(names, true)],
avatars: getAuthorWithId(names, true),
};

export const Default: Story = {
Expand Down
7 changes: 6 additions & 1 deletion apps/site/components/Common/AvatarGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ const AvatarGroup: FC<AvatarGroupProps> = ({
);

return (
<div className={classNames(styles.avatarGroup, styles[size])}>
<div
className={classNames(styles.avatarGroup, styles[size], {
[styles.expandable]: avatars.length > limit,
})}
>
{renderAvatars.map(({ ...avatar }) => (
<Fragment key={avatar.nickname}>
<Tooltip
Expand All @@ -54,6 +58,7 @@ const AvatarGroup: FC<AvatarGroupProps> = ({
</Tooltip>
</Fragment>
))}

{avatars.length > limit && (
<span
onClick={isExpandable ? () => setShowMore(prev => !prev) : undefined}
Expand Down
2 changes: 2 additions & 0 deletions apps/site/components/Common/ProgressionSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { useTranslations } from 'next-intl';
import type { ComponentProps, FC } from 'react';
import { useRef } from 'react';
Expand Down
6 changes: 2 additions & 4 deletions apps/site/components/Common/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { OramaSearchBox, OramaSearchButton } from '@orama/react-components';
import { useTranslations, useLocale } from 'next-intl';
import { useTheme } from 'next-themes';
import { type FC } from 'react';
import type { FC } from 'react';

import { useRouter } from '@/navigation.mjs';
import {
Expand Down Expand Up @@ -132,9 +132,7 @@ const SearchButton: FC = () => {
const fullURLObject = new URL(event.detail.result.path, BASE_URL);

// result.path already contains LOCALE. Locale is set to undefined here so router does not add it once again.
router.push(fullURLObject.href, {
locale: undefined,
});
router.push(fullURLObject.href, { locale: undefined });
}}
/>
</>
Expand Down
Loading
Loading