fix: avoid avatar flickering (#1319)

This commit is contained in:
Himself65
2023-03-04 23:07:13 -06:00
committed by DarkSky
parent f63d54a9de
commit 1e11f727fd
8 changed files with 69 additions and 95 deletions

View File

@@ -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<
/>
</StyledTabButtonWrapper>
<StyledSettingContent>
<Component {...props} key={currentTab} data-tab-ui={currentTab} />
{/* todo: add skeleton */}
<Suspense fallback="loading panel...">
<Component {...props} key={currentTab} data-tab-ui={currentTab} />
</Suspense>
</StyledSettingContent>
</StyledSettingContainer>
);

View File

@@ -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<PanelProps> = ({
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 (
<>
<StyledRow>
@@ -59,7 +54,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
{isOwner ? (
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange}
fileChange={update}
data-testid="upload-avatar"
>
<>

View File

@@ -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<BlockSuiteWorkspaceAvatar> = ({
style,
...props
}) => {
const [avatar] = useBlockSuiteWorkspaceAvatar(workspace);
const avatarURL = useWorkspaceBlobImage(avatar ?? null, workspace);
const [avatar] = useBlockSuiteWorkspaceBlobUrl(workspace);
return (
<Avatar
{...props}
size={size}
name={workspace.meta.name ?? UNTITLED_WORKSPACE_NAME}
avatar_url={avatarURL ?? ''}
avatar_url={avatar ?? ''}
style={style}
/>
);

View File

@@ -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<string | undefined>(
() => 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;
}

View File

@@ -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;
}

View File

@@ -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<Theme>(
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', [])
);
}

View File

@@ -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]
);
}

View File

@@ -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',