1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-30 20:39:46 +02:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Laurent Cozic
8d0e562c8a Android release v1.0.181 2018-12-20 14:58:35 +01:00
Laurent Cozic
c98e67c003 /bin/bash: qa: command not found 2018-12-20 14:53:10 +01:00
Laurent Cozic
5565538b80 Android: Trying to get notifications to work in Android 8.x 2018-12-20 14:52:56 +01:00
Helmut K. C. Tessarek
958979e1d7 CLI v1.0.119 2018-12-19 11:13:18 +01:00
Laurent Cozic
685845e097 Update website 2018-12-17 23:47:35 +01:00
Laurent Cozic
3813f9e417 Clipper release v1.0.8 2018-12-17 23:42:13 +01:00
Laurent Cozic
40cf3fb4d0 Android release v1.0.179 2018-12-17 23:25:21 +01:00
Laurent Cozic
3f88b16603 iOS v10.0.27 2018-12-17 23:12:35 +01:00
Laurent Cozic
32c02275a2 Upgrade React Native 2018-12-17 23:11:53 +01:00
Laurent Cozic
c0d679b6c2 Android release v1.0.178 2018-12-16 18:41:51 +01:00
Laurent Cozic
eb789b9b9a Electron release v1.0.119 2018-12-16 18:35:37 +01:00
Laurent Cozic
b1898141c3 Mobile: Fixes #382: Implemented new search engine for mobile and highlight searched words in notes 2018-12-16 18:32:42 +01:00
Laurent Cozic
3231bfaff0 Mobile: Fixes #1045: Display notebooks as a tree in notebook dropdown 2018-12-16 17:18:24 +01:00
Laurent Cozic
6bb09c9c30 Updated FAQ with info about F-Droid and how to fix Nextcloud issues 2018-12-16 14:30:56 +01:00
Laurent Cozic
35d3fe03ab Android: Fixes #321: Changed notification library to Firebase to get more reliable notifications 2018-12-16 14:11:45 +01:00
Laurent Cozic
f05929cd17 All: Fixes #1033: Handle hard break when rendering Markdown to HTML 2018-12-16 11:41:15 +01:00
Laurent Cozic
982c9828da Desktop: Fixes #1039: Always print or export to PDF using light theme 2018-12-16 02:49:06 +01:00
42 changed files with 1189 additions and 751 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "1.0.118",
"version": "1.0.119",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -19,7 +19,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.0.118",
"version": "1.0.119",
"bin": {
"joplin": "./main.js"
},

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "1.0.7",
"version": "1.0.8",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplin.cozic.net",
"icons": {

View File

@@ -911,32 +911,35 @@ class NoteTextComponent extends React.Component {
async doCommand(command) {
if (!command || !this.state.note) return;
let commandProcessed = true;
let fn = null;
if (command.name === 'exportPdf' && this.webview_) {
this.commandSavePdf();
fn = this.commandSavePdf;
} else if (command.name === 'print' && this.webview_) {
this.webview_.print();
fn = this.commandPrint;
} else if (command.name === 'textBold') {
this.commandTextBold();
fn = this.commandTextBold;
} else if (command.name === 'textItalic') {
this.commandTextItalic();
fn = this.commandTextItalic;
} else if (command.name === 'insertDateTime' ) {
this.commandDateTime();
fn = this.commandDateTime;
} else if (command.name === 'commandStartExternalEditing') {
this.commandStartExternalEditing();
fn = this.commandStartExternalEditing;
} else if (command.name === 'showLocalSearch') {
this.commandShowLocalSearch();
} else {
commandProcessed = false;
fn = this.commandShowLocalSearch;
}
if (commandProcessed) {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: null,
});
}
if (!fn) return;
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: null,
});
requestAnimationFrame(() => {
fn = fn.bind(this);
fn();
});
}
commandShowLocalSearch() {
@@ -994,6 +997,41 @@ class NoteTextComponent extends React.Component {
});
}
printTo_(target, options) {
const previousBody = this.state.note.body;
const tempBody = "# " + this.state.note.title + "\n\n" + previousBody;
const previousTheme = Setting.value('theme');
Setting.setValue('theme', Setting.THEME_LIGHT);
this.lastSetHtml_ = '';
this.updateHtml(tempBody);
this.forceUpdate();
const restoreSettings = () => {
Setting.setValue('theme', previousTheme);
this.lastSetHtml_ = '';
this.updateHtml(previousBody);
this.forceUpdate();
}
setTimeout(() => {
if (target === 'pdf') {
this.webview_.printToPDF({}, (error, data) => {
restoreSettings();
if (error) {
bridge().showErrorMessageBox(error.message);
} else {
shim.fsDriver().writeFile(options.path, data, 'buffer');
}
});
} else if (target === 'printer') {
this.webview_.print();
restoreSettings();
}
}, 100);
}
commandSavePdf() {
const path = bridge().showSaveDialog({
filters: [{ name: _('PDF File'), extensions: ['pdf']}],
@@ -1001,35 +1039,12 @@ class NoteTextComponent extends React.Component {
});
if (path) {
// Temporarily add a <h2> title in the webview
const newHtml = this.insertHtmlHeading_(this.lastSetHtml_, this.state.note.title);
this.webview_.send('setHtml', newHtml);
setTimeout(() => {
this.webview_.printToPDF({}, (error, data) => {
if (error) {
bridge().showErrorMessageBox(error.message);
} else {
shim.fsDriver().writeFile(path, data, 'buffer');
}
// Refresh the webview, restoring the previous content
this.lastSetHtml_ = '';
this.forceUpdate();
});
}, 100);
this.printTo_('pdf', { path: path });
}
}
insertHtmlHeading_(s, heading) {
const tag = 'h2';
const marker = '<!-- START_OF_DOCUMENT -->'
let splitStyle = s.split(marker);
const index = splitStyle.length > 1 ? 1 : 0;
let toInsert = escapeHtml(heading);
toInsert = '<' + tag + '>' + toInsert + '</' + tag + '>';
splitStyle[index] = toInsert + splitStyle[index];
return splitStyle.join(marker);
commandPrint() {
this.printTo_('printer');
}
async commandStartExternalEditing() {

View File

@@ -36,7 +36,6 @@
<body id="body">
<div id="hlScriptContainer"></div>
<div id="markScriptContainer"></div>
<!-- START_OF_DOCUMENT -->
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
<script>

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "1.0.118",
"version": "1.0.119",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "1.0.118",
"version": "1.0.119",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {

View File

@@ -36,7 +36,7 @@ wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/Joplin_insta
Operating System | Download | Alt. Download
-----------------|----------|----------------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.177/joplin-v1.0.177.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.181/joplin-v1.0.181.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplin.cozic.net/images/BadgeIOS.png'/></a> | -
## Terminal application

View File

@@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097413
versionName "1.0.177"
versionCode 2097417
versionName "1.0.181"
ndk {
abiFilters "armeabi-v7a", "x86"
}
@@ -137,24 +137,39 @@ android {
}
dependencies {
compile project(':react-native-camera')
compile project(':react-native-file-viewer')
compile project(':react-native-securerandom')
compile project(':react-native-push-notification')
compile project(':react-native-fs')
compile project(':react-native-image-picker')
compile project(':react-native-vector-icons')
compile project(':react-native-fs')
implementation project(':react-native-firebase')
implementation (project(':react-native-camera')) {
// This is required because com.google.firebase requires v16.0.x of com.google.android.gms
// while react-native-camera requires v15.x, which results in broken dependencies with
// this error message:
//
// The library com.google.android.gms:play-services-base is being requested by various other libraries at [[15.0.1,15.0.1]], but resolves to 16.0.1
//
// For the record: found solution by removing all Firebase stuff here and running "gradlew.bat :app:dependencies"
// That shows that react-native-camera was the one requiring v15.0.1.
exclude group: "com.google.android.gms"
}
implementation project(':react-native-file-viewer')
implementation project(':react-native-securerandom')
implementation project(':react-native-fs')
implementation project(':react-native-image-picker')
implementation project(':react-native-vector-icons')
implementation project(':react-native-fs')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
compile project(':react-native-sqlite-storage')
compile project(':rn-fetch-blob')
compile project(':react-native-document-picker')
compile project(':react-native-image-resizer')
compile project(':react-native-share-extension')
compile project(':react-native-version-info')
compile "com.facebook.react:react-native:+"
implementation project(':react-native-sqlite-storage')
implementation project(':rn-fetch-blob')
implementation project(':react-native-document-picker')
implementation project(':react-native-image-resizer')
implementation project(':react-native-share-extension')
implementation project(':react-native-version-info')
implementation "com.facebook.react:react-native:+"
implementation "com.google.android.gms:play-services-base:16.0.1" // For Firebase
implementation "com.google.firebase:firebase-core:16.0.4" // For Firebase
implementation "com.google.firebase:firebase-messaging:17.3.4" // For Firebase
implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // For Firebase - this line if you wish to use badge on Android
// To fix the error below, which happened after adding react-native-camera.
// Doesn't make any sense since rn-camera neither defines v26 nor 27 but
@@ -183,3 +198,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
}
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
apply plugin: 'com.google.gms.google-services' // For Firebase

View File

@@ -0,0 +1,42 @@
{
"project_info": {
"project_number": "790045682275",
"firebase_url": "https://joplin-b5b20.firebaseio.com",
"project_id": "joplin-b5b20",
"storage_bucket": "joplin-b5b20.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:790045682275:android:8b68903cf881e9f7",
"android_client_info": {
"package_name": "net.cozic.joplin"
}
},
"oauth_client": [
{
"client_id": "790045682275-fkusmvsm7gv3nve7h0sg0uuor9njf4sm.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCbHjUWAKcbldLTuoN7JybJ8dfznwBG_gM"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View File

@@ -11,25 +11,23 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<!-- ============================= -->
<!-- START RNFirebaseNotifications -->
<!-- ============================= -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- ============================= -->
<!-- END RNFirebaseNotifications -->
<!-- ============================= -->
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- ==================================== -->
<!-- START react-native-push-notification -->
<!-- ==================================== -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- ================================== -->
<!-- END react-native-push-notification -->
<!-- ================================== -->
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="26" />
@@ -41,27 +39,65 @@
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<!-- ==================================== -->
<!-- START react-native-push-notification -->
<!-- ==================================== -->
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
<!-- ============================= -->
<!-- START RNFirebaseNotifications -->
<!-- ============================= -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_access_alarm" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id"/>
<receiver android:name="io.invertase.firebase.notifications.RNFirebaseNotificationReceiver"/>
<receiver android:enabled="true" android:exported="true" android:name="io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
<!-- ================================== -->
<!-- END react-native-push-notification -->
<!-- ================================== -->
<!-- ============================= -->
<!-- END RNFirebaseNotifications -->
<!-- ============================= -->
<!-- ============================= -->
<!-- START RNFirebaseNotifications -->
<!-- ============================= -->
<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="io.invertase.firebase.messaging.RNFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<!-- "If you want to be able to react to data-only messages when your app is in the background, e.g. to display a heads up notification" -->
<service android:name="io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" />
<!-- ============================= -->
<!-- END RNFirebaseNotifications -->
<!-- ============================= -->
<!-- 2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification -->
<!-- Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed. -->
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleInstance">
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@@ -3,10 +3,12 @@ package net.cozic.joplin;
import android.app.Application;
import com.facebook.react.ReactApplication;
import io.invertase.firebase.RNFirebasePackage;
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import org.reactnative.camera.RNCameraPackage;
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
import net.rhogan.rnsecurerandom.RNSecureRandomPackage;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
import com.imagepicker.ImagePickerPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
@@ -38,12 +40,14 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new ImageResizerPackage(),
new MainReactPackage(),
new RNCameraPackage(),
new RNFileViewerPackage(),
new RNSecureRandomPackage(),
new ReactNativePushNotificationPackage(),
new ImageResizerPackage(),
new RNFirebasePackage(),
new RNFirebaseMessagingPackage(),
new RNFirebaseNotificationsPackage(),
new RNCameraPackage(),
new RNFileViewerPackage(),
new RNSecureRandomPackage(),
new ImagePickerPackage(),
new ReactNativeDocumentPicker(),
new RNFetchBlobPackage(),

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -1,3 +1,4 @@
<resources>
<string name="app_name">Joplin</string>
<string name="default_notification_channel_id">net.cozic.joplin.notification</string>
</resources>

View File

@@ -13,7 +13,8 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.0' // Upgraded from 3.1.4 to 3.2.0 for Firebase
classpath 'com.google.gms:google-services:4.0.1' // For Firebase
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -23,6 +24,8 @@ buildscript {
allprojects {
repositories {
mavenLocal()
google()
jcenter() // Was added by me - still needed?
maven {
url "https://maven.google.com"
}
@@ -30,8 +33,6 @@ allprojects {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
jcenter() // Was added by me - still needed?
google()
}
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

View File

@@ -1,12 +1,12 @@
rootProject.name = 'Joplin'
include ':react-native-firebase'
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
include ':react-native-camera'
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
include ':react-native-file-viewer'
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')
include ':react-native-securerandom'
project(':react-native-securerandom').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-securerandom/android')
include ':react-native-push-notification'
project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android')
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-image-picker'

View File

@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>10.0.26</string>
<string>10.0.27</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>26</string>
<string>27</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@@ -34,7 +34,7 @@ const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
const SyncTargetDropbox = require('lib/SyncTargetDropbox.js');
const EncryptionService = require('lib/services/EncryptionService');
const ResourceFetcher = require('lib/services/ResourceFetcher');
const SearchEngine = require('lib/services/SearchEngine');
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const BaseService = require('lib/services/BaseService');
@@ -220,27 +220,7 @@ class BaseApplication {
notes = await Tag.notes(parentId, options);
} else if (parentType === BaseModel.TYPE_SEARCH) {
const search = BaseModel.byId(state.searches, parentId);
const results = await SearchEngine.instance().search(search.query_pattern);
const noteIds = results.map(n => n.id);
const previewOptions = {
order: [],
fields: Note.previewFields(),
conditions: ['id IN ("' + noteIds.join('","') + '")'],
}
notes = await Note.previews(null, previewOptions);
// By default, the notes will be returned in reverse order
// or maybe random order so sort them here in the correct order
// (search engine returns the results in order of relevance).
const sortedNotes = [];
for (let i = 0; i < notes.length; i++) {
const idx = noteIds.indexOf(notes[i].id);
sortedNotes[idx] = notes[i];
}
notes = sortedNotes;
notes = await SearchEngineUtils.notesForQuery(search.query_pattern);
}
}

View File

@@ -7,6 +7,7 @@ const { shim } = require('lib/shim.js');
const { _ } = require('lib/locale');
const md5 = require('md5');
const MdToHtml_Katex = require('lib/MdToHtml_Katex');
const { pregQuote } = require('lib/string-utils.js');
class MdToHtml {
@@ -311,6 +312,8 @@ class MdToHtml {
output.push(t.content);
} else if (t.type === 'softbreak') {
output.push('<br/>');
} else if (t.type === 'hardbreak') {
output.push('<br/>');
} else if (t.type === 'hr') {
output.push('<hr/>');
} else {
@@ -411,10 +414,30 @@ class MdToHtml {
return output.join('');
}
applyHighlightedKeywords_(body, keywords) {
for (let i = 0; i < keywords.length; i++) {
const k = keywords[i];
let regexString = '';
if (k.type === 'regex') {
regexString = k.value;
} else {
regexString = pregQuote(k);
}
const re = new RegExp('(^|\n|\b)(' + regexString + ')(\n|\b|$)', 'gi');
body = body.replace(re, '$1<span class="highlighted-keyword">$2</span>$3');
}
return body;
}
render(body, style, options = null) {
if (!options) options = {};
if (!options.postMessageSyntax) options.postMessageSyntax = 'postMessage';
if (!options.paddingBottom) options.paddingBottom = '0';
if (!options.highlightedKeywords) options.highlightedKeywords = [];
const cacheKey = this.makeContentKey(this.loadedResources_, body, style, options);
if (this.cachedContentKey_ === cacheKey) return this.cachedContent_;
@@ -425,38 +448,34 @@ class MdToHtml {
html: true,
});
body = this.applyHighlightedKeywords_(body, options.highlightedKeywords);
// Add `file:` protocol in linkify to allow text in the format of "file://..." to translate into
// file-URL links in html view
md.linkify.add('file:', {
validate: function (text, pos, self) {
var tail = text.slice(pos);
if (!self.re.file) {
self.re.file = new RegExp(
'^[\\/]{2,3}[\\S]+' // matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
);
}
if (self.re.file.test(tail)) {
return tail.match(self.re.file)[0].length;
}
return 0;
}
validate: function (text, pos, self) {
var tail = text.slice(pos);
if (!self.re.file) {
// matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
self.re.file = new RegExp('^[\\/]{2,3}[\\S]+');
}
if (self.re.file.test(tail)) {
return tail.match(self.re.file)[0].length;
}
return 0;
}
});
// enable file link URLs in MarkdownIt. Keeps other URL restrictions of MarkdownIt untouched.
// Format [link name](file://...)
md.validateLink = function (url) {
var BAD_PROTO_RE = /^(vbscript|javascript|data):/;
var GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
// url should be normalized at this point, and existing entities are decoded
// url should be normalized at this point, and existing entities are decoded
var str = url.trim().toLowerCase();
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
}
// This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
@@ -607,6 +626,11 @@ class MdToHtml {
padding-left: .2em;
}
.highlighted-keyword {
background-color: #F3B717;
color: black;
}
/*
This is to fix https://github.com/laurent22/joplin/issues/764
Without this, the tag attached to an equation float at an absoluate position of the page,

View File

@@ -34,7 +34,7 @@ class Dropdown extends React.Component {
// Dimensions doesn't return quite the right dimensions so leave an extra gap to make
// sure nothing is off screen.
const listMaxHeight = windowHeight;
const listHeight = Math.min(items.length * itemHeight, listMaxHeight); //Dimensions.get('window').height - this.state.headerSize.y - this.state.headerSize.height - 50;
const listHeight = Math.min(items.length * itemHeight, listMaxHeight);
const maxListTop = windowHeight - listHeight;
const listTop = Math.min(maxListTop, this.state.headerSize.y + this.state.headerSize.height);
@@ -60,10 +60,6 @@ class Dropdown extends React.Component {
const headerWrapperStyle = Object.assign({}, this.props.headerWrapperStyle ? this.props.headerWrapperStyle : {}, {
height: 35,
// borderWidth: 1,
// borderColor: '#ccc',
//paddingLeft: 20,
//paddingRight: 20,
flex: 1,
flexDirection: 'row',
alignItems: 'center',
@@ -91,6 +87,8 @@ class Dropdown extends React.Component {
}
}
if (this.props.labelTransform && this.props.labelTransform === 'trim') headerLabel = headerLabel.trim();
const closeList = () => {
this.setState({ listVisible: false });
}

View File

@@ -87,6 +87,7 @@ class NoteBodyViewer extends Component {
}, 100);
},
paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
highlightedKeywords: this.props.highlightedKeywords,
};
let html = this.mdToHtml_.render(note ? note.body : '', this.props.webViewStyle, mdOptions);

View File

@@ -362,18 +362,25 @@ class ScreenHeaderComponent extends Component {
if (folderPickerOptions && folderPickerOptions.enabled) {
const addFolderChildren = (folders, pickerItems, indent) => {
folders.sort((a, b) => {
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : +1;
});
for (let i = 0; i < folders.length; i++) {
const f = folders[i];
pickerItems.push({ label: ' '.repeat(indent) + ' ' + Folder.displayTitle(f), value: f.id });
pickerItems = addFolderChildren(f.children, pickerItems, indent + 1);
}
return pickerItems;
}
const titlePickerItems = (mustSelect) => {
let output = [];
if (mustSelect) output.push({ label: _('Move to notebook...'), value: null });
for (let i = 0; i < this.props.folders.length; i++) {
let f = this.props.folders[i];
output.push({ label: Folder.displayTitle(f), value: f.id });
}
output.sort((a, b) => {
if (a.value === null) return -1;
if (b.value === null) return +1;
return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : +1;
});
const folderTree = Folder.buildTree(this.props.folders);
output = addFolderChildren(folderTree, output, 0);
return output;
}
@@ -381,6 +388,7 @@ class ScreenHeaderComponent extends Component {
<Dropdown
items={titlePickerItems(!!folderPickerOptions.mustSelect)}
itemHeight={35}
labelTransform="trim"
selectedValue={('selectedFolderId' in folderPickerOptions) ? folderPickerOptions.selectedFolderId : null}
itemListStyle={{
backgroundColor: theme.backgroundColor,

View File

@@ -37,6 +37,7 @@ const AlarmService = require('lib/services/AlarmService.js');
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
const ShareExtension = require('react-native-share-extension').default;
const CameraView = require('lib/components/CameraView');
const SearchEngine = require('lib/services/SearchEngine');
import FileViewer from 'react-native-file-viewer';
@@ -587,12 +588,19 @@ class NoteScreenComponent extends BaseScreenComponent {
this.saveOneProperty('body', newBody);
};
let keywords = [];
if (this.props.searchQuery) {
const parsedQuery = SearchEngine.instance().parseQuery(this.props.searchQuery);
keywords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
}
bodyComponent = <NoteBodyViewer
onJoplinLinkClick={this.onJoplinLinkClick_}
ref="noteBodyViewer"
style={this.styles().noteBodyViewer}
webViewStyle={theme}
note={note}
highlightedKeywords={keywords}
onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}
/>
} else {
@@ -737,6 +745,7 @@ const NoteScreen = connect(
folderId: state.selectedFolderId,
itemType: state.selectedItemType,
folders: state.folders,
searchQuery: state.searchQuery,
theme: state.settings.theme,
sharedData: state.sharedData,
showAdvancedOptions: state.settings.showAdvancedOptions,

View File

@@ -9,6 +9,7 @@ const { NoteItem } = require('lib/components/note-item.js');
const { BaseScreenComponent } = require('lib/components/base-screen.js');
const { themeStyle } = require('lib/components/global-style.js');
const { dialogs } = require('lib/dialogs.js');
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
const DialogBox = require('react-native-dialogbox').default;
class SearchScreenComponent extends BaseScreenComponent {
@@ -105,17 +106,22 @@ class SearchScreenComponent extends BaseScreenComponent {
let notes = []
if (query) {
let p = query.split(' ');
let temp = [];
for (let i = 0; i < p.length; i++) {
let t = p[i].trim();
if (!t) continue;
temp.push(t);
}
notes = await SearchEngineUtils.notesForQuery(query);
notes = await Note.previews(null, {
anywherePattern: '*' + temp.join('*') + '*',
});
// Keeping the code below in case of compatibility issue with old versions
// of Android and SQLite FTS.
// let p = query.split(' ');
// let temp = [];
// for (let i = 0; i < p.length; i++) {
// let t = p[i].trim();
// if (!t) continue;
// temp.push(t);
// }
// notes = await Note.previews(null, {
// anywherePattern: '*' + temp.join('*') + '*',
// });
}
if (!this.isMounted_) return;

View File

@@ -152,6 +152,28 @@ class Folder extends BaseItem {
return getNestedChildren(all, '');
}
static buildTree(folders) {
const idToFolders = {};
for (let i = 0; i < folders.length; i++) {
idToFolders[folders[i].id] = folders[i];
idToFolders[folders[i].id].children = [];
}
const rootFolders = [];
for (let folderId in idToFolders) {
if (!idToFolders.hasOwnProperty(folderId)) continue;
const folder = idToFolders[folderId];
if (!folder.parent_id) {
rootFolders.push(folder);
} else {
idToFolders[folder.parent_id].children.push(folder);
}
}
return rootFolders;
}
static load(id) {
if (id == this.conflictFolderId()) return this.conflictFolder();
return super.load(id);

View File

@@ -1,7 +1,13 @@
const PushNotification = require('react-native-push-notification');
import firebase from 'react-native-firebase';
class AlarmServiceDriver {
constructor() {
this.channel_ = new firebase.notifications.Android.Channel('net.cozic.joplin.notification', 'Joplin Alarm',firebase.notifications.Android.Importance.Max)
.setDescription('Displays a notification for alarms associated with to-dos.');
firebase.notifications().android.createChannel(this.channel_);
}
hasPersistentNotifications() {
return true;
}
@@ -10,25 +16,25 @@ class AlarmServiceDriver {
throw new Error('Available only for non-persistent alarms');
}
firebaseNotificationId_(joplinNotificationId) {
return 'net.cozic.joplin-' + joplinNotificationId;
}
async clearNotification(id) {
PushNotification.cancelLocalNotifications({ id: id + '' });
return firebase.notifications().cancelNotification(this.firebaseNotificationId_(id))
}
async scheduleNotification(notification) {
// Arguments must be set in a certain way and certain format otherwise it cannot be
// cancelled later on. See:
// https://github.com/zo0r/react-native-push-notification/issues/570#issuecomment-337642922
const androidNotification = {
id: notification.id + '',
message: notification.title,
date: notification.date,
userInfo: { id: notification.id + '' },
number: 0,
};
const firebaseNotification = new firebase.notifications.Notification()
firebaseNotification.setNotificationId(this.firebaseNotificationId_(notification.id));
firebaseNotification.setTitle(notification.title)
if ('body' in notification) firebaseNotification.body = notification.body;
firebaseNotification.android.setChannelId('net.cozic.joplin.notification');
firebaseNotification.android.setSmallIcon('ic_stat_access_alarm');
if ('body' in notification) androidNotification.body = notification.body;
PushNotification.localNotificationSchedule(androidNotification);
await firebase.notifications().scheduleNotification(firebaseNotification, {
fireDate: notification.date.getTime(),
});
}
}

View File

@@ -4,6 +4,7 @@ const ItemChange = require('lib/models/ItemChange.js');
const Setting = require('lib/models/Setting.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { pregQuote } = require('lib/string-utils.js');
class SearchEngine {
@@ -105,12 +106,9 @@ class SearchEngine {
term = term.substr(1);
}
const preg_quote = (str, delimiter) => {
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
} // [^ \t,\.,\+\-\\*?!={}<>\|:"\'\(\)[\]]
let regexString = preg_quote(term);
let regexString = pregQuote(term);
if (regexString[regexString.length - 1] === '*') {
regexString = regexString.substr(0, regexString.length - 2) + '[^' + preg_quote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']' + '*';
regexString = regexString.substr(0, regexString.length - 2) + '[^' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']' + '*';
}
return regexString;

View File

@@ -0,0 +1,32 @@
const SearchEngine = require('lib/services/SearchEngine');
const Note = require('lib/models/Note');
class SearchEngineUtils {
static async notesForQuery(query) {
const results = await SearchEngine.instance().search(query);
const noteIds = results.map(n => n.id);
const previewOptions = {
order: [],
fields: Note.previewFields(),
conditions: ['id IN ("' + noteIds.join('","') + '")'],
}
const notes = await Note.previews(null, previewOptions);
// By default, the notes will be returned in reverse order
// or maybe random order so sort them here in the correct order
// (search engine returns the results in order of relevance).
const sortedNotes = [];
for (let i = 0; i < notes.length; i++) {
const idx = noteIds.indexOf(notes[i].id);
sortedNotes[idx] = notes[i];
}
return sortedNotes;
}
}
module.exports = SearchEngineUtils;

View File

@@ -224,4 +224,8 @@ function escapeHtml(s) {
.replace(/'/g, "&#039;");
}
module.exports = { removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, escapeHtml };
function pregQuote(str, delimiter) {
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
}
module.exports = { removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, escapeHtml, pregQuote };

File diff suppressed because it is too large Load Diff

View File

@@ -23,8 +23,8 @@
"moment": "^2.18.1",
"prop-types": "^15.6.0",
"query-string": "4.3.4",
"react": "^16.5.0",
"react-native": "^0.57.1",
"react": "^16.6.3",
"react-native": "^0.57.8",
"react-native-action-button": "^2.6.9",
"react-native-camera": "^1.3.0",
"react-native-datepicker": "^1.6.0",
@@ -32,13 +32,13 @@
"react-native-document-picker": "^2.1.0",
"react-native-dropdownalert": "^3.1.2",
"react-native-file-viewer": "^1.0.5",
"react-native-firebase": "^5.1.1",
"react-native-fs": "^2.11.17",
"react-native-image-picker": "^0.26.7",
"react-native-image-resizer": "^1.0.0",
"react-native-material-dropdown": "^0.5.2",
"react-native-popup-dialog": "^0.9.35",
"react-native-popup-menu": "^0.10.0",
"react-native-push-notification": "^3.0.1",
"react-native-securerandom": "^0.1.1",
"react-native-share-extension": "^1.2.1",
"react-native-side-menu": "^1.1.3",
@@ -64,7 +64,7 @@
"babel-jest": "19.0.0",
"babel-preset-react-native": "1.9.1",
"jest": "19.0.2",
"react-test-renderer": "16.0.0-alpha.6"
"react-test-renderer": "^16.6.3"
},
"jest": {
"preset": "react-native"

View File

@@ -52,6 +52,7 @@ const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
const DropdownAlert = require('react-native-dropdownalert').default;
const ShareExtension = require('react-native-share-extension').default;
const ResourceFetcher = require('lib/services/ResourceFetcher');
const SearchEngine = require('lib/services/SearchEngine');
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
@@ -65,8 +66,6 @@ SyncTargetRegistry.addClass(SyncTargetOneDriveDev);
SyncTargetRegistry.addClass(SyncTargetNextcloud);
SyncTargetRegistry.addClass(SyncTargetWebDAV);
SyncTargetRegistry.addClass(SyncTargetDropbox);
// Disabled because not fully working
SyncTargetRegistry.addClass(SyncTargetFilesystem);
const FsDriverRN = require('lib/fs-driver-rn.js').FsDriverRN;
@@ -497,6 +496,9 @@ async function initialize(dispatch) {
ResourceFetcher.instance().setLogger(reg.logger());
ResourceFetcher.instance().start();
SearchEngine.instance().setDb(reg.db());
SearchEngine.instance().setLogger(reg.logger());
reg.scheduleSync().then(() => {
// Wait for the first sync before updating the notifications, since synchronisation
// might change the notifications.

View File

@@ -304,8 +304,13 @@ notepad++.exe --openSession # Opens Notepad ++ in new window
on this server.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</code></pre><p>In this case, <a href="https://github.com/laurent22/joplin/issues/309">make sure you enter the correct WebDAV URL</a>.</p>
<h2 id="nginx-sync-not-working">Nginx sync not working</h2>
<p>As of now, Joplin is not compatible with the Nginx WebDAV server: <a href="https://github.com/laurent22/joplin/issues/808">https://github.com/laurent22/joplin/issues/808</a></p>
<h2 id="nextcloud-sync-is-not-working">Nextcloud sync is not working</h2>
<ul>
<li>Check your username and password. <strong>Type it manually</strong> (without copying and pasting it) and try again.</li>
<li>Check the WebDAV URL - to get the correct URL, go to Nextcloud and, in the left sidebar, click on &quot;Settings&quot; and copy the WebDAV URL from there. <strong>Do not forget to add the folder you&#39;ve created to that URL</strong>. For example, if the base the WebDAV URL is &quot;<a href="https://example.com/nextcloud/remote.php/webdav/">https://example.com/nextcloud/remote.php/webdav/</a>&quot; and you want the notes to be synced in the &quot;Joplin&quot; directory, you need to give the URL &quot;<a href="https://example.com/nextcloud/remote.php/webdav/Joplin">https://example.com/nextcloud/remote.php/webdav/Joplin</a>&quot; <strong>and you need to create the &quot;Joplin&quot; directory yourself</strong>.</li>
</ul>
<h1 id="could-you-publish-joplin-on-f-droid-">Could you publish Joplin on F-droid?</h1>
<p>Joplin relies on Firebase to enable reliable notifications on Android. Since F-Droid <a href="https://gitlab.com/fdroid/rfp/issues/434#note_55239154">do not accept applications that depend on this package</a>, it is not currently possible to have Joplin in that repository. To avoid using Google Play, you have the option to directly download the Joplin APK file.</p>
<h1 id="why-is-it-named-joplin-">Why is it named Joplin?</h1>
<p>The name comes from the composer and pianist <a href="https://en.wikipedia.org/wiki/Scott_Joplin">Scott Joplin</a>, which I often listen to. His name is also easy to remember and type so it fell like a good choice. And, to quote a user on Hacker News, &quot;though Scott Joplin&#39;s ragtime musical style has a lot in common with some very informal music, his own approach was more educated, sophisticated, and precise. Every note was in its place for a reason, and he was known to prefer his pieces to be performed exactly as written. So you could say that compared to the people who came before him, his notes were more organized&quot;.</p>

View File

@@ -303,7 +303,7 @@
<tr>
<td>Android</td>
<td><a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a></td>
<td>or <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.0.177/joplin-v1.0.177.apk">Download APK File</a></td>
<td>or <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.0.181/joplin-v1.0.181.apk">Download APK File</a></td>
</tr>
<tr>
<td>iOS</td>

View File

@@ -261,15 +261,15 @@
<tbody>
<tr>
<td>Total Windows downloads</td>
<td>87190</td>
<td>97941</td>
</tr>
<tr>
<td>Total macOs downloads</td>
<td>31431</td>
<td>34731</td>
</tr>
<tr>
<td>Total Linux downloads</td>
<td>29400</td>
<td>32862</td>
</tr>
<tr>
<td>Windows %</td>
@@ -300,202 +300,202 @@
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.117">v1.0.117</a></td>
<td>2018-11-24T12:05:24Z</td>
<td>2</td>
<td></td>
<td></td>
<td>2 </td>
<td>10465</td>
<td>3235</td>
<td>3400</td>
<td>17100</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.116">v1.0.116</a></td>
<td>2018-11-20T19:09:24Z</td>
<td>3288</td>
<td>1054</td>
<td>686</td>
<td>5028</td>
<td>3440</td>
<td>1078</td>
<td>698</td>
<td>5216</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.115">v1.0.115</a></td>
<td>2018-11-16T16:52:02Z</td>
<td>3592</td>
<td>1259</td>
<td>772</td>
<td>5623</td>
<td>3618</td>
<td>1267</td>
<td>777</td>
<td>5662</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.114">v1.0.114</a></td>
<td>2018-10-24T20:14:10Z</td>
<td>11331</td>
<td>3460</td>
<td>11352</td>
<td>3466</td>
<td>3817</td>
<td>18608</td>
<td>18635</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.111">v1.0.111</a></td>
<td>2018-09-30T20:15:09Z</td>
<td>11833</td>
<td>3105</td>
<td>3639</td>
<td>18577</td>
<td>11842</td>
<td>3109</td>
<td>3641</td>
<td>18592</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.110">v1.0.110</a></td>
<td>2018-09-29T12:29:21Z</td>
<td>919</td>
<td>368</td>
<td>920</td>
<td>369</td>
<td>99</td>
<td>1386</td>
<td>1388</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.109">v1.0.109</a></td>
<td>2018-09-27T18:01:41Z</td>
<td>2063</td>
<td>2067</td>
<td>673</td>
<td>307</td>
<td>3043</td>
<td>308</td>
<td>3048</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.108">v1.0.108</a></td>
<td>2018-09-29T18:49:29Z</td>
<td>4</td>
<td>7</td>
<td></td>
<td></td>
<td>4 </td>
<td>7 </td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.107">v1.0.107</a></td>
<td>2018-09-16T19:51:07Z</td>
<td>7108</td>
<td>7118</td>
<td>2108</td>
<td>1694</td>
<td>10910</td>
<td>10920</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.106">v1.0.106</a></td>
<td>2018-09-08T15:23:40Z</td>
<td>4511</td>
<td>1435</td>
<td>295</td>
<td>6241</td>
<td>4521</td>
<td>1436</td>
<td>304</td>
<td>6261</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.105">v1.0.105</a></td>
<td>2018-09-05T11:29:36Z</td>
<td>4572</td>
<td>4581</td>
<td>1549</td>
<td>1433</td>
<td>7554</td>
<td>1435</td>
<td>7565</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.104">v1.0.104</a></td>
<td>2018-06-28T20:25:36Z</td>
<td>14958</td>
<td>14969</td>
<td>4649</td>
<td>6948</td>
<td>26555</td>
<td>6955</td>
<td>26573</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.103">v1.0.103</a></td>
<td>2018-06-21T19:38:13Z</td>
<td>2001</td>
<td>854</td>
<td>664</td>
<td>3519</td>
<td>665</td>
<td>3520</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.101">v1.0.101</a></td>
<td>2018-06-17T18:35:11Z</td>
<td>1284</td>
<td>1287</td>
<td>577</td>
<td>397</td>
<td>2258</td>
<td>398</td>
<td>2262</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.100">v1.0.100</a></td>
<td>2018-06-14T17:41:43Z</td>
<td>849</td>
<td>406</td>
<td>851</td>
<td>407</td>
<td>226</td>
<td>1481</td>
<td>1484</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.99">v1.0.99</a></td>
<td>2018-06-10T13:18:23Z</td>
<td>1228</td>
<td>1229</td>
<td>576</td>
<td>370</td>
<td>2174</td>
<td>2175</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.97">v1.0.97</a></td>
<td>2018-06-09T19:23:34Z</td>
<td>291</td>
<td>131</td>
<td>292</td>
<td>132</td>
<td>51</td>
<td>473</td>
<td>475</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.96">v1.0.96</a></td>
<td>2018-05-26T16:36:39Z</td>
<td>2672</td>
<td>1195</td>
<td>1165</td>
<td>5032</td>
<td>2680</td>
<td>1197</td>
<td>1177</td>
<td>5054</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.95">v1.0.95</a></td>
<td>2018-05-25T13:04:30Z</td>
<td>381</td>
<td>186</td>
<td>81</td>
<td>648</td>
<td>383</td>
<td>187</td>
<td>83</td>
<td>653</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.94">v1.0.94</a></td>
<td>2018-05-21T20:52:59Z</td>
<td>1094</td>
<td>553</td>
<td>354</td>
<td>2001</td>
<td>356</td>
<td>2003</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.93">v1.0.93</a></td>
<td>2018-05-14T11:36:01Z</td>
<td>1766</td>
<td>859</td>
<td>872</td>
<td>738</td>
<td>3363</td>
<td>3376</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.91">v1.0.91</a></td>
<td>2018-05-10T14:48:04Z</td>
<td>812</td>
<td>531</td>
<td>286</td>
<td>1629</td>
<td>287</td>
<td>1630</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.89">v1.0.89</a></td>
<td>2018-05-09T13:05:05Z</td>
<td>469</td>
<td>206</td>
<td>90</td>
<td>765</td>
<td>472</td>
<td>207</td>
<td>92</td>
<td>771</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.85">v1.0.85</a></td>
<td>2018-05-01T21:08:24Z</td>
<td>1638</td>
<td>929</td>
<td>607</td>
<td>3174</td>
<td>1639</td>
<td>930</td>
<td>608</td>
<td>3177</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.83">v1.0.83</a></td>
<td>2018-04-04T19:43:58Z</td>
<td>4470</td>
<td>2385</td>
<td>4481</td>
<td>2386</td>
<td>2627</td>
<td>9482</td>
<td>9494</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.82">v1.0.82</a></td>
@@ -510,8 +510,8 @@
<td>2018-03-28T08:13:58Z</td>
<td>984</td>
<td>566</td>
<td>742</td>
<td>2292</td>
<td>743</td>
<td>2293</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.79">v1.0.79</a></td>
@@ -526,8 +526,8 @@
<td>2018-03-17T15:27:18Z</td>
<td>1302</td>
<td>838</td>
<td>841</td>
<td>2981</td>
<td>842</td>
<td>2982</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.77">v1.0.77</a></td>

View File

@@ -80,9 +80,14 @@ For example:
In this case, [make sure you enter the correct WebDAV URL](https://github.com/laurent22/joplin/issues/309).
## Nginx sync not working
## Nextcloud sync is not working
As of now, Joplin is not compatible with the Nginx WebDAV server: https://github.com/laurent22/joplin/issues/808
- Check your username and password. **Type it manually** (without copying and pasting it) and try again.
- Check the WebDAV URL - to get the correct URL, go to Nextcloud and, in the left sidebar, click on "Settings" and copy the WebDAV URL from there. **Do not forget to add the folder you've created to that URL**. For example, if the base the WebDAV URL is "https://example.com/nextcloud/remote.php/webdav/" and you want the notes to be synced in the "Joplin" directory, you need to give the URL "https://example.com/nextcloud/remote.php/webdav/Joplin" **and you need to create the "Joplin" directory yourself**.
# Could you publish Joplin on F-droid?
Joplin relies on Firebase to enable reliable notifications on Android. Since F-Droid [do not accept applications that depend on this package](https://gitlab.com/fdroid/rfp/issues/434#note_55239154), it is not currently possible to have Joplin in that repository. To avoid using Google Play, you have the option to directly download the Joplin APK file.
# Why is it named Joplin?

View File

@@ -2,42 +2,42 @@
Name | Value
--- | ---
Total Windows downloads | 87190
Total macOs downloads | 31431
Total Linux downloads | 29400
Total Windows downloads | 97941
Total macOs downloads | 34731
Total Linux downloads | 32862
Windows % | 59%
macOS % | 21%
Linux % | 20%
Version | Date | Windows | macOS | Linux | Total
--- | --- | --- | --- | --- | ---
[v1.0.117](https://github.com/laurent22/joplin/releases/tag/v1.0.117) | 2018-11-24T12:05:24Z | 2 | | | 2
[v1.0.116](https://github.com/laurent22/joplin/releases/tag/v1.0.116) | 2018-11-20T19:09:24Z | 3288 | 1054 | 686 | 5028
[v1.0.115](https://github.com/laurent22/joplin/releases/tag/v1.0.115) | 2018-11-16T16:52:02Z | 3592 | 1259 | 772 | 5623
[v1.0.114](https://github.com/laurent22/joplin/releases/tag/v1.0.114) | 2018-10-24T20:14:10Z | 11331 | 3460 | 3817 | 18608
[v1.0.111](https://github.com/laurent22/joplin/releases/tag/v1.0.111) | 2018-09-30T20:15:09Z | 11833 | 3105 | 3639 | 18577
[v1.0.110](https://github.com/laurent22/joplin/releases/tag/v1.0.110) | 2018-09-29T12:29:21Z | 919 | 368 | 99 | 1386
[v1.0.109](https://github.com/laurent22/joplin/releases/tag/v1.0.109) | 2018-09-27T18:01:41Z | 2063 | 673 | 307 | 3043
[v1.0.108](https://github.com/laurent22/joplin/releases/tag/v1.0.108) | 2018-09-29T18:49:29Z | 4 | | | 4
[v1.0.107](https://github.com/laurent22/joplin/releases/tag/v1.0.107) | 2018-09-16T19:51:07Z | 7108 | 2108 | 1694 | 10910
[v1.0.106](https://github.com/laurent22/joplin/releases/tag/v1.0.106) | 2018-09-08T15:23:40Z | 4511 | 1435 | 295 | 6241
[v1.0.105](https://github.com/laurent22/joplin/releases/tag/v1.0.105) | 2018-09-05T11:29:36Z | 4572 | 1549 | 1433 | 7554
[v1.0.104](https://github.com/laurent22/joplin/releases/tag/v1.0.104) | 2018-06-28T20:25:36Z | 14958 | 4649 | 6948 | 26555
[v1.0.103](https://github.com/laurent22/joplin/releases/tag/v1.0.103) | 2018-06-21T19:38:13Z | 2001 | 854 | 664 | 3519
[v1.0.101](https://github.com/laurent22/joplin/releases/tag/v1.0.101) | 2018-06-17T18:35:11Z | 1284 | 577 | 397 | 2258
[v1.0.100](https://github.com/laurent22/joplin/releases/tag/v1.0.100) | 2018-06-14T17:41:43Z | 849 | 406 | 226 | 1481
[v1.0.99](https://github.com/laurent22/joplin/releases/tag/v1.0.99) | 2018-06-10T13:18:23Z | 1228 | 576 | 370 | 2174
[v1.0.97](https://github.com/laurent22/joplin/releases/tag/v1.0.97) | 2018-06-09T19:23:34Z | 291 | 131 | 51 | 473
[v1.0.96](https://github.com/laurent22/joplin/releases/tag/v1.0.96) | 2018-05-26T16:36:39Z | 2672 | 1195 | 1165 | 5032
[v1.0.95](https://github.com/laurent22/joplin/releases/tag/v1.0.95) | 2018-05-25T13:04:30Z | 381 | 186 | 81 | 648
[v1.0.94](https://github.com/laurent22/joplin/releases/tag/v1.0.94) | 2018-05-21T20:52:59Z | 1094 | 553 | 354 | 2001
[v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1766 | 859 | 738 | 3363
[v1.0.91](https://github.com/laurent22/joplin/releases/tag/v1.0.91) | 2018-05-10T14:48:04Z | 812 | 531 | 286 | 1629
[v1.0.89](https://github.com/laurent22/joplin/releases/tag/v1.0.89) | 2018-05-09T13:05:05Z | 469 | 206 | 90 | 765
[v1.0.85](https://github.com/laurent22/joplin/releases/tag/v1.0.85) | 2018-05-01T21:08:24Z | 1638 | 929 | 607 | 3174
[v1.0.83](https://github.com/laurent22/joplin/releases/tag/v1.0.83) | 2018-04-04T19:43:58Z | 4470 | 2385 | 2627 | 9482
[v1.0.117](https://github.com/laurent22/joplin/releases/tag/v1.0.117) | 2018-11-24T12:05:24Z | 10465 | 3235 | 3400 | 17100
[v1.0.116](https://github.com/laurent22/joplin/releases/tag/v1.0.116) | 2018-11-20T19:09:24Z | 3440 | 1078 | 698 | 5216
[v1.0.115](https://github.com/laurent22/joplin/releases/tag/v1.0.115) | 2018-11-16T16:52:02Z | 3618 | 1267 | 777 | 5662
[v1.0.114](https://github.com/laurent22/joplin/releases/tag/v1.0.114) | 2018-10-24T20:14:10Z | 11352 | 3466 | 3817 | 18635
[v1.0.111](https://github.com/laurent22/joplin/releases/tag/v1.0.111) | 2018-09-30T20:15:09Z | 11842 | 3109 | 3641 | 18592
[v1.0.110](https://github.com/laurent22/joplin/releases/tag/v1.0.110) | 2018-09-29T12:29:21Z | 920 | 369 | 99 | 1388
[v1.0.109](https://github.com/laurent22/joplin/releases/tag/v1.0.109) | 2018-09-27T18:01:41Z | 2067 | 673 | 308 | 3048
[v1.0.108](https://github.com/laurent22/joplin/releases/tag/v1.0.108) | 2018-09-29T18:49:29Z | 7 | | | 7
[v1.0.107](https://github.com/laurent22/joplin/releases/tag/v1.0.107) | 2018-09-16T19:51:07Z | 7118 | 2108 | 1694 | 10920
[v1.0.106](https://github.com/laurent22/joplin/releases/tag/v1.0.106) | 2018-09-08T15:23:40Z | 4521 | 1436 | 304 | 6261
[v1.0.105](https://github.com/laurent22/joplin/releases/tag/v1.0.105) | 2018-09-05T11:29:36Z | 4581 | 1549 | 1435 | 7565
[v1.0.104](https://github.com/laurent22/joplin/releases/tag/v1.0.104) | 2018-06-28T20:25:36Z | 14969 | 4649 | 6955 | 26573
[v1.0.103](https://github.com/laurent22/joplin/releases/tag/v1.0.103) | 2018-06-21T19:38:13Z | 2001 | 854 | 665 | 3520
[v1.0.101](https://github.com/laurent22/joplin/releases/tag/v1.0.101) | 2018-06-17T18:35:11Z | 1287 | 577 | 398 | 2262
[v1.0.100](https://github.com/laurent22/joplin/releases/tag/v1.0.100) | 2018-06-14T17:41:43Z | 851 | 407 | 226 | 1484
[v1.0.99](https://github.com/laurent22/joplin/releases/tag/v1.0.99) | 2018-06-10T13:18:23Z | 1229 | 576 | 370 | 2175
[v1.0.97](https://github.com/laurent22/joplin/releases/tag/v1.0.97) | 2018-06-09T19:23:34Z | 292 | 132 | 51 | 475
[v1.0.96](https://github.com/laurent22/joplin/releases/tag/v1.0.96) | 2018-05-26T16:36:39Z | 2680 | 1197 | 1177 | 5054
[v1.0.95](https://github.com/laurent22/joplin/releases/tag/v1.0.95) | 2018-05-25T13:04:30Z | 383 | 187 | 83 | 653
[v1.0.94](https://github.com/laurent22/joplin/releases/tag/v1.0.94) | 2018-05-21T20:52:59Z | 1094 | 553 | 356 | 2003
[v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1766 | 872 | 738 | 3376
[v1.0.91](https://github.com/laurent22/joplin/releases/tag/v1.0.91) | 2018-05-10T14:48:04Z | 812 | 531 | 287 | 1630
[v1.0.89](https://github.com/laurent22/joplin/releases/tag/v1.0.89) | 2018-05-09T13:05:05Z | 472 | 207 | 92 | 771
[v1.0.85](https://github.com/laurent22/joplin/releases/tag/v1.0.85) | 2018-05-01T21:08:24Z | 1639 | 930 | 608 | 3177
[v1.0.83](https://github.com/laurent22/joplin/releases/tag/v1.0.83) | 2018-04-04T19:43:58Z | 4481 | 2386 | 2627 | 9494
[v1.0.82](https://github.com/laurent22/joplin/releases/tag/v1.0.82) | 2018-03-31T19:16:31Z | 684 | 383 | 93 | 1160
[v1.0.81](https://github.com/laurent22/joplin/releases/tag/v1.0.81) | 2018-03-28T08:13:58Z | 984 | 566 | 742 | 2292
[v1.0.81](https://github.com/laurent22/joplin/releases/tag/v1.0.81) | 2018-03-28T08:13:58Z | 984 | 566 | 743 | 2293
[v1.0.79](https://github.com/laurent22/joplin/releases/tag/v1.0.79) | 2018-03-23T18:00:11Z | 919 | 509 | 353 | 1781
[v1.0.78](https://github.com/laurent22/joplin/releases/tag/v1.0.78) | 2018-03-17T15:27:18Z | 1302 | 838 | 841 | 2981
[v1.0.78](https://github.com/laurent22/joplin/releases/tag/v1.0.78) | 2018-03-17T15:27:18Z | 1302 | 838 | 842 | 2982
[v1.0.77](https://github.com/laurent22/joplin/releases/tag/v1.0.77) | 2018-03-16T15:12:35Z | 165 | 87 | 25 | 277