You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-30 20:39:46 +02:00
Compare commits
17 Commits
android-v1
...
android-v1
Author | SHA1 | Date | |
---|---|---|---|
|
8d0e562c8a | ||
|
c98e67c003 | ||
|
5565538b80 | ||
|
958979e1d7 | ||
|
685845e097 | ||
|
3813f9e417 | ||
|
40cf3fb4d0 | ||
|
3f88b16603 | ||
|
32c02275a2 | ||
|
c0d679b6c2 | ||
|
eb789b9b9a | ||
|
b1898141c3 | ||
|
3231bfaff0 | ||
|
6bb09c9c30 | ||
|
35d3fe03ab | ||
|
f05929cd17 | ||
|
982c9828da |
2
CliClient/package-lock.json
generated
2
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.0.118",
|
||||
"version": "1.0.119",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.0.118",
|
||||
"version": "1.0.119",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
|
@@ -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": {
|
||||
|
@@ -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() {
|
||||
|
@@ -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>
|
||||
|
2
ElectronClient/app/package-lock.json
generated
2
ElectronClient/app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.0.118",
|
||||
"version": "1.0.119",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.0.118",
|
||||
"version": "1.0.119",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
@@ -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
|
||||
|
@@ -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
|
42
ReactNativeClient/android/app/google-services.json
Normal file
42
ReactNativeClient/android/app/google-services.json
Normal 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"
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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 |
@@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">Joplin</string>
|
||||
<string name="default_notification_channel_id">net.cozic.joplin.notification</string>
|
||||
</resources>
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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'
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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 });
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
32
ReactNativeClient/lib/services/SearchEngineUtils.js
Normal file
32
ReactNativeClient/lib/services/SearchEngineUtils.js
Normal 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;
|
@@ -224,4 +224,8 @@ function escapeHtml(s) {
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
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 };
|
1096
ReactNativeClient/package-lock.json
generated
1096
ReactNativeClient/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
@@ -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.
|
||||
|
@@ -304,8 +304,13 @@ notepad++.exe --openSession # Opens Notepad ++ in new window
|
||||
on this server.</p>
|
||||
</body></html>
|
||||
</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 "Settings" and copy the WebDAV URL from there. <strong>Do not forget to add the folder you've created to that URL</strong>. For example, if the base the WebDAV URL is "<a href="https://example.com/nextcloud/remote.php/webdav/">https://example.com/nextcloud/remote.php/webdav/</a>" and you want the notes to be synced in the "Joplin" directory, you need to give the URL "<a href="https://example.com/nextcloud/remote.php/webdav/Joplin">https://example.com/nextcloud/remote.php/webdav/Joplin</a>" <strong>and you need to create the "Joplin" 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, "though Scott Joplin'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".</p>
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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?
|
||||
|
||||
|
@@ -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
|
Reference in New Issue
Block a user