Add support for reusable content (#2531)

This commit is contained in:
Steven H
2024-10-18 10:59:46 +01:00
committed by GitHub
parent 2fa08519b6
commit c3675fdfe7
7 changed files with 131 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': minor
---
Added support for new Reusable Content block.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -16,7 +16,7 @@
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
},
"dependencies": {
"@gitbook/api": "^0.69.0",
"@gitbook/api": "^0.71.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -32,6 +32,7 @@ import { BlockMath } from './Math';
import { OpenAPI } from './OpenAPI';
import { Paragraph } from './Paragraph';
import { Quote } from './Quote';
import { ReusableContent } from './ReusableContent';
import { Stepper } from './Stepper';
import { StepperStep } from './StepperStep';
import { Table } from './Table';
@@ -107,7 +108,7 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
case 'synced-block':
return <BlockSyncedBlock {...props} {...contextProps} block={block} />;
case 'reusable-content':
return null;
return <ReusableContent {...props} {...contextProps} block={block} />;
case 'stepper':
return <Stepper {...props} {...contextProps} block={block} />;
case 'stepper-step':
@@ -0,0 +1,34 @@
import { DocumentBlockReusableContent } from '@gitbook/api';
import { getDocument } from '@/lib/api';
import { BlockProps } from './Block';
import { UnwrappedBlocks } from './Blocks';
export async function ReusableContent(props: BlockProps<DocumentBlockReusableContent>) {
const { block, context, ancestorBlocks } = props;
if (!context.content) {
throw new Error(`Expected a content context to render a reusable content block`);
}
const resolved = await context.resolveContentRef(block.data.ref);
if (!resolved?.reusableContent?.document) {
return null;
}
const document = await getDocument(context.content.spaceId, resolved.reusableContent.document);
if (!document) {
return null;
}
return (
<UnwrappedBlocks
nodes={document.nodes}
document={document}
ancestorBlocks={[...ancestorBlocks, block]}
context={context}
/>
);
}
+62 -1
View File
@@ -12,7 +12,7 @@ import {
RequestRenderIntegrationUI,
RevisionFile,
SiteCustomizationSettings,
SpaceIntegrationScript,
RevisionReusableContent,
} from '@gitbook/api';
import assertNever from 'assert-never';
import { headers } from 'next/headers';
@@ -491,6 +491,43 @@ const getRevisionFileById = cache({
},
});
const getRevisionReusableContentById = cache({
name: 'api.getRevisionReusableContentById.v1',
tag: (spaceId, revisionId) =>
getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
get: async (
spaceId: string,
revisionId: string,
reusableContentId: string,
options: CacheFunctionOptions,
) => {
try {
const response = await (async () => {
return api().spaces.getReusableContentInRevisionById(
spaceId,
revisionId,
reusableContentId,
{
metadata: false,
},
{
...noCacheFetchOptions,
signal: options.signal,
},
);
})();
return cacheResponse(response, cacheTtl_7days);
} catch (error: any) {
if (error instanceof GitBookAPIError && error.code === 404) {
return { data: null, ...cacheTtl_7days };
}
throw error;
}
},
});
/**
* Get all the files in a revision of a space.
* It should not be used directly, use `getRevisionFile` instead.
@@ -582,6 +619,30 @@ export const getRevisionFile = batch<[string, string, string], RevisionFile | nu
},
);
/**
* Get reusable content in a revision.
*/
export const getReusableContent = async (
spaceId: string,
revisionId: string,
reusableContentId: string,
): Promise<RevisionReusableContent | null> => {
const hasRevisionInMemory = await getRevision.hasInMemory(spaceId, revisionId, {
metadata: false,
});
if (hasRevisionInMemory) {
const revision = await getRevision(spaceId, revisionId, { metadata: false });
return (
revision.reusableContents.find(
(reusableContent) => reusableContent.id === reusableContentId,
) ?? null
);
} else {
return getRevisionReusableContentById(spaceId, revisionId, reusableContentId);
}
};
/**
* Get a document by its ID.
*/
+27 -2
View File
@@ -1,4 +1,11 @@
import { ContentRef, Revision, RevisionFile, RevisionPageDocument, Space } from '@gitbook/api';
import {
ContentRef,
Revision,
RevisionFile,
RevisionPageDocument,
RevisionReusableContent,
Space,
} from '@gitbook/api';
import assertNever from 'assert-never';
import React from 'react';
@@ -9,6 +16,7 @@ import {
SpaceContentPointer,
getCollection,
getDocument,
getReusableContent,
getRevisionFile,
getSpace,
getSpaceContentData,
@@ -35,6 +43,8 @@ export interface ResolvedContentRef {
active: boolean;
/** File, if the reference is a file */
file?: RevisionFile;
/** Resolved reusable content, if the ref points to reusable content on a revision. */
reusableContent?: RevisionReusableContent;
}
export interface ContentRefContext extends PageHrefContext {
@@ -255,7 +265,22 @@ export async function resolveContentRef(
};
}
case 'reusable-content':
case 'reusable-content': {
const reusableContent = await getReusableContent(
space.id,
revisionId,
contentRef.reusableContent,
);
if (!reusableContent) {
return null;
}
return {
href: gitbookAppHref(`/s/${space.id}`),
text: reusableContent.title,
active: false,
reusableContent,
};
}
case 'synced-block':
return null;