diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/import-template.tsx b/packages/frontend/core/src/components/cloud/share-header-right-item/import-template.tsx
new file mode 100644
index 000000000..5bc38bc9d
--- /dev/null
+++ b/packages/frontend/core/src/components/cloud/share-header-right-item/import-template.tsx
@@ -0,0 +1,24 @@
+import { Button } from '@affine/component';
+import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
+import { useI18n } from '@affine/i18n';
+
+export const ImportTemplateButton = ({
+ workspaceId,
+ docId,
+ name,
+}: {
+ workspaceId: string;
+ docId: string;
+ name: string;
+}) => {
+ const t = useI18n();
+ const { jumpToImportTemplate } = useNavigateHelper();
+ return (
+
+ );
+};
diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx b/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx
index 4db381e37..88a86f0b4 100644
--- a/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx
+++ b/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx
@@ -2,6 +2,7 @@ import { AuthService } from '@affine/core/modules/cloud';
import type { DocMode } from '@blocksuite/blocks';
import { useLiveData, useService } from '@toeverything/infra';
+import { ImportTemplateButton } from './import-template';
import { PresentButton } from './present';
import { SignIn } from './sign-in';
import * as styles from './styles.css';
@@ -9,28 +10,45 @@ import { PublishPageUserAvatar } from './user-avatar';
export type ShareHeaderRightItemProps = {
workspaceId: string;
- pageId: string;
+ docId: string;
publishMode: DocMode;
+ isTemplate?: boolean;
+ templateName?: string;
};
-const ShareHeaderRightItem = ({ ...props }: ShareHeaderRightItemProps) => {
+const ShareHeaderRightItem = ({
+ publishMode,
+ isTemplate,
+ templateName,
+ workspaceId,
+ docId,
+}: ShareHeaderRightItemProps) => {
const loginStatus = useLiveData(useService(AuthService).session.status$);
- const { publishMode } = props;
const authenticated = loginStatus === 'authenticated';
return (
- {authenticated ? null :
}
- {publishMode === 'edgeless' ?
: null}
- {authenticated ? (
+ {isTemplate ? (
+
+ ) : (
<>
-
-
+ {authenticated ? null :
}
+ {publishMode === 'edgeless' ?
: null}
+ {authenticated ? (
+ <>
+
+
+ >
+ ) : null}
>
- ) : null}
+ )}
);
};
diff --git a/packages/frontend/core/src/hooks/use-navigate-helper.ts b/packages/frontend/core/src/hooks/use-navigate-helper.ts
index d9d3b2cea..5c7fd27ea 100644
--- a/packages/frontend/core/src/hooks/use-navigate-helper.ts
+++ b/packages/frontend/core/src/hooks/use-navigate-helper.ts
@@ -182,6 +182,15 @@ export function useNavigateHelper() {
[navigate]
);
+ const jumpToImportTemplate = useCallback(
+ (workspaceId: string, docId: string, name: string) => {
+ return navigate(
+ `/template/import?workspaceId=${encodeURIComponent(workspaceId)}&docId=${encodeURIComponent(docId)}&name=${encodeURIComponent(name)}`
+ );
+ },
+ [navigate]
+ );
+
return useMemo(
() => ({
jumpToPage,
@@ -197,6 +206,7 @@ export function useNavigateHelper() {
jumpToTags,
jumpToTag,
openInApp,
+ jumpToImportTemplate,
}),
[
jumpToPage,
@@ -212,6 +222,7 @@ export function useNavigateHelper() {
jumpToTags,
jumpToTag,
openInApp,
+ jumpToImportTemplate,
]
);
}
diff --git a/packages/frontend/core/src/modules/import-template/services/import.ts b/packages/frontend/core/src/modules/import-template/services/import.ts
index 35c070014..b97a4e7cf 100644
--- a/packages/frontend/core/src/modules/import-template/services/import.ts
+++ b/packages/frontend/core/src/modules/import-template/services/import.ts
@@ -1,4 +1,5 @@
import type { WorkspaceFlavour } from '@affine/env/workspace';
+import { ZipTransformer } from '@blocksuite/blocks';
import type { WorkspaceMetadata, WorkspacesService } from '@toeverything/infra';
import { Service } from '@toeverything/infra';
@@ -16,13 +17,18 @@ export class ImportTemplateService extends Service {
metadata: workspaceMetadata,
});
await workspace.engine.waitForRootDocReady();
- const newDoc = workspace.docCollection.createDoc({});
- await workspace.engine.doc.storage.behavior.doc.set(
- newDoc.spaceDoc.guid,
- docBinary
+ const [importedDoc] = await ZipTransformer.importDocs(
+ workspace.docCollection,
+ new Blob([docBinary], {
+ type: 'application/zip',
+ })
);
- disposeWorkspace();
- return newDoc.id;
+ if (importedDoc) {
+ disposeWorkspace();
+ return importedDoc.id;
+ } else {
+ throw new Error('Failed to import doc');
+ }
}
async importToNewWorkspace(
diff --git a/packages/frontend/core/src/modules/import-template/store/downloader.ts b/packages/frontend/core/src/modules/import-template/store/downloader.ts
index f87a4963e..e01fbe1e3 100644
--- a/packages/frontend/core/src/modules/import-template/store/downloader.ts
+++ b/packages/frontend/core/src/modules/import-template/store/downloader.ts
@@ -7,9 +7,12 @@ export class TemplateDownloaderStore extends Store {
super();
}
- async download(workspaceId: string, docId: string) {
+ async download(
+ /* not support workspaceid for now */ _workspaceId: string,
+ docId: string
+ ) {
const response = await this.fetchService.fetch(
- `/api/workspaces/${workspaceId}/docs/${docId}`,
+ `https://affine.pro/templates/snapshots/${docId}.zip `,
{
priority: 'high',
} as any
diff --git a/packages/frontend/core/src/pages/workspace/share/share-header.tsx b/packages/frontend/core/src/pages/workspace/share/share-header.tsx
index cc57947a5..f09d01a2a 100644
--- a/packages/frontend/core/src/pages/workspace/share/share-header.tsx
+++ b/packages/frontend/core/src/pages/workspace/share/share-header.tsx
@@ -11,10 +11,14 @@ export function ShareHeader({
pageId,
publishMode,
docCollection,
+ isTemplate,
+ templateName,
}: {
pageId: string;
publishMode: DocMode;
docCollection: DocCollection;
+ isTemplate?: boolean;
+ templateName?: string;
}) {
return (
@@ -23,8 +27,10 @@ export function ShareHeader({
diff --git a/packages/frontend/core/src/pages/workspace/share/share-page.tsx b/packages/frontend/core/src/pages/workspace/share/share-page.tsx
index 741909df0..30f11d03a 100644
--- a/packages/frontend/core/src/pages/workspace/share/share-page.tsx
+++ b/packages/frontend/core/src/pages/workspace/share/share-page.tsx
@@ -32,7 +32,7 @@ import {
WorkspacesService,
} from '@toeverything/infra';
import clsx from 'clsx';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { PageNotFound } from '../../404';
@@ -57,14 +57,18 @@ export const SharePage = ({
const location = useLocation();
- const [mode, setMode] = useState(null);
-
- useEffect(() => {
+ const { mode, isTemplate, templateName } = useMemo(() => {
const searchParams = new URLSearchParams(location.search);
const queryStringMode = searchParams.get('mode') as DocMode | null;
- if (queryStringMode && DocModes.includes(queryStringMode)) {
- setMode(queryStringMode);
- }
+
+ return {
+ mode:
+ queryStringMode && DocModes.includes(queryStringMode)
+ ? queryStringMode
+ : null,
+ isTemplate: searchParams.has('isTemplate'),
+ templateName: searchParams.get('templateName') || '',
+ };
}, [location.search]);
useEffect(() => {
@@ -88,6 +92,8 @@ export const SharePage = ({
workspaceBinary={data.workspaceBinary}
docBinary={data.docBinary}
publishMode={mode || data.publishMode}
+ isTemplate={isTemplate}
+ templateName={templateName}
/>
);
} else {
@@ -101,12 +107,16 @@ const SharePageInner = ({
workspaceBinary,
docBinary,
publishMode = 'page' as DocMode,
+ isTemplate,
+ templateName,
}: {
workspaceId: string;
docId: string;
workspaceBinary: Uint8Array;
docBinary: Uint8Array;
publishMode?: DocMode;
+ isTemplate?: boolean;
+ templateName?: string;
}) => {
const workspacesService = useService(WorkspacesService);
@@ -218,6 +228,8 @@ const SharePageInner = ({
pageId={page.id}
publishMode={publishMode}
docCollection={page.blockSuiteDoc.collection}
+ isTemplate={isTemplate}
+ templateName={templateName}
/>
import('./pages/import-template'),
},
+ {
+ path: '/template/preview',
+ loader: ({ request }) => {
+ const url = new URL(request.url);
+ const workspaceId = url.searchParams.get('workspaceId');
+ const docId = url.searchParams.get('docId');
+ const templateName = url.searchParams.get('name');
+
+ return redirect(
+ `/workspace/${workspaceId}/${docId}?isTemplate=true&templateName=${encodeURIComponent(templateName ?? '')}`
+ );
+ },
+ },
{
path: '/auth/:authType',
lazy: () => import(/* webpackChunkName: "auth" */ './pages/auth/auth'),
diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json
index 2b8577751..a754b821f 100644
--- a/packages/frontend/i18n/src/resources/en.json
+++ b/packages/frontend/i18n/src/resources/en.json
@@ -1457,6 +1457,7 @@
"com.affine.share-page.footer.get-started": "Get started for free",
"com.affine.share-page.header.login": "Login or Sign Up",
"com.affine.share-page.header.present": "Present",
+ "com.affine.share-page.header.import-template": "Use This Template",
"com.affine.shortcutsTitle.edgeless": "Edgeless",
"com.affine.shortcutsTitle.general": "General",
"com.affine.shortcutsTitle.markdownSyntax": "Markdown syntax",