mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-19 11:06:25 +08:00
feat: follow publish mode if no params
This commit is contained in:
@@ -1,24 +1,21 @@
|
||||
import { PublicDocMode } from '@affine/graphql';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
fetchSharedPublishMode,
|
||||
getResolvedPublishMode,
|
||||
getSearchWithMode,
|
||||
parsePublishMode,
|
||||
} from '../desktop/pages/workspace/share/share-page.utils';
|
||||
|
||||
describe('getResolvedPublishMode', () => {
|
||||
test('prefers the query mode when it is present', () => {
|
||||
expect(getResolvedPublishMode('edgeless', PublicDocMode.Page)).toBe(
|
||||
'edgeless'
|
||||
);
|
||||
expect(getResolvedPublishMode('page', PublicDocMode.Edgeless)).toBe('page');
|
||||
expect(getResolvedPublishMode('edgeless', 'page')).toBe('edgeless');
|
||||
expect(getResolvedPublishMode('page', 'edgeless')).toBe('page');
|
||||
});
|
||||
|
||||
test('falls back to the published public mode for shared docs', () => {
|
||||
expect(getResolvedPublishMode(null, PublicDocMode.Edgeless)).toBe(
|
||||
'edgeless'
|
||||
);
|
||||
expect(getResolvedPublishMode(null, PublicDocMode.Page)).toBe('page');
|
||||
expect(getResolvedPublishMode(null, 'edgeless')).toBe('edgeless');
|
||||
expect(getResolvedPublishMode(null, 'page')).toBe('page');
|
||||
});
|
||||
|
||||
test('defaults to page when no mode is available', () => {
|
||||
@@ -27,6 +24,66 @@ describe('getResolvedPublishMode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsePublishMode', () => {
|
||||
test('accepts valid publish modes only', () => {
|
||||
expect(parsePublishMode('page')).toBe('page');
|
||||
expect(parsePublishMode('edgeless')).toBe('edgeless');
|
||||
expect(parsePublishMode('invalid')).toBeNull();
|
||||
expect(parsePublishMode(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchSharedPublishMode', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test('reads publish mode from the HEAD response', async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
|
||||
new Response(null, {
|
||||
status: 200,
|
||||
headers: { 'publish-mode': 'edgeless' },
|
||||
})
|
||||
);
|
||||
|
||||
await expect(
|
||||
fetchSharedPublishMode({
|
||||
serverBaseUrl: 'https://app.affine.pro',
|
||||
workspaceId: 'workspace-id',
|
||||
docId: 'doc-id',
|
||||
})
|
||||
).resolves.toBe('edgeless');
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
new URL(
|
||||
'/api/workspaces/workspace-id/docs/doc-id',
|
||||
'https://app.affine.pro'
|
||||
),
|
||||
expect.objectContaining({ method: 'HEAD' })
|
||||
);
|
||||
});
|
||||
|
||||
test('falls back to GET when HEAD misses the header', async () => {
|
||||
vi.spyOn(globalThis, 'fetch')
|
||||
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
||||
.mockResolvedValueOnce(
|
||||
new Response(null, {
|
||||
status: 200,
|
||||
headers: { 'publish-mode': 'page' },
|
||||
})
|
||||
);
|
||||
|
||||
await expect(
|
||||
fetchSharedPublishMode({
|
||||
serverBaseUrl: 'https://app.affine.pro',
|
||||
workspaceId: 'workspace-id',
|
||||
docId: 'doc-id',
|
||||
})
|
||||
).resolves.toBe('page');
|
||||
expect(globalThis.fetch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSearchWithMode', () => {
|
||||
test('adds mode to an empty search string', () => {
|
||||
expect(getSearchWithMode('', 'edgeless')).toBe('?mode=edgeless');
|
||||
|
||||
@@ -37,6 +37,10 @@ import { PageNotFound } from '../../404';
|
||||
import { ShareFooter } from './share-footer';
|
||||
import { ShareHeader } from './share-header';
|
||||
import * as styles from './share-page.css';
|
||||
import {
|
||||
fetchSharedPublishMode,
|
||||
getResolvedPublishMode,
|
||||
} from './share-page.utils';
|
||||
import { useSharedModeQuerySync } from './use-shared-mode-query-sync';
|
||||
|
||||
const useUpdateBasename = (workspace: Workspace | null) => {
|
||||
@@ -127,19 +131,58 @@ const SharePageInner = ({
|
||||
const [page, setPage] = useState<Doc | null>(null);
|
||||
const [editor, setEditor] = useState<Editor | null>(null);
|
||||
const [noPermission, setNoPermission] = useState(false);
|
||||
const [fetchedPublishMode, setFetchedPublishMode] = useState<
|
||||
DocMode | null | undefined
|
||||
>(() => (publishMode === undefined ? undefined : null));
|
||||
const [editorContainer, setActiveBlocksuiteEditor] =
|
||||
useActiveBlocksuiteEditor();
|
||||
const resolvedPublishMode = publishMode ?? null;
|
||||
const resolvedPublishMode =
|
||||
publishMode !== undefined
|
||||
? publishMode
|
||||
: fetchedPublishMode === undefined
|
||||
? null
|
||||
: getResolvedPublishMode(null, fetchedPublishMode);
|
||||
const currentPublishMode = useSharedModeQuerySync({
|
||||
editor,
|
||||
resolvedPublishMode,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editor || workspace || page) {
|
||||
if (publishMode !== undefined) {
|
||||
setFetchedPublishMode(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
setFetchedPublishMode(undefined);
|
||||
|
||||
void fetchSharedPublishMode({
|
||||
serverBaseUrl: serverService.server.baseUrl,
|
||||
workspaceId,
|
||||
docId,
|
||||
signal: abortController.signal,
|
||||
})
|
||||
.then(mode => {
|
||||
if (!abortController.signal.aborted) {
|
||||
setFetchedPublishMode(mode);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (!abortController.signal.aborted) {
|
||||
console.error(err);
|
||||
setFetchedPublishMode(null);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [docId, publishMode, serverService.server.baseUrl, workspaceId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (resolvedPublishMode === null) return;
|
||||
if (editor || workspace || page) return;
|
||||
|
||||
// create a workspace for share page
|
||||
const { workspace: sharedWorkspace } = workspacesService.open(
|
||||
{
|
||||
@@ -188,7 +231,7 @@ const SharePageInner = ({
|
||||
setPage(doc);
|
||||
|
||||
const editor = doc.scope.get(EditorsService).createEditor();
|
||||
editor.setMode(resolvedPublishMode ?? doc.getPrimaryMode() ?? 'page');
|
||||
editor.setMode(resolvedPublishMode);
|
||||
|
||||
if (selector) {
|
||||
editor.setSelector(selector);
|
||||
|
||||
@@ -1,15 +1,67 @@
|
||||
import { PublicDocMode } from '@affine/graphql';
|
||||
import { type DocMode, DocModes } from '@blocksuite/affine/model';
|
||||
|
||||
export const getResolvedPublishMode = (
|
||||
queryMode: DocMode | null,
|
||||
publicMode?: PublicDocMode | null
|
||||
publishMode?: DocMode | null
|
||||
): DocMode => {
|
||||
if (queryMode && DocModes.includes(queryMode)) {
|
||||
return queryMode;
|
||||
}
|
||||
|
||||
return publicMode === PublicDocMode.Edgeless ? 'edgeless' : 'page';
|
||||
return publishMode === 'edgeless' ? 'edgeless' : 'page';
|
||||
};
|
||||
|
||||
export const parsePublishMode = (
|
||||
publishMode: string | null | undefined
|
||||
): DocMode | null => {
|
||||
if (!publishMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DocModes.includes(publishMode as DocMode)
|
||||
? (publishMode as DocMode)
|
||||
: null;
|
||||
};
|
||||
|
||||
export const fetchSharedPublishMode = async ({
|
||||
serverBaseUrl,
|
||||
workspaceId,
|
||||
docId,
|
||||
signal,
|
||||
}: {
|
||||
serverBaseUrl: string;
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<DocMode | null> => {
|
||||
const url = new URL(
|
||||
`/api/workspaces/${encodeURIComponent(workspaceId)}/docs/${encodeURIComponent(docId)}`,
|
||||
serverBaseUrl
|
||||
);
|
||||
const headers = {
|
||||
Accept: 'application/octet-stream',
|
||||
'x-affine-version': BUILD_CONFIG.appVersion,
|
||||
};
|
||||
const headResponse = await globalThis.fetch(url, {
|
||||
method: 'HEAD',
|
||||
headers,
|
||||
signal,
|
||||
});
|
||||
const headMode = parsePublishMode(headResponse.headers.get('publish-mode'));
|
||||
|
||||
if (headMode || headResponse.status === 404 || !headResponse.ok) {
|
||||
return headMode;
|
||||
}
|
||||
|
||||
const getResponse = await globalThis.fetch(url, {
|
||||
headers,
|
||||
signal,
|
||||
});
|
||||
try {
|
||||
return parsePublishMode(getResponse.headers.get('publish-mode'));
|
||||
} finally {
|
||||
await getResponse.body?.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
export const getSearchWithMode = (search: string, mode: DocMode) => {
|
||||
|
||||
@@ -200,13 +200,14 @@ test('share page with default edgeless', async ({ page, browser }) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
await skipOnboarding(context);
|
||||
const url: string = await page.evaluate(() =>
|
||||
navigator.clipboard.readText()
|
||||
const url = new URL(
|
||||
await page.evaluate(() => navigator.clipboard.readText())
|
||||
);
|
||||
url.searchParams.delete('mode');
|
||||
const page2 = await context.newPage();
|
||||
await page2.goto(url);
|
||||
await page2.goto(url.toString());
|
||||
await waitForEditorLoad(page2);
|
||||
await expect(page.locator('affine-edgeless-root')).toBeVisible({
|
||||
await expect(page2.locator('affine-edgeless-root')).toBeVisible({
|
||||
timeout: 1000,
|
||||
});
|
||||
await expect(page2.locator('affine-paragraph').first()).toContainText(
|
||||
|
||||
Reference in New Issue
Block a user