refactor(core): using computed data & optimizing data fetching timing & loading initial values (#12478)

## TL;DR

refactor workspace embedding:

* using computed data
* optimizing data fetching timing(constructor -> component mounted)
* set loading initial values to `true`

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Improved loading indicators and state handling for embedding settings, including a more accurate loading state for the embedding toggle.
- **Bug Fixes**
  - The embedding toggle now safely handles unknown or loading states and is disabled while loading, preventing unintended interactions.
- **Refactor**
  - Simplified pagination logic and optimized initial data fetching for a smoother user experience.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
yoyoyohamapi
2025-05-26 07:17:37 +00:00
parent d06bb0222f
commit da22391910
2 changed files with 26 additions and 25 deletions

View File

@@ -24,7 +24,6 @@ import {
import { COUNT_PER_PAGE } from '../constants'; import { COUNT_PER_PAGE } from '../constants';
import type { EmbeddingStore } from '../stores/embedding'; import type { EmbeddingStore } from '../stores/embedding';
import type { import type {
AttachmentFile,
IgnoredDoc, IgnoredDoc,
LocalAttachmentFile, LocalAttachmentFile,
PersistedAttachmentFile, PersistedAttachmentFile,
@@ -55,7 +54,7 @@ interface EmbeddingProgress {
} }
export class Embedding extends Entity { export class Embedding extends Entity {
enabled$ = new LiveData<boolean>(false); enabled$ = new LiveData<boolean | null>(null);
error$ = new LiveData<any>(null); error$ = new LiveData<any>(null);
attachments$ = new LiveData<Attachments>({ attachments$ = new LiveData<Attachments>({
edges: [], edges: [],
@@ -66,36 +65,28 @@ export class Embedding extends Entity {
totalCount: 0, totalCount: 0,
}); });
ignoredDocs$ = new LiveData<IgnoredDocs>([]); ignoredDocs$ = new LiveData<IgnoredDocs>([]);
isEnabledLoading$ = new LiveData(false); isEnabledLoading$ = new LiveData(true);
isAttachmentsLoading$ = new LiveData(false); isAttachmentsLoading$ = new LiveData(true);
isIgnoredDocsLoading$ = new LiveData(false); isIgnoredDocsLoading$ = new LiveData(true);
embeddingProgress$ = new LiveData<EmbeddingProgress | null>(null); embeddingProgress$ = new LiveData<EmbeddingProgress | null>(null);
isEmbeddingProgressLoading$ = new LiveData(false); isEmbeddingProgressLoading$ = new LiveData(true);
private readonly EMBEDDING_PROGRESS_POLL_INTERVAL = 3000; private readonly EMBEDDING_PROGRESS_POLL_INTERVAL = 3000;
private readonly stopEmbeddingProgress$ = new Subject<void>(); private readonly stopEmbeddingProgress$ = new Subject<void>();
uploadingAttachments$ = new LiveData<LocalAttachmentFile[]>([]); uploadingAttachments$ = new LiveData<LocalAttachmentFile[]>([]);
mergedAttachments$ = new LiveData<AttachmentFile[]>([]);
constructor( constructor(
private readonly workspaceService: WorkspaceService, private readonly workspaceService: WorkspaceService,
private readonly store: EmbeddingStore private readonly store: EmbeddingStore
) { ) {
super(); super();
this.getEnabled();
this.getAttachments({ first: COUNT_PER_PAGE, after: null });
this.getIgnoredDocs();
this.getEmbeddingProgress();
this.uploadingAttachments$.subscribe(() => this.updateMergedAttachments());
this.attachments$.subscribe(() => this.updateMergedAttachments());
this.updateMergedAttachments();
} }
private updateMergedAttachments() { mergedAttachments$ = LiveData.computed(get => {
const uploading = this.uploadingAttachments$.value; const uploading = get(this.uploadingAttachments$);
const uploaded = this.attachments$.value.edges.map(edge => edge.node); const uploaded = get(this.attachments$).edges.map(edge => edge.node);
this.mergedAttachments$.next([...uploading, ...uploaded].slice(0, 10)); return [...uploading, ...uploaded].slice(0, 10);
} });
getEnabled = effect( getEnabled = effect(
exhaustMap(() => { exhaustMap(() => {

View File

@@ -12,6 +12,7 @@ import { useLiveData, useService } from '@toeverything/infra';
import type React from 'react'; import type React from 'react';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { COUNT_PER_PAGE } from '../constants';
import { EmbeddingService } from '../services/embedding'; import { EmbeddingService } from '../services/embedding';
import { Attachments } from './attachments'; import { Attachments } from './attachments';
import EmbeddingProgress from './embedding-progress'; import EmbeddingProgress from './embedding-progress';
@@ -23,9 +24,7 @@ export const EmbeddingSettings: React.FC<EmbeddingSettingsProps> = () => {
const t = useI18n(); const t = useI18n();
const embeddingService = useService(EmbeddingService); const embeddingService = useService(EmbeddingService);
const embeddingEnabled = useLiveData(embeddingService.embedding.enabled$); const embeddingEnabled = useLiveData(embeddingService.embedding.enabled$);
const { pageInfo, totalCount } = useLiveData( const { totalCount } = useLiveData(embeddingService.embedding.attachments$);
embeddingService.embedding.attachments$
);
const attachments = useLiveData( const attachments = useLiveData(
embeddingService.embedding.mergedAttachments$ embeddingService.embedding.mergedAttachments$
); );
@@ -38,6 +37,9 @@ export const EmbeddingSettings: React.FC<EmbeddingSettingsProps> = () => {
embeddingService.embedding.isIgnoredDocsLoading$ embeddingService.embedding.isIgnoredDocsLoading$
); );
const workspaceDialogService = useService(WorkspaceDialogService); const workspaceDialogService = useService(WorkspaceDialogService);
const isEnabledLoading = useLiveData(
embeddingService.embedding.isEnabledLoading$
);
const handleEmbeddingToggle = useCallback( const handleEmbeddingToggle = useCallback(
(checked: boolean) => { (checked: boolean) => {
@@ -74,10 +76,9 @@ export const EmbeddingSettings: React.FC<EmbeddingSettingsProps> = () => {
(offset: number) => { (offset: number) => {
embeddingService.embedding.getAttachments({ embeddingService.embedding.getAttachments({
offset, offset,
after: pageInfo.endCursor,
}); });
}, },
[embeddingService.embedding, pageInfo.endCursor] [embeddingService.embedding]
); );
const handleSelectDoc = useCallback(() => { const handleSelectDoc = useCallback(() => {
@@ -113,6 +114,14 @@ export const EmbeddingSettings: React.FC<EmbeddingSettingsProps> = () => {
useEffect(() => { useEffect(() => {
embeddingService.embedding.startEmbeddingProgressPolling(); embeddingService.embedding.startEmbeddingProgressPolling();
embeddingService.embedding.getEnabled();
embeddingService.embedding.getAttachments({
first: COUNT_PER_PAGE,
after: null,
});
embeddingService.embedding.getIgnoredDocs();
embeddingService.embedding.getEmbeddingProgress();
return () => { return () => {
embeddingService.embedding.stopEmbeddingProgressPolling(); embeddingService.embedding.stopEmbeddingProgressPolling();
}; };
@@ -139,8 +148,9 @@ export const EmbeddingSettings: React.FC<EmbeddingSettingsProps> = () => {
> >
<Switch <Switch
data-testid="workspace-embedding-setting-switch" data-testid="workspace-embedding-setting-switch"
checked={embeddingEnabled} checked={embeddingEnabled ?? false}
onChange={handleEmbeddingToggle} onChange={handleEmbeddingToggle}
disabled={isEnabledLoading}
/> />
</SettingRow> </SettingRow>