diff --git a/apps/web/src/components/affine/workspace-setting-detail/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/index.tsx
index 1010183e7..675622a68 100644
--- a/apps/web/src/components/affine/workspace-setting-detail/index.tsx
+++ b/apps/web/src/components/affine/workspace-setting-detail/index.tsx
@@ -1,6 +1,7 @@
import { useTranslation } from '@affine/i18n';
import React, {
MouseEvent,
+ Suspense,
useCallback,
useEffect,
useMemo,
@@ -152,7 +153,10 @@ export const WorkspaceSettingDetail: React.FC<
/>
-
+ {/* todo: add skeleton */}
+
+
+
);
diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx
index 724f3d716..9babd8237 100644
--- a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx
+++ b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx
@@ -1,9 +1,9 @@
import { Button, FlexWrapper, MuiFade } from '@affine/component';
import { useTranslation } from '@affine/i18n';
-import { assertExists } from '@blocksuite/store';
import React, { useState } from 'react';
import { useIsWorkspaceOwner } from '../../../../../hooks/affine/use-is-workspace-owner';
+import { useBlockSuiteWorkspaceBlobUrl } from '../../../../../hooks/use-blocksuite-workspace-blob-url';
import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name';
import { RemWorkspaceFlavour } from '../../../../../shared';
import { Upload } from '../../../../pure/file-upload';
@@ -43,14 +43,9 @@ export const GeneralPanel: React.FC = ({
setName(name);
};
- const fileChange = async (file: File) => {
- const blob = new Blob([file], { type: file.type });
- const blobs = await workspace.blockSuiteWorkspace.blobs;
- assertExists(blobs);
- const blobId = await blobs.set(blob);
- workspace.blockSuiteWorkspace.meta.setAvatar(blobId);
- };
-
+ const [, update] = useBlockSuiteWorkspaceBlobUrl(
+ workspace.blockSuiteWorkspace
+ );
return (
<>
@@ -59,7 +54,7 @@ export const GeneralPanel: React.FC = ({
{isOwner ? (
<>
diff --git a/apps/web/src/components/pure/workspace-avatar/index.tsx b/apps/web/src/components/pure/workspace-avatar/index.tsx
index 03d7da77f..95ef60359 100644
--- a/apps/web/src/components/pure/workspace-avatar/index.tsx
+++ b/apps/web/src/components/pure/workspace-avatar/index.tsx
@@ -1,8 +1,7 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env';
import React from 'react';
-import { useBlockSuiteWorkspaceAvatar } from '../../../hooks/use-blocksuite-workspace-avatar';
-import { useWorkspaceBlobImage } from '../../../hooks/use-workspace-blob';
+import { useBlockSuiteWorkspaceBlobUrl } from '../../../hooks/use-blocksuite-workspace-blob-url';
import { BlockSuiteWorkspace, RemWorkspace } from '../../../shared';
import { stringToColour } from '../../../utils';
@@ -88,14 +87,14 @@ export const BlockSuiteWorkspaceAvatar: React.FC = ({
style,
...props
}) => {
- const [avatar] = useBlockSuiteWorkspaceAvatar(workspace);
- const avatarURL = useWorkspaceBlobImage(avatar ?? null, workspace);
+ const [avatar] = useBlockSuiteWorkspaceBlobUrl(workspace);
+
return (
);
diff --git a/apps/web/src/hooks/use-blocksuite-workspace-avatar.ts b/apps/web/src/hooks/use-blocksuite-workspace-avatar.ts
deleted file mode 100644
index adef40406..000000000
--- a/apps/web/src/hooks/use-blocksuite-workspace-avatar.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { assertExists } from '@blocksuite/store';
-import { useCallback, useEffect, useState } from 'react';
-
-import { BlockSuiteWorkspace } from '../shared';
-
-export function useBlockSuiteWorkspaceAvatar(
- blockSuiteWorkspace: BlockSuiteWorkspace | null
-) {
- const [avatar, set] = useState(
- () => blockSuiteWorkspace?.meta.avatar
- );
- useEffect(() => {
- if (blockSuiteWorkspace) {
- set(blockSuiteWorkspace.meta.avatar);
- const dispose = blockSuiteWorkspace.meta.commonFieldsUpdated.on(() => {
- set(blockSuiteWorkspace.meta.avatar);
- });
- return () => {
- dispose.dispose();
- };
- }
- }, [blockSuiteWorkspace]);
- const setAvatar = useCallback(
- (avatar: string) => {
- assertExists(blockSuiteWorkspace);
- blockSuiteWorkspace.meta.setAvatar(avatar);
- set(avatar);
- },
- [blockSuiteWorkspace]
- );
- return [avatar, setAvatar] as const;
-}
diff --git a/apps/web/src/hooks/use-blocksuite-workspace-blob-url.ts b/apps/web/src/hooks/use-blocksuite-workspace-blob-url.ts
new file mode 100644
index 000000000..996cc8c83
--- /dev/null
+++ b/apps/web/src/hooks/use-blocksuite-workspace-blob-url.ts
@@ -0,0 +1,37 @@
+import { assertExists } from '@blocksuite/store';
+import { useCallback } from 'react';
+import useSWR from 'swr';
+
+import { QueryKey } from '../plugins/affine/fetcher';
+import { BlockSuiteWorkspace } from '../shared';
+
+export function useBlockSuiteWorkspaceBlobUrl(
+ // todo: remove `null` from type
+ blockSuiteWorkspace: BlockSuiteWorkspace | null
+) {
+ const { data: avatar, mutate } = useSWR(
+ blockSuiteWorkspace
+ ? [
+ QueryKey.getImage,
+ blockSuiteWorkspace.room,
+ blockSuiteWorkspace.meta.avatar,
+ ]
+ : null,
+ {
+ fallbackData: null,
+ }
+ );
+ const setAvatar = useCallback(
+ async (file: File) => {
+ assertExists(blockSuiteWorkspace);
+ const blob = new Blob([file], { type: file.type });
+ const blobs = await blockSuiteWorkspace.blobs;
+ assertExists(blobs);
+ const blobId = await blobs.set(blob);
+ blockSuiteWorkspace.meta.setAvatar(blobId);
+ await mutate(blobId);
+ },
+ [blockSuiteWorkspace, mutate]
+ );
+ return [avatar ?? null, setAvatar] as const;
+}
diff --git a/apps/web/src/hooks/use-system-theme.ts b/apps/web/src/hooks/use-system-theme.ts
deleted file mode 100644
index ea95055e3..000000000
--- a/apps/web/src/hooks/use-system-theme.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Theme } from '@affine/component';
-import { useCallback, useSyncExternalStore } from 'react';
-
-const themeRef = {
- current: 'light',
- media: null,
-} as {
- current: Theme;
- media: MediaQueryList | null;
-};
-
-if (typeof window !== 'undefined') {
- themeRef.media = window.matchMedia('(prefers-color-scheme: light)');
-}
-
-export function useSystemTheme() {
- return useSyncExternalStore(
- useCallback(onStoreChange => {
- if (themeRef.media) {
- const media = themeRef.media;
- media.addEventListener('change', onStoreChange);
- return () => {
- media.addEventListener('change', onStoreChange);
- };
- }
- return () => {};
- }, []),
- useCallback(
- () =>
- themeRef.media ? (themeRef.media.matches ? 'light' : 'dark') : 'light',
- []
- ),
- useCallback(() => 'light', [])
- );
-}
diff --git a/apps/web/src/hooks/use-workspace.ts b/apps/web/src/hooks/use-workspace.ts
deleted file mode 100644
index 553eec423..000000000
--- a/apps/web/src/hooks/use-workspace.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useMemo } from 'react';
-
-import { RemWorkspace } from '../shared';
-import { useWorkspaces } from './use-workspaces';
-
-export function useWorkspace(workspaceId: string | null): RemWorkspace | null {
- const workspaces = useWorkspaces();
- return useMemo(
- () => workspaces.find(ws => ws.id === workspaceId) ?? null,
- [workspaces, workspaceId]
- );
-}
diff --git a/apps/web/src/plugins/affine/fetcher.ts b/apps/web/src/plugins/affine/fetcher.ts
index 16917ae52..797b180a6 100644
--- a/apps/web/src/plugins/affine/fetcher.ts
+++ b/apps/web/src/plugins/affine/fetcher.ts
@@ -1,3 +1,6 @@
+import { assertExists } from '@blocksuite/store';
+
+import { jotaiStore, workspacesAtom } from '../../atoms';
import { createAffineProviders } from '../../blocksuite';
import { Unreachable } from '../../components/affine/affine-error-eoundary';
import { AffineWorkspace, RemWorkspaceFlavour } from '../../shared';
@@ -34,6 +37,20 @@ export const fetcher = async (
workspace_id: query[1],
email: query[2],
});
+ } else if (query[0] === QueryKey.getImage) {
+ const workspaceId = query[1];
+ const key = query[2];
+ if (typeof key !== 'string') {
+ throw new TypeError('key must be a string');
+ }
+ const workspaces = await jotaiStore.get(workspacesAtom);
+ const workspace = workspaces.find(({ id }) => id === workspaceId);
+ assertExists(workspace);
+ const storage = await workspace.blockSuiteWorkspace.blobs;
+ if (!storage) {
+ return null;
+ }
+ return storage.get(key);
}
} else {
if (query === QueryKey.getWorkspaces) {
@@ -60,6 +77,7 @@ export const fetcher = async (
};
export const QueryKey = {
+ getImage: 'getImage',
getUser: 'getUser',
getWorkspaces: 'getWorkspaces',
downloadWorkspace: 'downloadWorkspace',