mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-20 17:43:24 +00:00
Filter unindexable spaces
This commit is contained in:
@@ -2,7 +2,9 @@ import type { GitBookSiteContext } from '@/lib/context';
|
||||
|
||||
import { CONTAINER_STYLE, HEADER_HEIGHT_DESKTOP } from '@/components/layout';
|
||||
import { getSpaceLanguage, t } from '@/intl/server';
|
||||
import { filterSectionsWithIndexableSpaces } from '@/lib/seo';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { flattenSectionsFromGroup } from '@/lib/utils';
|
||||
import { SearchContainer } from '../Search';
|
||||
import { SiteSectionTabs, encodeClientSiteSections } from '../SiteSections';
|
||||
import { HeaderLink } from './HeaderLink';
|
||||
@@ -15,7 +17,7 @@ import { TranslationsDropdown } from './SpacesDropdown';
|
||||
/**
|
||||
* Render the header for the space.
|
||||
*/
|
||||
export function Header(props: {
|
||||
export async function Header(props: {
|
||||
context: GitBookSiteContext;
|
||||
withTopHeader?: boolean;
|
||||
withVariants?: 'generic' | 'translations';
|
||||
@@ -23,6 +25,14 @@ export function Header(props: {
|
||||
const { context, withTopHeader, withVariants } = props;
|
||||
const { siteSpace, siteSpaces, sections, customization } = context;
|
||||
|
||||
// Filter sections to only include those with indexable spaces
|
||||
const filteredSections = sections?.list
|
||||
? await filterSectionsWithIndexableSpaces(
|
||||
context,
|
||||
flattenSectionsFromGroup(sections.list).filter((s) => s.object === 'site-section')
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<header
|
||||
id="site-header"
|
||||
@@ -92,7 +102,6 @@ export function Header(props: {
|
||||
/>
|
||||
<HeaderLogo context={context} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
@@ -130,7 +139,7 @@ export function Header(props: {
|
||||
).length > 1
|
||||
) ?? false
|
||||
}
|
||||
withSections={!!sections}
|
||||
withSections={filteredSections.length > 1}
|
||||
section={
|
||||
sections
|
||||
? // Client-encode to avoid a serialisation issue that was causing the language selector to disappear
|
||||
|
||||
@@ -15,6 +15,8 @@ import { tcls } from '@/lib/tailwind';
|
||||
import { getSpaceLanguage } from '@/intl/server';
|
||||
import type { VisitorAuthClaims } from '@/lib/adaptive';
|
||||
import { GITBOOK_APP_URL } from '@/lib/env';
|
||||
import { hasIndexableSpaces } from '@/lib/seo';
|
||||
import { flattenSectionsFromGroup } from '@/lib/utils';
|
||||
import { AIChatProvider } from '../AI';
|
||||
import type { RenderAIMessageOptions } from '../AI';
|
||||
import { AIChat } from '../AIChat';
|
||||
@@ -103,7 +105,12 @@ export function SpaceLayout(props: SpaceLayoutProps) {
|
||||
|
||||
const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None;
|
||||
|
||||
const withSections = Boolean(sections && sections.list.length > 1);
|
||||
const withSections = Boolean(
|
||||
sections &&
|
||||
flattenSectionsFromGroup(sections?.list ?? []).filter(
|
||||
(s) => s.object === 'site-section' && hasIndexableSpaces(s)
|
||||
).length > 1
|
||||
);
|
||||
|
||||
const currentLanguage = getSpaceLanguage(context);
|
||||
const withVariants: 'generic' | 'translations' | undefined =
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { GitBookSiteContext } from '@/lib/context';
|
||||
import { type RevisionPageDocument, type RevisionPageGroup, SiteVisibility } from '@gitbook/api';
|
||||
import { resolveFirstDocument } from '@/lib/pages';
|
||||
import {
|
||||
type RevisionPageDocument,
|
||||
type RevisionPageGroup,
|
||||
SiteVisibility,
|
||||
type Space,
|
||||
} from '@gitbook/api';
|
||||
import { headers } from 'next/headers';
|
||||
|
||||
/**
|
||||
@@ -45,3 +51,87 @@ export async function isSiteIndexable(context: GitBookSiteContext) {
|
||||
function shouldIndexVisibility(visibility: SiteVisibility) {
|
||||
return visibility === SiteVisibility.Public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the first page of a site space is indexable.
|
||||
*/
|
||||
export async function isFirstPageIndexable(
|
||||
context: GitBookSiteContext,
|
||||
space: Space
|
||||
): Promise<boolean> {
|
||||
// Get the revision for the site space
|
||||
const revision = await context.dataFetcher.getRevision({
|
||||
spaceId: space.id,
|
||||
revisionId: space.revision,
|
||||
});
|
||||
|
||||
if (revision.error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the first document page in the revision
|
||||
const firstDocument = resolveFirstDocument(revision.data.pages, []);
|
||||
|
||||
if (!firstDocument) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the first page is indexable
|
||||
return isPageIndexable(firstDocument.ancestors, firstDocument.page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a section has any site spaces with indexable first pages.
|
||||
* This function actually checks the indexability by calling the async function.
|
||||
*/
|
||||
export async function hasIndexableSpaces(
|
||||
context: GitBookSiteContext,
|
||||
section: { siteSpaces: Array<{ space: Space }> }
|
||||
): Promise<boolean> {
|
||||
// Check if any site space in the section has an indexable first page
|
||||
const results = await Promise.allSettled(
|
||||
section.siteSpaces.map((siteSpace) => isFirstPageIndexable(context, siteSpace.space))
|
||||
);
|
||||
|
||||
// Return true if any of the checks succeeded and returned true
|
||||
return results.some((result) => result.status === 'fulfilled' && result.value === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter sections to only include those that have at least one indexable site space.
|
||||
*/
|
||||
export async function filterSectionsWithIndexableSpaces<
|
||||
T extends { siteSpaces: Array<{ space: Space }> },
|
||||
>(context: GitBookSiteContext, sections: T[]): Promise<T[]> {
|
||||
const filteredSections: T[] = [];
|
||||
|
||||
for (const section of sections) {
|
||||
const hasIndexable = await hasIndexableSpaces(context, section);
|
||||
if (hasIndexable) {
|
||||
filteredSections.push(section);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredSections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-compute indexable status for all site spaces in sections.
|
||||
* This should be called during context creation to avoid async operations in components.
|
||||
*/
|
||||
export async function computeSectionsIndexableStatus(
|
||||
context: GitBookSiteContext,
|
||||
sections: Array<{ siteSpaces: Array<{ space: Space }> }>
|
||||
): Promise<Array<{ siteSpaces: Array<{ space: Space; indexable: boolean }> }>> {
|
||||
return Promise.all(
|
||||
sections.map(async (section) => ({
|
||||
...section,
|
||||
siteSpaces: await Promise.all(
|
||||
section.siteSpaces.map(async (siteSpace) => ({
|
||||
...siteSpace,
|
||||
indexable: await isFirstPageIndexable(context, siteSpace.space),
|
||||
}))
|
||||
),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user