From c703521b6c1f501145629f14c76039cc06aff663 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 5 Dec 2017 18:56:39 +0000 Subject: [PATCH] All: Handling of unsyncable items --- ElectronClient/app/app.js | 8 + ElectronClient/app/gui/MainScreen.jsx | 7 +- ElectronClient/app/gui/Root.jsx | 4 +- ElectronClient/app/gui/StatusScreen.jsx | 147 ++++++++++++++++++ .../app/gui/SyncDisabledItemsScreen.jsx | 45 ------ ElectronClient/app/style.css | 13 ++ ElectronClient/app/theme.js | 3 + ReactNativeClient/lib/models/base-item.js | 3 +- ReactNativeClient/lib/reducer.js | 9 +- ReactNativeClient/lib/synchronizer.js | 3 +- 10 files changed, 189 insertions(+), 53 deletions(-) create mode 100644 ElectronClient/app/gui/StatusScreen.jsx delete mode 100644 ElectronClient/app/gui/SyncDisabledItemsScreen.jsx diff --git a/ElectronClient/app/app.js b/ElectronClient/app/app.js index f4ab0ebc0..a951f2c0f 100644 --- a/ElectronClient/app/app.js +++ b/ElectronClient/app/app.js @@ -259,6 +259,14 @@ class Application extends BaseApplication { }, { label: _('Tools'), submenu: [{ + label: _('Synchronisation status'), + click: () => { + this.dispatch({ + type: 'NAV_GO', + routeName: 'Status', + }); + } + },{ label: _('Options'), click: () => { this.dispatch({ diff --git a/ElectronClient/app/gui/MainScreen.jsx b/ElectronClient/app/gui/MainScreen.jsx index 74fa3d457..744996f10 100644 --- a/ElectronClient/app/gui/MainScreen.jsx +++ b/ElectronClient/app/gui/MainScreen.jsx @@ -338,17 +338,17 @@ class MainScreenComponent extends React.Component { const onViewDisabledItemsClick = () => { this.props.dispatch({ type: 'NAV_GO', - routeName: 'SyncDisabledItems', + routeName: 'Status', }); } - const messageComp = ( + const messageComp = this.props.hasDisabledSyncItems ? (
{_('Some items cannot be synchronised.')} { onViewDisabledItemsClick() }}>{_('View them now')}
- ); + ) : null; return (
@@ -381,6 +381,7 @@ const mapStateToProps = (state) => { noteVisiblePanes: state.noteVisiblePanes, folders: state.folders, notes: state.notes, + hasDisabledSyncItems: state.hasDisabledSyncItems, }; }; diff --git a/ElectronClient/app/gui/Root.jsx b/ElectronClient/app/gui/Root.jsx index c54b57799..f09c57fc5 100644 --- a/ElectronClient/app/gui/Root.jsx +++ b/ElectronClient/app/gui/Root.jsx @@ -8,7 +8,7 @@ const { Setting } = require('lib/models/setting.js'); const { MainScreen } = require('./MainScreen.min.js'); const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js'); -const { SyncDisabledItemsScreen } = require('./SyncDisabledItemsScreen.min.js'); +const { StatusScreen } = require('./StatusScreen.min.js'); const { ImportScreen } = require('./ImportScreen.min.js'); const { ConfigScreen } = require('./ConfigScreen.min.js'); const { Navigator } = require('./Navigator.min.js'); @@ -76,7 +76,7 @@ class RootComponent extends React.Component { OneDriveLogin: { screen: OneDriveLoginScreen, title: () => _('OneDrive Login') }, Import: { screen: ImportScreen, title: () => _('Import') }, Config: { screen: ConfigScreen, title: () => _('Options') }, - SyncDisabledItems: { screen: SyncDisabledItemsScreen, title: () => _('Items that cannot be synchronised') }, + Status: { screen: StatusScreen, title: () => _('Synchronisation Status') }, }; return ( diff --git a/ElectronClient/app/gui/StatusScreen.jsx b/ElectronClient/app/gui/StatusScreen.jsx new file mode 100644 index 000000000..266f0fafa --- /dev/null +++ b/ElectronClient/app/gui/StatusScreen.jsx @@ -0,0 +1,147 @@ +const React = require('react'); +const { connect } = require('react-redux'); +const { reg } = require('lib/registry.js'); +const { Setting } = require('lib/models/setting.js'); +const { bridge } = require('electron').remote.require('./bridge'); +const { Header } = require('./Header.min.js'); +const { themeStyle } = require('../theme.js'); +const { _ } = require('lib/locale.js'); +const { ReportService } = require('lib/services/report.js'); +const { BaseItem } = require('lib/models/base-item.js'); + +class StatusScreenComponent extends React.Component { + + constructor() { + super(); + this.state = { + report: [], + disabledItems: [], + }; + } + + componentWillMount() { + this.resfreshScreen(); + } + + async resfreshScreen() { + const service = new ReportService(); + const report = await service.status(Setting.value('sync.target')); + const disabledItems = await BaseItem.syncDisabledItems(); + this.setState({ + report: report, + disabledItems: disabledItems, + }); + } + + render() { + const theme = themeStyle(this.props.theme); + const style = this.props.style; + + const headerStyle = { + width: style.width, + }; + + const containerPadding = 10; + + const containerStyle = { + padding: containerPadding, + overflowY: 'auto', + height: style.height - theme.headerHeight - containerPadding * 2, + }; + + function renderSectionTitleHtml(key, title) { + return

{title}

+ } + + function renderSectionHtml(key, section) { + let itemsHtml = []; + + itemsHtml.push(renderSectionTitleHtml(section.title, section.title)); + + for (let n in section.body) { + if (!section.body.hasOwnProperty(n)) continue; + itemsHtml.push(
{section.body[n]}
); + } + + return ( +
+ {itemsHtml} +
+ ); + } + + function renderBodyHtml(report, disabledItems) { + let output = []; + let baseStyle = { + paddingLeft: 6, + paddingRight: 6, + paddingTop: 2, + paddingBottom: 2, + flex: 0, + color: theme.color, + fontSize: theme.fontSize, + }; + + let sectionsHtml = []; + + if (disabledItems.length) { + const titleHtml = [renderSectionTitleHtml('disabled_sync_items', _('Items that cannot be synchronised'))]; + const trsHtml = []; + for (let i = 0; i < disabledItems.length; i++) { + const row = disabledItems[i]; + trsHtml.push({row.item.title}{row.syncInfo.sync_disabled_reason}); + } + + sectionsHtml.push( +
+ {titleHtml} + + + + {trsHtml} + +
{_('Name')}{_('Reason')}
+
+ ); + } + + for (let i = 0; i < report.length; i++) { + let section = report[i]; + if (!section.body.length) continue; + sectionsHtml.push(renderSectionHtml(i, section)); + } + + return ( +
+ {sectionsHtml} +
+ ); + } + + console.info(this.state.disabledItems); + + let body = renderBodyHtml(this.state.report, this.state.disabledItems); + + return ( +
+
+
+ {body} +
+
+ ); + } + +} + +const mapStateToProps = (state) => { + return { + theme: state.settings.theme, + settings: state.settings, + locale: state.settings.locale, + }; +}; + +const StatusScreen = connect(mapStateToProps)(StatusScreenComponent); + +module.exports = { StatusScreen }; \ No newline at end of file diff --git a/ElectronClient/app/gui/SyncDisabledItemsScreen.jsx b/ElectronClient/app/gui/SyncDisabledItemsScreen.jsx deleted file mode 100644 index 1da491635..000000000 --- a/ElectronClient/app/gui/SyncDisabledItemsScreen.jsx +++ /dev/null @@ -1,45 +0,0 @@ -const React = require('react'); -const { connect } = require('react-redux'); -const { reg } = require('lib/registry.js'); -const { Setting } = require('lib/models/setting.js'); -const { bridge } = require('electron').remote.require('./bridge'); -const { Header } = require('./Header.min.js'); -const { themeStyle } = require('../theme.js'); -const { _ } = require('lib/locale.js'); - -class SyncDisabledItemsScreenComponent extends React.Component { - - render() { - const theme = themeStyle(this.props.theme); - const style = this.props.style; - - const headerStyle = { - width: style.width, - }; - - const containerStyle = { - padding: 10, - }; - - return ( -
-
-
-
-
- ); - } - -} - -const mapStateToProps = (state) => { - return { - theme: state.settings.theme, - settings: state.settings, - locale: state.settings.locale, - }; -}; - -const SyncDisabledItemsScreen = connect(mapStateToProps)(SyncDisabledItemsScreenComponent); - -module.exports = { SyncDisabledItemsScreen }; \ No newline at end of file diff --git a/ElectronClient/app/style.css b/ElectronClient/app/style.css index 6c2365285..a54281d27 100644 --- a/ElectronClient/app/style.css +++ b/ElectronClient/app/style.css @@ -9,6 +9,19 @@ body, textarea { overflow: hidden; } +table { + border-collapse: collapse; +} + +table th { + text-align: left; +} + +table td, table th { + padding: .5em; + border: 1px solid #ccc; +} + /* By default, the Ice Editor displays invalid characters, such as non-breaking spaces as red boxes, but since those are actually valid characters and common in imported Evernote data, we hide them here. */ diff --git a/ElectronClient/app/theme.js b/ElectronClient/app/theme.js index 1f02828de..1fe56dc2c 100644 --- a/ElectronClient/app/theme.js +++ b/ElectronClient/app/theme.js @@ -71,6 +71,9 @@ globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, { color: globalStyle.color2, }); +globalStyle.h2Style = Object.assign({}, globalStyle.textStyle); +globalStyle.h2Style.fontSize *= 1.3; + let themeCache_ = {}; function themeStyle(theme) { diff --git a/ReactNativeClient/lib/models/base-item.js b/ReactNativeClient/lib/models/base-item.js index d3e1ecf92..82017012e 100644 --- a/ReactNativeClient/lib/models/base-item.js +++ b/ReactNativeClient/lib/models/base-item.js @@ -387,7 +387,8 @@ class BaseItem extends BaseModel { const rows = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_disabled = 1'); let output = []; for (let i = 0; i < rows.length; i++) { - const item = await this.loadItem(rows[i].item_type, rows[i].id); + const item = await this.loadItem(rows[i].item_type, rows[i].item_id); + if (!item) continue; // The referenced item no longer exist output.push({ syncInfo: rows[i], item: item, diff --git a/ReactNativeClient/lib/reducer.js b/ReactNativeClient/lib/reducer.js index 2c7c37efb..bb6bcfe9c 100644 --- a/ReactNativeClient/lib/reducer.js +++ b/ReactNativeClient/lib/reducer.js @@ -25,7 +25,8 @@ const defaultState = { searchQuery: '', settings: {}, appState: 'starting', - windowContentSize: { width: 0, height: 0 }, + //windowContentSize: { width: 0, height: 0 }, + hasDisabledSyncItems: false, }; // When deleting a note, tag or folder @@ -395,6 +396,12 @@ const reducer = (state = defaultState, action) => { newState.appState = action.state; break; + case 'SYNC_HAS_DISABLED_SYNC_ITEMS': + + newState = Object.assign({}, state); + newState.hasDisabledSyncItems = true; + break; + } } catch (error) { error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action); diff --git a/ReactNativeClient/lib/synchronizer.js b/ReactNativeClient/lib/synchronizer.js index 79ba2a875..2faf767e3 100644 --- a/ReactNativeClient/lib/synchronizer.js +++ b/ReactNativeClient/lib/synchronizer.js @@ -253,8 +253,9 @@ class Synchronizer { this.logSyncOperation(action, local, remote, reason); - async function handleCannotSyncItem(syncTargetId, item, cannotSyncReason) { + const handleCannotSyncItem = async (syncTargetId, item, cannotSyncReason) => { await ItemClass.saveSyncDisabled(syncTargetId, item, cannotSyncReason); + this.dispatch({ type: 'SYNC_HAS_DISABLED_SYNC_ITEMS' }); } if (local.type_ == BaseModel.TYPE_RESOURCE && (action == 'createRemote' || (action == 'itemConflict' && remote))) {