@@ -66,10 +66,9 @@ export async function downloadJson(filename: string, data: any): Promise<void> {
6666 const json = JSON . stringify ( data , null , 2 ) ;
6767 const blob = new Blob ( [ json ] , { type : "application/json" } ) ;
6868
69- const w : any = window as any ;
70- if ( w && typeof w . showSaveFilePicker === "function" ) {
69+ if ( typeof window ?. showSaveFilePicker === "function" ) {
7170 try {
72- const handle = await w . showSaveFilePicker ( {
71+ const handle = await window . showSaveFilePicker ( {
7372 suggestedName : filename ,
7473 types : [
7574 {
@@ -82,11 +81,28 @@ export async function downloadJson(filename: string, data: any): Promise<void> {
8281 await writable . write ( blob ) ;
8382 await writable . close ( ) ;
8483 return ;
85- } catch ( err : any ) {
86- if ( err && ( err . name === "AbortError" || err . name === "NotAllowedError" ) ) {
87- showWarningToast ( t ( 'toasts.exportCanceled' , { reason : err . name } ) ) ;
84+ } catch ( err : unknown ) {
85+ const isError = err instanceof Error ;
86+ const errorName = isError ? err . name : 'Unknown' ;
87+ const errorMessage = isError ? err . message : '' ;
88+
89+ // Check if this is a file system error disguised as AbortError
90+ const isFileSystemError = errorName === "AbortError" && (
91+ errorMessage . includes ( 'Failed to create' ) ||
92+ errorMessage . includes ( 'Failed to truncate' ) ||
93+ errorMessage . includes ( 'permission' ) ||
94+ errorMessage . includes ( 'access' ) ||
95+ errorMessage . includes ( 'denied' )
96+ ) ;
97+
98+ // Only treat as user cancellation if it's truly a user abort
99+ if ( errorName === "AbortError" && ! isFileSystemError ) {
88100 return ;
89101 }
102+
103+ const reason = isError ? `${ errorName } : ${ errorMessage } ` : 'Unknown error' ;
104+ showErrorToast ( t ( 'toasts.failedToExportJson' , { reason } ) , { duration : 10000 } ) ;
105+ return ;
90106 }
91107 }
92108
@@ -102,18 +118,19 @@ export async function downloadJson(filename: string, data: any): Promise<void> {
102118 showSuccessToast ( t ( 'toasts.exportedToDownloads' , { filename } ) ) ;
103119 } catch ( e ) {
104120 console . warn ( `${ APP_NAME } : downloadJson failed` , e ) ;
105- showErrorToast ( t ( 'toasts.failedToExportJson' ) ) ;
121+ const isError = e instanceof Error ;
122+ const reason = isError ? `${ e . name } : ${ e . message } ` : 'Unknown error' ;
123+ showErrorToast ( t ( 'toasts.failedToExportJson' , { reason } ) , { duration : 10000 } ) ;
106124 }
107125}
108126
109127export async function uploadJson < T > ( ) : Promise < T | null > {
110128 try {
111- const w : any = window as any ;
112129 let fileContent : string | undefined ;
113130
114- if ( w && typeof w . showOpenFilePicker === "function" ) {
131+ if ( typeof window ? .showOpenFilePicker === "function" ) {
115132 try {
116- const [ handle ] = await w . showOpenFilePicker ( {
133+ const [ handle ] = await window . showOpenFilePicker ( {
117134 multiple : false ,
118135 types : [
119136 {
@@ -124,11 +141,15 @@ export async function uploadJson<T>(): Promise<T | null> {
124141 } ) ;
125142 const file = await handle . getFile ( ) ;
126143 fileContent = await file . text ( ) ;
127- } catch ( err : any ) {
128- if ( err && ( err . name === "AbortError" || err . name === "NotAllowedError" ) ) {
129- showWarningToast ( t ( 'toasts.importCanceled' , { reason : err . name } ) ) ;
144+ } catch ( err : unknown ) {
145+ const isError = err instanceof Error ;
146+ const errorName = isError ? err . name : 'Unknown' ;
147+ if ( errorName === "AbortError" ) {
130148 return null ;
131149 }
150+ const reason = isError ? `${ err . name } : ${ err . message } ` : 'Unknown error' ;
151+ showErrorToast ( t ( 'toasts.failedToImportJson' , { reason } ) , { duration : 10000 } ) ;
152+ return null ;
132153 }
133154 } else {
134155 // Fallback for browsers that don't support showOpenFilePicker
@@ -141,10 +162,27 @@ export async function uploadJson<T>(): Promise<T | null> {
141162 if ( input . files ?. length ) {
142163 try {
143164 const file = input . files [ 0 ] ;
165+
166+ if ( file . size === 0 ) {
167+ showErrorToast ( t ( 'toasts.failedToImportJsonAccess' ) , { duration : 10000 } ) ;
168+ resolve ( null ) ;
169+ return ;
170+ }
171+
144172 const text = await file . text ( ) ;
173+
174+ if ( text . length === 0 ) {
175+ showErrorToast ( t ( 'toasts.failedToImportJsonAccess' ) , { duration : 10000 } ) ;
176+ resolve ( null ) ;
177+ return ;
178+ }
179+
145180 resolve ( text ) ;
146181 } catch ( readErr ) {
147182 console . error ( `${ APP_NAME } : file read failed` , readErr ) ;
183+ const isError = readErr instanceof Error ;
184+ const reason = isError ? `${ readErr . name } : ${ readErr . message } ` : 'Unknown error' ;
185+ showErrorToast ( t ( 'toasts.failedToImportJson' , { reason } ) , { duration : 10000 } ) ;
148186 resolve ( null ) ;
149187 }
150188 } else {
@@ -166,7 +204,9 @@ export async function uploadJson<T>(): Promise<T | null> {
166204 return data as T ;
167205 } catch ( e ) {
168206 console . warn ( `${ APP_NAME } : uploadJson failed` , e ) ;
169- showErrorToast ( t ( 'toasts.failedToImportJson' ) ) ;
207+ const isError = e instanceof Error ;
208+ const reason = isError ? `${ e . name } : ${ e . message } ` : 'Unknown error' ;
209+ showErrorToast ( t ( 'toasts.failedToImportJson' , { reason } ) , { duration : 10000 } ) ;
170210 return null ;
171211 }
172212}
0 commit comments