Skip to content

Commit 5075f97

Browse files
committed
refactor: 重构ClipboardSyncService
1 parent 32d6047 commit 5075f97

16 files changed

Lines changed: 197 additions & 187 deletions

src/api/clients/S3Client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export class S3Client implements ISyncClipboardAPI {
218218
fileName: content.fileName,
219219
});
220220

221-
const { contentToProfileDto } = await import('@/utils/clipboard/dtoConvert');
221+
const { contentToProfileDto } = await import('@/utils/clipboard/convert');
222222
const profile = await contentToProfileDto(content, { signal: options?.signal });
223223

224224
// 确保 file/ 目录标记存在

src/api/clients/SyncClipboardClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class SyncClipboardClient extends APIClient implements ISyncClipboardAPI,
141141
fileName: content.fileName,
142142
});
143143

144-
const { contentToProfileDto } = await import('@/utils/clipboard/dtoConvert');
144+
const { contentToProfileDto } = await import('@/utils/clipboard/convert');
145145
const profile = await contentToProfileDto(content, { signal });
146146

147147
if (!profile.hash) {

src/api/clients/WebDAVClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export class WebDAVClient extends APIClient implements ISyncClipboardAPI {
323323
fileName: content.fileName,
324324
});
325325

326-
const { contentToProfileDto } = await import('@/utils/clipboard/dtoConvert');
326+
const { contentToProfileDto } = await import('@/utils/clipboard/convert');
327327
const profile = await contentToProfileDto(content, { signal: options?.signal });
328328

329329
if (profile.hasData && profile.dataName && content.fileUri) {

src/services/history/HistoryService.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
* 同时承担发布订阅职责:管理所有变更回调,HistoryStorage 不再直接维护订阅者列表。
55
*/
66

7-
import type { HistoryItem } from '@/types/clipboard';
7+
import type { HistoryItem, ClipboardContent } from '@/types/clipboard';
88
import type { HistoryFilter, HistorySort } from '@/types/storage';
99
import type { HistoryChangeCallback } from '@/storage/HistoryStorage';
10+
import { HistorySyncStatus } from '@/types/clipboard';
11+
import { clipboardContentToItem } from '@/utils/clipboard/convert';
1012
import { historyStorage } from '@/storage';
1113

1214
/**
@@ -48,6 +50,21 @@ export class HistoryService {
4850
return historyStorage.addItem(item);
4951
}
5052

53+
addRemoteContent(content: ClipboardContent): Promise<HistoryItem> {
54+
const hasData = content.hasData ?? false;
55+
const fileUri = content.fileUri;
56+
57+
const historyItem = clipboardContentToItem(content, {
58+
hasData,
59+
hasRemoteData: hasData,
60+
fileUri,
61+
syncStatus: HistorySyncStatus.Synced,
62+
isLocalFileReady: !hasData || !!fileUri,
63+
});
64+
65+
return historyStorage.addItem(historyItem);
66+
}
67+
5168
addItems(items: HistoryItem[]): Promise<void> {
5269
return historyStorage.addItems(items);
5370
}

src/services/history/HistorySyncService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import type { IHistoryAPI } from '@/api/history';
77
import { HistoryRecordDto, HistoryRecordUpdateDto, ProfileTypeFilter } from '@/types/history';
8-
import { dtoToHistoryItem } from '@/utils/clipboard/dtoConvert';
8+
import { dtoToHistoryItem } from '@/utils/clipboard/convert';
99
import { SyncConflictError, RecordNotFoundError } from '@/errors';
1010
import { HistoryStorage } from '../../storage/HistoryStorage';
1111
import { HistoryItem, HistorySyncStatus } from '@/types/clipboard';
@@ -642,7 +642,7 @@ export class HistorySyncService {
642642

643643
// hasData === false: 上传元数据
644644
try {
645-
const { historyItemToDto } = await import('@/utils/clipboard/dtoConvert');
645+
const { historyItemToDto } = await import('@/utils/clipboard/convert');
646646
const dto = historyItemToDto(item);
647647

648648
console.log(`[HistorySyncService] Uploading LocalOnly record: ${item.profileHash}`);
@@ -750,7 +750,7 @@ export class HistorySyncService {
750750
}
751751

752752
try {
753-
const { historyItemToDto } = await import('@/utils/clipboard/dtoConvert');
753+
const { historyItemToDto } = await import('@/utils/clipboard/convert');
754754
const dto = historyItemToDto(item);
755755

756756
const createdRecord = await this.historyAPI.uploadRecord(dto);

src/services/history/HistoryTracker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import type { ClipboardContent } from '@/types/clipboard';
77
import { clipboardMonitor } from '../clipboard/ClipboardMonitor';
88
import { loadLastTrackedHash, saveLastTrackedHash } from './lastTrackedHashStorage';
9-
import { clipboardContentToItem } from '../../utils/clipboard/dtoConvert';
9+
import { clipboardContentToItem } from '../../utils/clipboard/convert';
1010
import { historyService } from './HistoryService';
1111

1212
export class HistoryTracker {

src/services/history/HistoryTransferQueue.ts

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import type { IHistoryAPI } from '@/api/history';
77
import { RecordNotFoundError } from '@/errors';
88
import { HistoryStorage } from '../../storage/HistoryStorage';
9-
import { HistorySyncStatus } from '@/types/clipboard';
9+
import { HistorySyncStatus, type ClipboardContent } from '@/types/clipboard';
1010
import { getHistoryFileDir } from '@/utils/fileStorage';
11+
import { historyItemToContent } from '@/utils/clipboard/convert';
1112
import { File } from 'expo-file-system';
1213

1314
export type TransferType = 'upload' | 'download';
@@ -35,6 +36,7 @@ export interface TransferTask {
3536
isImmediateTask: boolean;
3637
abortController: AbortController;
3738
userCancelled?: boolean;
39+
awaiter: Promise<ClipboardContent>;
3840
}
3941

4042
export interface TransferQueueConfig {
@@ -60,6 +62,10 @@ export class HistoryTransferQueue {
6062
private pendingTasks: TransferTask[] = [];
6163
private activeTasks: Map<string, TransferTask> = new Map();
6264
private taskStatusCallbacks: Set<TaskStatusChangedCallback> = new Set();
65+
private taskCompletionResolvers: Map<
66+
string,
67+
{ resolve: (content: ClipboardContent) => void; reject: (error: Error) => void }
68+
> = new Map();
6369

6470
private config: TransferQueueConfig;
6571
private isRunning = false;
@@ -167,6 +173,14 @@ export class HistoryTransferQueue {
167173
const item = parsed ? await this.historyStorage.getItem(parsed.hash) : null;
168174
const displayName = item?.text || profileId;
169175

176+
const key = this.getTaskKey(profileId, 'download');
177+
let resolveAwaiter: (content: ClipboardContent) => void;
178+
let rejectAwaiter: (error: Error) => void;
179+
const awaiter = new Promise<ClipboardContent>((resolve, reject) => {
180+
resolveAwaiter = resolve;
181+
rejectAwaiter = reject;
182+
});
183+
170184
const task: TransferTask = {
171185
profileId,
172186
displayName,
@@ -178,8 +192,10 @@ export class HistoryTransferQueue {
178192
failureCount: 0,
179193
isImmediateTask: isImmediate,
180194
abortController: new AbortController(),
195+
awaiter,
181196
};
182197

198+
this.taskCompletionResolvers.set(key, { resolve: resolveAwaiter!, reject: rejectAwaiter! });
183199
this.pendingTasks.push(task);
184200
this.notifyStatusChanged(task);
185201
this.signalQueue();
@@ -205,6 +221,14 @@ export class HistoryTransferQueue {
205221
const item = parsed ? await this.historyStorage.getItem(parsed.hash) : null;
206222
const displayName = item?.text || profileId;
207223

224+
const key = this.getTaskKey(profileId, 'upload');
225+
let resolveAwaiter: (content: ClipboardContent) => void;
226+
let rejectAwaiter: (error: Error) => void;
227+
const awaiter = new Promise<ClipboardContent>((resolve, reject) => {
228+
resolveAwaiter = resolve;
229+
rejectAwaiter = reject;
230+
});
231+
208232
const task: TransferTask = {
209233
profileId,
210234
displayName,
@@ -216,8 +240,10 @@ export class HistoryTransferQueue {
216240
failureCount: 0,
217241
isImmediateTask: isImmediate,
218242
abortController: new AbortController(),
243+
awaiter,
219244
};
220245

246+
this.taskCompletionResolvers.set(key, { resolve: resolveAwaiter!, reject: rejectAwaiter! });
221247
this.pendingTasks.push(task);
222248
this.notifyStatusChanged(task);
223249
this.signalQueue();
@@ -345,7 +371,6 @@ export class HistoryTransferQueue {
345371
* 执行任务
346372
*/
347373
private async executeTask(task: TransferTask): Promise<void> {
348-
// 用户取消的任务不执行
349374
if (task.userCancelled || task.status === 'cancelled') {
350375
console.log(`[HistoryTransferQueue] Task was cancelled, skipping: ${task.profileId}`);
351376
return;
@@ -355,11 +380,13 @@ export class HistoryTransferQueue {
355380
task.startedTime = Date.now();
356381
this.notifyStatusChanged(task);
357382

383+
let resultContent: ClipboardContent | undefined;
384+
358385
try {
359386
if (task.type === 'download') {
360-
await this.executeDownloadTask(task);
387+
resultContent = await this.executeDownloadTask(task);
361388
} else {
362-
await this.executeUploadTask(task);
389+
resultContent = await this.executeUploadTask(task);
363390
}
364391

365392
task.status = 'completed';
@@ -383,7 +410,6 @@ export class HistoryTransferQueue {
383410
error
384411
);
385412

386-
// 检查是否需要重试
387413
if (
388414
task.failureCount < this.config.maxConsecutiveFailures &&
389415
this.consecutiveFailures < this.config.maxConsecutiveFailures
@@ -403,13 +429,25 @@ export class HistoryTransferQueue {
403429
this.notifyStatusChanged(task);
404430
const key = this.getTaskKey(task.profileId, task.type);
405431
this.activeTasks.delete(key);
432+
433+
const resolver = this.taskCompletionResolvers.get(key);
434+
if (resolver) {
435+
this.taskCompletionResolvers.delete(key);
436+
if (task.status === 'completed' && resultContent) {
437+
resolver.resolve(resultContent);
438+
} else if (task.status === 'failed') {
439+
resolver.reject(new Error(task.errorMessage || 'Task failed'));
440+
} else {
441+
resolver.reject(new Error(`Task ${task.status}`));
442+
}
443+
}
406444
}
407445
}
408446

409447
/**
410448
* 执行下载任务
411449
*/
412-
private async executeDownloadTask(task: TransferTask): Promise<void> {
450+
private async executeDownloadTask(task: TransferTask): Promise<ClipboardContent> {
413451
if (!this.historyAPI) {
414452
throw new Error('History API not initialized');
415453
}
@@ -472,11 +510,14 @@ export class HistoryTransferQueue {
472510
await this.historyStorage.updateItem(parsed.hash, {
473511
fileUri: destinationUri,
474512
isLocalFileReady: true,
475-
// dataName: item.dataName,
476-
// text: item.text,
477513
});
478514

479515
console.log(`[HistoryTransferQueue] Download task completed: ${task.profileId}`);
516+
517+
return {
518+
...historyItemToContent(item),
519+
fileUri: destinationUri,
520+
};
480521
}
481522

482523
/**
@@ -486,7 +527,7 @@ export class HistoryTransferQueue {
486527
* 2. 如果存在,直接标记为已同步
487528
* 3. 如果不存在,执行上传
488529
*/
489-
private async executeUploadTask(task: TransferTask): Promise<void> {
530+
private async executeUploadTask(task: TransferTask): Promise<ClipboardContent> {
490531
if (!this.historyAPI) {
491532
throw new Error('History API not initialized');
492533
}
@@ -525,7 +566,7 @@ export class HistoryTransferQueue {
525566
console.log(
526567
`[HistoryTransferQueue] Record already exists on server: ${task.profileId}, marked as synced`
527568
);
528-
return;
569+
return historyItemToContent(item);
529570
}
530571
} catch (error) {
531572
// 如果是 404 错误,继续执行上传
@@ -543,7 +584,7 @@ export class HistoryTransferQueue {
543584
throw new Error(`No file to upload: ${task.profileId}`);
544585
}
545586

546-
const { historyItemToDto } = await import('@/utils/clipboard/dtoConvert');
587+
const { historyItemToDto } = await import('@/utils/clipboard/convert');
547588
const dto = historyItemToDto(item);
548589

549590
await this.historyAPI.uploadRecord(dto, item.fileUri, task.abortController.signal, (info) => {
@@ -558,6 +599,8 @@ export class HistoryTransferQueue {
558599
});
559600

560601
await this.historyStorage.updateSyncStatus(parsed.hash, HistorySyncStatus.Synced);
602+
603+
return historyItemToContent(item);
561604
}
562605

563606
/**

0 commit comments

Comments
 (0)