mirror of https://github.com/laurent22/joplin.git
All: Better handling of resource download errors, and added resource info to sync status screen
parent
a6b3ddc7ed
commit
f74db06176
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue