feat(core): adjust history modal styles (#11675)

- add avatar/name info to each history snapshot item
- add avatar to audio transcription job owner

fix AF-2483

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/4a2b88c2-311a-470e-a8a9-113931cdf17e.png)
This commit is contained in:
pengx17
2025-04-15 07:16:52 +00:00
parent e02b159bf4
commit 7b2ae7f573
7 changed files with 137 additions and 103 deletions

View File

@@ -1,18 +0,0 @@
import { useEffect, useState } from 'react';
export const useBlobUrl = (buffer?: Buffer) => {
const [blobUrl, setBlobUrl] = useState<string | null>(null);
useEffect(() => {
if (!buffer) {
return;
}
const url = URL.createObjectURL(new Blob([buffer]));
setBlobUrl(url);
return () => {
URL.revokeObjectURL(url);
};
}, [buffer]);
return blobUrl;
};

View File

@@ -1,4 +1,4 @@
import { Loading, Scrollable } from '@affine/component';
import { Avatar, Loading, Scrollable } from '@affine/component';
import { EditorLoading } from '@affine/component/page-detail-skeleton';
import { Button, IconButton } from '@affine/component/ui/button';
import { Modal, useConfirmModal } from '@affine/component/ui/modal';
@@ -304,6 +304,7 @@ const PageHistoryList = ({
<PlanPrompt />
{historyListByDay.map(([day, list], i) => {
const collapsed = collapsedMap[i];
const isLastGroup = i === historyListByDay.length - 1;
return (
<Collapsible.Root
open={!collapsed}
@@ -328,8 +329,9 @@ const PageHistoryList = ({
</div>
{day}
</Collapsible.Trigger>
<Collapsible.Content>
<Collapsible.Content className={styles.historyItemGroupContent}>
{list.map((history, idx) => {
const isLastItem = idx === list.length - 1;
return (
<Fragment key={history.timestamp}>
<div
@@ -341,14 +343,43 @@ const PageHistoryList = ({
}}
data-active={activeVersion === history.timestamp}
>
<button>
<span className={styles.historyItemTimestamp}>
{i18nTime(history.timestamp, {
absolute: { noDate: true, accuracy: 'minute' },
})}
</button>
{activeVersion === history.timestamp ? (
<>
<span>·</span>
<div className={styles.historyItemCurrent}>
{t['current']()}
</div>
</>
) : null}
</span>
<div className={styles.historyItemNameWrapper}>
<Avatar
className={styles.historyItemAvatar}
url={history.editor?.avatarUrl ?? ''}
name={history.editor?.name ?? ''}
size={22}
/>
<span className={styles.historyItemName}>
{history.editor?.name ?? t['unnamed']()}
</span>
</div>
</div>
{idx > list.length - 1 ? (
<div className={styles.historyItemGap} />
{isLastGroup && isLastItem && onLoadMore ? (
<Button
variant="plain"
loading={loadingMore}
disabled={loadingMore}
className={styles.historyItemLoadMore}
onClick={onLoadMore}
>
{t[
'com.affine.history.confirm-restore-modal.load-more'
]()}
</Button>
) : null}
</Fragment>
);
@@ -357,17 +388,6 @@ const PageHistoryList = ({
</Collapsible.Root>
);
})}
{onLoadMore ? (
<Button
variant="plain"
loading={loadingMore}
disabled={loadingMore}
className={styles.historyItemLoadMore}
onClick={onLoadMore}
>
{t['com.affine.history.confirm-restore-modal.load-more']()}
</Button>
) : null}
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>

View File

@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { createVar, globalStyle, style } from '@vanilla-extract/css';
import { range } from 'lodash-es';
@@ -9,6 +10,7 @@ const previewTopOffset = createVar('preview-top-offset');
export const root = style({
height: '100%',
width: '100%',
fontFamily: 'Inter',
vars: {
[headerHeight]: '52px',
[footerHeight]: '68px',
@@ -119,32 +121,8 @@ export const editor = style({
});
export const rowWrapper = style({
display: 'flex',
height: '100%',
position: 'relative',
overflow: 'hidden',
':before': {
content: '""',
width: 1,
height: '100%',
backgroundColor: cssVar('borderColor'),
position: 'absolute',
left: 16,
top: 0,
bottom: 0,
transform: 'translate(-50%)',
},
selectors: {
'&:is(:last-of-type, :first-of-type):not(:last-of-type:first-of-type)::before':
{
height: '50%',
},
'&:last-of-type:first-of-type::before': {
display: 'none',
},
'&:first-of-type::before': {
top: '50%',
},
},
});
export const loadingContainer = style({
display: 'flex',
@@ -166,6 +144,8 @@ export const historyListScrollable = style({
export const historyListScrollableInner = style({
display: 'flex',
flexDirection: 'column',
paddingBottom: 16,
height: '-webkit-fill-available',
});
export const historyListHeader = style({
display: 'flex',
@@ -179,71 +159,101 @@ export const historyListHeader = style({
export const historyItemGroup = style({
display: 'flex',
flexDirection: 'column',
padding: '0 8px',
gap: 8,
marginTop: 4,
marginBottom: 4,
});
export const historyItemGroupContent = style({
position: 'relative',
display: 'flex',
flexDirection: 'column',
gap: 8,
paddingLeft: 24,
'::before': {
position: 'absolute',
top: 0,
left: 15.5,
content: '""',
display: 'block',
height: '100%',
width: '1px',
borderLeft: `1px solid ${cssVar('borderColor')}`,
},
});
export const historyItemGroupTitle = style({
display: 'flex',
alignItems: 'center',
padding: '0 12px 0 4px',
borderRadius: 4,
whiteSpace: 'nowrap',
color: cssVar('textSecondaryColor'),
fontSize: cssVar('fontXs'),
color: cssVarV2('text/secondary'),
fontWeight: 500,
textTransform: 'capitalize',
gap: 4,
backgroundColor: cssVar('backgroundPrimaryColor'),
height: 28,
backgroundColor: cssVarV2('layer/background/primary'),
height: 24,
':hover': {
background: cssVar('hoverColor'),
background: cssVarV2('layer/background/hoverOverlay'),
},
});
export const historyItem = style([
rowWrapper,
{
borderRadius: 4,
display: 'flex',
alignItems: 'center',
padding: '0 32px',
height: 30,
gap: 4,
flexDirection: 'column',
padding: '4px 8px',
cursor: 'pointer',
selectors: {
'&:hover, &[data-active=true]': {
backgroundColor: cssVar('hoverColor'),
},
// draw circle
'&::after': {
content: '""',
width: 7,
height: 7,
backgroundColor: cssVar('backgroundSecondaryColor'),
borderRadius: '50%',
border: `1px solid ${cssVar('borderColor')}`,
position: 'absolute',
left: 16,
top: '50%',
bottom: 0,
transform: 'translate(-50%, -50%)',
},
'&[data-active=true]::after': {
backgroundColor: cssVar('primaryColor'),
borderColor: cssVar('black30'),
},
':hover': {
background: cssVarV2('layer/background/hoverOverlay'),
},
},
]);
export const historyItemGap = style([
rowWrapper,
{
height: 16,
},
]);
export const historyItemTimestamp = style({
color: cssVarV2('text/primary'),
borderRadius: 4,
fontSize: cssVar('fontXs'),
fontWeight: 500,
display: 'flex',
alignItems: 'center',
gap: '0.5em',
});
export const historyItemCurrent = style({
color: cssVarV2('text/emphasis'),
textTransform: 'capitalize',
});
export const historyItemNameWrapper = style({
display: 'flex',
alignItems: 'center',
gap: 4,
color: cssVarV2('text/secondary'),
fontSize: cssVar('fontXs'),
});
export const historyItemAvatar = style({
backgroundColor: cssVarV2('layer/background/secondary'),
});
export const historyItemName = style({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
});
export const historyItemLoadMore = style([
historyItem,
{
cursor: 'pointer',
color: cssVar('textSecondaryColor'),
color: cssVarV2('text/secondary'),
flexShrink: 0,
borderRadius: 0,
selectors: {
'&:hover': {
backgroundColor: cssVar('hoverColor'),
},
},
textAlign: 'left',
alignItems: 'flex-start',
},
]);
globalStyle(`${historyItem} button`, {
@@ -302,7 +312,7 @@ export const collapsedIconContainer = style({
color: 'inherit',
});
export const planPromptWrapper = style({
padding: '4px 12px',
padding: '4px 8px',
});
export const planPrompt = style({
gap: 6,

View File

@@ -2,6 +2,8 @@ import { style } from '@vanilla-extract/css';
export const publicUserLabel = style({
fontSize: 'inherit',
display: 'flex',
alignItems: 'center',
});
export const publicUserLabelLoading = style([
@@ -18,3 +20,7 @@ export const publicUserLabelRemoved = style([
textDecoration: 'line-through',
},
]);
export const publicUserLabelAvatar = style({
marginRight: '0.5em',
});

View File

@@ -1,3 +1,4 @@
import { Avatar } from '@affine/component';
import { useCurrentServerService } from '@affine/core/components/providers/current-server-scope';
import { useI18n } from '@affine/i18n';
import { useLiveData } from '@toeverything/infra';
@@ -34,5 +35,15 @@ export const PublicUserLabel = ({ id }: { id: string }) => {
);
}
return <span className={styles.publicUserLabel}>{user?.name}</span>;
return (
<span className={styles.publicUserLabel}>
<Avatar
url={user?.avatar}
name={user?.name ?? ''}
size={20}
className={styles.publicUserLabelAvatar}
/>
{user?.name}
</span>
);
};

View File

@@ -585,6 +585,10 @@ export function useAFFiNEI18N(): {
* `all`
*/
all(): string;
/**
* `current`
*/
current(): string;
/**
* `Automatically check for new updates periodically.`
*/

View File

@@ -135,6 +135,7 @@
"Unknown User": "Unknown User",
"Deleted User": "Deleted User",
"all": "all",
"current": "current",
"com.affine.aboutAFFiNE.autoCheckUpdate.description": "Automatically check for new updates periodically.",
"com.affine.aboutAFFiNE.autoCheckUpdate.title": "Check for updates automatically",
"com.affine.aboutAFFiNE.autoDownloadUpdate.description": "Automatically download updates (to this device).",