fix(core): some storage setting enhancements (#9877)
fix AF-2157, AF-2155, AF-2156 1. add shift selection for grid blob card 2. various style issues 3. unused blobs list will also wait for workspace syncing
This commit is contained in:
@@ -2,4 +2,3 @@ export * from './accept-invite-page';
|
|||||||
export * from './invite-modal';
|
export * from './invite-modal';
|
||||||
export * from './invite-team-modal';
|
export * from './invite-team-modal';
|
||||||
export * from './member-limit-modal';
|
export * from './member-limit-modal';
|
||||||
export * from './pagination';
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import { globalStyle, style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
export const inviteModalTitle = style({
|
export const inviteModalTitle = style({
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
@@ -35,52 +35,6 @@ export const userWrapper = style({
|
|||||||
gap: '4px',
|
gap: '4px',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const pagination = style({
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '6px',
|
|
||||||
marginTop: 5,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const pageItem = style({
|
|
||||||
display: 'inline-flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
fontSize: cssVar('fontXs'),
|
|
||||||
color: cssVarV2('text/primary'),
|
|
||||||
borderRadius: '4px',
|
|
||||||
|
|
||||||
selectors: {
|
|
||||||
'&:hover': {
|
|
||||||
background: cssVarV2('layer/background/hoverOverlay'),
|
|
||||||
},
|
|
||||||
'&.active': {
|
|
||||||
color: cssVarV2('text/emphasis'),
|
|
||||||
cursor: 'default',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
},
|
|
||||||
'&.label': {
|
|
||||||
color: cssVarV2('icon/primary'),
|
|
||||||
fontSize: '16px',
|
|
||||||
},
|
|
||||||
'&.disabled': {
|
|
||||||
opacity: '.4',
|
|
||||||
cursor: 'default',
|
|
||||||
color: cssVarV2('text/disable'),
|
|
||||||
pointerEvents: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
globalStyle(`${pageItem} a`, {
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const modalContent = style({
|
export const modalContent = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './pagination';
|
||||||
export { SettingHeader } from './setting-header';
|
export { SettingHeader } from './setting-header';
|
||||||
export { SettingRow } from './setting-row';
|
export { SettingRow } from './setting-row';
|
||||||
export * from './workspace-detail-skeleton';
|
export * from './workspace-detail-skeleton';
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const pageItem = style({
|
||||||
|
display: 'inline-flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
fontSize: cssVar('fontXs'),
|
||||||
|
color: cssVarV2('text/primary'),
|
||||||
|
borderRadius: '4px',
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
'&:hover': {
|
||||||
|
background: cssVarV2('layer/background/hoverOverlay'),
|
||||||
|
},
|
||||||
|
'&.active': {
|
||||||
|
color: cssVarV2('text/emphasis'),
|
||||||
|
cursor: 'default',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
},
|
||||||
|
'&.label': {
|
||||||
|
color: cssVarV2('icon/primary'),
|
||||||
|
fontSize: '16px',
|
||||||
|
},
|
||||||
|
'&.disabled': {
|
||||||
|
opacity: '.4',
|
||||||
|
cursor: 'default',
|
||||||
|
color: cssVarV2('text/disable'),
|
||||||
|
pointerEvents: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
globalStyle(`${pageItem} a`, {
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pagination = style({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '6px',
|
||||||
|
marginTop: 5,
|
||||||
|
});
|
||||||
@@ -3,7 +3,7 @@ import clsx from 'clsx';
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import ReactPaginate from 'react-paginate';
|
import ReactPaginate from 'react-paginate';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './pagination.css';
|
||||||
export interface PaginationProps {
|
export interface PaginationProps {
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
@@ -7,8 +7,10 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { Pagination } from '@affine/component/member-components';
|
import {
|
||||||
import { SettingHeader } from '@affine/component/setting-components';
|
Pagination,
|
||||||
|
SettingHeader,
|
||||||
|
} from '@affine/component/setting-components';
|
||||||
import { Avatar } from '@affine/component/ui/avatar';
|
import { Avatar } from '@affine/component/ui/avatar';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
@@ -209,26 +211,30 @@ export const BackupSettingPanel = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (backupWorkspaces?.items.length === 0) {
|
if (backupWorkspaces?.items.length === 0 || !backupWorkspaces) {
|
||||||
return <Empty />;
|
return <Empty />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.list}>
|
<div className={styles.list}>
|
||||||
{backupWorkspaces?.items
|
{backupWorkspaces.items
|
||||||
.slice(pageNum * PAGE_SIZE, (pageNum + 1) * PAGE_SIZE)
|
.slice(pageNum * PAGE_SIZE, (pageNum + 1) * PAGE_SIZE)
|
||||||
.map(item => <BackupWorkspaceItem key={item.id} item={item} />)}
|
.map(item => (
|
||||||
</div>
|
<BackupWorkspaceItem key={item.id} item={item} />
|
||||||
<div className={styles.pagination}>
|
))}
|
||||||
<Pagination
|
|
||||||
totalCount={backupWorkspaces?.items.length ?? 0}
|
|
||||||
countPerPage={PAGE_SIZE}
|
|
||||||
pageNum={pageNum}
|
|
||||||
onPageChange={(_, pageNum) => {
|
|
||||||
setPageNum(pageNum);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
{backupWorkspaces.items.length > PAGE_SIZE && (
|
||||||
|
<div className={styles.pagination}>
|
||||||
|
<Pagination
|
||||||
|
totalCount={backupWorkspaces?.items.length ?? 0}
|
||||||
|
countPerPage={PAGE_SIZE}
|
||||||
|
pageNum={pageNum}
|
||||||
|
onPageChange={(_, pageNum) => {
|
||||||
|
setPageNum(pageNum);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, [isLoading, backupWorkspaces, pageNum]);
|
}, [isLoading, backupWorkspaces, pageNum]);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const listContainer = style({
|
|||||||
export const list = style({
|
export const list = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '8px',
|
gap: '4px',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const listItem = style({
|
export const listItem = style({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Skeleton } from '@affine/component';
|
import { Skeleton } from '@affine/component';
|
||||||
import { Pagination } from '@affine/component/member-components';
|
|
||||||
import {
|
import {
|
||||||
|
Pagination,
|
||||||
SettingHeader,
|
SettingHeader,
|
||||||
SettingRow,
|
SettingRow,
|
||||||
SettingWrapper,
|
SettingWrapper,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button, Loading } from '@affine/component';
|
import { Button, Loading } from '@affine/component';
|
||||||
import { Pagination } from '@affine/component/member-components';
|
|
||||||
import {
|
import {
|
||||||
|
Pagination,
|
||||||
SettingHeader,
|
SettingHeader,
|
||||||
SettingRow,
|
SettingRow,
|
||||||
SettingWrapper,
|
SettingWrapper,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Avatar, IconButton, Loading, Menu, notify } from '@affine/component';
|
import { Avatar, IconButton, Loading, Menu, notify } from '@affine/component';
|
||||||
import { Pagination } from '@affine/component/member-components';
|
import { Pagination } from '@affine/component/setting-components';
|
||||||
import { type AuthAccountInfo, AuthService } from '@affine/core/modules/cloud';
|
import { type AuthAccountInfo, AuthService } from '@affine/core/modules/cloud';
|
||||||
import {
|
import {
|
||||||
type Member,
|
type Member,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
useDisposable,
|
useDisposable,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { Pagination } from '@affine/component/member-components';
|
import { Pagination } from '@affine/component/setting-components';
|
||||||
import { BlobManagementService } from '@affine/core/modules/blob-management/services';
|
import { BlobManagementService } from '@affine/core/modules/blob-management/services';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { ListedBlobRecord } from '@affine/nbstore';
|
import type { ListedBlobRecord } from '@affine/nbstore';
|
||||||
@@ -80,7 +80,8 @@ const BlobPreview = ({ blobRecord }: { blobRecord: ListedBlobRecord }) => {
|
|||||||
<div className={styles.blobPreviewFooter}>
|
<div className={styles.blobPreviewFooter}>
|
||||||
<div className={styles.blobPreviewName}>{blobRecord.key}</div>
|
<div className={styles.blobPreviewName}>{blobRecord.key}</div>
|
||||||
<div className={styles.blobPreviewInfo}>
|
<div className={styles.blobPreviewInfo}>
|
||||||
{data?.type} · {bytes(blobRecord.size)}
|
{data?.type ? `${data.type} · ` : ''}
|
||||||
|
{bytes(blobRecord.size)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,7 +94,7 @@ const BlobCard = ({
|
|||||||
selected,
|
selected,
|
||||||
}: {
|
}: {
|
||||||
blobRecord: ListedBlobRecord;
|
blobRecord: ListedBlobRecord;
|
||||||
onClick: () => void;
|
onClick: (e: React.MouseEvent) => void;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
@@ -119,6 +120,8 @@ export const BlobManagementPanel = () => {
|
|||||||
const isLoading = useLiveData(unusedBlobsEntity.isLoading$);
|
const isLoading = useLiveData(unusedBlobsEntity.isLoading$);
|
||||||
const [pageNum, setPageNum] = useState(0);
|
const [pageNum, setPageNum] = useState(0);
|
||||||
const [skip, setSkip] = useState(0);
|
const [skip, setSkip] = useState(0);
|
||||||
|
const [selectionAnchor, setSelectionAnchor] =
|
||||||
|
useState<ListedBlobRecord | null>(null);
|
||||||
|
|
||||||
const [unusedBlobs, setUnusedBlobs] = useState<ListedBlobRecord[]>([]);
|
const [unusedBlobs, setUnusedBlobs] = useState<ListedBlobRecord[]>([]);
|
||||||
const unusedBlobsPage = useMemo(() => {
|
const unusedBlobsPage = useMemo(() => {
|
||||||
@@ -149,9 +152,60 @@ export const BlobManagementPanel = () => {
|
|||||||
setSelectedBlobs(prev => prev.filter(b => b.key !== blob.key));
|
setSelectedBlobs(prev => prev.filter(b => b.key !== blob.key));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSelectAll = useCallback(() => {
|
const handleBlobClick = useCallback(
|
||||||
unusedBlobsPage.forEach(blob => handleSelectBlob(blob));
|
(blob: ListedBlobRecord, event: React.MouseEvent) => {
|
||||||
}, [unusedBlobsPage, handleSelectBlob]);
|
const isMetaKey = event.metaKey || event.ctrlKey;
|
||||||
|
|
||||||
|
if (event.shiftKey && selectionAnchor) {
|
||||||
|
// Shift+click: Select range from anchor to current
|
||||||
|
const anchorIndex = unusedBlobsPage.findIndex(
|
||||||
|
b => b.key === selectionAnchor.key
|
||||||
|
);
|
||||||
|
const currentIndex = unusedBlobsPage.findIndex(b => b.key === blob.key);
|
||||||
|
|
||||||
|
if (anchorIndex !== -1 && currentIndex !== -1) {
|
||||||
|
const start = Math.min(anchorIndex, currentIndex);
|
||||||
|
const end = Math.max(anchorIndex, currentIndex);
|
||||||
|
const blobsToSelect = unusedBlobsPage.slice(start, end + 1);
|
||||||
|
|
||||||
|
setSelectedBlobs(prev => {
|
||||||
|
// If meta/ctrl is also pressed, add to existing selection
|
||||||
|
const baseSelection = isMetaKey ? prev : [];
|
||||||
|
const newSelection = new Set([...baseSelection, ...blobsToSelect]);
|
||||||
|
return Array.from(newSelection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selectedBlobs.includes(blob)) {
|
||||||
|
handleUnselectBlob(blob);
|
||||||
|
} else {
|
||||||
|
handleSelectBlob(blob);
|
||||||
|
}
|
||||||
|
if (selectedBlobs.length === 0) {
|
||||||
|
setSelectionAnchor(selectedBlobs.includes(blob) ? null : blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
selectionAnchor,
|
||||||
|
unusedBlobsPage,
|
||||||
|
selectedBlobs,
|
||||||
|
handleSelectBlob,
|
||||||
|
handleUnselectBlob,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelectAll = useCallback(
|
||||||
|
(e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
unusedBlobsPage.forEach(blob => handleSelectBlob(blob));
|
||||||
|
},
|
||||||
|
[unusedBlobsPage, handleSelectBlob]
|
||||||
|
);
|
||||||
|
|
||||||
|
const showSelectAll = !unusedBlobsPage.every(blob =>
|
||||||
|
selectedBlobs.includes(blob)
|
||||||
|
);
|
||||||
|
|
||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
|
|
||||||
@@ -193,10 +247,7 @@ export const BlobManagementPanel = () => {
|
|||||||
if (blobPreviewGridRef.current) {
|
if (blobPreviewGridRef.current) {
|
||||||
const unselectBlobs = (e: MouseEvent) => {
|
const unselectBlobs = (e: MouseEvent) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (
|
if (!blobPreviewGridRef.current?.contains(target)) {
|
||||||
!blobPreviewGridRef.current?.contains(target) &&
|
|
||||||
!target.closest('modal-transition-container')
|
|
||||||
) {
|
|
||||||
setSelectedBlobs([]);
|
setSelectedBlobs([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -216,9 +267,11 @@ export const BlobManagementPanel = () => {
|
|||||||
{`${selectedBlobs.length} ${t['com.affine.settings.workspace.storage.unused-blobs.selected']()}`}
|
{`${selectedBlobs.length} ${t['com.affine.settings.workspace.storage.unused-blobs.selected']()}`}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.spacer} />
|
<div className={styles.spacer} />
|
||||||
<Button onClick={handleSelectAll} variant="primary">
|
{showSelectAll && (
|
||||||
{t['com.affine.keyboardShortcuts.selectAll']()}
|
<Button onClick={handleSelectAll} variant="primary">
|
||||||
</Button>
|
{t['com.affine.keyboardShortcuts.selectAll']()}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
loading={deleting}
|
loading={deleting}
|
||||||
onClick={handleDeleteSelectedBlobs}
|
onClick={handleDeleteSelectedBlobs}
|
||||||
@@ -243,31 +296,29 @@ export const BlobManagementPanel = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className={styles.blobPreviewGrid} ref={blobPreviewGridRef}>
|
<div className={styles.blobPreviewGrid} ref={blobPreviewGridRef}>
|
||||||
{unusedBlobs.slice(skip, skip + PAGE_SIZE).map(blob => {
|
{unusedBlobsPage.map(blob => {
|
||||||
const selected = selectedBlobs.includes(blob);
|
const selected = selectedBlobs.includes(blob);
|
||||||
return (
|
return (
|
||||||
<BlobCard
|
<BlobCard
|
||||||
key={blob.key}
|
key={blob.key}
|
||||||
blobRecord={blob}
|
blobRecord={blob}
|
||||||
onClick={() =>
|
onClick={e => handleBlobClick(blob, e)}
|
||||||
selected
|
|
||||||
? handleUnselectBlob(blob)
|
|
||||||
: handleSelectBlob(blob)
|
|
||||||
}
|
|
||||||
selected={selected}
|
selected={selected}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Pagination
|
{unusedBlobs.length > PAGE_SIZE && (
|
||||||
pageNum={pageNum}
|
<Pagination
|
||||||
totalCount={unusedBlobs.length}
|
pageNum={pageNum}
|
||||||
countPerPage={PAGE_SIZE}
|
totalCount={unusedBlobs.length}
|
||||||
onPageChange={(_, pageNum) => {
|
countPerPage={PAGE_SIZE}
|
||||||
setPageNum(pageNum);
|
onPageChange={(_, pageNum) => {
|
||||||
setSkip(pageNum * PAGE_SIZE);
|
setPageNum(pageNum);
|
||||||
}}
|
setSkip(pageNum * PAGE_SIZE);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ export const blobCard = style({
|
|||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
userSelect: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const loadingContainer = style({
|
export const loadingContainer = style({
|
||||||
@@ -123,7 +124,7 @@ export const blobGridItemCheckbox = style({
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 8,
|
top: 8,
|
||||||
right: 8,
|
right: 8,
|
||||||
fontSize: 16,
|
fontSize: 24,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
selectors: {
|
selectors: {
|
||||||
[`${blobCard}:hover &`]: {
|
[`${blobCard}:hover &`]: {
|
||||||
@@ -135,6 +136,10 @@ export const blobGridItemCheckbox = style({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
globalStyle(`${blobGridItemCheckbox} path`, {
|
||||||
|
backgroundColor: cssVarV2('layer/background/primary'),
|
||||||
|
});
|
||||||
|
|
||||||
export const blobImagePreview = style({
|
export const blobImagePreview = style({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ import {
|
|||||||
onStart,
|
onStart,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { fileTypeFromBuffer } from 'file-type';
|
import { fileTypeFromBuffer } from 'file-type';
|
||||||
import { EMPTY, mergeMap, switchMap } from 'rxjs';
|
import {
|
||||||
|
combineLatest,
|
||||||
|
EMPTY,
|
||||||
|
filter,
|
||||||
|
firstValueFrom,
|
||||||
|
fromEvent,
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
} from 'rxjs';
|
||||||
|
|
||||||
import type { DocsSearchService } from '../../docs-search';
|
import type { DocsSearchService } from '../../docs-search';
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
@@ -91,10 +101,22 @@ export class UnusedBlobs extends Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getUnusedBlobs(abortSignal?: AbortSignal) {
|
async getUnusedBlobs(abortSignal?: AbortSignal) {
|
||||||
// wait for the indexer to finish
|
// Wait for both sync and indexing to complete
|
||||||
await this.docsSearchService.indexer.status$.waitFor(
|
const ready$ = combineLatest([
|
||||||
status => status.remaining === undefined || status.remaining === 0,
|
this.workspaceService.workspace.engine.doc.state$.pipe(
|
||||||
|
filter(state => state.syncing === 0 && !state.syncRetrying)
|
||||||
|
),
|
||||||
|
this.docsSearchService.indexer.status$.pipe(
|
||||||
|
filter(
|
||||||
|
status => status.remaining === undefined || status.remaining === 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]).pipe(map(() => true));
|
||||||
|
|
||||||
|
await firstValueFrom(
|
||||||
abortSignal
|
abortSignal
|
||||||
|
? ready$.pipe(takeUntil(fromEvent(abortSignal, 'abort')))
|
||||||
|
: ready$
|
||||||
);
|
);
|
||||||
|
|
||||||
const [blobs, usedBlobs] = await Promise.all([
|
const [blobs, usedBlobs] = await Promise.all([
|
||||||
|
|||||||
Reference in New Issue
Block a user