diff --git a/CliClient/app/import-enex.js b/CliClient/app/import-enex.js index 737e23101b..17f27e88d6 100644 --- a/CliClient/app/import-enex.js +++ b/CliClient/app/import-enex.js @@ -350,8 +350,8 @@ function importEnex(parentFolderId, filePath, importOptions = null) { id: noteResource.id, data: decodedData, mime: noteResource.mime, - title: noteResource.filename, - filename: noteResource.filename, + title: noteResource.filename ? noteResource.filename : '', + filename: noteResource.filename ? noteResource.filename : '', }; note.resources.push(r); diff --git a/CliClient/app/main.js b/CliClient/app/main.js index 44eae6c756..6c2f0ac482 100644 --- a/CliClient/app/main.js +++ b/CliClient/app/main.js @@ -8,6 +8,7 @@ import { FileApiDriverOneDrive } from 'lib/file-api-driver-onedrive.js'; import { FileApiDriverMemory } from 'lib/file-api-driver-memory.js'; import { FileApiDriverLocal } from 'lib/file-api-driver-local.js'; import { OneDriveApiNodeUtils } from './onedrive-api-node-utils.js'; +import { JoplinDatabase } from 'lib/joplin-database.js'; import { Database } from 'lib/database.js'; import { DatabaseDriverNode } from 'lib/database-driver-node.js'; import { BaseModel } from 'lib/base-model.js'; @@ -927,7 +928,12 @@ async function main() { await fs.mkdirp(resourceDir, 0o755); await fs.mkdirp(tempDir, 0o755); + // let logDatabase = new Database(new DatabaseDriverNode()); + // await logDatabase.open({ name: profileDir + '/database-log.sqlite' }); + // await logDatabase.exec(Logger.databaseCreateTableSql()); + logger.addTarget('file', { path: profileDir + '/log.txt' }); + // logger.addTarget('database', { database: logDatabase, source: 'main' }); logger.setLevel(logLevel); dbLogger.addTarget('file', { path: profileDir + '/log-database.txt' }); @@ -947,7 +953,7 @@ async function main() { BaseItem.loadClass('Tag', Tag); BaseItem.loadClass('NoteTag', NoteTag); - database_ = new Database(new DatabaseDriverNode()); + database_ = new JoplinDatabase(new DatabaseDriverNode()); database_.setLogger(dbLogger); await database_.open({ name: profileDir + '/database.sqlite' }); BaseModel.db_ = database_; diff --git a/CliClient/tests/test-utils.js b/CliClient/tests/test-utils.js index a9d878105d..53830f2c62 100644 --- a/CliClient/tests/test-utils.js +++ b/CliClient/tests/test-utils.js @@ -1,5 +1,5 @@ import fs from 'fs-extra'; -import { Database } from 'lib/database.js'; +import { JoplinDatabase } from 'lib/joplin-database.js'; import { DatabaseDriverNode } from 'lib/database-driver-node.js'; import { BaseModel } from 'lib/base-model.js'; import { Folder } from 'lib/models/folder.js'; @@ -25,8 +25,11 @@ const fsDriver = new FsDriverNode(); Logger.fsDriver_ = fsDriver; Resource.fsDriver_ = fsDriver; +const logDir = __dirname + '/../tests/logs'; +fs.mkdirpSync(logDir, 0o755); + const logger = new Logger(); -logger.addTarget('file', { path: __dirname + '/../tests/logs/log.txt' }); +logger.addTarget('file', { path: logDir + '/log.txt' }); logger.setLevel(Logger.LEVEL_DEBUG); BaseItem.loadClass('Note', Note); @@ -87,7 +90,7 @@ function setupDatabase(id = null) { return fs.unlink(filePath).catch(() => { // Don't care if the file doesn't exist }).then(() => { - databases_[id] = new Database(new DatabaseDriverNode()); + databases_[id] = new JoplinDatabase(new DatabaseDriverNode()); databases_[id].setLogger(logger); return databases_[id].open({ name: filePath }).then(() => { BaseModel.db_ = databases_[id]; diff --git a/ReactNativeClient/android/app/build.gradle b/ReactNativeClient/android/app/build.gradle index 57a428764e..b572a55ba7 100644 --- a/ReactNativeClient/android/app/build.gradle +++ b/ReactNativeClient/android/app/build.gradle @@ -83,62 +83,73 @@ def enableSeparateBuildPerCPUArchitecture = false def enableProguardInReleaseBuilds = false android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" - defaultConfig { - applicationId "com.awesomeproject" - minSdkVersion 16 - targetSdkVersion 22 - versionCode 1 - versionName "1.0" - ndk { - abiFilters "armeabi-v7a", "x86" - } - } - splits { - abi { - reset() - enable enableSeparateBuildPerCPUArchitecture - universalApk false // If true, also generate a universal APK - include "armeabi-v7a", "x86" - } - } - buildTypes { - release { - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } - // applicationVariants are e.g. debug, release - applicationVariants.all { variant -> - variant.outputs.each { output -> - // For each separate APK per architecture, set a unique version code as described here: - // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits - def versionCodes = ["armeabi-v7a":1, "x86":2] - def abi = output.getFilter(OutputFile.ABI) - if (abi != null) { // null for the universal-debug, universal-release variants - output.versionCodeOverride = - versionCodes.get(abi) * 1048576 + defaultConfig.versionCode - } - } - } + defaultConfig { + applicationId "com.awesomeproject" + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include "armeabi-v7a", "x86" + } + } + signingConfigs { + release { + if (project.hasProperty('JOPLIN_RELEASE_STORE_FILE')) { + storeFile file(JOPLIN_RELEASE_STORE_FILE) + storePassword JOPLIN_RELEASE_STORE_PASSWORD + keyAlias JOPLIN_RELEASE_KEY_ALIAS + keyPassword JOPLIN_RELEASE_KEY_PASSWORD + } + } + } + buildTypes { + release { + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + signingConfig signingConfigs.release + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } } dependencies { - compile project(':react-native-fs') - 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') - compile project(':react-native-fetch-blob') + compile project(':react-native-fs') + 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') + compile project(':react-native-fetch-blob') } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { - from configurations.compile - into 'libs' + from configurations.compile + into 'libs' } apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" diff --git a/ReactNativeClient/android/app/src/main/java/com/awesomeproject/MainActivity.java b/ReactNativeClient/android/app/src/main/java/com/awesomeproject/MainActivity.java index 74accd2174..39cf7cb7f3 100644 --- a/ReactNativeClient/android/app/src/main/java/com/awesomeproject/MainActivity.java +++ b/ReactNativeClient/android/app/src/main/java/com/awesomeproject/MainActivity.java @@ -4,12 +4,12 @@ import com.facebook.react.ReactActivity; public class MainActivity extends ReactActivity { - /** - * Returns the name of the main component registered from JavaScript. - * This is used to schedule rendering of the component. - */ - @Override - protected String getMainComponentName() { - return "AwesomeProject"; - } + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "AwesomeProject"; + } } diff --git a/ReactNativeClient/lib/components/item-list.js b/ReactNativeClient/lib/components/item-list.js index 5fc1ffd994..53d9ab44fa 100644 --- a/ReactNativeClient/lib/components/item-list.js +++ b/ReactNativeClient/lib/components/item-list.js @@ -37,6 +37,7 @@ class ItemListComponent extends Component { await Note.save({ id: note.id, todo_completed: checked }); } + listView_itemLongPress(itemId) {} listView_itemPress(itemId) {} render() { @@ -50,8 +51,8 @@ class ItemListComponent extends Component { return ( - - { !!Number(item.is_todo) && { this.todoCheckbox_change(item.id, checked) }}/> }{item.title} [{item.id}] + + { !!Number(item.is_todo) && { this.todoCheckbox_change(item.id, checked) }}/> }{item.title} ); diff --git a/ReactNativeClient/lib/components/screen-header.js b/ReactNativeClient/lib/components/screen-header.js index 677cbb7fff..5fdd97e514 100644 --- a/ReactNativeClient/lib/components/screen-header.js +++ b/ReactNativeClient/lib/components/screen-header.js @@ -41,22 +41,6 @@ class ScreenHeaderComponent extends Component { } } - async menu_synchronize() { - if (reg.oneDriveApi().auth()) { - const sync = await reg.synchronizer(); - try { - sync.start(); - } catch (error) { - Log.error(error); - } - } else { - this.props.dispatch({ - type: 'Navigation/NAVIGATE', - routeName: 'OneDriveLogin', - }); - } - } - render() { let key = 0; let menuOptionComponents = []; @@ -72,15 +56,10 @@ class ScreenHeaderComponent extends Component { menuOptionComponents.push(); } - menuOptionComponents.push( - this.menu_synchronize()} key={'menuOption_' + key++}> - {_('Synchronize')} - ); - - menuOptionComponents.push( - - {_('Configuration')} - ); + // menuOptionComponents.push( + // + // {_('Configuration')} + // ); let title = 'title' in this.props && this.props.title !== null ? this.props.title : _(this.props.navState.routeName); diff --git a/ReactNativeClient/lib/components/screens/note.js b/ReactNativeClient/lib/components/screens/note.js index f87d560c01..be7c569aa1 100644 --- a/ReactNativeClient/lib/components/screens/note.js +++ b/ReactNativeClient/lib/components/screens/note.js @@ -85,7 +85,7 @@ class NoteScreenComponent extends React.Component { { isTodo && } this.title_changeText(text)} /> - this.body_changeText(text)} /> + this.body_changeText(text)} /> { todoComponents } ); } diff --git a/ReactNativeClient/lib/components/side-menu-content.js b/ReactNativeClient/lib/components/side-menu-content.js index 5aae3b5d51..cffeb9d040 100644 --- a/ReactNativeClient/lib/components/side-menu-content.js +++ b/ReactNativeClient/lib/components/side-menu-content.js @@ -1,8 +1,10 @@ import { connect } from 'react-redux' -import { Button } from 'react-native'; +import { Button, Text } from 'react-native'; import { Log } from 'lib/log.js'; import { Note } from 'lib/models/note.js'; import { NotesScreenUtils } from 'lib/components/screens/notes-utils.js' +import { reg } from 'lib/registry.js'; +import { _ } from 'lib/locale.js'; const React = require('react'); const { @@ -11,7 +13,6 @@ const { ScrollView, View, Image, - Text, } = require('react-native'); const { Component } = React; @@ -41,6 +42,11 @@ const styles = StyleSheet.create({ class SideMenuContentComponent extends Component { + constructor() { + super(); + this.state = { syncReportText: '' }; + } + folder_press(folder) { this.props.dispatch({ type: 'SIDE_MENU_CLOSE', @@ -49,19 +55,57 @@ class SideMenuContentComponent extends Component { NotesScreenUtils.openNoteList(folder.id); } + async synchronize_press() { + if (reg.oneDriveApi().auth()) { + let options = { + onProgress: (report) => { + let line = []; + line.push(_('Items to upload: %d/%d.', report.createRemote + report.updateRemote, report.remotesToUpdate)); + line.push(_('Remote items to delete: %d/%d.', report.deleteRemote, report.remotesToDelete)); + line.push(_('Items to download: %d/%d.', report.createLocal + report.updateLocal, report.localsToUdpate)); + line.push(_('Local items to delete: %d/%d.', report.deleteLocal, report.localsToDelete)); + this.setState({ syncReportText: line.join("\n") }); + }, + }; + + try { + const sync = await reg.synchronizer() + sync.start(options); + } catch (error) { + Log.error(error); + } + } else { + this.props.dispatch({ + type: 'Navigation/NAVIGATE', + routeName: 'OneDriveLogin', + }); + } + } + render() { - let buttons = []; + let keyIndex = 0; + let key = () => { + return 'smitem_' + (keyIndex++); + } + + let items = []; for (let i = 0; i < this.props.folders.length; i++) { let f = this.props.folders[i]; let title = f.title ? f.title : ''; - buttons.push( -