From 1e11f727fd38894610a86bad4a73ccf060bb87f7 Mon Sep 17 00:00:00 2001 From: Himself65 Date: Sat, 4 Mar 2023 23:07:13 -0600 Subject: [PATCH] fix: avoid avatar flickering (#1319) --- .../affine/workspace-setting-detail/index.tsx | 6 ++- .../panel/general/index.tsx | 15 +++----- .../pure/workspace-avatar/index.tsx | 9 ++--- .../hooks/use-blocksuite-workspace-avatar.ts | 32 ---------------- .../use-blocksuite-workspace-blob-url.ts | 37 +++++++++++++++++++ apps/web/src/hooks/use-system-theme.ts | 35 ------------------ apps/web/src/hooks/use-workspace.ts | 12 ------ apps/web/src/plugins/affine/fetcher.ts | 18 +++++++++ 8 files changed, 69 insertions(+), 95 deletions(-) delete mode 100644 apps/web/src/hooks/use-blocksuite-workspace-avatar.ts create mode 100644 apps/web/src/hooks/use-blocksuite-workspace-blob-url.ts delete mode 100644 apps/web/src/hooks/use-system-theme.ts delete mode 100644 apps/web/src/hooks/use-workspace.ts 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',