@@ -29,6 +29,21 @@ export interface RooHistoryImportResult extends RooHistoryImportPaths {
2929 importedFileCount : number
3030}
3131
32+ export interface RooHistoryImportProgress {
33+ copiedFileCount : number
34+ totalFileCount : number
35+ importedTaskCount : number
36+ totalTaskCount : number
37+ currentTaskId ?: string
38+ currentFileName ?: string
39+ }
40+
41+ interface ImportableTaskPlan {
42+ taskId : string
43+ sourceTaskDirectory : string
44+ fileNames : string [ ]
45+ }
46+
3247const toComparablePath = ( candidatePath : string ) => {
3348 const resolvedPath = path . resolve ( candidatePath )
3449 return process . platform === "win32" ? resolvedPath . toLowerCase ( ) : resolvedPath
@@ -90,6 +105,70 @@ const pathExists = async (candidatePath: string) => {
90105 }
91106}
92107
108+ const getImportableTaskFileNames = async ( sourceTaskDirectory : string ) => {
109+ const fileNames : string [ ] = [ ]
110+
111+ for ( const fileName of IMPORTABLE_TASK_FILE_NAMES ) {
112+ try {
113+ await fs . access ( path . join ( sourceTaskDirectory , fileName ) )
114+ fileNames . push ( fileName )
115+ } catch ( error ) {
116+ if ( isSkippableImportError ( error ) ) {
117+ continue
118+ }
119+
120+ throw error
121+ }
122+ }
123+
124+ return fileNames
125+ }
126+
127+ const collectImportableTaskPlans = async ( sourceRoots : string [ ] ) => {
128+ const taskPlans : ImportableTaskPlan [ ] = [ ]
129+ const taskIds = new Set < string > ( )
130+
131+ for ( const sourceRoot of sourceRoots ) {
132+ const sourceTasksRoot = path . join ( sourceRoot , "tasks" )
133+ let entries : Dirent [ ]
134+
135+ try {
136+ entries = await fs . readdir ( sourceTasksRoot , { withFileTypes : true } )
137+ } catch ( error ) {
138+ const nodeError = error as NodeJS . ErrnoException
139+ if ( nodeError . code === "ENOENT" ) {
140+ continue
141+ }
142+ throw error
143+ }
144+
145+ for ( const entry of entries ) {
146+ if ( ! entry . isDirectory ( ) || entry . name . startsWith ( "." ) || entry . name . startsWith ( "_" ) ) {
147+ continue
148+ }
149+
150+ const sourceTaskDirectory = path . join ( sourceTasksRoot , entry . name )
151+ const fileNames = await getImportableTaskFileNames ( sourceTaskDirectory )
152+
153+ if ( ! fileNames . includes ( GlobalFileNames . historyItem ) ) {
154+ continue
155+ }
156+
157+ taskPlans . push ( {
158+ taskId : entry . name ,
159+ sourceTaskDirectory,
160+ fileNames,
161+ } )
162+ taskIds . add ( entry . name )
163+ }
164+ }
165+
166+ return {
167+ taskPlans,
168+ totalTaskCount : taskIds . size ,
169+ }
170+ }
171+
93172export const resolveRooHistoryImportPaths = async ( globalStoragePath : string ) : Promise < RooHistoryImportPaths > => {
94173 const zooExtensionDomain = `${ Package . publisher } .${ Package . name } `
95174 const zooStorageRoot = await getStorageBasePath ( globalStoragePath )
@@ -104,65 +183,77 @@ export const resolveRooHistoryImportPaths = async (globalStoragePath: string): P
104183 }
105184}
106185
107- export const importRooTaskHistory = async ( globalStoragePath : string ) : Promise < RooHistoryImportResult > => {
186+ export const importRooTaskHistory = async (
187+ globalStoragePath : string ,
188+ onProgress ?: ( progress : RooHistoryImportProgress ) => Promise < void > | void ,
189+ ) : Promise < RooHistoryImportResult > => {
108190 const paths = await resolveRooHistoryImportPaths ( globalStoragePath )
109191 const destinationComparablePath = toComparablePath ( paths . zooStorageRoot )
110192 const sourceRoots = paths . rooStorageRoots . filter (
111193 ( sourceRoot ) => toComparablePath ( sourceRoot ) !== destinationComparablePath ,
112194 )
113195 const destinationTasksRoot = path . join ( paths . zooStorageRoot , "tasks" )
196+ const { taskPlans, totalTaskCount } = await collectImportableTaskPlans ( sourceRoots )
114197 const importedTaskIds = new Set < string > ( )
115198 let importedFileCount = 0
199+ let totalFileCount = taskPlans . reduce ( ( count , taskPlan ) => count + taskPlan . fileNames . length , 0 )
200+ let copiedFileCount = 0
201+
202+ const reportProgress = async ( currentTaskId ?: string , currentFileName ?: string ) => {
203+ if ( ! onProgress ) {
204+ return
205+ }
206+
207+ await onProgress ( {
208+ copiedFileCount,
209+ totalFileCount,
210+ importedTaskCount : importedTaskIds . size ,
211+ totalTaskCount,
212+ currentTaskId,
213+ currentFileName,
214+ } )
215+ }
116216
117217 await fs . mkdir ( destinationTasksRoot , { recursive : true } )
118218
119- for ( const sourceRoot of sourceRoots ) {
120- const sourceTasksRoot = path . join ( sourceRoot , "tasks" )
121- let entries : Dirent [ ]
219+ await reportProgress ( )
122220
123- try {
124- entries = await fs . readdir ( sourceTasksRoot , { withFileTypes : true } )
125- } catch ( error ) {
126- const nodeError = error as NodeJS . ErrnoException
127- if ( nodeError . code === "ENOENT" ) {
128- continue
221+ for ( const taskPlan of taskPlans ) {
222+ const destinationTaskDirectory = path . join ( destinationTasksRoot , taskPlan . taskId )
223+ const destinationTaskDirectoryExisted = await pathExists ( destinationTaskDirectory )
224+ const historyItemCopied = await copyTaskFileIfPresent (
225+ taskPlan . sourceTaskDirectory ,
226+ destinationTaskDirectory ,
227+ GlobalFileNames . historyItem ,
228+ )
229+
230+ if ( ! historyItemCopied ) {
231+ totalFileCount -= taskPlan . fileNames . length
232+ if ( ! destinationTaskDirectoryExisted ) {
233+ await fs . rm ( destinationTaskDirectory , { recursive : true , force : true } )
129234 }
130- throw error
235+ await reportProgress ( taskPlan . taskId , GlobalFileNames . historyItem )
236+ continue
131237 }
132238
133- for ( const entry of entries ) {
134- if ( ! entry . isDirectory ( ) || entry . name . startsWith ( "." ) || entry . name . startsWith ( "_" ) ) {
135- continue
136- }
239+ importedTaskIds . add ( taskPlan . taskId )
240+ importedFileCount += 1
241+ copiedFileCount += 1
242+ await reportProgress ( taskPlan . taskId , GlobalFileNames . historyItem )
137243
138- const sourceTaskDirectory = path . join ( sourceTasksRoot , entry . name )
139- const destinationTaskDirectory = path . join ( destinationTasksRoot , entry . name )
140- const destinationTaskDirectoryExisted = await pathExists ( destinationTaskDirectory )
141- const historyItemCopied = await copyTaskFileIfPresent (
142- sourceTaskDirectory ,
143- destinationTaskDirectory ,
144- GlobalFileNames . historyItem ,
145- )
146-
147- if ( ! historyItemCopied ) {
148- if ( ! destinationTaskDirectoryExisted ) {
149- await fs . rm ( destinationTaskDirectory , { recursive : true , force : true } )
150- }
244+ for ( const fileName of taskPlan . fileNames ) {
245+ if ( fileName === GlobalFileNames . historyItem ) {
151246 continue
152247 }
153248
154- importedTaskIds . add ( entry . name )
155- importedFileCount += 1
156-
157- for ( const fileName of IMPORTABLE_TASK_FILE_NAMES ) {
158- if ( fileName === GlobalFileNames . historyItem ) {
159- continue
160- }
161-
162- if ( await copyTaskFileIfPresent ( sourceTaskDirectory , destinationTaskDirectory , fileName ) ) {
163- importedFileCount += 1
164- }
249+ if ( await copyTaskFileIfPresent ( taskPlan . sourceTaskDirectory , destinationTaskDirectory , fileName ) ) {
250+ importedFileCount += 1
251+ copiedFileCount += 1
252+ } else {
253+ totalFileCount -= 1
165254 }
255+
256+ await reportProgress ( taskPlan . taskId , fileName )
166257 }
167258 }
168259
0 commit comments