diff --git a/packages/frontend/core/src/desktop/pages/workspace/index.tsx b/packages/frontend/core/src/desktop/pages/workspace/index.tsx index 4e2effc92e..bfe0cf6311 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/index.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/index.tsx @@ -4,8 +4,8 @@ import { workbenchRoutes } from '@affine/core/desktop/workbench-router'; import { DefaultServerService, ServersService, - WorkspaceServerService, } from '@affine/core/modules/cloud'; +import { GlobalDialogService } from '@affine/core/modules/dialogs'; import { DndService } from '@affine/core/modules/dnd/services'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { @@ -33,7 +33,6 @@ import { AffineErrorBoundary } from '../../../components/affine/affine-error-bou import { WorkbenchRoot } from '../../../modules/workbench'; import { AppContainer } from '../../components/app-container'; import { PageNotFound } from '../404'; -import { SignIn } from '../auth/sign-in'; import { WorkspaceLayout } from './layouts/workspace-layout'; import { SharePage } from './share/share-page'; @@ -53,8 +52,18 @@ declare global { } export const Component = (): ReactElement => { - const { workspacesService } = useServices({ + const { + workspacesService, + globalDialogService, + serversService, + defaultServerService, + globalContextService, + } = useServices({ WorkspacesService, + GlobalDialogService, + ServersService, + DefaultServerService, + GlobalContextService, }); const params = useParams(); @@ -88,11 +97,6 @@ export const Component = (): ReactElement => { const [workspaceNotFound, setWorkspaceNotFound] = useState(false); const listLoading = useLiveData(workspacesService.list.isRevalidating$); const workspaces = useLiveData(workspacesService.list.workspaces$); - const serversService = useService(ServersService); - const serverFromSearchParams = searchParams.get('server'); - const serverNotFound = serverFromSearchParams - ? serversService.getServerByBaseUrl(serverFromSearchParams) - : null; const meta = useMemo(() => { return workspaces.find(({ id }) => id === params.workspaceId); }, [workspaces, params.workspaceId]); @@ -125,36 +129,87 @@ export const Component = (): ReactElement => { return; }, [listLoading, meta, workspaceNotFound, workspacesService]); - if (workspaceNotFound) { - if (BUILD_CONFIG.isElectron && serverNotFound) { - const url = new URL(window.location.href); - url.searchParams.delete('server'); - const redirectUrl = url.toString().replace(window.location.origin, ''); - return ( - - - - ); + // server search params + const serverFromSearchParams = useLiveData( + searchParams.has('server') + ? serversService.serverByBaseUrl$(searchParams.get('server') as string) + : undefined + ); + // server from workspace + const serverFromWorkspace = useLiveData( + meta?.flavour && meta.flavour !== 'local' + ? serversService.server$(meta?.flavour) + : undefined + ); + const server = serverFromWorkspace ?? serverFromSearchParams; + + useEffect(() => { + if (server) { + globalContextService.globalContext.serverId.set(server.id); + return () => { + globalContextService.globalContext.serverId.set( + defaultServerService.server.id + ); + }; } + return; + }, [ + defaultServerService.server.id, + globalContextService.globalContext.serverId, + server, + ]); + + // if server is not found, and we have server in search params, we should show add selfhosted dialog + const needAddSelfhosted = server === undefined && searchParams.has('server'); + // use ref to avoid useEffect trigger twice + const addSelfhostedDialogOpened = useRef(false); + + useEffect(() => { + if (addSelfhostedDialogOpened.current) { + return; + } + addSelfhostedDialogOpened.current = true; + if (BUILD_CONFIG.isElectron && needAddSelfhosted) { + globalDialogService.open('sign-in', { + server: searchParams.get('server') as string, + }); + } + return; + }, [ + globalDialogService, + needAddSelfhosted, + searchParams, + serverFromSearchParams, + ]); + + if (workspaceNotFound) { if (detailDocRoute) { return ( - + + + ); } return ( - - - + + + + + ); } if (!meta) { return ; } - return ; + return ( + + + + ); }; const DNDContextProvider = ({ children }: PropsWithChildren) => { @@ -171,15 +226,12 @@ const DNDContextProvider = ({ children }: PropsWithChildren) => { }; const WorkspacePage = ({ meta }: { meta: WorkspaceMetadata }) => { - const { workspacesService, globalContextService, defaultServerService } = - useServices({ - WorkspacesService, - GlobalContextService, - DefaultServerService, - }); + const { workspacesService, globalContextService } = useServices({ + WorkspacesService, + GlobalContextService, + }); const [workspace, setWorkspace] = useState(null); - const workspaceServer = workspace?.scope.get(WorkspaceServerService).server; useLayoutEffect(() => { const ref = workspacesService.open({ metadata: meta }); @@ -238,30 +290,17 @@ const WorkspacePage = ({ meta }: { meta: WorkspaceMetadata }) => { }; localStorage.setItem('last_workspace_id', workspace.id); globalContextService.globalContext.workspaceId.set(workspace.id); - if (workspaceServer) { - globalContextService.globalContext.serverId.set(workspaceServer.id); - } globalContextService.globalContext.workspaceFlavour.set( workspace.flavour ); return () => { window.currentWorkspace = undefined; globalContextService.globalContext.workspaceId.set(null); - if (workspaceServer) { - globalContextService.globalContext.serverId.set( - defaultServerService.server.id - ); - } globalContextService.globalContext.workspaceFlavour.set(null); }; } return; - }, [ - defaultServerService.server.id, - globalContextService, - workspace, - workspaceServer, - ]); + }, [globalContextService, workspace]); if (!workspace) { return null; // skip this, workspace will be set in layout effect @@ -269,27 +308,23 @@ const WorkspacePage = ({ meta }: { meta: WorkspaceMetadata }) => { if (!isRootDocReady) { return ( - - - - - - + + + + ); } return ( - - - - - - - - - - + + + + + + + + ); }; diff --git a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx index c53adc0873..b695ac67c8 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx @@ -9,6 +9,7 @@ import { AuthService, FetchService, GraphQLService, + ServerService, } from '@affine/core/modules/cloud'; import { type Doc, DocsService } from '@affine/core/modules/doc'; import { @@ -64,8 +65,9 @@ export const SharePage = ({ workspaceId: string; docId: string; }) => { - const { shareReaderService } = useServices({ + const { shareReaderService, serverService } = useServices({ ShareReaderService, + ServerService, }); const isLoading = useLiveData(shareReaderService.reader.isLoading$); @@ -104,8 +106,12 @@ export const SharePage = ({ }, [location.search]); useEffect(() => { - shareReaderService.reader.loadShare({ workspaceId, docId }); - }, [shareReaderService, docId, workspaceId]); + shareReaderService.reader.loadShare({ + serverId: serverService.server.id, + workspaceId, + docId, + }); + }, [shareReaderService, docId, workspaceId, serverService.server.id]); let element: ReactNode = null; if (isLoading) { diff --git a/packages/frontend/core/src/modules/cloud/services/servers.ts b/packages/frontend/core/src/modules/cloud/services/servers.ts index e0e3084947..9fcbd5c172 100644 --- a/packages/frontend/core/src/modules/cloud/services/servers.ts +++ b/packages/frontend/core/src/modules/cloud/services/servers.ts @@ -55,6 +55,12 @@ export class ServersService extends Service { ); } + serverByBaseUrl$(url: string) { + return this.servers$.map(servers => + servers.find(server => server.baseUrl === url) + ); + } + private readonly serverPool = new ObjectPool({ onDelete(obj) { obj.dispose(); diff --git a/packages/frontend/core/src/modules/share-doc/entities/share-reader.ts b/packages/frontend/core/src/modules/share-doc/entities/share-reader.ts index 82c94db320..b9740e4cb0 100644 --- a/packages/frontend/core/src/modules/share-doc/entities/share-reader.ts +++ b/packages/frontend/core/src/modules/share-doc/entities/share-reader.ts @@ -31,8 +31,18 @@ export class ShareReader extends Entity { loadShare = effect( switchMap( - ({ workspaceId, docId }: { workspaceId: string; docId: string }) => { - return fromPromise(this.store.loadShare(workspaceId, docId)).pipe( + ({ + serverId, + workspaceId, + docId, + }: { + serverId: string; + workspaceId: string; + docId: string; + }) => { + return fromPromise( + this.store.loadShare(serverId, workspaceId, docId) + ).pipe( mergeMap(data => { if (!data) { this.data$.next(null); diff --git a/packages/frontend/core/src/modules/share-doc/index.ts b/packages/frontend/core/src/modules/share-doc/index.ts index c1a06f4bbc..816c6a8c4e 100644 --- a/packages/frontend/core/src/modules/share-doc/index.ts +++ b/packages/frontend/core/src/modules/share-doc/index.ts @@ -5,7 +5,7 @@ export { ShareReaderService } from './services/share-reader'; import { type Framework } from '@toeverything/infra'; -import { RawFetchProvider, WorkspaceServerService } from '../cloud'; +import { ServersService, WorkspaceServerService } from '../cloud'; import { DocScope, DocService } from '../doc'; import { WorkspaceLocalCache, @@ -26,7 +26,7 @@ export function configureShareDocsModule(framework: Framework) { framework .service(ShareReaderService) .entity(ShareReader, [ShareReaderStore]) - .store(ShareReaderStore, [RawFetchProvider]) + .store(ShareReaderStore, [ServersService]) .scope(WorkspaceScope) .service(ShareDocsListService, [WorkspaceService]) .store(ShareDocsStore, [WorkspaceServerService]) diff --git a/packages/frontend/core/src/modules/share-doc/stores/share-reader.ts b/packages/frontend/core/src/modules/share-doc/stores/share-reader.ts index ca8b6a3147..3159d4cef4 100644 --- a/packages/frontend/core/src/modules/share-doc/stores/share-reader.ts +++ b/packages/frontend/core/src/modules/share-doc/stores/share-reader.ts @@ -2,36 +2,31 @@ import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import type { DocMode } from '@blocksuite/affine/blocks'; import { Store } from '@toeverything/infra'; -import type { RawFetchProvider } from '../../cloud'; +import type { ServersService } from '../../cloud'; import { isBackendError } from '../../cloud'; export class ShareReaderStore extends Store { - constructor(private readonly rawFetch?: RawFetchProvider) { + constructor(private readonly serversService: ServersService) { super(); } - async loadShare(workspaceId: string, docId: string) { - if (!this.rawFetch) { - throw new Error('No Fetch Service'); + async loadShare(serverId: string, workspaceId: string, docId: string) { + const server = this.serversService.server$(serverId).value; + if (!server) { + throw new Error(`Server ${serverId} not found`); } try { - const docResponse = await this.rawFetch.fetch( + const docResponse = await server.fetch( `/api/workspaces/${workspaceId}/docs/${docId}` ); - if (docResponse.status !== 200) { - throw new Error('Failed to fetch workspace'); - } const publishMode = docResponse.headers.get( 'publish-mode' ) as DocMode | null; const docBinary = await docResponse.arrayBuffer(); - const workspaceResponse = await this.rawFetch.fetch( + const workspaceResponse = await server.fetch( `/api/workspaces/${workspaceId}/docs/${workspaceId}` ); - if (workspaceResponse.status !== 200) { - throw new Error('Failed to fetch workspace'); - } const workspaceBinary = await workspaceResponse.arrayBuffer(); return {