You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-29 22:48:10 +02:00
Mobile: Long press on images or other attachments to share them (#3367)
This commit is contained in:
24
ReactNativeClient/lib/components/BackButtonDialogBox.ts
Normal file
24
ReactNativeClient/lib/components/BackButtonDialogBox.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
const { BackButtonService } = require('lib/services/back-button.js');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
|
||||
export default class BackButtonDialogBox extends DialogBox {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.backHandler_ = () => {
|
||||
if (this.state.isVisible) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
BackButtonService.addHandler(this.backHandler_);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
BackButtonService.removeHandler(this.backHandler_);
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,20 @@ import Async from 'react-async';
|
||||
|
||||
const React = require('react');
|
||||
const Component = React.Component;
|
||||
const { Platform, View, Text } = require('react-native');
|
||||
const { Platform, View, Text, ToastAndroid } = require('react-native');
|
||||
const { WebView } = require('react-native-webview');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const { assetsToHeaders } = require('lib/joplin-renderer');
|
||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
const BackButtonDialogBox = require('lib/components/BackButtonDialogBox').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Share = require('react-native-share').default;
|
||||
|
||||
class NoteBodyViewer extends Component {
|
||||
constructor() {
|
||||
@@ -64,6 +69,8 @@ class NoteBodyViewer extends Component {
|
||||
resources: this.props.noteResources,
|
||||
codeTheme: theme.codeThemeCss,
|
||||
postMessageSyntax: 'window.joplinPostMessage_',
|
||||
enableLongPress: shim.isReactNative(),
|
||||
longPressDelay: 500, // TODO use system value
|
||||
};
|
||||
|
||||
const result = await this.markupToHtml_.render(
|
||||
@@ -203,6 +210,42 @@ class NoteBodyViewer extends Component {
|
||||
return this.forceUpdate_;
|
||||
}
|
||||
|
||||
async onResourceLongPress(msg) {
|
||||
try {
|
||||
const resourceId = msg.split(':')[1];
|
||||
const resource = await Resource.load(resourceId);
|
||||
const name = resource.title ? resource.title : resource.file_name;
|
||||
|
||||
const action = await dialogs.pop(this, name, [
|
||||
{ text: _('Open'), id: 'open' },
|
||||
{ text: _('Share'), id: 'share' },
|
||||
]);
|
||||
|
||||
if (action === 'open') {
|
||||
this.props.onJoplinLinkClick(`joplin://${resourceId}`);
|
||||
} else if (action === 'share') {
|
||||
const filename = resource.file_name ?
|
||||
`${resource.file_name}.${resource.file_extension}` :
|
||||
resource.title;
|
||||
const targetPath = `${Setting.value('resourceDir')}/${filename}`;
|
||||
|
||||
await shim.fsDriver().copy(Resource.fullPath(resource), targetPath);
|
||||
|
||||
await Share.open({
|
||||
type: resource.mime,
|
||||
filename: resource.title,
|
||||
url: `file://${targetPath}`,
|
||||
failOnCancel: false,
|
||||
});
|
||||
|
||||
await shim.fsDriver().remove(targetPath);
|
||||
}
|
||||
} catch (e) {
|
||||
reg.logger().error('Could not handle link long press', e);
|
||||
ToastAndroid.show('An error occurred, check log for details', ToastAndroid.SHORT);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// Note: useWebKit={false} is needed to go around this bug:
|
||||
// https://github.com/react-native-community/react-native-webview/issues/376
|
||||
@@ -256,7 +299,9 @@ class NoteBodyViewer extends Component {
|
||||
msg = msg.split(':');
|
||||
const resourceId = msg[1];
|
||||
if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId });
|
||||
} else {
|
||||
} else if (msg.startsWith('longclick:')) {
|
||||
this.onResourceLongPress(msg);
|
||||
} else if (msg.startsWith('joplin:')) {
|
||||
this.props.onJoplinLinkClick(msg);
|
||||
}
|
||||
}}
|
||||
@@ -264,6 +309,11 @@ class NoteBodyViewer extends Component {
|
||||
);
|
||||
}}
|
||||
</Async>
|
||||
<BackButtonDialogBox
|
||||
ref={dialogbox => {
|
||||
this.dialogbox = dialogbox;
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,22 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
|
||||
const r = utils.imageReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
|
||||
if (typeof r === 'string') return r;
|
||||
if (r) return `<img data-from-md ${htmlUtils.attributesHtml(Object.assign({}, r, { title: title }))}/>`;
|
||||
if (r) {
|
||||
let js = '';
|
||||
if (ruleOptions.enableLongPress) {
|
||||
const longPressDelay = ruleOptions.longPressDelay ? ruleOptions.longPressDelay : 500;
|
||||
const id = r['data-resource-id'];
|
||||
|
||||
const longPressHandler = `${ruleOptions.postMessageSyntax}('longclick:${id}')`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${longPressDelay});`;
|
||||
const touchEnd = 'if (!!t) clearTimeout(t); t=null';
|
||||
|
||||
js = ` ontouchstart="${touchStart}" ontouchend="${touchEnd}"`;
|
||||
}
|
||||
|
||||
return `<img data-from-md ${htmlUtils.attributesHtml(Object.assign({}, r, { title: title }))}${js}/>`;
|
||||
}
|
||||
return defaultRender(tokens, idx, options, env, self);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,12 +64,24 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
href = href.replace(/'/g, '%27');
|
||||
|
||||
let js = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)}, { resourceId: ${JSON.stringify(resourceId)} }); return false;`;
|
||||
if (ruleOptions.enableLongPress && !!resourceId) {
|
||||
const longPressDelay = ruleOptions.longPressDelay ? ruleOptions.longPressDelay : 500;
|
||||
|
||||
const onClick = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)})`;
|
||||
const onLongClick = `${ruleOptions.postMessageSyntax}("longclick:${resourceId}")`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${longPressDelay});`;
|
||||
const touchEnd = `if (!!t) {clearTimeout(t); t=null; ${onClick};}`;
|
||||
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}'`;
|
||||
}
|
||||
|
||||
if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place
|
||||
|
||||
if (ruleOptions.plainResourceRendering || pluginOptions.linkRenderingType === 2) {
|
||||
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${htmlentities(href)}' type='${htmlentities(mime)}'>`;
|
||||
} else {
|
||||
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' onclick='${js}' type='${htmlentities(mime)}'>${icon}`;
|
||||
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' ${js} type='${htmlentities(mime)}'>${icon}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
5
ReactNativeClient/package-lock.json
generated
5
ReactNativeClient/package-lock.json
generated
@@ -9287,6 +9287,11 @@
|
||||
"base64-js": "*"
|
||||
}
|
||||
},
|
||||
"react-native-share": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-3.3.3.tgz",
|
||||
"integrity": "sha512-KFyGe7hnD7ZDJCz1kWTI5iv2MfY5weIz+aTzqyomyxxFb73rHlCRHa+NRNYvnpTntOjnqyB0/GFf1W2J9y6vJQ=="
|
||||
},
|
||||
"react-native-side-menu": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-side-menu/-/react-native-side-menu-1.1.3.tgz",
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
"react-native-push-notification": "git+https://github.com/laurent22/react-native-push-notification.git",
|
||||
"react-native-quick-actions": "^0.3.13",
|
||||
"react-native-securerandom": "^1.0.0-rc.0",
|
||||
"react-native-share": "^3.3.3",
|
||||
"react-native-side-menu": "^1.1.3",
|
||||
"react-native-sqlite-storage": "^4.1.0",
|
||||
"react-native-vector-icons": "^6.6.0",
|
||||
|
||||
Reference in New Issue
Block a user