mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
feat(core): adjust web clipper page (#10779)
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
import { Logo1Icon } from '@blocksuite/icons/rc';
|
import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||||
|
import clsx from 'clsx';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { authHeaderWrapper } from './share.css';
|
import { authHeaderWrapper } from './share.css';
|
||||||
|
|
||||||
export const AuthHeader: FC<{
|
export const AuthHeader: FC<{
|
||||||
title: string;
|
title: string;
|
||||||
subTitle?: string;
|
subTitle?: string;
|
||||||
}> = ({ title, subTitle }) => {
|
className?: string;
|
||||||
|
}> = ({ title, subTitle, className }) => {
|
||||||
return (
|
return (
|
||||||
<div className={authHeaderWrapper}>
|
<div className={clsx(authHeaderWrapper, className)}>
|
||||||
<p>
|
<p>
|
||||||
<Logo1Icon className="logo" />
|
<Logo1Icon className="logo" />
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export const SignIn = ({
|
|||||||
(status: AuthSessionStatus) => {
|
(status: AuthSessionStatus) => {
|
||||||
if (status === 'authenticated') {
|
if (status === 'authenticated') {
|
||||||
if (redirectUrl) {
|
if (redirectUrl) {
|
||||||
|
if (redirectUrl.toUpperCase() === 'CLOSE_POPUP') {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
navigate(redirectUrl, {
|
navigate(redirectUrl, {
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Button } from '@affine/component';
|
import { Button } from '@affine/component';
|
||||||
|
import { AuthHeader } from '@affine/component/auth-components';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
|
||||||
import { useWorkspaceName } from '@affine/core/components/hooks/use-workspace-info';
|
import { useWorkspaceName } from '@affine/core/components/hooks/use-workspace-info';
|
||||||
import { WorkspaceSelector } from '@affine/core/components/workspace-selector';
|
import { WorkspaceSelector } from '@affine/core/components/workspace-selector';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService, ServerService } from '@affine/core/modules/cloud';
|
||||||
import {
|
import {
|
||||||
type ClipperInput,
|
type ClipperInput,
|
||||||
ImportClipperService,
|
ImportClipperService,
|
||||||
@@ -16,7 +16,7 @@ import { useI18n } from '@affine/i18n';
|
|||||||
import { AllDocsIcon } from '@blocksuite/icons/rc';
|
import { AllDocsIcon } from '@blocksuite/icons/rc';
|
||||||
import { LiveData, useLiveData, useService } from '@toeverything/infra';
|
import { LiveData, useLiveData, useService } from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import * as styles from './style.css';
|
import * as styles from './style.css';
|
||||||
|
|
||||||
@@ -36,7 +36,6 @@ export const Component = () => {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const session = useService(AuthService).session;
|
const session = useService(AuthService).session;
|
||||||
const notLogin = useLiveData(session.status$) === 'unauthenticated';
|
const notLogin = useLiveData(session.status$) === 'unauthenticated';
|
||||||
const isSessionRevalidating = useLiveData(session.isRevalidating$);
|
|
||||||
|
|
||||||
const [importing, setImporting] = useState(false);
|
const [importing, setImporting] = useState(false);
|
||||||
const [importingError, setImportingError] = useState<any>(null);
|
const [importingError, setImportingError] = useState<any>(null);
|
||||||
@@ -44,16 +43,22 @@ export const Component = () => {
|
|||||||
const [clipperInputSnapshot, setClipperInputSnapshot] =
|
const [clipperInputSnapshot, setClipperInputSnapshot] =
|
||||||
useState<ClipperInput | null>(null);
|
useState<ClipperInput | null>(null);
|
||||||
const isMissingInput = !clipperInputSnapshot;
|
const isMissingInput = !clipperInputSnapshot;
|
||||||
|
const workspaceStrategy = clipperInputSnapshot?.workspace ?? 'select-by-user';
|
||||||
|
const serverService = useService(ServerService);
|
||||||
const workspacesService = useService(WorkspacesService);
|
const workspacesService = useService(WorkspacesService);
|
||||||
|
const serverConfig = useLiveData(serverService.server.config$);
|
||||||
const workspaces = useLiveData(workspacesService.list.workspaces$);
|
const workspaces = useLiveData(workspacesService.list.workspaces$);
|
||||||
const [rawSelectedWorkspace, setSelectedWorkspace] =
|
const [rawSelectedWorkspace, setSelectedWorkspace] =
|
||||||
useState<WorkspaceMetadata | null>(null);
|
useState<WorkspaceMetadata | null>(null);
|
||||||
|
const [lastOpenedWorkspaceId] = useState(() =>
|
||||||
|
localStorage.getItem('last_workspace_id')
|
||||||
|
);
|
||||||
const selectedWorkspace =
|
const selectedWorkspace =
|
||||||
rawSelectedWorkspace ??
|
rawSelectedWorkspace ??
|
||||||
|
workspaces.find(w => w.id === lastOpenedWorkspaceId) ??
|
||||||
workspaces.find(w => w.flavour !== 'local') ??
|
workspaces.find(w => w.flavour !== 'local') ??
|
||||||
workspaces.at(0);
|
workspaces.at(0);
|
||||||
const selectedWorkspaceName = useWorkspaceName(selectedWorkspace);
|
const selectedWorkspaceName = useWorkspaceName(selectedWorkspace);
|
||||||
const { jumpToSignIn } = useNavigateHelper();
|
|
||||||
|
|
||||||
const noWorkspace = workspaces.length === 0;
|
const noWorkspace = workspaces.length === 0;
|
||||||
|
|
||||||
@@ -65,12 +70,6 @@ export const Component = () => {
|
|||||||
session.revalidate();
|
session.revalidate();
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isSessionRevalidating && notLogin) {
|
|
||||||
jumpToSignIn('/clipper/import');
|
|
||||||
}
|
|
||||||
}, [isSessionRevalidating, jumpToSignIn, notLogin]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!clipperInputSnapshot) {
|
if (!clipperInputSnapshot) {
|
||||||
setClipperInputSnapshot(clipperInput);
|
setClipperInputSnapshot(clipperInput);
|
||||||
@@ -99,6 +98,9 @@ export const Component = () => {
|
|||||||
selectedWorkspace,
|
selectedWorkspace,
|
||||||
clipperInputSnapshot
|
clipperInputSnapshot
|
||||||
);
|
);
|
||||||
|
window.postMessage({
|
||||||
|
type: 'affine-clipper:import:success',
|
||||||
|
});
|
||||||
window.close();
|
window.close();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setImportingError(err);
|
setImportingError(err);
|
||||||
@@ -119,6 +121,9 @@ export const Component = () => {
|
|||||||
'Workspace',
|
'Workspace',
|
||||||
clipperInputSnapshot
|
clipperInputSnapshot
|
||||||
);
|
);
|
||||||
|
window.postMessage({
|
||||||
|
type: 'affine-clipper:import:success',
|
||||||
|
});
|
||||||
window.close();
|
window.close();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setImportingError(err);
|
setImportingError(err);
|
||||||
@@ -127,8 +132,70 @@ export const Component = () => {
|
|||||||
}
|
}
|
||||||
}, [clipperInputSnapshot, importClipperService]);
|
}, [clipperInputSnapshot, importClipperService]);
|
||||||
|
|
||||||
|
const handleClickSignIn = useCallback(() => {
|
||||||
|
window.open(
|
||||||
|
`/sign-in?redirect_uri=${encodeURIComponent('CLOSE_POPUP')}`,
|
||||||
|
'_blank',
|
||||||
|
'popup'
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const autoImportTriggered = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMissingInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// use ref to avoid multiple auto import
|
||||||
|
// and make sure the following code only runs once
|
||||||
|
if (autoImportTriggered.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
autoImportTriggered.current = true;
|
||||||
|
|
||||||
|
// if not login, we don't auto import
|
||||||
|
if (notLogin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the workspace strategy is last-open-workspace, we automatically click the import button
|
||||||
|
if (
|
||||||
|
workspaceStrategy === 'last-open-workspace' &&
|
||||||
|
selectedWorkspace?.id === lastOpenedWorkspaceId
|
||||||
|
) {
|
||||||
|
handleImportToSelectedWorkspace();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
workspaceStrategy,
|
||||||
|
selectedWorkspace,
|
||||||
|
handleImportToSelectedWorkspace,
|
||||||
|
lastOpenedWorkspaceId,
|
||||||
|
isMissingInput,
|
||||||
|
notLogin,
|
||||||
|
]);
|
||||||
|
|
||||||
const disabled = isMissingInput || importing || notLogin;
|
const disabled = isMissingInput || importing || notLogin;
|
||||||
|
|
||||||
|
if (notLogin) {
|
||||||
|
// not login
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<AuthHeader
|
||||||
|
className={styles.authHeader}
|
||||||
|
title={t['com.affine.auth.sign.in']()}
|
||||||
|
subTitle={serverConfig.serverName}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={styles.mainButton}
|
||||||
|
variant="primary"
|
||||||
|
onClick={handleClickSignIn}
|
||||||
|
>
|
||||||
|
{t['com.affine.auth.sign.in']()}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<AllDocsIcon className={styles.mainIcon} />
|
<AllDocsIcon className={styles.mainIcon} />
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ export const container = style({
|
|||||||
padding: '16px',
|
padding: '16px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const authHeader = style({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
export const buttonContainer = style({
|
export const buttonContainer = style({
|
||||||
paddingTop: '16px',
|
paddingTop: '16px',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface ClipperInput {
|
|||||||
contentMarkdown: string;
|
contentMarkdown: string;
|
||||||
contentHtml: string;
|
contentHtml: string;
|
||||||
attachments: Record<string, Blob>;
|
attachments: Record<string, Blob>;
|
||||||
|
workspace?: 'select-by-user' | 'last-open-workspace';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImportClipperService extends Service {
|
export class ImportClipperService extends Service {
|
||||||
@@ -37,6 +38,7 @@ export class ImportClipperService extends Service {
|
|||||||
const docsService = workspace.scope.get(DocsService);
|
const docsService = workspace.scope.get(DocsService);
|
||||||
if (docId) {
|
if (docId) {
|
||||||
// only support page mode for now
|
// only support page mode for now
|
||||||
|
await docsService.changeDocTitle(docId, clipperInput.title);
|
||||||
docsService.list.setPrimaryMode(docId, 'page');
|
docsService.list.setPrimaryMode(docId, 'page');
|
||||||
workspace.engine.doc.addPriority(workspace.id, 100);
|
workspace.engine.doc.addPriority(workspace.id, 100);
|
||||||
workspace.engine.doc.addPriority(docId, 100);
|
workspace.engine.doc.addPriority(docId, 100);
|
||||||
|
|||||||
Reference in New Issue
Block a user