mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-23 18:53:36 +02:00
Desktop: Made sync more reliable by making it skip items that time out, and improved sync status screen
This commit is contained in:
parent
0b46880a00
commit
15fe119256
@ -87,12 +87,15 @@ function StatusScreen(props: Props) {
|
||||
|
||||
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
|
||||
|
||||
let currentListKey = '';
|
||||
let listItems: any[] = [];
|
||||
for (const n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
const item = section.body[n];
|
||||
let text = '';
|
||||
|
||||
let retryLink = null;
|
||||
let itemType = null;
|
||||
if (typeof item === 'object') {
|
||||
if (item.canRetry) {
|
||||
const onClick = async () => {
|
||||
@ -107,18 +110,40 @@ function StatusScreen(props: Props) {
|
||||
);
|
||||
}
|
||||
text = item.text;
|
||||
itemType = item.type;
|
||||
} else {
|
||||
text = item;
|
||||
}
|
||||
|
||||
if (itemType === 'openList') {
|
||||
currentListKey = item.key;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemType === 'closeList') {
|
||||
itemsHtml.push(<ul key={currentListKey}>{listItems}</ul>);
|
||||
currentListKey = '';
|
||||
listItems = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!text) text = '\xa0';
|
||||
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
if (currentListKey) {
|
||||
listItems.push(
|
||||
<li style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (section.canRetryAll) {
|
||||
|
@ -28,6 +28,22 @@ interface RemoteItem {
|
||||
type_?: number;
|
||||
}
|
||||
|
||||
function isCannotSyncError(error: any): boolean {
|
||||
if (!error) return false;
|
||||
if (['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) return true;
|
||||
|
||||
// If the request times out we give up too because sometimes it's due to the
|
||||
// file being large or some other connection issues, and we don't want that
|
||||
// file to block the sync process. The user can choose to retry later on.
|
||||
//
|
||||
// message: "network timeout at: .....
|
||||
// name: "FetchError"
|
||||
// type: "request-timeout"
|
||||
if (error.type === 'request-timeout' || error.message.includes('network timeout')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default class Synchronizer {
|
||||
|
||||
private db_: any;
|
||||
@ -514,7 +530,7 @@ export default class Synchronizer {
|
||||
|
||||
await this.apiCall('put', remoteContentPath, null, { path: localResourceContentPath, source: 'file', shareId: local.share_id });
|
||||
} catch (error) {
|
||||
if (error && ['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) {
|
||||
if (isCannotSyncError(error)) {
|
||||
await handleCannotSyncItem(ItemClass, syncTargetId, local, error.message);
|
||||
action = null;
|
||||
} else {
|
||||
|
@ -578,7 +578,7 @@ function localesFromLanguageCode(languageCode: string, locales: string[]): strin
|
||||
});
|
||||
}
|
||||
|
||||
function _(s: string, ...args: any[]) {
|
||||
function _(s: string, ...args: any[]): string {
|
||||
const strings = localeStrings(currentLocale_);
|
||||
let result = strings[s];
|
||||
if (result === '' || result === undefined) result = s;
|
||||
|
@ -756,6 +756,10 @@ export default class BaseItem extends BaseModel {
|
||||
return this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
public static async saveSyncEnabled(itemType: ModelType, itemId: string) {
|
||||
await this.db().exec('DELETE FROM sync_items WHERE item_type = ? AND item_id = ?', [itemType, itemId]);
|
||||
}
|
||||
|
||||
// When an item is deleted, its associated sync_items data is not immediately deleted for
|
||||
// performance reason. So this function is used to look for these remaining sync_items and
|
||||
// delete them.
|
||||
|
@ -10,6 +10,36 @@ import Resource from '../models/Resource';
|
||||
import { _ } from '../locale';
|
||||
const { toTitleCase } = require('../string-utils.js');
|
||||
|
||||
enum CanRetryType {
|
||||
E2EE = 'e2ee',
|
||||
ResourceDownload = 'resourceDownload',
|
||||
ItemSync = 'itemSync',
|
||||
}
|
||||
|
||||
enum ReportItemType {
|
||||
OpenList = 'openList',
|
||||
CloseList = 'closeList',
|
||||
}
|
||||
|
||||
type RerportItemOrString = ReportItem | string;
|
||||
|
||||
interface ReportSection {
|
||||
title: string;
|
||||
body: RerportItemOrString[];
|
||||
name?: string;
|
||||
canRetryAll?: boolean;
|
||||
retryAllHandler?: ()=> void;
|
||||
}
|
||||
|
||||
interface ReportItem {
|
||||
type?: ReportItemType;
|
||||
key?: string;
|
||||
text?: string;
|
||||
canRetry?: boolean;
|
||||
canRetryType?: CanRetryType;
|
||||
retryHandler?: ()=> void;
|
||||
}
|
||||
|
||||
export default class ReportService {
|
||||
csvEscapeCell(cell: string) {
|
||||
cell = this.csvValueToString(cell);
|
||||
@ -110,10 +140,10 @@ export default class ReportService {
|
||||
return output;
|
||||
}
|
||||
|
||||
async status(syncTarget: number) {
|
||||
async status(syncTarget: number): Promise<ReportSection[]> {
|
||||
const r = await this.syncStatus(syncTarget);
|
||||
const sections = [];
|
||||
let section: any = null;
|
||||
const sections: ReportSection[] = [];
|
||||
let section: ReportSection = null;
|
||||
|
||||
const disabledItems = await BaseItem.syncDisabledItems(syncTarget);
|
||||
|
||||
@ -122,17 +152,29 @@ export default class ReportService {
|
||||
|
||||
section.body.push(_('These items will remain on the device but will not be uploaded to the sync target. In order to find these items, either search for the title or the ID (which is displayed in brackets above).'));
|
||||
|
||||
section.body.push('');
|
||||
section.body.push({ type: ReportItemType.OpenList, key: 'disabledSyncItems' });
|
||||
|
||||
for (let i = 0; i < disabledItems.length; i++) {
|
||||
const row = disabledItems[i];
|
||||
let msg: string = '';
|
||||
if (row.location === BaseItem.SYNC_ITEM_LOCATION_LOCAL) {
|
||||
section.body.push(_('%s (%s) could not be uploaded: %s', row.item.title, row.item.id, row.syncInfo.sync_disabled_reason));
|
||||
msg = _('%s (%s) could not be uploaded: %s', row.item.title, row.item.id, row.syncInfo.sync_disabled_reason);
|
||||
} else {
|
||||
section.body.push(_('Item "%s" could not be downloaded: %s', row.syncInfo.item_id, row.syncInfo.sync_disabled_reason));
|
||||
msg = _('Item "%s" could not be downloaded: %s', row.syncInfo.item_id, row.syncInfo.sync_disabled_reason);
|
||||
}
|
||||
|
||||
section.body.push({
|
||||
text: msg,
|
||||
canRetry: true,
|
||||
canRetryType: CanRetryType.ItemSync,
|
||||
retryHandler: async () => {
|
||||
await BaseItem.saveSyncEnabled(row.item.type_, row.item.id);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
section.body.push({ type: ReportItemType.CloseList });
|
||||
|
||||
sections.push(section);
|
||||
}
|
||||
|
||||
@ -150,7 +192,7 @@ export default class ReportService {
|
||||
section.body.push({
|
||||
text: _('%s: %s', toTitleCase(BaseModel.modelTypeToName(row.type_)), row.id),
|
||||
canRetry: true,
|
||||
canRetryType: 'e2ee',
|
||||
canRetryType: CanRetryType.E2EE,
|
||||
retryHandler: async () => {
|
||||
await DecryptionWorker.instance().clearDisabledItem(row.type_, row.id);
|
||||
void DecryptionWorker.instance().scheduleStart();
|
||||
@ -158,11 +200,12 @@ export default class ReportService {
|
||||
});
|
||||
}
|
||||
|
||||
const retryHandlers: any[] = [];
|
||||
const retryHandlers: Function[] = [];
|
||||
|
||||
for (let i = 0; i < section.body.length; i++) {
|
||||
if (section.body[i].canRetry) {
|
||||
retryHandlers.push(section.body[i].retryHandler);
|
||||
const item: RerportItemOrString = section.body[i];
|
||||
if (typeof item !== 'string' && item.canRetry) {
|
||||
retryHandlers.push(item.retryHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +253,7 @@ export default class ReportService {
|
||||
section.body.push({
|
||||
text: _('%s (%s): %s', row.resource_title, row.resource_id, row.fetch_error),
|
||||
canRetry: true,
|
||||
canRetryType: 'resourceDownload',
|
||||
canRetryType: CanRetryType.ResourceDownload,
|
||||
retryHandler: async () => {
|
||||
await Resource.resetErrorStatus(row.resource_id);
|
||||
void ResourceFetcher.instance().autoAddResources();
|
||||
|
Loading…
x
Reference in New Issue
Block a user