From 4977055a2ed1c067830f81dc8ea246c4d3289df4 Mon Sep 17 00:00:00 2001 From: EYHN Date: Tue, 5 Nov 2024 11:16:00 +0000 Subject: [PATCH] fix(ios): fix mobile blob storage (#8702) --- packages/frontend/apps/ios/src/fetch.ts | 25 +++++++++---------- .../affine/page-history-modal/data.ts | 22 ++++++++++------ .../pages/workspace/share/share-page.tsx | 13 +++++++--- .../modules/workspace-engine/impls/cloud.ts | 12 +++++++-- .../impls/engine/blob-cloud.ts | 25 ++++++++++--------- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/packages/frontend/apps/ios/src/fetch.ts b/packages/frontend/apps/ios/src/fetch.ts index adf9e785bc..bd4dc6e4fa 100644 --- a/packages/frontend/apps/ios/src/fetch.ts +++ b/packages/frontend/apps/ios/src/fetch.ts @@ -141,37 +141,36 @@ export function configureFetchProvider(framework: Framework) { undefined, optionHeaders['Content-Type'] || optionHeaders['content-type'] ); + const accept = optionHeaders['Accept'] || optionHeaders['accept']; const nativeResponse = await CapacitorHttp.request({ url: request.url, method: method, data: requestData, dataType: type as any, responseType: - (optionHeaders['Accept'] || optionHeaders['accept']) === - 'application/octet-stream' - ? 'arraybuffer' - : undefined, + accept === 'application/octet-stream' ? 'arraybuffer' : undefined, headers: Object.assign(Object.assign({}, headers), optionHeaders), }); const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; - let data = ( - contentType === null || contentType === void 0 - ? void 0 - : contentType.startsWith('application/json') - ) - ? JSON.stringify(nativeResponse.data) - : contentType === 'application/octet-stream' + let data = + accept === 'application/octet-stream' ? base64ToUint8Array(nativeResponse.data) - : nativeResponse.data; + : contentType === null || contentType === void 0 + ? void 0 + : contentType.startsWith('application/json') + ? JSON.stringify(nativeResponse.data) + : contentType === 'application/octet-stream' + ? base64ToUint8Array(nativeResponse.data) + : nativeResponse.data; // use null data for 204 No Content HTTP response if (nativeResponse.status === 204) { data = null; } // intercept & parse response before returning - const response = new Response(data, { + const response = new Response(new Blob([data], { type: contentType }), { headers: nativeResponse.headers, status: nativeResponse.status, }); diff --git a/packages/frontend/core/src/components/affine/page-history-modal/data.ts b/packages/frontend/core/src/components/affine/page-history-modal/data.ts index 13562d1f12..bcfec57606 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/data.ts +++ b/packages/frontend/core/src/components/affine/page-history-modal/data.ts @@ -1,6 +1,6 @@ import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-page-meta'; import { useDocCollectionPage } from '@affine/core/components/hooks/use-block-suite-workspace-page'; -import { FetchService } from '@affine/core/modules/cloud'; +import { FetchService, GraphQLService } from '@affine/core/modules/cloud'; import { DebugLogger } from '@affine/debug'; import type { ListHistoryQuery } from '@affine/graphql'; import { listHistoryQuery, recoverDocMutation } from '@affine/graphql'; @@ -102,11 +102,16 @@ const docCollectionMap = new Map(); // assume the workspace is a cloud workspace since the history feature is only enabled for cloud workspace const getOrCreateShellWorkspace = ( workspaceId: string, - fetchService: FetchService + fetchService: FetchService, + graphQLService: GraphQLService ) => { let docCollection = docCollectionMap.get(workspaceId); if (!docCollection) { - const blobStorage = new CloudBlobStorage(workspaceId, fetchService); + const blobStorage = new CloudBlobStorage( + workspaceId, + fetchService, + graphQLService + ); docCollection = new DocCollection({ id: workspaceId, blobSources: { @@ -144,6 +149,7 @@ export const useSnapshotPage = ( ts?: string ) => { const fetchService = useService(FetchService); + const graphQLService = useService(GraphQLService); const snapshot = usePageHistory(docCollection.id, pageDocId, ts); const page = useMemo(() => { if (!ts) { @@ -152,7 +158,8 @@ export const useSnapshotPage = ( const pageId = pageDocId + '-' + ts; const historyShellWorkspace = getOrCreateShellWorkspace( docCollection.id, - fetchService + fetchService, + graphQLService ); let page = historyShellWorkspace.getDoc(pageId); if (!page && snapshot) { @@ -167,18 +174,19 @@ export const useSnapshotPage = ( }); // must load before applyUpdate } return page ?? undefined; - }, [ts, pageDocId, docCollection.id, fetchService, snapshot]); + }, [ts, pageDocId, docCollection.id, fetchService, graphQLService, snapshot]); useEffect(() => { const historyShellWorkspace = getOrCreateShellWorkspace( docCollection.id, - fetchService + fetchService, + graphQLService ); // apply the rootdoc's update to the current workspace // this makes sure the page reference links are not deleted ones in the preview const update = encodeStateAsUpdate(docCollection.doc); applyUpdate(historyShellWorkspace.doc, update); - }, [docCollection, fetchService]); + }, [docCollection, fetchService, graphQLService]); return page; }; 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 d2de55dada..fbb4398d34 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 @@ -6,7 +6,11 @@ import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-he import { PageDetailEditor } from '@affine/core/components/page-detail-editor'; import { SharePageNotFoundError } from '@affine/core/components/share-page-not-found-error'; import { AppContainer } from '@affine/core/desktop/components/app-container'; -import { AuthService, FetchService } from '@affine/core/modules/cloud'; +import { + AuthService, + FetchService, + GraphQLService, +} from '@affine/core/modules/cloud'; import { type Editor, type EditorSelector, @@ -147,7 +151,7 @@ const SharePageInner = ({ }) => { const workspacesService = useService(WorkspacesService); const fetchService = useService(FetchService); - + const graphQLService = useService(GraphQLService); const [workspace, setWorkspace] = useState(null); const [page, setPage] = useState(null); const [editor, setEditor] = useState(null); @@ -181,7 +185,9 @@ const SharePageInner = ({ return EmptyBlobStorage; }, getRemoteBlobStorages() { - return [new CloudBlobStorage(workspaceId, fetchService)]; + return [ + new CloudBlobStorage(workspaceId, fetchService, graphQLService), + ]; }, } ); @@ -221,6 +227,7 @@ const SharePageInner = ({ workspaceBinary, docBinary, fetchService, + graphQLService, ]); const pageTitle = useLiveData(page?.title$); diff --git a/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts b/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts index 22e60dcbd9..dab92dc960 100644 --- a/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts +++ b/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts @@ -237,7 +237,11 @@ export class CloudWorkspaceFlavourProviderService return localBlob; } - const cloudBlob = new CloudBlobStorage(id, this.fetchService); + const cloudBlob = new CloudBlobStorage( + id, + this.fetchService, + this.graphqlService + ); return await cloudBlob.get(blob); } getEngineProvider(workspaceId: string): WorkspaceEngineProvider { @@ -259,7 +263,11 @@ export class CloudWorkspaceFlavourProviderService }, getRemoteBlobStorages: () => { return [ - new CloudBlobStorage(workspaceId, this.fetchService), + new CloudBlobStorage( + workspaceId, + this.fetchService, + this.graphqlService + ), new StaticBlobStorage(), ]; }, diff --git a/packages/frontend/core/src/modules/workspace-engine/impls/engine/blob-cloud.ts b/packages/frontend/core/src/modules/workspace-engine/impls/engine/blob-cloud.ts index f67e939309..3eacf5dbfb 100644 --- a/packages/frontend/core/src/modules/workspace-engine/impls/engine/blob-cloud.ts +++ b/packages/frontend/core/src/modules/workspace-engine/impls/engine/blob-cloud.ts @@ -1,7 +1,6 @@ -import type { FetchService } from '@affine/core/modules/cloud'; +import type { FetchService, GraphQLService } from '@affine/core/modules/cloud'; import { deleteBlobMutation, - fetcher, listBlobsQuery, setBlobMutation, UserFriendlyError, @@ -14,7 +13,8 @@ import { bufferToBlob } from '../../utils/buffer-to-blob'; export class CloudBlobStorage implements BlobStorage { constructor( private readonly workspaceId: string, - private readonly fetchService: FetchService + private readonly fetchService: FetchService, + private readonly gqlService: GraphQLService ) {} name = 'cloud'; @@ -46,13 +46,14 @@ export class CloudBlobStorage implements BlobStorage { async set(key: string, value: Blob) { // set blob will check blob size & quota - return await fetcher({ - query: setBlobMutation, - variables: { - workspaceId: this.workspaceId, - blob: new File([value], key), - }, - }) + return await this.gqlService + .gql({ + query: setBlobMutation, + variables: { + workspaceId: this.workspaceId, + blob: new File([value], key), + }, + }) .then(res => res.setBlob) .catch(err => { const error = UserFriendlyError.fromAnyError(err); @@ -65,7 +66,7 @@ export class CloudBlobStorage implements BlobStorage { } async delete(key: string) { - await fetcher({ + await this.gqlService.gql({ query: deleteBlobMutation, variables: { workspaceId: key, @@ -75,7 +76,7 @@ export class CloudBlobStorage implements BlobStorage { } async list() { - const result = await fetcher({ + const result = await this.gqlService.gql({ query: listBlobsQuery, variables: { workspaceId: this.workspaceId,