All: Better handling of resource download errors, and added resource info to sync status screen

pull/2238/head
Laurent Cozic 2019-12-28 20:23:38 +01:00
parent a6b3ddc7ed
commit f74db06176
4 changed files with 88 additions and 2 deletions

View File

@ -843,7 +843,11 @@ class NoteTextComponent extends React.Component {
if (item.type_ === BaseModel.TYPE_RESOURCE) {
const localState = await Resource.localState(item);
if (localState.fetch_status !== Resource.FETCH_STATUS_DONE || !!item.encryption_blob_encrypted) {
bridge().showErrorMessageBox(_('This attachment is not downloaded or not decrypted yet.'));
if (localState.fetch_status === Resource.FETCH_STATUS_ERROR) {
bridge().showErrorMessageBox(`${_('There was an error downloading this attachment:')}\n\n${localState.fetch_error}`);
} else {
bridge().showErrorMessageBox(_('This attachment is not downloaded or not decrypted yet'));
}
return;
}
const filePath = Resource.fullPath(item);

View File

@ -9,6 +9,7 @@ const { filename, safeFilename } = require('lib/path-utils.js');
const { FsDriverDummy } = require('lib/fs-driver-dummy.js');
const markdownUtils = require('lib/markdownUtils');
const JoplinError = require('lib/JoplinError');
const { _ } = require('lib/locale.js');
class Resource extends BaseItem {
static tableName() {
@ -34,6 +35,15 @@ class Resource extends BaseItem {
return this.db().selectAll(`SELECT resource_id, fetch_status FROM resource_local_states WHERE resource_id IN ("${resourceIds.join('","')}")`);
}
static errorFetchStatuses() {
return this.db().selectAll(`
SELECT title AS resource_title, resource_id, fetch_error
FROM resource_local_states
LEFT JOIN resources ON resources.id = resource_local_states.resource_id
WHERE fetch_status = ?
`, [Resource.FETCH_STATUS_ERROR]);
}
static needToBeFetched(resourceDownloadMode = null, limit = null) {
let sql = ['SELECT * FROM resources WHERE encryption_applied = 0 AND id IN (SELECT resource_id FROM resource_local_states WHERE fetch_status = ?)'];
if (resourceDownloadMode !== 'always') {
@ -48,6 +58,10 @@ class Resource extends BaseItem {
return await this.db().exec('UPDATE resource_local_states SET fetch_status = ? WHERE fetch_status = ?', [Resource.FETCH_STATUS_IDLE, Resource.FETCH_STATUS_STARTED]);
}
static resetErrorStatus(resourceId) {
return this.db().exec('UPDATE resource_local_states SET fetch_status = ?, fetch_error = "" WHERE resource_id = ?', [Resource.FETCH_STATUS_IDLE, resourceId]);
}
static fsDriver() {
if (!Resource.fsDriver_) Resource.fsDriver_ = new FsDriverDummy();
return Resource.fsDriver_;
@ -249,10 +263,33 @@ class Resource extends BaseItem {
}
static async downloadedButEncryptedBlobCount() {
const r = await this.db().selectOne('SELECT count(*) as total FROM resource_local_states WHERE fetch_status = ? AND resource_id IN (SELECT id FROM resources WHERE encryption_blob_encrypted = 1)', [Resource.FETCH_STATUS_DONE]);
const r = await this.db().selectOne(`
SELECT count(*) as total
FROM resource_local_states
WHERE fetch_status = ? AND resource_id IN (SELECT id FROM resources WHERE encryption_blob_encrypted = 1)
`, [Resource.FETCH_STATUS_DONE]);
return r ? r.total : 0;
}
static async downloadStatusCounts(status) {
const r = await this.db().selectOne(`
SELECT count(*) as total
FROM resource_local_states
WHERE fetch_status = ?
`, [status]);
return r ? r.total : 0;
}
static fetchStatusToLabel(status) {
if (status === Resource.FETCH_STATUS_IDLE) return _('Not downloaded');
if (status === Resource.FETCH_STATUS_STARTED) return _('Downloading');
if (status === Resource.FETCH_STATUS_DONE) return _('Downloaded');
if (status === Resource.FETCH_STATUS_ERROR) return _('Error');
throw new Error(`Invalid status: ${status}`);
}
}
Resource.IMAGE_MAX_DIMENSION = 1920;

View File

@ -222,6 +222,9 @@ class ResourceFetcher extends BaseService {
this.logger().info(`ResourceFetcher: Auto-added resources: ${count}`);
this.addingResources_ = false;
const errorCount = await Resource.downloadStatusCounts(Resource.FETCH_STATUS_ERROR);
if (errorCount) this.dispatch({ type: 'SYNC_HAS_DISABLED_SYNC_ITEMS' });
}
async start() {

View File

@ -5,6 +5,8 @@ const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const ResourceFetcher = require('lib/services/ResourceFetcher');
const Resource = require('lib/models/Resource');
const { _ } = require('lib/locale.js');
const { toTitleCase } = require('lib/string-utils.js');
@ -158,6 +160,46 @@ class ReportService {
sections.push(section);
}
{
section = { title: _('Attachments'), body: [], name: 'resources' };
const statuses = [Resource.FETCH_STATUS_IDLE, Resource.FETCH_STATUS_STARTED, Resource.FETCH_STATUS_DONE, Resource.FETCH_STATUS_ERROR];
for (const status of statuses) {
if (status === Resource.FETCH_STATUS_DONE) {
const downloadedButEncryptedBlobCount = await Resource.downloadedButEncryptedBlobCount();
const downloadedCount = await Resource.downloadStatusCounts(Resource.FETCH_STATUS_DONE);
section.body.push(_('%s: %d', _('Downloaded and decrypted'), downloadedCount - downloadedButEncryptedBlobCount));
section.body.push(_('%s: %d', _('Downloaded and encrypted'), downloadedButEncryptedBlobCount));
} else {
const count = await Resource.downloadStatusCounts(status);
section.body.push(_('%s: %d', Resource.fetchStatusToLabel(status), count));
}
}
sections.push(section);
}
const resourceErrorFetchStatuses = await Resource.errorFetchStatuses();
if (resourceErrorFetchStatuses.length) {
section = { title: _('Attachments that could not be downloaded'), body: [], name: 'failedResourceDownload' };
for (let i = 0; i < resourceErrorFetchStatuses.length; i++) {
const row = resourceErrorFetchStatuses[i];
section.body.push({
text: _('%s (%s): %s', row.resource_title, row.resource_id, row.fetch_error),
canRetry: true,
retryHandler: async () => {
await Resource.resetErrorStatus(row.resource_id);
ResourceFetcher.instance().autoAddResources();
},
});
}
sections.push(section);
}
section = { title: _('Sync status (synced items / total items)'), body: [] };
for (let n in r.items) {