Skip to content

Commit

Permalink
Desktop: Made sync more reliable by making it skip items that time ou…
Browse files Browse the repository at this point in the history
…t, and improved sync status screen
  • Loading branch information
laurent22 committed May 15, 2021
1 parent 0b46880 commit 15fe119
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 19 deletions.
37 changes: 31 additions & 6 deletions packages/app-desktop/gui/StatusScreen/StatusScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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) {
Expand Down
18 changes: 17 additions & 1 deletion packages/lib/Synchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions packages/lib/models/BaseItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
65 changes: 54 additions & 11 deletions packages/lib/services/ReportService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}

Expand All @@ -150,19 +192,20 @@ 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();
},
});
}

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);
}
}

Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 15fe119

Please sign in to comment.