mirror of https://github.com/laurent22/joplin.git
Reorganised RN app
parent
037537db52
commit
e5d7294b69
|
@ -129,6 +129,7 @@ dependencies {
|
|||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile project(':react-native-sqlite-storage')
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.facebook.react.ReactNativeHost;
|
|||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import org.pgsqlite.SQLitePluginPackage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -22,6 +23,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new SQLitePluginPackage(),
|
||||
new MainReactPackage()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
rootProject.name = 'AwesomeProject'
|
||||
|
||||
include ':app'
|
||||
|
||||
include ':react-native-sqlite-storage'
|
||||
project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android')
|
|
@ -5,6 +5,27 @@ import { connect } from 'react-redux'
|
|||
import { createStore } from 'redux';
|
||||
import { Provider } from 'react-redux'
|
||||
|
||||
import { WebApi } from 'src/web-api.js'
|
||||
import { Database } from 'src/database.js'
|
||||
|
||||
import { Log } from 'src/log.js'
|
||||
|
||||
let debugMode = true;
|
||||
|
||||
let db = new Database();
|
||||
db.setDebugEnabled(debugMode);
|
||||
db.open();
|
||||
|
||||
|
||||
// let test = {
|
||||
// 'abcd' : 123,
|
||||
// 'efgh' : 456,
|
||||
// }
|
||||
|
||||
// for (let [key, value] of test) {
|
||||
// console.info(key, value);
|
||||
// }
|
||||
|
||||
let defaultState = {
|
||||
'myButtonLabel': 'clicko123456',
|
||||
'counter': 0,
|
||||
|
@ -108,90 +129,18 @@ class App extends Component {
|
|||
|
||||
}
|
||||
|
||||
const queryString = require('query-string');
|
||||
|
||||
class Api {
|
||||
|
||||
constructor(baseUrl, clientId) {
|
||||
this.baseUrl_ = baseUrl;
|
||||
this.clientId_ = clientId;
|
||||
}
|
||||
|
||||
makeRequest(method, path, query, data) {
|
||||
let url = this.baseUrl_;
|
||||
if (path) url += '/' + path;
|
||||
if (query) url += '?' + queryString(query);
|
||||
let options = {};
|
||||
options.method = method.toUpperCase();
|
||||
if (data) {
|
||||
var formData = new FormData();
|
||||
for (var key in data) {
|
||||
if (!data.hasOwnProperty(key)) continue;
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
options.body = formData;
|
||||
}
|
||||
|
||||
return {
|
||||
url: url,
|
||||
options: options
|
||||
};
|
||||
}
|
||||
|
||||
exec(method, path, query, data) {
|
||||
let that = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
let r = that.makeRequest(method, path, query, data);
|
||||
|
||||
fetch(r.url, r.options)
|
||||
.then(function(response) {
|
||||
let responseClone = response.clone();
|
||||
return response.json()
|
||||
.then(function(data) {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
responseClone.text()
|
||||
.done(function(text) {
|
||||
reject('Cannot parse JSON: ' + text);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get(path, query) {
|
||||
return this.exec('GET', path, query);
|
||||
}
|
||||
|
||||
post(path, query, data) {
|
||||
return this.exec('POST', path, query, data);
|
||||
}
|
||||
|
||||
delete(path, query) {
|
||||
return this.exec('DELETE', path, query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let api = new Api('http://192.168.1.2', 'A7D301DA7D301DA7D301DA7D301DA7D3');
|
||||
api.exec('POST', 'sessions', null, {
|
||||
'email': 'laurent@cozic.net',
|
||||
'password': '12345678',
|
||||
})
|
||||
.then(function(data) {
|
||||
console.info('GOT DATA:');
|
||||
console.info(data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.warn('GOT ERROR:');
|
||||
console.warn(error);
|
||||
})
|
||||
// let api = new WebApi('http://192.168.1.2', 'A7D301DA7D301DA7D301DA7D301DA7D3');
|
||||
// api.exec('POST', 'sessions', null, {
|
||||
// 'email': 'laurent@cozic.net',
|
||||
// 'password': '12345678',
|
||||
// })
|
||||
// .then(function(data) {
|
||||
// console.info('GOT DATA:');
|
||||
// console.info(data);
|
||||
// })
|
||||
// .catch(function(error) {
|
||||
// console.warn('GOT ERROR:');
|
||||
// console.warn(error);
|
||||
// })
|
||||
|
||||
AppRegistry.registerComponent('AwesomeProject', () => App);
|
|
@ -17,7 +17,8 @@
|
|||
"react-test-renderer": "16.0.0-alpha.6",
|
||||
"redux": "3.6.0",
|
||||
"react-redux": "4.4.8",
|
||||
"query-string": "4.3.4"
|
||||
"query-string": "4.3.4",
|
||||
"react-native-sqlite-storage": "3.3.*"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
import SQLite from 'react-native-sqlite-storage';
|
||||
import { Log } from 'src/log.js';
|
||||
|
||||
const structureSql = `
|
||||
CREATE TABLE folders (
|
||||
id TEXT PRIMARY KEY,
|
||||
parent_id TEXT NOT NULL DEFAULT "",
|
||||
title TEXT NOT NULL DEFAULT "",
|
||||
created_time INT NOT NULL DEFAULT 0,
|
||||
updated_time INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE notes (
|
||||
id TEXT PRIMARY KEY,
|
||||
parent_id TEXT NOT NULL DEFAULT "",
|
||||
title TEXT NOT NULL DEFAULT "",
|
||||
body TEXT NOT NULL DEFAULT "",
|
||||
created_time INT NOT NULL DEFAULT 0,
|
||||
updated_time INT NOT NULL DEFAULT 0,
|
||||
latitude NUMERIC NOT NULL DEFAULT 0,
|
||||
longitude NUMERIC NOT NULL DEFAULT 0,
|
||||
altitude NUMERIC NOT NULL DEFAULT 0,
|
||||
source TEXT NOT NULL DEFAULT "",
|
||||
author TEXT NOT NULL DEFAULT "",
|
||||
source_url TEXT NOT NULL DEFAULT "",
|
||||
is_todo BOOLEAN NOT NULL DEFAULT 0,
|
||||
todo_due INT NOT NULL DEFAULT "",
|
||||
todo_completed INT NOT NULL DEFAULT "",
|
||||
source_application TEXT NOT NULL DEFAULT "",
|
||||
application_data TEXT NOT NULL DEFAULT "",
|
||||
\`order\` INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE tags (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT,
|
||||
created_time INT,
|
||||
updated_time INT
|
||||
);
|
||||
|
||||
CREATE TABLE note_tags (
|
||||
id INTEGER PRIMARY KEY,
|
||||
note_id TEXT,
|
||||
tag_id TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE resources (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT,
|
||||
mime TEXT,
|
||||
filename TEXT,
|
||||
created_time INT,
|
||||
updated_time INT
|
||||
);
|
||||
|
||||
CREATE TABLE note_resources (
|
||||
id INTEGER PRIMARY KEY,
|
||||
note_id TEXT,
|
||||
resource_id TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE version (
|
||||
version INT
|
||||
);
|
||||
|
||||
CREATE TABLE changes (
|
||||
id INTEGER PRIMARY KEY,
|
||||
\`type\` INT,
|
||||
item_id TEXT,
|
||||
item_type INT,
|
||||
item_field TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE settings (
|
||||
\`key\` TEXT PRIMARY KEY,
|
||||
\`value\` TEXT,
|
||||
\`type\` INT
|
||||
);
|
||||
|
||||
INSERT INTO version (version) VALUES (1);
|
||||
`;
|
||||
|
||||
class Database {
|
||||
|
||||
constructor() {}
|
||||
|
||||
setDebugEnabled(v) {
|
||||
SQLite.DEBUG(v);
|
||||
}
|
||||
|
||||
open() {
|
||||
this.db_ = SQLite.openDatabase({ name: 'joplin.sqlite', location: 'Documents' }, (db) => {
|
||||
Log.info('Database was open successfully');
|
||||
}, (error) => {
|
||||
Log.error('Cannot open database: ', error);
|
||||
});
|
||||
|
||||
this.updateSchema();
|
||||
}
|
||||
|
||||
sqlStringToLines(sql) {
|
||||
let output = [];
|
||||
let lines = sql.split("\n");
|
||||
let statement = '';
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
if (line == '') continue;
|
||||
if (line.substr(0, 2) == "--") continue;
|
||||
statement += line;
|
||||
if (line[line.length - 1] == ';') {
|
||||
output.push(statement);
|
||||
statement = '';
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
selectOne(sql, params = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db_.executeSql(sql, params, (r) => {
|
||||
resolve(r.rows.length ? r.rows.item(0) : null);
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateSchema() {
|
||||
Log.info('Checking for database schema update...');
|
||||
|
||||
this.selectOne('SELECT * FROM version LIMIT 1').then((row) => {
|
||||
// TODO: version update logic
|
||||
}).catch((error) => {
|
||||
// Assume that error was:
|
||||
// { message: 'no such table: version (code 1): , while compiling: SELECT * FROM version', code: 0 }
|
||||
// which means the database is empty and the tables need to be created.
|
||||
|
||||
Log.info('Database is new - creating the schema...');
|
||||
|
||||
let statements = this.sqlStringToLines(structureSql)
|
||||
this.db_.transaction((tx) => {
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
tx.executeSql(statements[i]);
|
||||
}
|
||||
}, (error) => {
|
||||
Log.error('Could not create database schema:', error);
|
||||
}, () => {
|
||||
Log.info('Database schema created successfully');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Database };
|
|
@ -0,0 +1,19 @@
|
|||
// Custom wrapper for `console` to allow for custom logging (to file, etc.) if needed.
|
||||
|
||||
class Log {
|
||||
|
||||
static info(...o) {
|
||||
console.info(...o);
|
||||
}
|
||||
|
||||
static warn(...o) {
|
||||
console.info(...o);
|
||||
}
|
||||
|
||||
static error(...o) {
|
||||
console.info(...o);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Log };
|
|
@ -0,0 +1 @@
|
|||
{ "name": "src" }
|
|
@ -0,0 +1,70 @@
|
|||
const queryString = require('query-string');
|
||||
|
||||
class WebApi {
|
||||
|
||||
constructor(baseUrl, clientId) {
|
||||
this.baseUrl_ = baseUrl;
|
||||
this.clientId_ = clientId;
|
||||
}
|
||||
|
||||
makeRequest(method, path, query, data) {
|
||||
let url = this.baseUrl_;
|
||||
if (path) url += '/' + path;
|
||||
if (query) url += '?' + queryString(query);
|
||||
let options = {};
|
||||
options.method = method.toUpperCase();
|
||||
if (data) {
|
||||
var formData = new FormData();
|
||||
for (var key in data) {
|
||||
if (!data.hasOwnProperty(key)) continue;
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
options.body = formData;
|
||||
}
|
||||
|
||||
return {
|
||||
url: url,
|
||||
options: options
|
||||
};
|
||||
}
|
||||
|
||||
exec(method, path, query, data) {
|
||||
let that = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
let r = that.makeRequest(method, path, query, data);
|
||||
|
||||
fetch(r.url, r.options).then(function(response) {
|
||||
let responseClone = response.clone();
|
||||
return response.json().then(function(data) {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
responseClone.text().then(function(text) {
|
||||
reject('Cannot parse JSON: ' + text);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get(path, query) {
|
||||
return this.exec('GET', path, query);
|
||||
}
|
||||
|
||||
post(path, query, data) {
|
||||
return this.exec('POST', path, query, data);
|
||||
}
|
||||
|
||||
delete(path, query) {
|
||||
return this.exec('DELETE', path, query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { WebApi };
|
|
@ -5,33 +5,38 @@
|
|||
"path": ".",
|
||||
"folder_exclude_patterns": [
|
||||
"var",
|
||||
"vendor",
|
||||
"QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug",
|
||||
"QtClient/data/resources",
|
||||
"app/data/uploads",
|
||||
"ReactNativeClient/node_modules",
|
||||
"ReactNativeClient/android",
|
||||
"ReactNativeClient/ios",
|
||||
"vendor",
|
||||
"QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug",
|
||||
"QtClient/data/resources",
|
||||
"app/data/uploads",
|
||||
"ReactNativeClient/node_modules",
|
||||
"ReactNativeClient/android/app/build",
|
||||
"ReactNativeClient/android/build",
|
||||
"ReactNativeClient/android/.idea",
|
||||
"ReactNativeClient/android/.gradle",
|
||||
"ReactNativeClient/android/local.properties",
|
||||
"ReactNativeClient/ios"
|
||||
],
|
||||
"file_exclude_patterns": [
|
||||
"*.pro.user",
|
||||
"*.pro.user.*"
|
||||
]
|
||||
"file_exclude_patterns": [
|
||||
"*.pro.user",
|
||||
"*.pro.user.*",
|
||||
"*.iml"
|
||||
]
|
||||
}
|
||||
],
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"name": "Build evernote-import",
|
||||
"shell_cmd": "D:\\Programmes\\cygwin\\bin\\bash.exe --login D:\\Web\\www\\joplin\\QtClient\\evernote-import\\build.sh"
|
||||
},
|
||||
{
|
||||
"name": "Qt Creator - Build and run",
|
||||
"shell_cmd": "D:\\NonPortableApps\\AutoHotkey\\AutoHotkey.exe D:\\Docs\\PROGS\\AutoHotKey\\QtRun\\QtRun.ahk"
|
||||
},
|
||||
{
|
||||
"name": "Build QtClient CLI - Linux",
|
||||
"shell_cmd": "/home/laurent/src/notes/QtClient/JoplinQtClient/build.sh"
|
||||
}
|
||||
]
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"name": "Build evernote-import",
|
||||
"shell_cmd": "D:\\Programmes\\cygwin\\bin\\bash.exe --login D:\\Web\\www\\joplin\\QtClient\\evernote-import\\build.sh"
|
||||
},
|
||||
{
|
||||
"name": "Qt Creator - Build and run",
|
||||
"shell_cmd": "D:\\NonPortableApps\\AutoHotkey\\AutoHotkey.exe D:\\Docs\\PROGS\\AutoHotKey\\QtRun\\QtRun.ahk"
|
||||
},
|
||||
{
|
||||
"name": "Build QtClient CLI - Linux",
|
||||
"shell_cmd": "/home/laurent/src/notes/QtClient/JoplinQtClient/build.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -32,12 +32,24 @@ abstract class ApiController extends Controller {
|
|||
$r->send();
|
||||
echo "\n";
|
||||
} else {
|
||||
$msg = array();
|
||||
$msg[] = 'Exception: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine();
|
||||
$msg[] = '';
|
||||
$msg[] = $e->getTraceAsString();
|
||||
echo implode("\n", $msg);
|
||||
$r = array(
|
||||
'error' => $e->getMessage(),
|
||||
'code' => 0,
|
||||
'type' => 'Exception',
|
||||
//'trace' => $e->getTraceAsString(),
|
||||
);
|
||||
$response = new JsonResponse($r);
|
||||
$response->setStatusCode(500);
|
||||
$response->send();
|
||||
echo "\n";
|
||||
|
||||
|
||||
// $msg = array();
|
||||
// $msg[] = 'Exception: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine();
|
||||
// $msg[] = '';
|
||||
// $msg[] = $e->getTraceAsString();
|
||||
// echo implode("\n", $msg);
|
||||
// echo "\n";
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ class Session extends BaseModel {
|
|||
$ok = self::verifyPassword($password, $user->password);
|
||||
if (!$ok) throw new AuthException();
|
||||
|
||||
if (!$clientId) throw new \Exception('clientId is required');
|
||||
|
||||
$session = new Session();
|
||||
$session->owner_id = $user->id;
|
||||
$session->client_id = $clientId;
|
||||
|
|
20
web/app.php
20
web/app.php
|
@ -21,7 +21,21 @@ try {
|
|||
$response->send();
|
||||
$kernel->terminate($request, $response);
|
||||
} catch(\Exception $e) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
echo $e->getMessage() . "\n";
|
||||
echo $e->getTraceAsString();
|
||||
// Separate exception handling for anything that could not be caught in ApiController, for
|
||||
// example if the route doesn't exist.
|
||||
$class = get_class($e);
|
||||
$errorType = explode("\\", $class);
|
||||
$errorType = $errorType[count($errorType) - 1];
|
||||
$response = array(
|
||||
'error' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
'type' => $errorType,
|
||||
);
|
||||
if ($errorType == 'NotFoundHttpException') {
|
||||
header('HTTP/1.1 404 Not found');
|
||||
} else {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
|
||||
die(json_encode($response) . "\n");
|
||||
}
|
Loading…
Reference in New Issue