1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-08 23:07:32 +02:00

Compare commits

...

1 Commits

Author SHA1 Message Date
Laurent Cozic
30931aba2c Update NoteBodyViewer to prepare switch to WebKit 2020-10-12 18:00:10 +01:00
6 changed files with 43 additions and 25 deletions

View File

@@ -200,6 +200,7 @@ ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/BackButtonDialogBox.js ReactNativeClient/lib/components/BackButtonDialogBox.js
ReactNativeClient/lib/components/NoteBodyViewer.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/errorUtils.js ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js ReactNativeClient/lib/eventManager.js

1
.gitignore vendored
View File

@@ -194,6 +194,7 @@ ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/BackButtonDialogBox.js ReactNativeClient/lib/components/BackButtonDialogBox.js
ReactNativeClient/lib/components/NoteBodyViewer.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/errorUtils.js ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js ReactNativeClient/lib/eventManager.js

View File

@@ -13,7 +13,7 @@ import {
Image, Image,
} from 'react-native'; } from 'react-native';
import { renderFormatButtons } from './renderButtons'; import { renderFormatButtons } from './renderButtons';
import { NoteBodyViewer } from 'lib/components/note-body-viewer.js'; const NoteBodyViewer = require('lib/components/NoteBodyViewer').default;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
buttonContainer: { buttonContainer: {

View File

@@ -255,6 +255,8 @@ PODS:
- React - React
- RNSecureRandom (1.0.0-rc.0): - RNSecureRandom (1.0.0-rc.0):
- React - React
- RNShare (3.3.3):
- React
- RNVectorIcons (6.6.0): - RNVectorIcons (6.6.0):
- React - React
- Yoga (1.14.0) - Yoga (1.14.0)
@@ -303,6 +305,7 @@ DEPENDENCIES:
- RNFS (from `../node_modules/react-native-fs`) - RNFS (from `../node_modules/react-native-fs`)
- RNQuickAction (from `../node_modules/react-native-quick-actions`) - RNQuickAction (from `../node_modules/react-native-quick-actions`)
- RNSecureRandom (from `../node_modules/react-native-securerandom`) - RNSecureRandom (from `../node_modules/react-native-securerandom`)
- RNShare (from `../node_modules/react-native-share`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -391,6 +394,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-quick-actions" :path: "../node_modules/react-native-quick-actions"
RNSecureRandom: RNSecureRandom:
:path: "../node_modules/react-native-securerandom" :path: "../node_modules/react-native-securerandom"
RNShare:
:path: "../node_modules/react-native-share"
RNVectorIcons: RNVectorIcons:
:path: "../node_modules/react-native-vector-icons" :path: "../node_modules/react-native-vector-icons"
Yoga: Yoga:
@@ -438,6 +443,7 @@ SPEC CHECKSUMS:
RNFS: 416676c3a9ae404454bade10e3d78147c7c33a40 RNFS: 416676c3a9ae404454bade10e3d78147c7c33a40
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNSecureRandom: 1f19ad1492f7ed416b8fc79e92216a1f73f13a4c RNSecureRandom: 1f19ad1492f7ed416b8fc79e92216a1f73f13a4c
RNShare: f4ec422e27904e0dc9310038d1110460a59ad30d
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b

View File

@@ -1,35 +1,37 @@
import Async from 'react-async'; import Setting from 'lib/models/Setting';
import shim from 'lib/shim';
const Async = require('react-async').default;
const React = require('react'); const React = require('react');
const Component = React.Component; const Component = React.Component;
const { Platform, View, Text, ToastAndroid } = require('react-native'); const { Platform, View, Text, ToastAndroid } = require('react-native');
const { WebView } = require('react-native-webview'); const { WebView } = require('react-native-webview');
const { themeStyle } = require('lib/components/global-style.js'); const { themeStyle } = require('lib/components/global-style.js');
const Setting = require('lib/models/Setting').default; const BackButtonDialogBox = require('lib/components/BackButtonDialogBox').default;
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
const shim = require('lib/shim').default;
const { assetsToHeaders } = require('lib/joplin-renderer'); const { assetsToHeaders } = require('lib/joplin-renderer');
const shared = require('lib/components/shared/note-screen-shared.js'); const shared = require('lib/components/shared/note-screen-shared.js');
const markupLanguageUtils = require('lib/markupLanguageUtils'); const markupLanguageUtils = require('lib/markupLanguageUtils');
const { dialogs } = require('lib/dialogs.js'); const { dialogs } = require('lib/dialogs.js');
const BackButtonDialogBox = require('lib/components/BackButtonDialogBox').default;
const Resource = require('lib/models/Resource.js'); const Resource = require('lib/models/Resource.js');
const Share = require('react-native-share').default; const Share = require('react-native-share').default;
class NoteBodyViewer extends Component { export default class NoteBodyViewer extends Component {
private forceUpdate_:boolean = false;
private isMounted_:boolean = false;
private markupToHtml_:any;
constructor() { constructor() {
super(); super();
this.state = { this.state = {
resources: {}, resources: {},
webViewLoaded: false, webViewLoaded: false,
bodyHtml: '', bodyHtml: '',
}; };
this.forceUpdate_ = false;
this.isMounted_ = false;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml(); this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
this.reloadNote = this.reloadNote.bind(this); this.reloadNote = this.reloadNote.bind(this);
@@ -88,22 +90,23 @@ class NoteBodyViewer extends Component {
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode'); const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
const injectedJs = []; const injectedJs = [];
injectedJs.push('try {');
injectedJs.push(shim.injectedJs('webviewLib')); injectedJs.push(shim.injectedJs('webviewLib'));
// Note that this postMessage function accepts two arguments, for compatibility with the desktop version, but // Note that this postMessage function accepts two arguments, for compatibility with the desktop version, but
// the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app). // the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app).
injectedJs.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };'); injectedJs.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });'); injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
injectedJs.push(` injectedJs.push(`
const readyStateCheckInterval = shim.setInterval(function() { const readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") { if (document.readyState === "complete") {
shim.clearInterval(readyStateCheckInterval); clearInterval(readyStateCheckInterval);
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload(); if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
const hash = "${this.props.noteHash}"; const hash = "${this.props.noteHash}";
// Gives it a bit of time before scrolling to the anchor // Gives it a bit of time before scrolling to the anchor
// so that images are loaded. // so that images are loaded.
if (hash) { if (hash) {
shim.setTimeout(() => { setTimeout(() => {
const e = document.getElementById(hash); const e = document.getElementById(hash);
if (!e) { if (!e) {
console.warn('Cannot find hash', hash); console.warn('Cannot find hash', hash);
@@ -115,6 +118,11 @@ class NoteBodyViewer extends Component {
} }
}, 10); }, 10);
`); `);
injectedJs.push('} catch (e) {');
injectedJs.push(' window.ReactNativeWebView.postMessage("error:" + e.message + ": " + JSON.stringify(e))');
injectedJs.push(' true;');
injectedJs.push('}');
injectedJs.push('true;');
html = html =
` `
@@ -173,8 +181,8 @@ class NoteBodyViewer extends Component {
}, 100); }, 100);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps:any, nextState:any) {
const safeGetNoteProp = (props, propName) => { const safeGetNoteProp = (props:any, propName:string) => {
if (!props) return null; if (!props) return null;
if (!props.note) return null; if (!props.note) return null;
return props.note[propName]; return props.note[propName];
@@ -210,7 +218,7 @@ class NoteBodyViewer extends Component {
return this.forceUpdate_; return this.forceUpdate_;
} }
async onResourceLongPress(msg) { async onResourceLongPress(msg:string) {
try { try {
const resourceId = msg.split(':')[1]; const resourceId = msg.split(':')[1];
const resource = await Resource.load(resourceId); const resource = await Resource.load(resourceId);
@@ -256,7 +264,7 @@ class NoteBodyViewer extends Component {
// https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-503754654 // https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-503754654
const webViewStyle = { backgroundColor: this.props.webViewStyle.backgroundColor }; const webViewStyle:any = { backgroundColor: this.props.webViewStyle.backgroundColor };
// On iOS, the onLoadEnd() event is never fired so always // On iOS, the onLoadEnd() event is never fired so always
// display the webview (don't do the little trick // display the webview (don't do the little trick
// to avoid the white flash). // to avoid the white flash).
@@ -267,7 +275,9 @@ class NoteBodyViewer extends Component {
return ( return (
<View style={this.props.style}> <View style={this.props.style}>
<Async promiseFn={this.reloadNote} watchFn={this.watchFn}> <Async promiseFn={this.reloadNote} watchFn={this.watchFn}>
{({ data, error, isPending }) => { {(args:any) => {
const { data, error, isPending } = args;
if (error) { if (error) {
console.error(error); console.error(error);
return <Text>{error.message}</Text>; return <Text>{error.message}</Text>;
@@ -286,7 +296,7 @@ class NoteBodyViewer extends Component {
allowFileAccess={true} allowFileAccess={true}
onLoadEnd={() => this.onLoadEnd()} onLoadEnd={() => this.onLoadEnd()}
onError={() => reg.logger().error('WebView error')} onError={() => reg.logger().error('WebView error')}
onMessage={event => { onMessage={(event:any) => {
// Since RN 58 (or 59) messages are now escaped twice??? // Since RN 58 (or 59) messages are now escaped twice???
let msg = unescape(unescape(event.nativeEvent.data)); let msg = unescape(unescape(event.nativeEvent.data));
@@ -296,13 +306,15 @@ class NoteBodyViewer extends Component {
const newBody = shared.toggleCheckbox(msg, this.props.note.body); const newBody = shared.toggleCheckbox(msg, this.props.note.body);
if (this.props.onCheckboxChange) this.props.onCheckboxChange(newBody); if (this.props.onCheckboxChange) this.props.onCheckboxChange(newBody);
} else if (msg.indexOf('markForDownload:') === 0) { } else if (msg.indexOf('markForDownload:') === 0) {
msg = msg.split(':'); const splittedMsg = msg.split(':');
const resourceId = msg[1]; const resourceId = splittedMsg[1];
if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId }); if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId });
} else if (msg.startsWith('longclick:')) { } else if (msg.startsWith('longclick:')) {
this.onResourceLongPress(msg); this.onResourceLongPress(msg);
} else if (msg.startsWith('joplin:')) { } else if (msg.startsWith('joplin:')) {
this.props.onJoplinLinkClick(msg); this.props.onJoplinLinkClick(msg);
} else if (msg.startsWith('error:')) {
console.error('Webview injected script error: ' + msg);
} }
}} }}
/> />
@@ -310,7 +322,7 @@ class NoteBodyViewer extends Component {
}} }}
</Async> </Async>
<BackButtonDialogBox <BackButtonDialogBox
ref={dialogbox => { ref={(dialogbox:any) => {
this.dialogbox = dialogbox; this.dialogbox = dialogbox;
}} }}
/> />
@@ -318,5 +330,3 @@ class NoteBodyViewer extends Component {
); );
} }
} }
module.exports = { NoteBodyViewer };

View File

@@ -32,7 +32,7 @@ const { BaseScreenComponent } = require('lib/components/base-screen.js');
const { themeStyle, editorFont } = require('lib/components/global-style.js'); const { themeStyle, editorFont } = require('lib/components/global-style.js');
const { dialogs } = require('lib/dialogs.js'); const { dialogs } = require('lib/dialogs.js');
const DialogBox = require('react-native-dialogbox').default; const DialogBox = require('react-native-dialogbox').default;
const { NoteBodyViewer } = require('lib/components/note-body-viewer.js'); const NoteBodyViewer = require('lib/components/NoteBodyViewer').default;
const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker'); const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker');
const ImageResizer = require('react-native-image-resizer').default; const ImageResizer = require('react-native-image-resizer').default;
const shared = require('lib/components/shared/note-screen-shared.js'); const shared = require('lib/components/shared/note-screen-shared.js');