mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 03:26:47 +08:00
feat(editor): add blobState$ to BlobEngine (#11756)
Closes: [BS-3137](https://linear.app/affine-design/issue/BS-3137/在-bs-添加-blobstate-接口) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced real-time status tracking for file upload and download operations, allowing users to monitor progress and errors more effectively. - **Improvements** - Enhanced icon display for attachments, providing a refreshed visual experience. - Improved update frequency and accuracy for file synchronization status indicators. - **Bug Fixes** - Refined internal logic for filtering status updates, ensuring more precise feedback during file operations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -104,6 +104,10 @@ export class BlobEngine {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blobState$(key: string) {
|
||||||
|
return this.main.blobState$?.(key) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
if (this._abort) {
|
if (this._abort) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
import type { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export interface BlobState {
|
||||||
|
uploading: boolean;
|
||||||
|
downloading: boolean;
|
||||||
|
errorMessage?: string | null;
|
||||||
|
overSize: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BlobSource {
|
export interface BlobSource {
|
||||||
name: string;
|
name: string;
|
||||||
readonly: boolean;
|
readonly: boolean;
|
||||||
@@ -5,4 +14,6 @@ export interface BlobSource {
|
|||||||
set: (key: string, value: Blob) => Promise<string>;
|
set: (key: string, value: Blob) => Promise<string>;
|
||||||
delete: (key: string) => Promise<void>;
|
delete: (key: string) => Promise<void>;
|
||||||
list: () => Promise<string[]>;
|
list: () => Promise<string[]>;
|
||||||
|
// This state is only available when uploading to the cloud or downloading from the cloud.
|
||||||
|
blobState$?: (key: string) => Observable<BlobState> | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export class BlobFrontend {
|
|||||||
return this.sync.state$;
|
return this.sync.state$;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blobState$(blobId: string) {
|
||||||
|
return this.sync.blobState$(blobId);
|
||||||
|
}
|
||||||
|
|
||||||
async get(blobId: string) {
|
async get(blobId: string) {
|
||||||
await this.waitForConnected();
|
await this.waitForConnected();
|
||||||
await using lock = await this.lock.lock('blob', blobId);
|
await using lock = await this.lock.lock('blob', blobId);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export class BlobSyncImpl implements BlobSync {
|
|||||||
return combineLatest(
|
return combineLatest(
|
||||||
this.peers.map(peer => peer.blobPeerState$(blobId))
|
this.peers.map(peer => peer.blobPeerState$(blobId))
|
||||||
).pipe(
|
).pipe(
|
||||||
throttleTime(1000),
|
throttleTime(1000, undefined, { leading: true, trailing: true }),
|
||||||
map(
|
map(
|
||||||
peers =>
|
peers =>
|
||||||
({
|
({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { difference } from 'lodash-es';
|
import { difference } from 'lodash-es';
|
||||||
import { Observable, ReplaySubject, share, Subject } from 'rxjs';
|
import { filter, Observable, ReplaySubject, share, Subject } from 'rxjs';
|
||||||
|
|
||||||
import type { BlobRecord, BlobStorage } from '../../storage';
|
import type { BlobRecord, BlobStorage } from '../../storage';
|
||||||
import { OverCapacityError, OverSizeError } from '../../storage';
|
import { OverCapacityError, OverSizeError } from '../../storage';
|
||||||
@@ -412,11 +412,13 @@ class BlobSyncPeerStatus {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
const dispose = this.statusUpdatedSubject$.subscribe(updatedBlobId => {
|
const dispose = this.statusUpdatedSubject$
|
||||||
if (updatedBlobId === blobId || updatedBlobId === true) {
|
.pipe(
|
||||||
next();
|
filter(
|
||||||
}
|
updatedBlobId => updatedBlobId === blobId || updatedBlobId === true
|
||||||
});
|
)
|
||||||
|
)
|
||||||
|
.subscribe(() => next());
|
||||||
return () => {
|
return () => {
|
||||||
dispose.unsubscribe();
|
dispose.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -268,8 +268,8 @@ class WorkerBlobSync implements BlobSync {
|
|||||||
downloadBlob(blobId: string): Promise<boolean> {
|
downloadBlob(blobId: string): Promise<boolean> {
|
||||||
return this.client.call('blobSync.downloadBlob', blobId);
|
return this.client.call('blobSync.downloadBlob', blobId);
|
||||||
}
|
}
|
||||||
uploadBlob(blob: BlobRecord): Promise<true> {
|
uploadBlob(blob: BlobRecord, force?: boolean): Promise<true> {
|
||||||
return this.client.call('blobSync.uploadBlob', blob);
|
return this.client.call('blobSync.uploadBlob', { blob, force });
|
||||||
}
|
}
|
||||||
fullDownload(peerId?: string, signal?: AbortSignal): Promise<void> {
|
fullDownload(peerId?: string, signal?: AbortSignal): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -216,7 +216,8 @@ class StoreConsumer {
|
|||||||
'blobSync.state': () => this.blobSync.state$,
|
'blobSync.state': () => this.blobSync.state$,
|
||||||
'blobSync.blobState': blobId => this.blobSync.blobState$(blobId),
|
'blobSync.blobState': blobId => this.blobSync.blobState$(blobId),
|
||||||
'blobSync.downloadBlob': key => this.blobSync.downloadBlob(key),
|
'blobSync.downloadBlob': key => this.blobSync.downloadBlob(key),
|
||||||
'blobSync.uploadBlob': blob => this.blobSync.uploadBlob(blob),
|
'blobSync.uploadBlob': ({ blob, force }) =>
|
||||||
|
this.blobSync.uploadBlob(blob, force),
|
||||||
'blobSync.fullDownload': peerId =>
|
'blobSync.fullDownload': peerId =>
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ interface GroupedWorkerOps {
|
|||||||
state: [void, BlobSyncState];
|
state: [void, BlobSyncState];
|
||||||
blobState: [string, BlobSyncBlobState];
|
blobState: [string, BlobSyncBlobState];
|
||||||
downloadBlob: [string, boolean];
|
downloadBlob: [string, boolean];
|
||||||
uploadBlob: [BlobRecord, true];
|
uploadBlob: [{ blob: BlobRecord; force?: boolean }, true];
|
||||||
fullDownload: [string | null, void];
|
fullDownload: [string | null, void];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ export class Workspace extends Entity {
|
|||||||
});
|
});
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
|
/* eslint-disable rxjs/finnish */
|
||||||
|
blobState$: key => this.engine.blob.blobState$(key),
|
||||||
name: 'blob',
|
name: 'blob',
|
||||||
readonly: false,
|
readonly: false,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user