mirror of https://github.com/laurent22/joplin.git
Merge branch 'dev' into release-1.3
commit
ff50361e19
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_}
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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).
|
||||
|
|
Loading…
Reference in New Issue