Skip to content

Commit

Permalink
🐛 fix "share log" error due to leftover loggerDB tmp file
Browse files Browse the repository at this point in the history
e-mission/e-mission-docs#1110

After sharing the tmp DB file, we try to clear it, but if this operation fails or the app exits before we get to this, for any reason, all subsequent attempts to share the DB file would fail with error 12 (PATH_EXISTS_ERR)
A simple solution is to attempt to clear the tmp file before *and* after sharing.

I also changed this to async/await syntax to make it clearer what is happening.

Testing done:

I ran a dev build of emission on an Android test phone. From profile I clicked Share log and saw the "tmp file did not exist, that is fine..." log
The share menu came up but before dismissing it I went into app settings and Force Quit the app.
This caused a leftover tmp file that was not cleared. (I never saw "Successfully cleaned up file loggerDB" in the console)

Then I launched the app again and repeated the same steps. This time I saw "Successfully cleaned up file..." in the console. The user-visible behavior was the same (share menu popped up). After allowing the share menu to dismiss, I saw "Successfully cleaned up file loggerDB" again
  • Loading branch information
JGreenlee committed Feb 14, 2025
1 parent 157afec commit 5d8117b
Showing 1 changed file with 98 additions and 96 deletions.
194 changes: 98 additions & 96 deletions www/js/services/shareLocalDBFile.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,91 @@
import i18next from 'i18next';
import { displayError, displayErrorMsg, logDebug, logWarn } from '../plugin/logger';
import { displayError, displayErrorMsg, logDebug } from '../plugin/logger';

function localDBHelpers(fileName: string, fileExtension: string = '.txt') {
async function localCopyFile() {
return new Promise<void>((resolve, reject) => {
let pathToFile, parentDirectory;
if (window['cordova'].platformId == 'android') {
// parentDirectory: file:///data/user/0/edu.berkeley.eecs.emission/files/
parentDirectory = window['cordova'].file.dataDirectory.replace('files', 'databases');
// pathToFile: /data/user/0/edu.berkeley.eecs.emission/files/
pathToFile = parentDirectory.replace('file://', '') + fileName;
} else if (window['cordova'].platformId == 'ios') {
// parentDirectory: file:///var/mobile/Containers/Data/Application/<32-hex-digit-id>/Library/NoCloud/../
parentDirectory = window['cordova'].file.dataDirectory + '../';
pathToFile = 'LocalDatabase/' + fileName;
} else {
displayErrorMsg('Error: Unknown OS!');
throw new Error('Error: Unknown OS!');
}
function localCopyFile(fileName: string, fileExtension: string = '.txt') {
return new Promise<void>((resolve, reject) => {
let pathToFile, parentDirectory;
if (window['cordova'].platformId == 'android') {
// parentDirectory: file:///data/user/0/edu.berkeley.eecs.emission/files/
parentDirectory = window['cordova'].file.dataDirectory.replace('files', 'databases');
// pathToFile: /data/user/0/edu.berkeley.eecs.emission/files/
pathToFile = parentDirectory.replace('file://', '') + fileName;
} else if (window['cordova'].platformId == 'ios') {
// parentDirectory: file:///var/mobile/Containers/Data/Application/<32-hex-digit-id>/Library/NoCloud/../
parentDirectory = window['cordova'].file.dataDirectory + '../';
pathToFile = 'LocalDatabase/' + fileName;
} else {
displayErrorMsg('Error: Unknown OS!');
throw new Error('Error: Unknown OS!');
}

window['resolveLocalFileSystemURL'](parentDirectory, (fs) => {
// On iOS, pass in relative path to getFile https://github.com/e-mission/e-mission-phone/pull/1160#issuecomment-2192112472
// On Android, pass in absolute path to getFile https://github.com/e-mission/e-mission-phone/pull/1160#issuecomment-2204297874
fs.filesystem.root.getFile(pathToFile, { create: false, exclusive: false }, (fileEntry) => {
// logDebug(`fileEntry ${fileEntry.nativeURL} is file? ${fileEntry.isFile.toString()}`);
logDebug(`fileEntry is: ${JSON.stringify(fileEntry, null, 2)}`);
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (copyDir) => {
logDebug(`DirectoryEntry is: ${JSON.stringify(copyDir.filesystem.root, null, 2)}`);
window['resolveLocalFileSystemURL'](parentDirectory, (fs) => {
// On iOS, pass in relative path to getFile https://github.com/e-mission/e-mission-phone/pull/1160#issuecomment-2192112472
// On Android, pass in absolute path to getFile https://github.com/e-mission/e-mission-phone/pull/1160#issuecomment-2204297874
fs.filesystem.root.getFile(pathToFile, { create: false, exclusive: false }, (fileEntry) => {
// logDebug(`fileEntry ${fileEntry.nativeURL} is file? ${fileEntry.isFile.toString()}`);
logDebug(`fileEntry is: ${JSON.stringify(fileEntry, null, 2)}`);
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (copyDir) => {
logDebug(`DirectoryEntry is: ${JSON.stringify(copyDir.filesystem.root, null, 2)}`);

fileEntry.copyTo(
copyDir.filesystem.root,
fileName + fileExtension,
(res) => {
logDebug(`Res: ${res}`);
resolve();
},
(rej) => {
displayErrorMsg(`Rej: ${JSON.stringify(rej, null, 2)}`);
reject();
},
);
});
fileEntry.copyTo(
copyDir.filesystem.root,
fileName + fileExtension,
(res) => {
logDebug(`Res: ${res}`);
resolve();
},
(rej) => {
displayErrorMsg(`Rej: ${JSON.stringify(rej, null, 2)}`);
reject();
},
);
});
});
});
}
});
}

function localShareFile() {
return new Promise<void>((resolve, reject) => {
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (fs) => {
fs.filesystem.root.getFile(
fileName + fileExtension,
null,
(fileEntry) => {
const shareObj = {
files: [fileEntry.nativeURL],
message: i18next.t('shareFile-service.send-log.body-please-fill-in-what-is-wrong'),
subject: i18next.t('shareFile-service.send-log.subject-logs'),
};
window['plugins'].socialsharing.shareWithOptions(
shareObj,
(result) => {
logDebug(`Share Completed? ${result.completed}`); // On Android, most likely returns false
logDebug(`Shared to app: ${result.app}`);
resolve();
},
(error) => {
displayError(error, `Sharing failed with error`);
},
);
},
(error) => {
displayError(error, 'Error while sharing logs');
reject(error);
},
);
});
function localShareFile(fileName: string, fileExtension: string = '.txt') {
return new Promise<void>((resolve, reject) => {
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (fs) => {
fs.filesystem.root.getFile(
fileName + fileExtension,
null,
(fileEntry) => {
const shareObj = {
files: [fileEntry.nativeURL],
message: i18next.t('shareFile-service.send-log.body-please-fill-in-what-is-wrong'),
subject: i18next.t('shareFile-service.send-log.subject-logs'),
};
window['plugins'].socialsharing.shareWithOptions(
shareObj,
(result) => {
logDebug(`Share Completed? ${result.completed}`); // On Android, most likely returns false
logDebug(`Shared to app: ${result.app}`);
resolve();
},
(error) => {
displayError(error, `Sharing failed with error`);
},
);
},
(error) => {
displayError(error, 'Error while sharing logs');
reject(error);
},
);
});
}
});
}

function localClearTmpFile() {
return new Promise<void>((resolve, reject) => {
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (fs) => {
fs.filesystem.root.getFile(fileName + fileExtension, null, (fileEntry) => {
function localClearTmpFile(fileName: string, fileExtension: string = '.txt') {
return new Promise<void>((resolve, reject) => {
window['resolveLocalFileSystemURL'](window['cordova'].file.cacheDirectory, (fs) => {
fs.filesystem.root.getFile(
fileName + fileExtension,
null,
(fileEntry) => {
logDebug(`got fileEntry: ${JSON.stringify(fileEntry, null, 2)}`);
fileEntry.remove(
() => {
logDebug(`Successfully cleaned up file ${fileName}`);
Expand All @@ -93,29 +96,28 @@ function localDBHelpers(fileName: string, fileExtension: string = '.txt') {
reject(err);
},
);
});
});
},
(getFileError) => {
logDebug(`tmp file did not exist, that is fine: ${JSON.stringify(getFileError)}`);
resolve();
},
);
});
}

return {
copyFile: localCopyFile,
shareData: localShareFile,
clearTmpFile: localClearTmpFile,
};
});
}
export async function sendLocalDBFile(database: string) {

export async function sendLocalDBFile(dbFileName: string) {
alert(i18next.t('shareFile-service.send-to'));

const dataMethods = localDBHelpers(database);
dataMethods
.copyFile()
.then(dataMethods.shareData)
.then(dataMethods.clearTmpFile)
.then(() => {
logDebug(`File Shared!`);
})
.catch((err) => {
displayError(err);
});
try {
// in case there is a leftover tmp file, clear it before sharing
await localClearTmpFile(dbFileName);
await localCopyFile(dbFileName);
await localShareFile(dbFileName);
// clear the tmp file after sharing
await localClearTmpFile(dbFileName);
logDebug(`File Shared!`);
} catch (err) {
displayError(err);
}
}

0 comments on commit 5d8117b

Please sign in to comment.