@@ -92,6 +92,16 @@ const headerProps = {
9292 justifyContent : 'center' ,
9393} as SxProps ;
9494
95+ /** Successful import/sync puts a JSON change report in errMsg; hide raw JSON and malformed fragments in titles/messages. */
96+ function userVisibleImportErrMsg ( errMsg : string | undefined ) : string {
97+ const raw = errMsg ?. trim ( ) ?? '' ;
98+ if ( ! raw ) return '' ;
99+ if ( tryParseJSON ( raw ) !== false ) return '' ;
100+ const lead = raw . trimStart ( ) ;
101+ if ( lead . startsWith ( '[' ) || lead . startsWith ( '{' ) ) return '' ;
102+ return raw ;
103+ }
104+
95105interface ITeamSelectorProps {
96106 selectedTeamId : string ;
97107 onTeamChange : ( teamId : string ) => void ;
@@ -171,10 +181,12 @@ interface IProps {
171181 syncBuffer ?: Buffer | undefined ;
172182 syncFile ?: string | undefined ;
173183 isOpen : boolean ;
184+ offerPtf : boolean ;
174185 onOpen : ( val : boolean ) => void ;
175186}
176187export function ImportTab ( props : IProps ) {
177- const { isOpen, onOpen, project, planName, syncBuffer, syncFile } = props ;
188+ const { isOpen, onOpen, project, planName, syncBuffer, syncFile, offerPtf } =
189+ props ;
178190 const t : IImportStrings = useSelector ( importSelector , shallowEqual ) ;
179191 const ta : IActivityStateStrings = useSelector ( activitySelector , shallowEqual ) ;
180192 const ts : ISharedStrings = useSelector ( sharedSelector , shallowEqual ) ;
@@ -226,6 +238,7 @@ export function ImportTab(props: IProps) {
226238 const { getOrganizedBy } = useOrganizedBy ( ) ;
227239 const forceDataChanges = useDataChanges ( ) ;
228240 const { handleElectronImport, getElectronImportData } = useElectronImport ( ) ;
241+ const getImportFileRef = useRef ( false ) ;
229242 const headerRow = ( ) =>
230243 t . plan +
231244 '\t' +
@@ -302,9 +315,30 @@ export function ImportTab(props: IProps) {
302315 if ( isElectron && syncFile && syncBuffer ) {
303316 uploadSyncITF ( syncBuffer , syncFile ) ;
304317 } // Need to ask user if they want to import PTF or ITF
305- else setShowImportTypeSelection ( true ) ;
318+ else if ( offerPtf ) setShowImportTypeSelection ( true ) ;
319+ else {
320+ setSelectedImportType ( isOffline ? UploadType . PTF : UploadType . ITF ) ;
321+ if ( ! getImportFileRef . current )
322+ getImportFile ( isOffline ? UploadType . PTF : UploadType . ITF ) ;
323+ }
306324 } , [ ] ) ;
307325
326+ const getImportFile = async ( type : UploadType ) => {
327+ setShowImportTypeSelection ( false ) ;
328+ if ( type === UploadType . PTF && isOffline ) {
329+ // For offline PTF, we use electronImport which uses the Electron file picker
330+ getImportFileRef . current = true ;
331+ await electronImport ( ) ;
332+ getImportFileRef . current = false ;
333+ } else {
334+ setUploadVisible ( true ) ;
335+ }
336+ } ;
337+
338+ const handleImportTypeSelected = ( ) => {
339+ getImportFile ( selectedImportType ) ;
340+ } ;
341+
308342 const setImporting = ( importing : boolean , errMsg ?: string ) => {
309343 importingRef . current = importing ;
310344 setBusy ( importing ) ;
@@ -341,22 +375,6 @@ export function ImportTab(props: IProps) {
341375 } else handleActionRefused ( ) ;
342376 } ;
343377
344- const handleImportTypeSelected = ( ) => {
345- setShowImportTypeSelection ( false ) ;
346- if ( selectedImportType === UploadType . PTF && isOffline ) {
347- //this has it's own upload dialog so we don't need to setUploadVisible(true);
348- uploadPTFOffline ( ) ;
349- } else {
350- setUploadVisible ( true ) ;
351- }
352- } ;
353-
354- const uploadPTFOffline = ( ) => {
355- // For offline PTF, we use electronImport which uses the Electron file picker
356- electronImport ( ) ;
357- setUploadVisible ( false ) ;
358- } ;
359-
360378 const handleFileUpload = (
361379 files : File [ ] ,
362380 importAction : ( props : any ) => void ,
@@ -393,13 +411,10 @@ export function ImportTab(props: IProps) {
393411 } ;
394412
395413 const uploadITF = ( files : File [ ] ) => {
396- if ( ! project ) return ;
397414 handleFileUpload ( files , importProjectITFFromElectron , {
398- projectid : remoteIdNum (
399- 'project' ,
400- project ,
401- memory ?. keyMap as RecordKeyMap
402- ) ,
415+ projectid : project
416+ ? remoteIdNum ( 'project' , project , memory ?. keyMap as RecordKeyMap )
417+ : 0 ,
403418 token,
404419 errorReporter,
405420 pendingmsg : t . importPending ,
@@ -809,10 +824,11 @@ export function ImportTab(props: IProps) {
809824 //import completed ok but might have message
810825 const chdata = getChangeData ( importStatus . errMsg ) ;
811826 setChangeData ( [ ...changeData ] . concat ( chdata ) ) ;
827+ const syncExtra = userVisibleImportErrMsg ( importStatus . errMsg ) ;
812828 setImportTitle (
813829 chdata . length > 0
814830 ? t . onlineChangeReport
815- : t . importSyncDown + ' ' + importStatus . errMsg
831+ : t . importSyncDown + ' ' + syncExtra
816832 ) ;
817833 if ( remote ) forceDataChanges ( ) . then ( ( ) => setImporting ( false ) ) ;
818834 else {
@@ -839,10 +855,8 @@ export function ImportTab(props: IProps) {
839855
840856 const statusMsg = ( status : IAxiosStatus | undefined ) => {
841857 if ( ! status || status . statusMsg === 'Import Complete' ) return '' ;
842- return (
843- status ?. statusMsg +
844- ( status ?. errMsg && status ?. errMsg !== '[]' ? ': ' + status ?. errMsg : '' )
845- ) ;
858+ const extra = userVisibleImportErrMsg ( status . errMsg ) ;
859+ return status . statusMsg + ( extra ? ': ' + extra : '' ) ;
846860 } ;
847861 return (
848862 < StyledDialog
0 commit comments