Skip to content

Commit

Permalink
fix(core): fix ui flashing (#7056)
Browse files Browse the repository at this point in the history
  • Loading branch information
EYHN committed May 27, 2024
1 parent 306cf2a commit b356ddb
Show file tree
Hide file tree
Showing 33 changed files with 541 additions and 400 deletions.
12 changes: 8 additions & 4 deletions packages/common/infra/src/modules/workspace/entities/profile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DebugLogger } from '@affine/debug';
import { isEqual } from 'lodash-es';
import { catchError, EMPTY, exhaustMap, mergeMap } from 'rxjs';

import { Entity } from '../../../framework';
Expand Down Expand Up @@ -54,7 +55,10 @@ export class WorkspaceProfile extends Entity<{ metadata: WorkspaceMetadata }> {
providers.find(p => p.flavour === this.props.metadata.flavour) ?? null;
}

private setCache(info: WorkspaceProfileInfo) {
private setProfile(info: WorkspaceProfileInfo) {
if (isEqual(this.profile$.value, info)) {
return;
}
this.cache.setProfileCache(this.props.metadata.id, info);
}

Expand All @@ -69,7 +73,7 @@ export class WorkspaceProfile extends Entity<{ metadata: WorkspaceMetadata }> {
).pipe(
mergeMap(info => {
if (info) {
this.setCache({ ...this.profile$.value, ...info });
this.setProfile({ ...this.profile$.value, ...info });
}
return EMPTY;
}),
Expand All @@ -86,11 +90,11 @@ export class WorkspaceProfile extends Entity<{ metadata: WorkspaceMetadata }> {
syncWithWorkspace(workspace: Workspace) {
workspace.name$.subscribe(name => {
const old = this.profile$.value;
this.setCache({ ...old, name: name ?? old?.name });
this.setProfile({ ...old, name: name ?? old?.name });
});
workspace.avatar$.subscribe(avatar => {
const old = this.profile$.value;
this.setCache({ ...old, avatar: avatar ?? old?.avatar });
this.setProfile({ ...old, avatar: avatar ?? old?.avatar });
});
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { CollaborationIcon, SettingsIcon } from '@blocksuite/icons';
import type { WorkspaceMetadata } from '@toeverything/infra';
import clsx from 'clsx';
import { type MouseEvent, useCallback } from 'react';

import { Avatar, type AvatarProps } from '../../../ui/avatar';
import { type AvatarProps } from '../../../ui/avatar';
import { Button } from '../../../ui/button';
import { Skeleton } from '../../../ui/skeleton';
import * as styles from './styles.css';
Expand All @@ -24,7 +25,6 @@ export interface WorkspaceCardProps {
isOwner?: boolean;
openingId?: string | null;
enableCloudText?: string;
avatar?: string;
name?: string;
}

Expand Down Expand Up @@ -57,7 +57,6 @@ export const WorkspaceCard = ({
isOwner = true,
enableCloudText = 'Enable Cloud',
name,
avatar,
}: WorkspaceCardProps) => {
const isLocal = meta.flavour === WorkspaceFlavour.LOCAL;
const displayName = name ?? UNTITLED_WORKSPACE_NAME;
Expand All @@ -78,11 +77,12 @@ export const WorkspaceCard = ({
onClick(meta);
}, [onClick, meta])}
>
<Avatar
<WorkspaceAvatar
key={meta.id}
meta={meta}
imageProps={avatarImageProps}
fallbackProps={avatarImageProps}
size={28}
url={avatar}
name={name}
colorfulFallback
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ export const root = style({
'&[data-enable-animation="true"]': {
transition: `margin-left ${animationTimeout} .05s, margin-right ${animationTimeout} .05s, width ${animationTimeout} .05s`,
},
'&[data-is-floating="false"][data-transparent=true]': {
backgroundColor: 'transparent',
},
'&[data-transition-state="exited"]': {
// avoid focus on hidden panel
visibility: 'hidden',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { assertExists } from '@blocksuite/global/utils';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
import {
forwardRef,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { useTransition } from 'react-transition-state';

import * as styles from './resize-panel.css';
Expand Down Expand Up @@ -157,7 +164,7 @@ export const ResizePanel = forwardRef<HTMLDivElement, ResizePanelProps>(
const [{ status }, toggle] = useTransition({
timeout: animationTimeout,
});
useEffect(() => {
useLayoutEffect(() => {
toggle(open);
}, [open]);
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
useLiveData,
useService,
type WorkspaceMetadata,
WorkspacesService,
} from '@toeverything/infra';
import { useEffect, useLayoutEffect, useState } from 'react';

import { Avatar, type AvatarProps } from '../../ui/avatar';

const cache = new Map<string, { imageBitmap: ImageBitmap; key: string }>();

/**
* workspace avatar component with automatic cache, and avoid flashing
*/
export const WorkspaceAvatar = ({
meta,
...otherProps
}: { meta: WorkspaceMetadata } & AvatarProps) => {
const workspacesService = useService(WorkspacesService);

const profile = workspacesService.getProfile(meta);

useEffect(() => {
profile.revalidate();
}, [meta, profile]);

const avatarKey = useLiveData(profile.profile$.map(v => v?.avatar));

const [downloadedAvatar, setDownloadedAvatar] = useState<
{ imageBitmap: ImageBitmap; key: string } | undefined
>(cache.get(meta.id));

useLayoutEffect(() => {
if (!avatarKey || !meta) {
setDownloadedAvatar(undefined);
return;
}

let canceled = false;
workspacesService
.getWorkspaceBlob(meta, avatarKey)
.then(async blob => {
if (blob && !canceled) {
const image = document.createElement('img');
const objectUrl = URL.createObjectURL(blob);
image.src = objectUrl;
await image.decode();
// limit the size of the image data to reduce memory usage
const hRatio = 128 / image.naturalWidth;
const vRatio = 128 / image.naturalHeight;
const ratio = Math.min(hRatio, vRatio);
const imageBitmap = await createImageBitmap(image, {
resizeWidth: image.naturalWidth * ratio,
resizeHeight: image.naturalHeight * ratio,
});
URL.revokeObjectURL(objectUrl);
setDownloadedAvatar(prev => {
if (prev?.key === avatarKey) {
return prev;
}
return { imageBitmap, key: avatarKey };
});
cache.set(meta.id, {
imageBitmap,
key: avatarKey,
});
}
})
.catch(err => {
console.error('get workspace blob error: ' + err);
});

return () => {
canceled = true;
};
}, [meta, workspacesService, avatarKey]);

return <Avatar image={downloadedAvatar?.imageBitmap} {...otherProps} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ export interface WorkspaceListProps {
useIsWorkspaceOwner: (
workspaceMetadata: WorkspaceMetadata
) => boolean | undefined;
useWorkspaceAvatar: (
workspaceMetadata: WorkspaceMetadata
) => string | undefined;
useWorkspaceName: (
workspaceMetadata: WorkspaceMetadata
) => string | undefined;
Expand All @@ -34,15 +31,13 @@ const SortableWorkspaceItem = ({
item,
openingId,
useIsWorkspaceOwner,
useWorkspaceAvatar,
useWorkspaceName,
currentWorkspaceId,
onClick,
onSettingClick,
onEnableCloudClick,
}: SortableWorkspaceItemProps) => {
const isOwner = useIsWorkspaceOwner?.(item);
const avatar = useWorkspaceAvatar?.(item);
const name = useWorkspaceName?.(item);
return (
<div className={workspaceItemStyle} data-testid="draggable-item">
Expand All @@ -55,7 +50,6 @@ const SortableWorkspaceItem = ({
openingId={openingId}
isOwner={isOwner}
name={name}
avatar={avatar}
/>
</div>
);
Expand Down
Loading

0 comments on commit b356ddb

Please sign in to comment.