feat: support page sharing by meta (#1858)

This commit is contained in:
Himself65 2023-04-10 17:13:44 -05:00 committed by GitHub
parent ea2a146c82
commit 7fea55d81f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 18 deletions

View File

@ -0,0 +1,3 @@
import type { AffineWorkspace } from '@affine/workspace/type';
export function useAffinePublicPage(workspace: AffineWorkspace) {}

View File

@ -7,15 +7,17 @@ import { readFile } from 'node:fs/promises';
import { MessageCode } from '@affine/env/constant';
import { createStatusApis } from '@affine/workspace/affine/api/status';
import type { KeckProvider } from '@affine/workspace/affine/keck';
import { KeckProvider } from '@affine/workspace/affine/keck';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import user1 from '@affine-test/fixtures/built-in-user1.json';
import user2 from '@affine-test/fixtures/built-in-user2.json';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import { assertExists } from '@blocksuite/global/utils';
import type { Page } from '@blocksuite/store';
import type { Page, PageMeta } from '@blocksuite/store';
import { Workspace } from '@blocksuite/store';
import { faker } from '@faker-js/faker';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { WebSocket } from 'ws';
import { applyUpdate } from 'yjs';
import {
@ -32,6 +34,9 @@ import {
setLoginStorage,
} from '../login';
// @ts-expect-error
globalThis.WebSocket = WebSocket;
let workspaceApis: ReturnType<typeof createWorkspaceApis>;
let userApis: ReturnType<typeof createUserApis>;
let affineAuth: ReturnType<typeof createAffineAuth>;
@ -58,6 +63,15 @@ const mockUser = {
password: faker.internet.password(),
};
async function waitForConnected(provider: KeckProvider) {
return new Promise<void>(resolve => {
provider.once('status', ({ status }: any) => {
expect(status).toBe('connected');
resolve();
});
});
}
beforeEach(() => {
// create a new user for each test, so that each test can be run independently
mockUser.name = faker.name.fullName();
@ -99,30 +113,19 @@ declare global {
}
}
const wsUrl = `ws://127.0.0.1:3000/api/sync/`;
async function createWorkspace(
workspaceApi: typeof workspaceApis,
callback?: (workspace: Workspace) => void
): Promise<string> {
const workspace = new Workspace({
id: faker.datatype.uuid(),
})
.register(AffineSchemas)
.register(__unstableSchemas);
const workspace = createEmptyBlockSuiteWorkspace(
faker.datatype.uuid(),
_ => undefined
);
if (callback) {
callback(workspace);
}
const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc);
const data = await workspaceApi.createWorkspace(binary);
function waitForConnected(provider: KeckProvider) {
return new Promise<void>(resolve => {
provider.once('status', ({ status }: any) => {
expect(status).toBe('connected');
resolve();
});
});
}
createWorkspaceResponseSchema.parse(data);
return data.id;
}
@ -389,4 +392,82 @@ describe('api', () => {
timeout: 30000,
}
);
test(
'public page',
async () => {
const id = await createWorkspace(workspaceApis, workspace => {
const page = workspace.createPage('page0');
const { frameId } = initPage(page);
page.addBlock(
'affine:paragraph',
{
text: new page.Text('This is page0'),
},
frameId
);
});
const binary = await workspaceApis.downloadWorkspace(id, false);
const workspace = createEmptyBlockSuiteWorkspace(id, () => undefined);
Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(binary));
const workspace2 = createEmptyBlockSuiteWorkspace(id, () => undefined);
{
const wsUrl = `ws://127.0.0.1:3000/api/sync/`;
const provider = new KeckProvider(wsUrl, workspace.id, workspace.doc, {
params: { token: getLoginStorage()?.token },
// @ts-expect-error ignore the type
awareness: workspace.awarenessStore.awareness,
connect: false,
});
const provider2 = new KeckProvider(
wsUrl,
workspace2.id,
workspace2.doc,
{
params: { token: getLoginStorage()?.token },
// @ts-expect-error ignore the type
awareness: workspace2.awarenessStore.awareness,
connect: false,
}
);
provider.connect();
provider2.connect();
await Promise.all([
await waitForConnected(provider),
await waitForConnected(provider2),
]);
const pageId = 'page0';
const page = workspace.getPage(pageId) as Page;
expect(page).not.toBeNull();
expect(page).not.toBeUndefined();
workspace.setPageMeta(pageId, {
isPublic: true,
});
await new Promise(resolve => setTimeout(resolve, 1000));
const page2 = workspace2.getPage(pageId) as Page;
expect(page2).not.toBeNull();
const meta = workspace2.meta.getPageMeta(pageId) as PageMeta;
expect(meta.isPublic).toBe(true);
const binary = await workspaceApis.downloadPublicWorkspacePage(
id,
pageId
);
const publicWorkspace = createEmptyBlockSuiteWorkspace(
id,
() => undefined
);
Workspace.Y.applyUpdate(publicWorkspace.doc, new Uint8Array(binary));
const publicPage = publicWorkspace.getPage(pageId) as Page;
expect(publicPage).not.toBeNull();
}
},
{
timeout: 30000,
}
);
});

View File

@ -141,7 +141,13 @@ describe('ydoc sync', () => {
text: new page1.Text('hello world'),
}
);
workspace1.meta.setPageMeta(pageId, {
foo: 'bar',
});
await new Promise(resolve => setTimeout(resolve, 1000));
const pageMeta = workspace2.meta.getPageMeta(pageId);
expect(pageMeta).toBeDefined();
expect(pageMeta?.foo).toBe('bar');
const paragraph2 = page2.getBlockById(paragraphId) as ParagraphBlockModel;
const text = paragraph2.text as Text;
expect(text.toString()).toEqual(

View File

@ -380,7 +380,9 @@ export function createWorkspaceApis(prefixUrl = '/') {
{
method: 'GET',
}
).then(r => r.arrayBuffer());
).then(r =>
r.ok ? r.arrayBuffer() : Promise.reject(new Error(`${r.status}`))
);
},
downloadWorkspace: async (
workspaceId: string,