1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Mobile: Add quick actions (#2247)

* Super basic QuickAction is working!

* QuickAction successfully creates notes

* Update icons

* Update icons and support new to-do

* Update icons and support new to-do

* Fixed

* Extract QuickActions

* It works as long as you go out of the edit screen (saving is not sufficient)

* working? but still kinda buggy...

* Cleanup

* Cleanup

* Cleanup

* Use the same pattern as onJoplinLinkClick_

* Cleanup

* Cleanup

* Remove pluginAssets/index.js change

* manual Andoid linking

* Transition QuickActions.js to .ts

* Unstage QuickActions.js in favor of .ts

* Move QuickActions out of lib/

* Add comment about userInfo in QuickActions

* Remove redundant QuickActions file

* Remove pluginAssets/index to resolve conflict

* Update CONTRIBUTING.md to include test runner troubleshooting

* Add `npm run tsc` to Unit Tests docs

Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx>
This commit is contained in:
Devon Zuegel 2020-02-18 15:52:36 -08:00 committed by GitHub
parent e6cbd8c8f8
commit eeb9999334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1306 additions and 57 deletions

View File

@ -53,6 +53,7 @@ ReactNativeClient/lib/joplin-renderer/assets/
# Ignore files generated from TypeScript files # Ignore files generated from TypeScript files
ElectronClient/app/gui/ShareNoteDialog.js ElectronClient/app/gui/ShareNoteDialog.js
ReactNativeClient/lib/JoplinServerApi.js ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/QuickActions.js
ReactNativeClient/PluginAssetsLoader.js ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js

1
.gitignore vendored
View File

@ -49,6 +49,7 @@ Tools/commit_hook.txt
# Ignore files generated from TypeScript files # Ignore files generated from TypeScript files
ElectronClient/app/gui/ShareNoteDialog.js ElectronClient/app/gui/ShareNoteDialog.js
ReactNativeClient/lib/JoplinServerApi.js ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/QuickActions.js
ReactNativeClient/PluginAssetsLoader.js ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js

View File

@ -52,21 +52,32 @@ When submitting a pull request for a new feature or bug fix, please add unit tes
The tests are under CliClient/tests. To get them running, you first need to build the CLI app: The tests are under CliClient/tests. To get them running, you first need to build the CLI app:
```sh
npm run tsc # Build the .ts and .tsx files
cd CliClient cd CliClient
npm i npm i
./build.sh ./build.sh
```
To run all the test units: To run all the test units:
```sh
./run_test.sh ./run_test.sh
```
To run just one particular file: To run just one particular file:
```sh
./run_test.sh markdownUtils # Don't add the .js extension ./run_test.sh markdownUtils # Don't add the .js extension
```
To filter tests: To filter tests:
```sh
./run_test.sh "should handle conflict" # Will run all the test units that contain "should handle conflict" in their description ./run_test.sh "should handle conflict" # Will run all the test units that contain "should handle conflict" in their description
```
If you get the error `Cannot find module '/joplin/CliClient/node_modules/sqlite3/lib/binding/node-v79-darwin-x64/node_sqlite3.node'`, you may need to run `npm rebuild`.
## About abandoned pull requests ## About abandoned pull requests

View File

@ -2929,7 +2929,6 @@
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.4.tgz", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.4.tgz",
"integrity": "sha512-qHwPdGyGr9pOZBoSgUOuNPG20QYZVN00lFcxKQgjPUODSxVH7obQeLVVawa3B4cfSNtLIeczSzoy/xYA8XG5WQ==", "integrity": "sha512-qHwPdGyGr9pOZBoSgUOuNPG20QYZVN00lFcxKQgjPUODSxVH7obQeLVVawa3B4cfSNtLIeczSzoy/xYA8XG5WQ==",
"dev": true,
"requires": { "requires": {
"he": "1.1.1" "he": "1.1.1"
} }

View File

@ -6952,6 +6952,36 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true "dev": true
}
}
},
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"requires": {
"no-case": "^2.2.0"
}
},
"parse-glob": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
"integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
"dev": true,
"optional": true,
"requires": {
"glob-base": "^0.3.0",
"is-dotfile": "^1.0.0",
"is-extglob": "^1.0.0",
"is-glob": "^2.0.0"
},
"dependencies": {
"is-extglob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
"dev": true,
"optional": true
}, },
"semver-diff": { "semver-diff": {
"version": "2.1.0", "version": "2.1.0",
@ -7653,7 +7683,8 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
"dev": true "dev": true,
"optional": true
}, },
"repeat-string": { "repeat-string": {
"version": "1.6.1", "version": "1.6.1",

View File

@ -0,0 +1,50 @@
import {DeviceEventEmitter} from 'react-native';
import * as QuickActions from 'react-native-quick-actions';
const { _ } = require('lib/locale.js');
type TData = {
type: string
}
export default (dispatch: Function, folderId: string) => {
const userInfo = { url: '' };
QuickActions.setShortcutItems([
{type: 'New note', title: _('New note'), icon: 'Compose', userInfo},
{type: 'New to-do', title: _('New to-do'), icon: 'Add', userInfo},
]);
DeviceEventEmitter.addListener('quickActionShortcut', (data: TData) => {
// This dispatch is to momentarily go back to reset state, similar to what
// happens in onJoplinLinkClick_(). Easier to just go back, then go to the
// note since the Note screen doesn't handle reloading a different note.
//
// This hack is necessary because otherwise you get this problem:
// The first time you create a note from the quick-action menu, it works
// perfectly. But if you do it again immediately later, it re-opens the
// page to that first note you made rather than creating an entirely new
// note. If you navigate around enough (which I think changes the redux
// state sufficiently or something), then it'll work again.
dispatch({type: 'NAV_BACK'});
if (data.type === 'New note') {
dispatch({
type: 'NAV_GO',
noteId: null,
folderId,
routeName: 'Note',
itemType: 'note',
});
}
if (data.type === 'New to-do') {
dispatch({
type: 'NAV_GO',
noteId: null,
folderId,
routeName: 'Note',
itemType: 'todo',
});
}
});
};

View File

@ -180,6 +180,8 @@ android {
} }
dependencies { dependencies {
implementation project(':react-native-quick-actions')
implementation project(':@react-native-community_slider')
implementation "org.webkit:android-jsc:r241213" implementation "org.webkit:android-jsc:r241213"
compile project(':react-native-push-notification') compile project(':react-native-push-notification')
// implementation (project(':react-native-camera')) { // implementation (project(':react-native-camera')) {
@ -219,6 +221,7 @@ dependencies {
implementation project(':react-native-version-info') implementation project(':react-native-version-info')
implementation project(':react-native-camera') implementation project(':react-native-camera')
implementation "com.facebook.react:react-native:+" implementation "com.facebook.react:react-native:+"
implementation project(':react-native-quick-actions')
// implementation "com.google.android.gms:play-services-base:16.0.1" // For Firebase // implementation "com.google.android.gms:play-services-base:16.0.1" // For Firebase
// implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // For Firebase - this line if you wish to use badge on Android // implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // For Firebase - this line if you wish to use badge on Android

View File

@ -4,6 +4,9 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import com.facebook.react.PackageList; import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.reactNativeQuickActions.AppShortcutsPackage;
import com.reactnativecommunity.slider.ReactSliderPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
@ -19,8 +22,7 @@ import android.database.CursorWindow;
public class MainApplication extends Application implements ReactApplication { public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
new ReactNativeHost(this) {
@Override @Override
public boolean getUseDeveloperSupport() { public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG; return BuildConfig.DEBUG;
@ -35,6 +37,29 @@ public class MainApplication extends Application implements ReactApplication {
return packages; return packages;
} }
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new AppShortcutsPackage(),
new ReactSliderPackage(),
new RNCWebViewPackage(),
new ReactNativePushNotificationPackage(),
new ImageResizerPackage(),
new RNFileViewerPackage(),
new RNSecureRandomPackage(),
new ImagePickerPackage(),
new ReactNativeDocumentPicker(),
new RNFetchBlobPackage(),
new RNFSPackage(),
new SQLitePluginPackage(),
new VectorIconsPackage(),
new SharePackage(),
new RNCameraPackage(),
new RNVersionInfoPackage()
);
}
@Override @Override
protected String getJSMainModuleName() { protected String getJSMainModuleName() {
return "index"; return "index";
@ -64,6 +89,7 @@ public class MainApplication extends Application implements ReactApplication {
SoLoader.init(this, /* native exopackage */ false); SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this); // Remove this line if you don't want Flipper enabled initializeFlipper(this); // Remove this line if you don't want Flipper enabled
} }
/** /**
* Loads Flipper in React Native templates. * Loads Flipper in React Native templates.
* *

View File

@ -1,4 +1,22 @@
rootProject.name = 'Joplin' rootProject.name = 'Joplin'
include ':react-native-quick-actions'
project(':react-native-quick-actions').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-quick-actions/android')
include ':@react-native-community_slider'
project(':@react-native-community_slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/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-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-image-resizer'
project(':react-native-image-resizer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-resizer/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':react-native-fs' include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android') project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
*/ */
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RNQuickActionManager.h"
#import <React/RCTBridge.h> #import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h> #import <React/RCTBundleURLProvider.h>
@ -82,4 +83,8 @@ fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
#endif #endif
} }
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded)) completionHandler {
[RNQuickActionManager onQuickActionPress:shortcutItem completionHandler:completionHandler];
}
@end @end

View File

@ -131,7 +131,8 @@ class NoteScreenComponent extends BaseScreenComponent {
if (item.type_ === BaseModel.TYPE_NOTE) { if (item.type_ === BaseModel.TYPE_NOTE) {
// Easier to just go back, then go to the note since // Easier to just go back, then go to the note since
// the Note screen doesn't handle reloading a different note // the Note screen doesn't handle reloading a different note.
// Similar to creating a new note via QuickActions.
this.props.dispatch({ this.props.dispatch({
type: 'NAV_BACK', type: 'NAV_BACK',

View File

@ -8,13 +8,11 @@
// console.disableYellowBox = true // console.disableYellowBox = true
import { YellowBox } from 'react-native'; import {YellowBox, AppRegistry} from 'react-native';
YellowBox.ignoreWarnings([ YellowBox.ignoreWarnings([
'Require cycle: node_modules/react-native-', 'Require cycle: node_modules/react-native-',
'Require cycle: node_modules/rn-fetch-blob', 'Require cycle: node_modules/rn-fetch-blob',
]); ]);
const { AppRegistry } = require('react-native');
const { Root } = require('./root.js'); const { Root } = require('./root.js');
function main() { function main() {

View File

@ -7441,6 +7441,11 @@
"version": "git+https://github.com/laurent22/react-native-push-notification.git#04d0746035d1fb058d8f52696121a0d5a29acbc4", "version": "git+https://github.com/laurent22/react-native-push-notification.git#04d0746035d1fb058d8f52696121a0d5a29acbc4",
"from": "git+https://github.com/laurent22/react-native-push-notification.git" "from": "git+https://github.com/laurent22/react-native-push-notification.git"
}, },
"react-native-quick-actions": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/react-native-quick-actions/-/react-native-quick-actions-0.3.13.tgz",
"integrity": "sha512-Vz13a0+NV0mzCh/29tNt0qDzWPh8i2srTQW8eCSzGFDArnVm1COTOhTD0FY0hWHlxRY0ahvX+BlezTDvsyAuMA=="
},
"react-native-securerandom": { "react-native-securerandom": {
"version": "1.0.0-rc.0", "version": "1.0.0-rc.0",
"resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-1.0.0-rc.0.tgz", "resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-1.0.0-rc.0.tgz",

View File

@ -65,6 +65,8 @@
"react-native-popup-dialog": "^0.9.35", "react-native-popup-dialog": "^0.9.35",
"react-native-popup-menu": "^0.10.0", "react-native-popup-menu": "^0.10.0",
"react-native-push-notification": "git+https://github.com/laurent22/react-native-push-notification.git", "react-native-push-notification": "git+https://github.com/laurent22/react-native-push-notification.git",
"react-native-quick-actions": "^0.3.13",
"react-native-share-extension": "^1.2.1",
"react-native-securerandom": "^1.0.0-rc.0", "react-native-securerandom": "^1.0.0-rc.0",
"react-native-side-menu": "^1.1.3", "react-native-side-menu": "^1.1.3",
"react-native-sqlite-storage": "^4.1.0", "react-native-sqlite-storage": "^4.1.0",

View File

@ -82,6 +82,7 @@ const EncryptionService = require('lib/services/EncryptionService');
const MigrationService = require('lib/services/MigrationService'); const MigrationService = require('lib/services/MigrationService');
import PluginAssetsLoader from './PluginAssetsLoader'; import PluginAssetsLoader from './PluginAssetsLoader';
import setUpQuickActions from './QuickActions';
let storeDispatch = function() {}; let storeDispatch = function() {};
@ -534,6 +535,8 @@ async function initialize(dispatch) {
folderId: folder.id, folderId: folder.id,
}); });
} }
setUpQuickActions(dispatch, folderId);
} catch (error) { } catch (error) {
alert(`Initialization error: ${error.message}`); alert(`Initialization error: ${error.message}`);
reg.logger().error('Initialization error:', error); reg.logger().error('Initialization error:', error);

15
package-lock.json generated
View File

@ -109,14 +109,12 @@
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.3", "version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
"dev": true
}, },
"@types/react": { "@types/react": {
"version": "16.9.16", "version": "16.9.16",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.16.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.16.tgz",
"integrity": "sha512-dQ3wlehuBbYlfvRXfF5G+5TbZF3xqgkikK7DWAsQXe2KnzV+kjD4W2ea+ThCrKASZn9h98bjjPzoTYzfRqyBkw==", "integrity": "sha512-dQ3wlehuBbYlfvRXfF5G+5TbZF3xqgkikK7DWAsQXe2KnzV+kjD4W2ea+ThCrKASZn9h98bjjPzoTYzfRqyBkw==",
"dev": true,
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^2.2.0" "csstype": "^2.2.0"
@ -131,6 +129,14 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-native": {
"version": "0.61.4",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.61.4.tgz",
"integrity": "sha512-RWU51dCIEjvgT0QuclgAha/P9fdAvnDzilhatx85LcTKTH2S3PSOUNZlbxwyZLMrqpCek5xsBOjSA5nOIFYq4A==",
"requires": {
"@types/react": "*"
}
},
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz",
@ -593,8 +599,7 @@
"csstype": { "csstype": {
"version": "2.6.7", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==", "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ=="
"dev": true
}, },
"date-fns": { "date-fns": {
"version": "1.30.1", "version": "1.30.1",

View File

@ -35,5 +35,9 @@
"husky": "^3.0.2", "husky": "^3.0.2",
"lint-staged": "^9.2.1", "lint-staged": "^9.2.1",
"typescript": "^3.7.3" "typescript": "^3.7.3"
},
"dependencies": {
"@types/react-native": "^0.61.4",
"joplin-renderer": "^1.0.6"
} }
} }