mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Save window state
This commit is contained in:
parent
bf916d5b9b
commit
dbb5599b0f
@ -499,12 +499,16 @@ msgstr ""
|
|||||||
msgid "Help"
|
msgid "Help"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Documentation"
|
msgid "Website and documentation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "About Joplin"
|
msgid "About Joplin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid "%s %s (%s, %s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -550,12 +550,17 @@ msgstr "Options"
|
|||||||
msgid "Help"
|
msgid "Help"
|
||||||
msgstr "Aide"
|
msgstr "Aide"
|
||||||
|
|
||||||
msgid "Documentation"
|
#, fuzzy
|
||||||
|
msgid "Website and documentation"
|
||||||
msgstr "Documentation"
|
msgstr "Documentation"
|
||||||
|
|
||||||
msgid "About Joplin"
|
msgid "About Joplin"
|
||||||
msgstr "A props de Joplin"
|
msgstr "A props de Joplin"
|
||||||
|
|
||||||
|
#, fuzzy, javascript-format
|
||||||
|
msgid "%s %s (%s, %s)"
|
||||||
|
msgstr "%s %s (%s)"
|
||||||
|
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr "OK"
|
msgstr "OK"
|
||||||
|
|
||||||
|
@ -499,12 +499,16 @@ msgstr ""
|
|||||||
msgid "Help"
|
msgid "Help"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Documentation"
|
msgid "Website and documentation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "About Joplin"
|
msgid "About Joplin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid "%s %s (%s, %s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -29,7 +29,20 @@ class ElectronAppWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createWindow() {
|
createWindow() {
|
||||||
this.win_ = new BrowserWindow({width: 800, height: 600})
|
const windowStateKeeper = require('electron-window-state');
|
||||||
|
|
||||||
|
// Load the previous state with fallback to defaults
|
||||||
|
const windowState = windowStateKeeper({
|
||||||
|
defaultWidth: 800,
|
||||||
|
defaultHeight: 600,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.win_ = new BrowserWindow({
|
||||||
|
'x': windowState.x,
|
||||||
|
'y': windowState.y,
|
||||||
|
'width': windowState.width,
|
||||||
|
'height': windowState.height
|
||||||
|
})
|
||||||
|
|
||||||
this.win_.loadURL(url.format({
|
this.win_.loadURL(url.format({
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
pathname: path.join(__dirname, 'index.html'),
|
||||||
@ -42,6 +55,11 @@ class ElectronAppWrapper {
|
|||||||
this.win_.on('closed', () => {
|
this.win_.on('closed', () => {
|
||||||
this.win_ = null
|
this.win_ = null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Let us register listeners on the window, so we can update the state
|
||||||
|
// automatically (the listeners will be removed when the window is closed)
|
||||||
|
// and restore the maximized or full screen state
|
||||||
|
windowState.manage(this.win_);
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForElectronAppReady() {
|
async waitForElectronAppReady() {
|
||||||
|
@ -3,6 +3,7 @@ require('app-module-path').addPath(__dirname);
|
|||||||
const { BaseApplication } = require('lib/BaseApplication');
|
const { BaseApplication } = require('lib/BaseApplication');
|
||||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||||
const { Setting } = require('lib/models/setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
|
const { shim } = require('lib/shim.js');
|
||||||
const { BaseModel } = require('lib/base-model.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { _, setLocale } = require('lib/locale.js');
|
const { _, setLocale } = require('lib/locale.js');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
@ -30,6 +31,7 @@ const appDefaultState = Object.assign({}, defaultState, {
|
|||||||
fileToImport: null,
|
fileToImport: null,
|
||||||
windowCommand: null,
|
windowCommand: null,
|
||||||
noteVisiblePanes: ['editor', 'viewer'],
|
noteVisiblePanes: ['editor', 'viewer'],
|
||||||
|
windowContentSize: bridge().windowContentSize(),
|
||||||
});
|
});
|
||||||
|
|
||||||
class Application extends BaseApplication {
|
class Application extends BaseApplication {
|
||||||
@ -231,7 +233,7 @@ class Application extends BaseApplication {
|
|||||||
}, {
|
}, {
|
||||||
label: _('Help'),
|
label: _('Help'),
|
||||||
submenu: [{
|
submenu: [{
|
||||||
label: _('Documentation'),
|
label: _('Website and documentation'),
|
||||||
accelerator: 'F1',
|
accelerator: 'F1',
|
||||||
click () { bridge().openExternal('http://joplin.cozic.net') }
|
click () { bridge().openExternal('http://joplin.cozic.net') }
|
||||||
}, {
|
}, {
|
||||||
@ -242,7 +244,7 @@ class Application extends BaseApplication {
|
|||||||
p.description,
|
p.description,
|
||||||
'',
|
'',
|
||||||
'Copyright © 2016-2017',
|
'Copyright © 2016-2017',
|
||||||
_('%s %s (%s)', p.name, p.version, Setting.value('env')),
|
_('%s %s (%s, %s)', p.name, p.version, Setting.value('env'), process.platform),
|
||||||
];
|
];
|
||||||
bridge().showMessageBox({
|
bridge().showMessageBox({
|
||||||
message: message.join('\n'),
|
message: message.join('\n'),
|
||||||
@ -278,6 +280,16 @@ class Application extends BaseApplication {
|
|||||||
|
|
||||||
this.initRedux();
|
this.initRedux();
|
||||||
|
|
||||||
|
// const windowSize = Setting.value('windowSize');
|
||||||
|
// const width = windowSize && windowSize.width ? windowSize.width : 800;
|
||||||
|
// const height = windowSize && windowSize.height ? windowSize.height : 800;
|
||||||
|
// bridge().windowSetSize(width, height);
|
||||||
|
|
||||||
|
// this.store().dispatch({
|
||||||
|
// type: 'WINDOW_CONTENT_SIZE_SET',
|
||||||
|
// size: bridge().windowContentSize(),
|
||||||
|
// });
|
||||||
|
|
||||||
// Since the settings need to be loaded before the store is created, it will never
|
// Since the settings need to be loaded before the store is created, it will never
|
||||||
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
|
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
|
||||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||||
@ -297,13 +309,17 @@ class Application extends BaseApplication {
|
|||||||
id: Setting.value('activeFolderId'),
|
id: Setting.value('activeFolderId'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const runAutoUpdateCheck = function() {
|
// Note: Auto-update currently doesn't work in Linux: it downloads the update
|
||||||
bridge().checkForUpdatesAndNotify(Setting.value('profileDir') + '/log-autoupdater.txt');
|
// but then doesn't install it on exit.
|
||||||
}
|
if (shim.isWindows() || shim.isMac()) {
|
||||||
|
const runAutoUpdateCheck = function() {
|
||||||
|
bridge().checkForUpdatesAndNotify(Setting.value('profileDir') + '/log-autoupdater.txt');
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => { runAutoUpdateCheck() }, 5000);
|
setTimeout(() => { runAutoUpdateCheck() }, 5000);
|
||||||
// For those who leave the app always open
|
// For those who leave the app always open
|
||||||
setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000);
|
setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,17 @@ class Bridge {
|
|||||||
return { width: s[0], height: s[1] };
|
return { width: s[0], height: s[1] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
windowSize() {
|
||||||
|
if (!this.window()) return { width: 0, height: 0 };
|
||||||
|
const s = this.window().getSize();
|
||||||
|
return { width: s[0], height: s[1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
windowSetSize(width, height) {
|
||||||
|
if (!this.window()) return;
|
||||||
|
return this.window().setSize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
showOpenDialog(options) {
|
showOpenDialog(options) {
|
||||||
const {dialog} = require('electron');
|
const {dialog} = require('electron');
|
||||||
return dialog.showOpenDialog(options);
|
return dialog.showOpenDialog(options);
|
||||||
|
@ -113,7 +113,9 @@ class NoteListComponent extends React.Component {
|
|||||||
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
||||||
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted);
|
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted);
|
||||||
|
|
||||||
return <div key={item.id} style={style}>
|
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||||
|
// item is changed via sync.
|
||||||
|
return <div key={item.id + '_' + item.todo_completed} style={style}>
|
||||||
{checkbox}
|
{checkbox}
|
||||||
<a
|
<a
|
||||||
data-id={item.id}
|
data-id={item.id}
|
||||||
|
4
ElectronClient/app/gui/NoteList.min.js
vendored
4
ElectronClient/app/gui/NoteList.min.js
vendored
@ -115,9 +115,11 @@ class NoteListComponent extends React.Component {
|
|||||||
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
||||||
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted);
|
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted);
|
||||||
|
|
||||||
|
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||||
|
// item is changed via sync.
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
'div',
|
'div',
|
||||||
{ key: item.id, style: style },
|
{ key: item.id + '_' + item.todo_completed, style: style },
|
||||||
checkbox,
|
checkbox,
|
||||||
React.createElement(
|
React.createElement(
|
||||||
'a',
|
'a',
|
||||||
|
@ -4,6 +4,7 @@ const { createStore } = require('redux');
|
|||||||
const { connect, Provider } = require('react-redux');
|
const { connect, Provider } = require('react-redux');
|
||||||
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
const { Setting } = require('lib/models/setting.js');
|
||||||
|
|
||||||
const { MainScreen } = require('./MainScreen.min.js');
|
const { MainScreen } = require('./MainScreen.min.js');
|
||||||
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
||||||
@ -30,11 +31,6 @@ async function initialize(dispatch) {
|
|||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
store.dispatch({
|
|
||||||
type: 'WINDOW_CONTENT_SIZE_SET',
|
|
||||||
size: bridge().windowContentSize(),
|
|
||||||
});
|
|
||||||
|
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'NOTE_VISIBLE_PANES_SET',
|
type: 'NOTE_VISIBLE_PANES_SET',
|
||||||
panes: Setting.value('noteVisiblePanes'),
|
panes: Setting.value('noteVisiblePanes'),
|
||||||
|
6
ElectronClient/app/gui/Root.min.js
vendored
6
ElectronClient/app/gui/Root.min.js
vendored
@ -4,6 +4,7 @@ const { createStore } = require('redux');
|
|||||||
const { connect, Provider } = require('react-redux');
|
const { connect, Provider } = require('react-redux');
|
||||||
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
const { Setting } = require('lib/models/setting.js');
|
||||||
|
|
||||||
const { MainScreen } = require('./MainScreen.min.js');
|
const { MainScreen } = require('./MainScreen.min.js');
|
||||||
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
||||||
@ -30,11 +31,6 @@ async function initialize(dispatch) {
|
|||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
store.dispatch({
|
|
||||||
type: 'WINDOW_CONTENT_SIZE_SET',
|
|
||||||
size: bridge().windowContentSize()
|
|
||||||
});
|
|
||||||
|
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'NOTE_VISIBLE_PANES_SET',
|
type: 'NOTE_VISIBLE_PANES_SET',
|
||||||
panes: Setting.value('noteVisiblePanes')
|
panes: Setting.value('noteVisiblePanes')
|
||||||
|
@ -110,7 +110,7 @@ class Setting extends BaseModel {
|
|||||||
|
|
||||||
this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
|
this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
|
||||||
|
|
||||||
c.value = this.formatValue(key, value);
|
c.value = value;
|
||||||
|
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'SETTING_UPDATE_ONE',
|
type: 'SETTING_UPDATE_ONE',
|
||||||
@ -143,6 +143,7 @@ class Setting extends BaseModel {
|
|||||||
if (md.type == Setting.TYPE_INT) return value.toFixed(0);
|
if (md.type == Setting.TYPE_INT) return value.toFixed(0);
|
||||||
if (md.type == Setting.TYPE_BOOL) return value ? '1' : '0';
|
if (md.type == Setting.TYPE_BOOL) return value ? '1' : '0';
|
||||||
if (md.type == Setting.TYPE_ARRAY) return value ? JSON.stringify(value) : '[]';
|
if (md.type == Setting.TYPE_ARRAY) return value ? JSON.stringify(value) : '[]';
|
||||||
|
if (md.type == Setting.TYPE_OBJECT) return value ? JSON.stringify(value) : '{}';
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,11 +163,19 @@ class Setting extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (md.type === Setting.TYPE_ARRAY) {
|
if (md.type === Setting.TYPE_ARRAY) {
|
||||||
|
if (!value) return [];
|
||||||
if (Array.isArray(value)) return value;
|
if (Array.isArray(value)) return value;
|
||||||
if (typeof value === 'string') return JSON.parse(value);
|
if (typeof value === 'string') return JSON.parse(value);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (md.type === Setting.TYPE_OBJECT) {
|
||||||
|
if (!value) return {};
|
||||||
|
if (typeof value === 'object') return value;
|
||||||
|
if (typeof value === 'string') return JSON.parse(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +317,7 @@ class Setting extends BaseModel {
|
|||||||
if (typeId === Setting.TYPE_STRING) return 'string';
|
if (typeId === Setting.TYPE_STRING) return 'string';
|
||||||
if (typeId === Setting.TYPE_BOOL) return 'bool';
|
if (typeId === Setting.TYPE_BOOL) return 'bool';
|
||||||
if (typeId === Setting.TYPE_ARRAY) return 'array';
|
if (typeId === Setting.TYPE_ARRAY) return 'array';
|
||||||
|
if (typeId === Setting.TYPE_OBJECT) return 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -320,6 +330,7 @@ Setting.TYPE_INT = 1;
|
|||||||
Setting.TYPE_STRING = 2;
|
Setting.TYPE_STRING = 2;
|
||||||
Setting.TYPE_BOOL = 3;
|
Setting.TYPE_BOOL = 3;
|
||||||
Setting.TYPE_ARRAY = 4;
|
Setting.TYPE_ARRAY = 4;
|
||||||
|
Setting.TYPE_OBJECT = 5;
|
||||||
|
|
||||||
Setting.THEME_LIGHT = 1;
|
Setting.THEME_LIGHT = 1;
|
||||||
Setting.THEME_DARK = 2;
|
Setting.THEME_DARK = 2;
|
||||||
|
@ -10,86 +10,98 @@ shim.isReactNative = () => {
|
|||||||
return !shim.isNode();
|
return !shim.isNode();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shim.isLinux = () => {
|
||||||
|
return process && process.platform === 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
shim.isWindows = () => {
|
||||||
|
return process && process.platform === 'win32';
|
||||||
|
}
|
||||||
|
|
||||||
|
shim.isMac = () => {
|
||||||
|
return process && process.platform === 'darwin';
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/cheton/is-electron
|
// https://github.com/cheton/is-electron
|
||||||
shim.isElectron = () => {
|
shim.isElectron = () => {
|
||||||
// Renderer process
|
// Renderer process
|
||||||
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
|
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main process
|
// Main process
|
||||||
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
|
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect the user agent when the `nodeIntegration` option is set to true
|
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||||
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
|
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node requests can go wrong is so many different ways and with so
|
// Node requests can go wrong is so many different ways and with so
|
||||||
// many different error messages... This handler inspects the error
|
// many different error messages... This handler inspects the error
|
||||||
// and decides whether the request can safely be repeated or not.
|
// and decides whether the request can safely be repeated or not.
|
||||||
function fetchRequestCanBeRetried(error) {
|
function fetchRequestCanBeRetried(error) {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
|
|
||||||
// Unfortunately the error 'Network request failed' doesn't have a type
|
// Unfortunately the error 'Network request failed' doesn't have a type
|
||||||
// or error code, so hopefully that message won't change and is not localized
|
// or error code, so hopefully that message won't change and is not localized
|
||||||
if (error.message == 'Network request failed') return true;
|
if (error.message == 'Network request failed') return true;
|
||||||
|
|
||||||
// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up"
|
// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up"
|
||||||
if (error.code == 'ECONNRESET') return true;
|
if (error.code == 'ECONNRESET') return true;
|
||||||
|
|
||||||
// OneDrive (or Node?) sometimes sends back a "not found" error for resources
|
// OneDrive (or Node?) sometimes sends back a "not found" error for resources
|
||||||
// that definitely exist and in this case repeating the request works.
|
// that definitely exist and in this case repeating the request works.
|
||||||
// Error is:
|
// Error is:
|
||||||
// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443
|
// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443
|
||||||
if (error.code == 'ENOTFOUND') return true;
|
if (error.code == 'ENOTFOUND') return true;
|
||||||
|
|
||||||
// network timeout at: https://public-ch3302...859f9b0e3ab.md
|
// network timeout at: https://public-ch3302...859f9b0e3ab.md
|
||||||
if (error.message && error.message.indexOf('network timeout') === 0) return true;
|
if (error.message && error.message.indexOf('network timeout') === 0) return true;
|
||||||
|
|
||||||
// name: 'FetchError',
|
// name: 'FetchError',
|
||||||
// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443',
|
// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443',
|
||||||
// type: 'system',
|
// type: 'system',
|
||||||
// errno: 'EAI_AGAIN',
|
// errno: 'EAI_AGAIN',
|
||||||
// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443
|
// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443
|
||||||
//
|
//
|
||||||
// It's a Microsoft error: "A temporary failure in name resolution occurred."
|
// It's a Microsoft error: "A temporary failure in name resolution occurred."
|
||||||
if (error.code == 'EAI_AGAIN') return true;
|
if (error.code == 'EAI_AGAIN') return true;
|
||||||
|
|
||||||
// request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443
|
// request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443
|
||||||
// Code: ETIMEDOUT
|
// Code: ETIMEDOUT
|
||||||
if (error.code === 'ETIMEDOUT') return true;
|
if (error.code === 'ETIMEDOUT') return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shim.fetchWithRetry = async function(fetchFn, options = null) {
|
shim.fetchWithRetry = async function(fetchFn, options = null) {
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.timeout) options.timeout = 1000 * 120; // ms
|
if (!options.timeout) options.timeout = 1000 * 120; // ms
|
||||||
if (!('maxRetry' in options)) options.maxRetry = 5;
|
if (!('maxRetry' in options)) options.maxRetry = 5;
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
const response = await fetchFn();
|
const response = await fetchFn();
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (fetchRequestCanBeRetried(error)) {
|
if (fetchRequestCanBeRetried(error)) {
|
||||||
retryCount++;
|
retryCount++;
|
||||||
if (retryCount > options.maxRetry) throw error;
|
if (retryCount > options.maxRetry) throw error;
|
||||||
await time.sleep(retryCount * 3);
|
await time.sleep(retryCount * 3);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
27
ElectronClient/app/package-lock.json
generated
27
ElectronClient/app/package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Joplin",
|
"name": "Joplin",
|
||||||
"version": "0.10.0",
|
"version": "0.10.5",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1168,6 +1168,11 @@
|
|||||||
"mimic-response": "1.0.0"
|
"mimic-response": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-equal": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
|
||||||
|
},
|
||||||
"deep-extend": {
|
"deep-extend": {
|
||||||
"version": "0.4.2",
|
"version": "0.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||||
@ -1612,6 +1617,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"electron-window-state": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-azT9wxs4UU3+yLfI97XUrdtnYy0=",
|
||||||
|
"requires": {
|
||||||
|
"deep-equal": "1.0.1",
|
||||||
|
"jsonfile": "2.4.0",
|
||||||
|
"mkdirp": "0.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jsonfile": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||||
|
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "4.1.11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"electron-context-menu": "^0.9.1",
|
"electron-context-menu": "^0.9.1",
|
||||||
"electron-log": "^2.2.11",
|
"electron-log": "^2.2.11",
|
||||||
"electron-updater": "^2.16.1",
|
"electron-updater": "^2.16.1",
|
||||||
|
"electron-window-state": "^4.1.1",
|
||||||
"follow-redirects": "^1.2.5",
|
"follow-redirects": "^1.2.5",
|
||||||
"form-data": "^2.3.1",
|
"form-data": "^2.3.1",
|
||||||
"fs-extra": "^4.0.2",
|
"fs-extra": "^4.0.2",
|
||||||
|
@ -110,7 +110,7 @@ class Setting extends BaseModel {
|
|||||||
|
|
||||||
this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
|
this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
|
||||||
|
|
||||||
c.value = this.formatValue(key, value);
|
c.value = value;
|
||||||
|
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'SETTING_UPDATE_ONE',
|
type: 'SETTING_UPDATE_ONE',
|
||||||
@ -143,6 +143,7 @@ class Setting extends BaseModel {
|
|||||||
if (md.type == Setting.TYPE_INT) return value.toFixed(0);
|
if (md.type == Setting.TYPE_INT) return value.toFixed(0);
|
||||||
if (md.type == Setting.TYPE_BOOL) return value ? '1' : '0';
|
if (md.type == Setting.TYPE_BOOL) return value ? '1' : '0';
|
||||||
if (md.type == Setting.TYPE_ARRAY) return value ? JSON.stringify(value) : '[]';
|
if (md.type == Setting.TYPE_ARRAY) return value ? JSON.stringify(value) : '[]';
|
||||||
|
if (md.type == Setting.TYPE_OBJECT) return value ? JSON.stringify(value) : '{}';
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,11 +163,19 @@ class Setting extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (md.type === Setting.TYPE_ARRAY) {
|
if (md.type === Setting.TYPE_ARRAY) {
|
||||||
|
if (!value) return [];
|
||||||
if (Array.isArray(value)) return value;
|
if (Array.isArray(value)) return value;
|
||||||
if (typeof value === 'string') return JSON.parse(value);
|
if (typeof value === 'string') return JSON.parse(value);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (md.type === Setting.TYPE_OBJECT) {
|
||||||
|
if (!value) return {};
|
||||||
|
if (typeof value === 'object') return value;
|
||||||
|
if (typeof value === 'string') return JSON.parse(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +317,7 @@ class Setting extends BaseModel {
|
|||||||
if (typeId === Setting.TYPE_STRING) return 'string';
|
if (typeId === Setting.TYPE_STRING) return 'string';
|
||||||
if (typeId === Setting.TYPE_BOOL) return 'bool';
|
if (typeId === Setting.TYPE_BOOL) return 'bool';
|
||||||
if (typeId === Setting.TYPE_ARRAY) return 'array';
|
if (typeId === Setting.TYPE_ARRAY) return 'array';
|
||||||
|
if (typeId === Setting.TYPE_OBJECT) return 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -320,6 +330,7 @@ Setting.TYPE_INT = 1;
|
|||||||
Setting.TYPE_STRING = 2;
|
Setting.TYPE_STRING = 2;
|
||||||
Setting.TYPE_BOOL = 3;
|
Setting.TYPE_BOOL = 3;
|
||||||
Setting.TYPE_ARRAY = 4;
|
Setting.TYPE_ARRAY = 4;
|
||||||
|
Setting.TYPE_OBJECT = 5;
|
||||||
|
|
||||||
Setting.THEME_LIGHT = 1;
|
Setting.THEME_LIGHT = 1;
|
||||||
Setting.THEME_DARK = 2;
|
Setting.THEME_DARK = 2;
|
||||||
|
@ -10,86 +10,98 @@ shim.isReactNative = () => {
|
|||||||
return !shim.isNode();
|
return !shim.isNode();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shim.isLinux = () => {
|
||||||
|
return process && process.platform === 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
shim.isWindows = () => {
|
||||||
|
return process && process.platform === 'win32';
|
||||||
|
}
|
||||||
|
|
||||||
|
shim.isMac = () => {
|
||||||
|
return process && process.platform === 'darwin';
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/cheton/is-electron
|
// https://github.com/cheton/is-electron
|
||||||
shim.isElectron = () => {
|
shim.isElectron = () => {
|
||||||
// Renderer process
|
// Renderer process
|
||||||
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
|
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main process
|
// Main process
|
||||||
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
|
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect the user agent when the `nodeIntegration` option is set to true
|
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||||
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
|
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node requests can go wrong is so many different ways and with so
|
// Node requests can go wrong is so many different ways and with so
|
||||||
// many different error messages... This handler inspects the error
|
// many different error messages... This handler inspects the error
|
||||||
// and decides whether the request can safely be repeated or not.
|
// and decides whether the request can safely be repeated or not.
|
||||||
function fetchRequestCanBeRetried(error) {
|
function fetchRequestCanBeRetried(error) {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
|
|
||||||
// Unfortunately the error 'Network request failed' doesn't have a type
|
// Unfortunately the error 'Network request failed' doesn't have a type
|
||||||
// or error code, so hopefully that message won't change and is not localized
|
// or error code, so hopefully that message won't change and is not localized
|
||||||
if (error.message == 'Network request failed') return true;
|
if (error.message == 'Network request failed') return true;
|
||||||
|
|
||||||
// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up"
|
// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up"
|
||||||
if (error.code == 'ECONNRESET') return true;
|
if (error.code == 'ECONNRESET') return true;
|
||||||
|
|
||||||
// OneDrive (or Node?) sometimes sends back a "not found" error for resources
|
// OneDrive (or Node?) sometimes sends back a "not found" error for resources
|
||||||
// that definitely exist and in this case repeating the request works.
|
// that definitely exist and in this case repeating the request works.
|
||||||
// Error is:
|
// Error is:
|
||||||
// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443
|
// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443
|
||||||
if (error.code == 'ENOTFOUND') return true;
|
if (error.code == 'ENOTFOUND') return true;
|
||||||
|
|
||||||
// network timeout at: https://public-ch3302...859f9b0e3ab.md
|
// network timeout at: https://public-ch3302...859f9b0e3ab.md
|
||||||
if (error.message && error.message.indexOf('network timeout') === 0) return true;
|
if (error.message && error.message.indexOf('network timeout') === 0) return true;
|
||||||
|
|
||||||
// name: 'FetchError',
|
// name: 'FetchError',
|
||||||
// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443',
|
// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443',
|
||||||
// type: 'system',
|
// type: 'system',
|
||||||
// errno: 'EAI_AGAIN',
|
// errno: 'EAI_AGAIN',
|
||||||
// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443
|
// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443
|
||||||
//
|
//
|
||||||
// It's a Microsoft error: "A temporary failure in name resolution occurred."
|
// It's a Microsoft error: "A temporary failure in name resolution occurred."
|
||||||
if (error.code == 'EAI_AGAIN') return true;
|
if (error.code == 'EAI_AGAIN') return true;
|
||||||
|
|
||||||
// request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443
|
// request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443
|
||||||
// Code: ETIMEDOUT
|
// Code: ETIMEDOUT
|
||||||
if (error.code === 'ETIMEDOUT') return true;
|
if (error.code === 'ETIMEDOUT') return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shim.fetchWithRetry = async function(fetchFn, options = null) {
|
shim.fetchWithRetry = async function(fetchFn, options = null) {
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.timeout) options.timeout = 1000 * 120; // ms
|
if (!options.timeout) options.timeout = 1000 * 120; // ms
|
||||||
if (!('maxRetry' in options)) options.maxRetry = 5;
|
if (!('maxRetry' in options)) options.maxRetry = 5;
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
const response = await fetchFn();
|
const response = await fetchFn();
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (fetchRequestCanBeRetried(error)) {
|
if (fetchRequestCanBeRetried(error)) {
|
||||||
retryCount++;
|
retryCount++;
|
||||||
if (retryCount > options.maxRetry) throw error;
|
if (retryCount > options.maxRetry) throw error;
|
||||||
await time.sleep(retryCount * 3);
|
await time.sleep(retryCount * 3);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
||||||
|
6
linkToLocal.sh
Normal file
6
linkToLocal.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR/CliClient/node_modules"
|
||||||
|
rm -rf tkwidgets
|
||||||
|
ln -s /mnt/d/Docs/PROGS/Node/tkwidgets/src tkwidgets
|
Loading…
Reference in New Issue
Block a user