1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
This commit is contained in:
Laurent Cozic 2017-07-10 18:09:58 +00:00
parent fdeb797750
commit f3d8c34499
10 changed files with 248 additions and 144 deletions

View File

@ -29,6 +29,7 @@ import { reg } from 'lib/registry.js';
import { FsDriverNode } from './fs-driver-node.js'; import { FsDriverNode } from './fs-driver-node.js';
import { filename, basename } from 'lib/path-utils.js'; import { filename, basename } from 'lib/path-utils.js';
import { shim } from 'lib/shim.js'; import { shim } from 'lib/shim.js';
import { shimInit } from 'lib/shim-init-node.js';
import { _ } from 'lib/locale.js'; import { _ } from 'lib/locale.js';
import os from 'os'; import os from 'os';
import fs from 'fs-extra'; import fs from 'fs-extra';
@ -104,7 +105,8 @@ commands.push({
}; };
try { try {
await Note.save(note); note = await Note.save(note);
Note.updateGeolocation(note.id);
} catch (error) { } catch (error) {
this.log(error); this.log(error);
} }
@ -512,42 +514,54 @@ commands.push({
description: 'Synchronizes with remote storage.', description: 'Synchronizes with remote storage.',
options: [ options: [
['--random-failures', 'For debugging purposes. Do not use.'], ['--random-failures', 'For debugging purposes. Do not use.'],
['--stats', 'Displays stats about synchronization.'],
], ],
action: async function(args, end) { action: async function(args, end) {
let options = { if (args.options.stats) {
onProgress: (report) => { const report = await BaseItem.stats();
let line = []; for (let n in report.items) {
if (report.remotesToUpdate) line.push(_('Items to upload: %d/%d.', report.createRemote + report.updateRemote, report.remotesToUpdate)); if (!report.items.hasOwnProperty(n)) continue;
if (report.remotesToDelete) line.push(_('Remote items to delete: %d/%d.', report.deleteRemote, report.remotesToDelete)); const r = report.items[n];
if (report.localsToUdpate) line.push(_('Items to download: %d/%d.', report.createLocal + report.updateLocal, report.localsToUdpate)); this.log(_('%s: %d/%d', n, r.synced, r.total))
if (report.localsToDelete) line.push(_('Local items to delete: %d/%d.', report.deleteLocal, report.localsToDelete)); }
if (line.length) vorpalUtils.redraw(line.join(' ')); this.log(_('Total: %d/%d', report.total.synced, report.total.total));
}, } else {
onMessage: (msg) => { let options = {
vorpalUtils.redrawDone(); onProgress: (report) => {
this.log(msg); let line = [];
}, if (report.remotesToUpdate) line.push(_('Items to upload: %d/%d.', report.createRemote + report.updateRemote, report.remotesToUpdate));
randomFailures: args.options['random-failures'] === true, if (report.remotesToDelete) line.push(_('Remote items to delete: %d/%d.', report.deleteRemote, report.remotesToDelete));
}; if (report.localsToUdpate) line.push(_('Items to download: %d/%d.', report.createLocal + report.updateLocal, report.localsToUdpate));
if (report.localsToDelete) line.push(_('Local items to delete: %d/%d.', report.deleteLocal, report.localsToDelete));
if (line.length) vorpalUtils.redraw(line.join(' '));
},
onMessage: (msg) => {
vorpalUtils.redrawDone();
this.log(msg);
},
randomFailures: args.options['random-failures'] === true,
};
this.log(_('Synchronization target: %s', Setting.value('sync.target'))); this.log(_('Synchronization target: %s', Setting.value('sync.target')));
let sync = await synchronizer(Setting.value('sync.target')); let sync = await synchronizer(Setting.value('sync.target'));
if (!sync) { if (!sync) {
end(); end();
return; return;
}
try {
this.log(_('Starting synchronization...'));
await sync.start(options);
} catch (error) {
this.log(error);
}
vorpalUtils.redrawDone();
this.log(_('Done.'));
} }
try {
this.log(_('Starting synchronization...'));
await sync.start(options);
} catch (error) {
this.log(error);
}
vorpalUtils.redrawDone();
this.log(_('Done.'));
end(); end();
}, },
cancel: async function() { cancel: async function() {
@ -904,64 +918,7 @@ vorpalUtils.initialize(vorpal);
async function main() { async function main() {
shim.fetchBlob = async function(url, options) { shimInit();
if (!options || !options.path) throw new Error('fetchBlob: target file path is missing');
if (!options.method) options.method = 'GET';
const urlParse = require('url').parse;
url = urlParse(url.trim());
const http = url.protocol.toLowerCase() == 'http:' ? require('follow-redirects').http : require('follow-redirects').https;
const headers = options.headers ? options.headers : {};
const method = options.method ? options.method : 'GET';
if (method != 'GET') throw new Error('Only GET is supported');
const filePath = options.path;
function makeResponse(response) {
return {
ok: response.statusCode < 400,
path: filePath,
text: () => { return response.statusMessage; },
json: () => { return { message: response.statusCode + ': ' + response.statusMessage }; },
status: response.statusCode,
headers: response.headers,
};
}
const requestOptions = {
protocol: url.protocol,
host: url.host,
port: url.port,
method: method,
path: url.path + (url.query ? '?' + url.query : ''),
headers: headers,
};
return new Promise((resolve, reject) => {
try {
// Note: relative paths aren't supported
const file = fs.createWriteStream(filePath);
const request = http.get(requestOptions, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(() => {
resolve(makeResponse(response));
});
});
})
request.on('error', function(error) {
fs.unlink(filePath);
reject(error);
});
} catch(error) {
fs.unlink(filePath);
reject(error);
}
});
}
for (let commandIndex = 0; commandIndex < commands.length; commandIndex++) { for (let commandIndex = 0; commandIndex < commands.length; commandIndex++) {
let c = commands[commandIndex]; let c = commands[commandIndex];
@ -1046,9 +1003,6 @@ async function main() {
// If we still have arguments, pass it to Vorpal and exit // If we still have arguments, pass it to Vorpal and exit
if (argv.length) { if (argv.length) {
//vorpal.delimiter(' AAAAAAAAAAAAAAAAAAAAA');
//console.info(vorpal.ui.inquirer);
//vorpal.show();
let cmd = shellArgsToString(argv); let cmd = shellArgsToString(argv);
await vorpal.exec(cmd); await vorpal.exec(cmd);
await vorpal.exec('exit'); await vorpal.exec('exit');

View File

@ -7,7 +7,7 @@
"url": "https://github.com/laurent22/joplin" "url": "https://github.com/laurent22/joplin"
}, },
"url": "git://github.com/laurent22/joplin.git", "url": "git://github.com/laurent22/joplin.git",
"version": "0.8.30", "version": "0.8.31",
"bin": { "bin": {
"joplin": "./main_launcher.js" "joplin": "./main_launcher.js"
}, },

View File

@ -0,0 +1,28 @@
import { shim } from 'lib/shim.js'
import { netUtils } from 'lib/net-utils.js';
class GeolocationNode {
static async currentPosition(options = null) {
if (!options) options = {};
const ip = await netUtils.ip();
let response = await shim.fetch('https://freegeoip.net/json/' + ip);
if (!response.ok) throw new Error('Could not get geolocation: ' + await response.text());
response = await response.json();
return {
timestamp: (new Date()).getTime(),
coords: {
longitude: response.longitude,
altitude: 0,
latitude: response.latitude
}
}
}
}
export { GeolocationNode };

View File

@ -29,13 +29,40 @@ class BaseItem extends BaseModel {
} }
throw new Error('Invalid class name: ' + name); throw new Error('Invalid class name: ' + name);
}
// if (!this.classes_) this.classes_ = {}; static async stats() {
// if (this.classes_[name]) return this.classes_[name]; let output = {
// let filename = name.toLowerCase(); items: {},
// if (name == 'NoteTag') filename = 'note-tag'; total: {},
// this.classes_[name] = require('lib/models/' + filename + '.js')[name]; };
// return this.classes_[name];
let itemCount = 0;
let syncedCount = 0;
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
let d = BaseItem.syncItemDefinitions_[i];
let ItemClass = this.getClass(d.className);
let o = {
total: await ItemClass.count(),
synced: await ItemClass.syncedCount(),
};
output.items[d.className] = o;
itemCount += o.total;
syncedCount += o.synced;
}
output.total = {
total: itemCount,
synced: syncedCount,
};
return output;
}
static async syncedCount() {
const ItemClass = this.itemClass(this.modelType());
const r = await this.db().selectOne('SELECT count(*) as total FROM `' + ItemClass.tableName() + '` WHERE updated_time > sync_time');
return r.total;
} }
static systemPath(itemOrId) { static systemPath(itemOrId) {
@ -248,12 +275,6 @@ class BaseItem extends BaseModel {
} }
// import { Note } from 'lib/models/note.js';
// import { Folder } from 'lib/models/folder.js';
// import { Resource } from 'lib/models/resource.js';
// import { Tag } from 'lib/models/tag.js';
// import { NoteTag } from 'lib/models/note-tag.js';
// Also update: // Also update:
// - itemsThatNeedSync() // - itemsThatNeedSync()
// - syncedItems() // - syncedItems()

View File

@ -1,9 +1,9 @@
import { BaseModel } from 'lib/base-model.js'; import { BaseModel } from 'lib/base-model.js';
import { Log } from 'lib/log.js'; import { Log } from 'lib/log.js';
import { Folder } from 'lib/models/folder.js'; import { Folder } from 'lib/models/folder.js';
import { GeolocationReact } from 'lib/geolocation-react.js';
import { BaseItem } from 'lib/models/base-item.js'; import { BaseItem } from 'lib/models/base-item.js';
import { Setting } from 'lib/models/setting.js'; import { Setting } from 'lib/models/setting.js';
import { shim } from 'lib/shim.js';
import moment from 'moment'; import moment from 'moment';
import lodash from 'lodash'; import lodash from 'lodash';
@ -96,11 +96,11 @@ class Note extends BaseItem {
} }
static updateGeolocation(noteId) { static updateGeolocation(noteId) {
Log.info('Updating lat/long of note ' + noteId); this.logger().info('Updating lat/long of note ' + noteId);
let geoData = null; let geoData = null;
return GeolocationReact.currentPosition().then((data) => { return shim.Geolocation.currentPosition().then((data) => {
Log.info('Got lat/long'); this.logger().info('Got lat/long');
geoData = data; geoData = data;
return Note.load(noteId); return Note.load(noteId);
}).then((note) => { }).then((note) => {
@ -110,7 +110,7 @@ class Note extends BaseItem {
note.altitude = geoData.coords.altitude; note.altitude = geoData.coords.altitude;
return Note.save(note); return Note.save(note);
}).catch((error) => { }).catch((error) => {
Log.info('Cannot get location:', error); this.logger().warn('Cannot get location:', error);
}); });
} }

View File

@ -0,0 +1,15 @@
import { shim } from 'lib/shim.js'
const netUtils = {};
netUtils.ip = async () => {
let response = await shim.fetch('https://api.ipify.org/?format=json');
if (!response.ok) {
throw new Error('Could not retrieve IP: ' + await response.text());
}
let ip = await response.json();
return ip.ip;
}
export { netUtils };

View File

@ -0,0 +1,67 @@
import { shim } from 'lib/shim.js';
import { GeolocationNode } from 'lib/geolocation-node.js';
function shimInit() {
shim.Geolocation = GeolocationNode;
shim.fetchBlob = async function(url, options) {
if (!options || !options.path) throw new Error('fetchBlob: target file path is missing');
if (!options.method) options.method = 'GET';
const urlParse = require('url').parse;
url = urlParse(url.trim());
const http = url.protocol.toLowerCase() == 'http:' ? require('follow-redirects').http : require('follow-redirects').https;
const headers = options.headers ? options.headers : {};
const method = options.method ? options.method : 'GET';
if (method != 'GET') throw new Error('Only GET is supported');
const filePath = options.path;
function makeResponse(response) {
return {
ok: response.statusCode < 400,
path: filePath,
text: () => { return response.statusMessage; },
json: () => { return { message: response.statusCode + ': ' + response.statusMessage }; },
status: response.statusCode,
headers: response.headers,
};
}
const requestOptions = {
protocol: url.protocol,
host: url.host,
port: url.port,
method: method,
path: url.path + (url.query ? '?' + url.query : ''),
headers: headers,
};
return new Promise((resolve, reject) => {
try {
// Note: relative paths aren't supported
const file = fs.createWriteStream(filePath);
const request = http.get(requestOptions, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(() => {
resolve(makeResponse(response));
});
});
})
request.on('error', function(error) {
fs.unlink(filePath);
reject(error);
});
} catch(error) {
fs.unlink(filePath);
reject(error);
}
});
}
}
export { shimInit }

View File

@ -0,0 +1,42 @@
import { shim } from 'lib/shim.js';
import { GeolocationReact } from 'lib/geolocation-react.js';
function shimInit() {
shim.Geolocation = GeolocationReact;
shim.fetchBlob = async function(url, options) {
if (!options || !options.path) throw new Error('fetchBlob: target file path is missing');
if (!options.method) options.method = 'GET';
let headers = options.headers ? options.headers : {};
let method = options.method ? options.method : 'GET';
let dirs = RNFetchBlob.fs.dirs;
let localFilePath = options.path;
if (localFilePath.indexOf('/') !== 0) localFilePath = dirs.DocumentDir + '/' + localFilePath;
delete options.path;
try {
let response = await RNFetchBlob.config({
path: localFilePath
}).fetch(method, url, headers);
// Returns an object that's roughtly compatible with a standard Response object
let output = {
ok: response.respInfo.status < 400,
path: response.data,
text: response.text,
json: response.json,
status: response.respInfo.status,
headers: response.respInfo.headers,
};
return output;
} catch (error) {
throw new Error('fetchBlob: ' + method + ' ' + url + ': ' + error.toString());
}
}
}
export { shimInit }

View File

@ -1,5 +1,14 @@
let shim = {}; let shim = {};
shim.isNode = () => {
if (typeof process === 'undefined') return false;
return process.title = 'node';
};
shim.isReactNative = () => {
return !shim.isNode();
};
shim.fetch = typeof fetch !== 'undefined' ? fetch : null; shim.fetch = typeof fetch !== 'undefined' ? fetch : null;
shim.FormData = typeof FormData !== 'undefined' ? FormData : null; shim.FormData = typeof FormData !== 'undefined' ? FormData : null;

View File

@ -6,7 +6,7 @@ import { createStore } from 'redux';
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { StackNavigator } from 'react-navigation'; import { StackNavigator } from 'react-navigation';
import { addNavigationHelpers } from 'react-navigation'; import { addNavigationHelpers } from 'react-navigation';
import { shim } from 'lib/shim.js'; import { shimInit } from 'lib/shim-init-react.js';
import { Log } from 'lib/log.js' import { Log } from 'lib/log.js'
import { Logger } from 'lib/logger.js' import { Logger } from 'lib/logger.js'
import { Note } from 'lib/models/note.js' import { Note } from 'lib/models/note.js'
@ -225,42 +225,10 @@ let initializationState_ = 'waiting';
async function initialize(dispatch) { async function initialize(dispatch) {
if (initializationState_ != 'waiting') return; if (initializationState_ != 'waiting') return;
shimInit();
initializationState_ = 'in_progress'; initializationState_ = 'in_progress';
shim.fetchBlob = async function(url, options) {
if (!options || !options.path) throw new Error('fetchBlob: target file path is missing');
if (!options.method) options.method = 'GET';
let headers = options.headers ? options.headers : {};
let method = options.method ? options.method : 'GET';
let dirs = RNFetchBlob.fs.dirs;
let localFilePath = options.path;
if (localFilePath.indexOf('/') !== 0) localFilePath = dirs.DocumentDir + '/' + localFilePath;
delete options.path;
try {
let response = await RNFetchBlob.config({
path: localFilePath
}).fetch(method, url, headers);
// Returns an object that's roughtly compatible with a standard Response object
let output = {
ok: response.respInfo.status < 400,
path: response.data,
text: response.text,
json: response.json,
status: response.respInfo.status,
headers: response.respInfo.headers,
};
return output;
} catch (error) {
throw new Error('fetchBlob: ' + method + ' ' + url + ': ' + error.toString());
}
}
Setting.setConstant('env', __DEV__ ? 'dev' : 'prod'); Setting.setConstant('env', __DEV__ ? 'dev' : 'prod');
Setting.setConstant('appId', 'net.cozic.joplin'); Setting.setConstant('appId', 'net.cozic.joplin');
Setting.setConstant('appType', 'mobile'); Setting.setConstant('appType', 'mobile');