Merge branch 'dev' into release-1.3

pull/3921/head
Laurent Cozic 2020-10-13 09:42:26 +01:00
commit ff50361e19
13 changed files with 147 additions and 26 deletions

View File

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

1
.gitignore vendored
View File

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

View File

@ -15,6 +15,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.1\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
#: ElectronClient/services/plugins/UserWebviewDialogButtonBar.js:20
#: ElectronClient/checkForUpdates.js:139
@ -799,7 +801,6 @@ msgid "Toggle sidebar"
msgstr "사이드바 표시 전환"
#: ElectronClient/gui/MainScreen/commands/toggleEditors.js:17
#, fuzzy
msgid "Toggle editors"
msgstr "편집기 배치 형태 전환"
@ -1389,9 +1390,9 @@ msgstr ""
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:62
#: ReactNativeClient/lib/services/KeymapService.js:142
#, fuzzy, javascript-format
#, javascript-format
msgid "Error: %s"
msgstr "오류"
msgstr "오류: %s"
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:125
msgid "Command"
@ -1463,9 +1464,8 @@ msgid "Exit"
msgstr "종료"
#: ElectronClient/commands/copyDevCommand.js:17
#, fuzzy
msgid "Copy dev mode command to clipboard"
msgstr "경로를 클립보드에 복사"
msgstr "개발자 모드 명령을 클립보드에 복사"
#: ElectronClient/commands/startExternalEditing.js:18
msgid "Edit in external editor"
@ -1477,13 +1477,12 @@ msgid "Error opening note in editor: %s"
msgstr "편집기에서 노트를 열 수 없는 오류가 발생하였습니다: %s"
#: ElectronClient/commands/toggleExternalEditing.js:16
#, fuzzy
msgid "Toggle external editing"
msgstr "외부 편집을 그만 두시려면 클릭하세요"
msgstr "외부 편집 전환"
#: ElectronClient/commands/toggleExternalEditing.js:42
msgid "Stop"
msgstr ""
msgstr "중지"
#: ElectronClient/commands/stopExternalEditing.js:16
msgid "Stop external editing"
@ -2203,9 +2202,9 @@ msgid "Cannot load \"%s\" module for format \"%s\" and output \"%s\""
msgstr "\"%s\" 모듈(\"%s\" 포맷과 \"%s\" 출력을 위한)을 불러올 수 없습니다"
#: ReactNativeClient/lib/services/interop/InteropService.js:152
#, fuzzy, javascript-format
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\" and target \"%s\""
msgstr "\"%s\" 모듈(\"%s\" 포맷과 \"%s\" 출력을 위한)을 불러올 수 없습니다"
msgstr "\"%s\" 모듈(\"%s\" 포맷과 \"%s\" 대상을 위한)을 불러올 수 없습니다"
#: ReactNativeClient/lib/services/interop/InteropService.js:187
#, javascript-format
@ -2221,25 +2220,24 @@ msgid "Restored Notes"
msgstr "복구된 노트들"
#: ReactNativeClient/lib/services/KeymapService.js:236
#, fuzzy
msgid "command"
msgstr "명령"
#: ReactNativeClient/lib/services/KeymapService.js:236
#: ReactNativeClient/lib/services/KeymapService.js:241
#, fuzzy, javascript-format
#, javascript-format
msgid "\"%s\" is missing the required \"%s\" property."
msgstr "키맵 항목 %s에 필수 속성인 \"명령\"이 없습니다."
msgstr "\"%s\"에 \"%s\" 필수 속성이 없습니다."
#: ReactNativeClient/lib/services/KeymapService.js:241
#: ReactNativeClient/lib/services/KeymapService.js:248
msgid "accelerator"
msgstr ""
msgstr "단축키"
#: ReactNativeClient/lib/services/KeymapService.js:248
#, fuzzy, javascript-format
#, javascript-format
msgid "Invalid %s: %s."
msgstr "잘못된 답: %s"
msgstr "유효하지 않은 %s: %s."
#: ReactNativeClient/lib/services/KeymapService.js:266
#, javascript-format

View File

@ -397,12 +397,16 @@ function useMenu(props:Props) {
}, shim.isMac() ? noItem : printItem, {
type: 'separator',
platforms: ['darwin'],
}, {
},
!shim.isMac() ? noItem : {
label: _('Hide %s', 'Joplin'),
platforms: ['darwin'],
accelerator: shim.isMac() && keymapService.getAccelerator('hideApp'),
click: () => { bridge().electronApp().hide(); },
}, {
},
{
type: 'separator',
},
quitMenuItem],

View File

@ -141,7 +141,7 @@ export default function useListIdent(CodeMirror: any) {
cm.setCursor({ line: anchor.line, ch: line.length });
cm.execCommand('insertListElement');
cm.setOption('disableInput', true);
CodeMirror.Vim.handleKey(cm, 'i', 'macro');
};
CodeMirror.commands.insertListElement = function(cm: any) {

View File

@ -361,6 +361,17 @@ class SideBarComponent extends React.Component<Props, State> {
renderFolderItem(folder:any, selected:boolean, hasChildren:boolean, depth:number) {
const anchorRef = this.anchorItemRef('folder', folder.id);
const isExpanded = this.props.collapsedFolderIds.indexOf(folder.id) < 0;
let noteCount = folder.note_count;
// Thunderbird count: Subtract children note_count from parent folder if it expanded.
if (isExpanded) {
for (let i = 0; i < this.props.folders.length; i++) {
if (this.props.folders[i].parent_id === folder.id) {
noteCount -= this.props.folders[i].note_count;
}
}
}
return <FolderItem
key={folder.id}
@ -369,10 +380,10 @@ class SideBarComponent extends React.Component<Props, State> {
themeId={this.props.themeId}
depth={depth}
selected={selected}
isExpanded={this.props.collapsedFolderIds.indexOf(folder.id) < 0}
isExpanded={isExpanded}
hasChildren={hasChildren}
anchorRef={anchorRef}
noteCount={folder.note_count}
noteCount={noteCount}
onFolderDragStart_={this.onFolderDragStart_}
onFolderDragOver_={this.onFolderDragOver_}
onFolderDrop_={this.onFolderDrop_}

View 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_);
}
}

View File

@ -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>
);
}

View File

@ -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);
};
}

View File

@ -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}`;
}
};
}

View File

@ -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",

View File

@ -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",

View File

@ -43,5 +43,5 @@ Restart the app, and Joplin should load the plugin and execute its `onStart` han
# Next steps
- You might want to check the [plugin tutorial](https://github.com/laurent22/joplin/blob/dev/readme/api/tutorials/toc_plugin/) to get a good overview of how to create a complete plugin and how to use the plugin API.
- You might want to check the [plugin tutorial](https://github.com/laurent22/joplin/blob/dev/readme/api/tutorials/toc_plugin.md) to get a good overview of how to create a complete plugin and how to use the plugin API.
- For more information about the plugin API, check the [Plugin API reference](https://joplinapp.org/api/references/plugin_api/classes/joplin.html).