You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-26 22:41:17 +02:00
Various fixes
This commit is contained in:
@@ -36,7 +36,6 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
let pattern = args['pattern'];
|
let pattern = args['pattern'];
|
||||||
let suffix = '';
|
|
||||||
let items = [];
|
let items = [];
|
||||||
let options = args.options;
|
let options = args.options;
|
||||||
|
|
||||||
@@ -59,7 +58,6 @@ class Command extends BaseCommand {
|
|||||||
if (pattern == '/' || !app().currentFolder()) {
|
if (pattern == '/' || !app().currentFolder()) {
|
||||||
queryOptions.includeConflictFolder = true;
|
queryOptions.includeConflictFolder = true;
|
||||||
items = await Folder.all(queryOptions);
|
items = await Folder.all(queryOptions);
|
||||||
suffix = '/';
|
|
||||||
modelType = Folder.modelType();
|
modelType = Folder.modelType();
|
||||||
} else {
|
} else {
|
||||||
if (!app().currentFolder()) throw new Error(_('Please select a notebook first.'));
|
if (!app().currentFolder()) throw new Error(_('Please select a notebook first.'));
|
||||||
@@ -97,7 +95,7 @@ class Command extends BaseCommand {
|
|||||||
row.push(time.unixMsToLocalDateTime(item.updated_time));
|
row.push(time.unixMsToLocalDateTime(item.updated_time));
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = item.title + suffix;
|
let title = item.title;
|
||||||
if (!shortIdShown && (seenTitles.indexOf(item.title) >= 0 || !item.title)) {
|
if (!shortIdShown && (seenTitles.indexOf(item.title) >= 0 || !item.title)) {
|
||||||
title += ' (' + BaseModel.shortId(item.id) + ')';
|
title += ' (' + BaseModel.shortId(item.id) + ')';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -318,6 +318,11 @@ msgstr ""
|
|||||||
msgid "Please open this URL in your browser to authenticate the application:"
|
msgid "Please open this URL in your browser to authenticate the application:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Cannot refresh token: authentication data is missing. Starting the "
|
||||||
|
"synchronisation again may fix the problem."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||||
"destination."
|
"destination."
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 2.0.3\n"
|
"X-Generator: Poedit 2.0.3\n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
|
||||||
msgid "No notebook selected."
|
msgid "No notebook selected."
|
||||||
msgstr "Aucun carnet n'est sélectionné."
|
msgstr "Aucun carnet n'est sélectionné."
|
||||||
@@ -353,6 +355,11 @@ msgstr ""
|
|||||||
"Veuillez ouvrir cette URL dans votre navigateur internet pour autoriser le "
|
"Veuillez ouvrir cette URL dans votre navigateur internet pour autoriser le "
|
||||||
"logiciel :"
|
"logiciel :"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Cannot refresh token: authentication data is missing. Starting the "
|
||||||
|
"synchronisation again may fix the problem."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||||
"destination."
|
"destination."
|
||||||
@@ -512,7 +519,7 @@ msgstr "Nouveau carnet"
|
|||||||
|
|
||||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ce carnet ne contient aucune note. Créez-en une en cliquant sur le bouton "
|
"Ce carnet ne contient aucune note. Créez-en une en appuyant sur le bouton "
|
||||||
"(+)."
|
"(+)."
|
||||||
|
|
||||||
msgid "Log"
|
msgid "Log"
|
||||||
@@ -583,8 +590,8 @@ msgid ""
|
|||||||
"Click on the (+) button to create a new note or notebook. Click on the side "
|
"Click on the (+) button to create a new note or notebook. Click on the side "
|
||||||
"menu to access your existing notebooks."
|
"menu to access your existing notebooks."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Cliquez sur le bouton (+) pour une nouvelle note ou carnet. Ouvrez le menu "
|
"Appuyez sur le bouton (+) pour créer une nouvelle note ou carnet. Ouvrez le "
|
||||||
"latéral pour accéder à vos carnets."
|
"menu latéral pour accéder à vos carnets."
|
||||||
|
|
||||||
msgid "You currently have no notebook. Create one by clicking on (+) button."
|
msgid "You currently have no notebook. Create one by clicking on (+) button."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -318,6 +318,11 @@ msgstr ""
|
|||||||
msgid "Please open this URL in your browser to authenticate the application:"
|
msgid "Please open this URL in your browser to authenticate the application:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Cannot refresh token: authentication data is missing. Starting the "
|
||||||
|
"synchronisation again may fix the problem."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||||
"destination."
|
"destination."
|
||||||
|
|||||||
189
ReactNativeClient/lib/components/note-body-viewer.js
Normal file
189
ReactNativeClient/lib/components/note-body-viewer.js
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { WebView, View } from 'react-native';
|
||||||
|
import { globalStyle } from 'lib/components/global-style.js';
|
||||||
|
import { Resource } from 'lib/models/resource.js';
|
||||||
|
import { shim } from 'lib/shim.js';
|
||||||
|
import marked from 'lib/marked.js';
|
||||||
|
const Entities = require('html-entities').AllHtmlEntities;
|
||||||
|
const htmlentities = (new Entities()).encode;
|
||||||
|
|
||||||
|
class NoteBodyViewer extends Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
resources: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadResource(id) {
|
||||||
|
const resource = await Resource.load(id);
|
||||||
|
resource.base64 = await shim.readLocalFileBase64(Resource.fullPath(resource));
|
||||||
|
|
||||||
|
let newResources = Object.assign({}, this.state.resources);
|
||||||
|
newResources[id] = resource;
|
||||||
|
this.setState({ resources: newResources });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTickAt(body, index) {
|
||||||
|
let counter = -1;
|
||||||
|
while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) {
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
body = body.replace(/- \[(X| )\]/, function(v, p1) {
|
||||||
|
let s = p1 == ' ' ? 'NOTICK' : 'TICK';
|
||||||
|
if (index == counter) {
|
||||||
|
s = s == 'NOTICK' ? 'TICK' : 'NOTICK';
|
||||||
|
}
|
||||||
|
return '°°JOP°CHECKBOX°' + s + '°°';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
body = body.replace(/°°JOP°CHECKBOX°NOTICK°°/g, '- [ ]');
|
||||||
|
body = body.replace(/°°JOP°CHECKBOX°TICK°°/g, '- [X]');
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
markdownToHtml (body, style) {
|
||||||
|
// https://necolas.github.io/normalize.css/
|
||||||
|
const normalizeCss = `
|
||||||
|
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
|
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||||
|
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
||||||
|
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const css = `
|
||||||
|
body {
|
||||||
|
font-size: ` + style.htmlFontSize + `;
|
||||||
|
color: ` + style.htmlColor + `;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
a.checkbox {
|
||||||
|
font-size: 1.4em;
|
||||||
|
position: relative;
|
||||||
|
top: 0.1em;
|
||||||
|
text-decoration: none;
|
||||||
|
color: ` + style.htmlColor + `;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td, th {
|
||||||
|
border: 1px solid silver;
|
||||||
|
padding: .5em 1em .5em 1em;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: 1px solid ` + style.htmlDividerColor + `;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
let counter = -1;
|
||||||
|
while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) {
|
||||||
|
body = body.replace(/- \[(X| )\]/, function(v, p1) {
|
||||||
|
let s = p1 == ' ' ? 'NOTICK' : 'TICK';
|
||||||
|
counter++;
|
||||||
|
return '°°JOP°CHECKBOX°' + s + '°' + counter + '°°';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
|
||||||
|
renderer.link = function (href, title, text) {
|
||||||
|
if (Resource.isResourceUrl(href)) {
|
||||||
|
return '[Resource not yet supported: ' + htmlentities(text) + ']';
|
||||||
|
} else {
|
||||||
|
const js = "postMessage(" + JSON.stringify(href) + "); return false;";
|
||||||
|
let output = "<a title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>" + htmlentities(text) + '</a>';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.image = (href, title, text) => {
|
||||||
|
const resourceId = Resource.urlToId(href);
|
||||||
|
if (!this.state.resources[resourceId]) {
|
||||||
|
this.loadResource(resourceId);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = this.state.resources[resourceId];
|
||||||
|
if (r.mime == 'image/png' || r.mime == 'image/jpg' || r.mime == 'image/gif') {
|
||||||
|
const src = 'data:' + r.mime + ';base64,' + r.base64;
|
||||||
|
let output = '<img title="' + htmlentities(title) + '" src="' + src + '"/>';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '[Image: ' + htmlentities(r.title) + '(' + htmlentities(r.mime) + ')]';
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = body ? '<style>' + normalizeCss + "\n" + css + '</style>' + marked(body, { gfm: true, breaks: true, renderer: renderer }) : '';
|
||||||
|
|
||||||
|
let elementId = 1;
|
||||||
|
while (html.indexOf('°°JOP°') >= 0) {
|
||||||
|
html = html.replace(/°°JOP°CHECKBOX°([A-Z]+)°(\d+)°°/, function(v, type, index) {
|
||||||
|
const js = "postMessage('checkboxclick:" + type + ':' + index + "'); this.textContent = this.textContent == '☐' ? '☑' : '☐'; return false;";
|
||||||
|
return '<a href="#" onclick="' + js + '" class="checkbox">' + (type == 'NOTICK' ? '☐' : '☑') + '</a>';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let scriptHtml = '<script>document.body.scrollTop = ' + this.bodyScrollTop_ + ';</script>';
|
||||||
|
|
||||||
|
html = '<body onscroll="postMessage(\'bodyscroll:\' + document.body.scrollTop);">' + html + scriptHtml + '</body>';
|
||||||
|
|
||||||
|
// console.info(html);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const note = this.props.note;
|
||||||
|
const style = this.props.style;
|
||||||
|
const onCheckboxChange = this.props.onCheckboxChange;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={style}>
|
||||||
|
<WebView
|
||||||
|
source={{ html: this.markdownToHtml(note ? note.body : '', globalStyle) }}
|
||||||
|
onMessage={(event) => {
|
||||||
|
let msg = event.nativeEvent.data;
|
||||||
|
|
||||||
|
//reg.logger().info('postMessage received: ' + msg);
|
||||||
|
|
||||||
|
if (msg.indexOf('checkboxclick:') === 0) {
|
||||||
|
msg = msg.split(':');
|
||||||
|
let index = Number(msg[msg.length - 1]);
|
||||||
|
let currentState = msg[msg.length - 2]; // Not really needed but keep it anyway
|
||||||
|
const newBody = this.toggleTickAt(note.body, index);
|
||||||
|
if (onCheckboxChange) onCheckboxChange(newBody);
|
||||||
|
} else if (msg.indexOf('bodyscroll:') === 0) {
|
||||||
|
msg = msg.split(':');
|
||||||
|
this.bodyScrollTop_ = Number(msg[1]);
|
||||||
|
} else {
|
||||||
|
Linking.openURL(msg);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NoteBodyViewer };
|
||||||
@@ -17,9 +17,11 @@ let styles = {
|
|||||||
borderBottomColor: globalStyle.dividerColor,
|
borderBottomColor: globalStyle.dividerColor,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingLeft: globalStyle.marginLeft,
|
paddingLeft: globalStyle.marginLeft,
|
||||||
|
paddingRight: globalStyle.marginRight,
|
||||||
backgroundColor: globalStyle.backgroundColor,
|
backgroundColor: globalStyle.backgroundColor,
|
||||||
},
|
},
|
||||||
listItemText: {
|
listItemText: {
|
||||||
|
flex: 1,
|
||||||
color: globalStyle.color,
|
color: globalStyle.color,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -70,7 +72,7 @@ class NoteItemComponent extends Component {
|
|||||||
const listItemStyle = !!Number(note.is_todo) && checkboxChecked ? styles.listItemFadded : styles.listItem;
|
const listItemStyle = !!Number(note.is_todo) && checkboxChecked ? styles.listItemFadded : styles.listItem;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableHighlight onPress={() => this.onPress()} underlayColor="#0066FF">
|
<TouchableHighlight style={{borderWidth:1, borderColor:'red'}} onPress={() => this.onPress()} underlayColor="#0066FF">
|
||||||
<View style={ listItemStyle }>
|
<View style={ listItemStyle }>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
style={checkboxStyle}
|
style={checkboxStyle}
|
||||||
|
|||||||
@@ -12,15 +12,13 @@ import { ScreenHeader } from 'lib/components/screen-header.js';
|
|||||||
import { time } from 'lib/time-utils.js';
|
import { time } from 'lib/time-utils.js';
|
||||||
import { Checkbox } from 'lib/components/checkbox.js'
|
import { Checkbox } from 'lib/components/checkbox.js'
|
||||||
import { _ } from 'lib/locale.js';
|
import { _ } from 'lib/locale.js';
|
||||||
import marked from 'lib/marked.js';
|
|
||||||
import { reg } from 'lib/registry.js';
|
import { reg } from 'lib/registry.js';
|
||||||
import { shim } from 'lib/shim.js';
|
import { shim } from 'lib/shim.js';
|
||||||
import { BaseScreenComponent } from 'lib/components/base-screen.js';
|
import { BaseScreenComponent } from 'lib/components/base-screen.js';
|
||||||
import { dialogs } from 'lib/dialogs.js';
|
import { dialogs } from 'lib/dialogs.js';
|
||||||
import { globalStyle } from 'lib/components/global-style.js';
|
import { globalStyle } from 'lib/components/global-style.js';
|
||||||
import DialogBox from 'react-native-dialogbox';
|
import DialogBox from 'react-native-dialogbox';
|
||||||
const Entities = require('html-entities').AllHtmlEntities;
|
import { NoteBodyViewer } from 'lib/components/note-body-viewer.js';
|
||||||
const htmlentities = (new Entities()).encode;
|
|
||||||
|
|
||||||
const styleObject = {
|
const styleObject = {
|
||||||
titleTextInput: {
|
titleTextInput: {
|
||||||
@@ -37,7 +35,7 @@ const styleObject = {
|
|||||||
color: globalStyle.color,
|
color: globalStyle.color,
|
||||||
backgroundColor: globalStyle.backgroundColor,
|
backgroundColor: globalStyle.backgroundColor,
|
||||||
},
|
},
|
||||||
bodyViewContainer: {
|
noteBodyViewer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingLeft: globalStyle.marginLeft,
|
paddingLeft: globalStyle.marginLeft,
|
||||||
paddingRight: globalStyle.marginRight,
|
paddingRight: globalStyle.marginRight,
|
||||||
@@ -78,8 +76,6 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
resources: {},
|
resources: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.bodyScrollTop_ = 0;
|
|
||||||
|
|
||||||
this.saveButtonHasBeenShown_ = false;
|
this.saveButtonHasBeenShown_ = false;
|
||||||
|
|
||||||
this.backHandler = () => {
|
this.backHandler = () => {
|
||||||
@@ -237,10 +233,10 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleIsTodo_onPress() {
|
toggleIsTodo_onPress() {
|
||||||
let note = await Note.toggleIsTodo(this.state.note.id);
|
let newNote = Note.toggleIsTodo(this.state.note);
|
||||||
let newState = { note: note };
|
let newState = { note: newNote };
|
||||||
if (!note.id) newState.lastSavedNote = Object.assign({}, note);
|
//if (!newNote.id) newState.lastSavedNote = Object.assign({}, newNote);
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,15 +245,6 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
this.refreshNoteMetadata(true);
|
this.refreshNoteMetadata(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadResource(id) {
|
|
||||||
const resource = await Resource.load(id);
|
|
||||||
resource.base64 = await shim.readLocalFileBase64(Resource.fullPath(resource));
|
|
||||||
|
|
||||||
let newResources = Object.assign({}, this.state.resources);
|
|
||||||
newResources[id] = resource;
|
|
||||||
this.setState({ resources: newResources });
|
|
||||||
}
|
|
||||||
|
|
||||||
async showOnMap_onPress() {
|
async showOnMap_onPress() {
|
||||||
if (!this.state.note.id) return;
|
if (!this.state.note.id) return;
|
||||||
|
|
||||||
@@ -302,158 +289,11 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
let bodyComponent = null;
|
let bodyComponent = null;
|
||||||
if (this.state.mode == 'view') {
|
if (this.state.mode == 'view') {
|
||||||
function toggleTickAt(body, index) {
|
const onCheckboxChange = (newBody) => {
|
||||||
let counter = -1;
|
|
||||||
while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) {
|
|
||||||
counter++;
|
|
||||||
|
|
||||||
body = body.replace(/- \[(X| )\]/, function(v, p1) {
|
|
||||||
let s = p1 == ' ' ? 'NOTICK' : 'TICK';
|
|
||||||
if (index == counter) {
|
|
||||||
s = s == 'NOTICK' ? 'TICK' : 'NOTICK';
|
|
||||||
}
|
|
||||||
return '°°JOP°CHECKBOX°' + s + '°°';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
body = body.replace(/°°JOP°CHECKBOX°NOTICK°°/g, '- [ ]');
|
|
||||||
body = body.replace(/°°JOP°CHECKBOX°TICK°°/g, '- [X]');
|
|
||||||
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
const markdownToHtml = (body, style) => {
|
|
||||||
// https://necolas.github.io/normalize.css/
|
|
||||||
const normalizeCss = `
|
|
||||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
|
||||||
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
|
||||||
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
|
||||||
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const css = `
|
|
||||||
body {
|
|
||||||
font-size: ` + style.htmlFontSize + `;
|
|
||||||
color: ` + style.htmlColor + `;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
a.checkbox {
|
|
||||||
font-size: 1.4em;
|
|
||||||
position: relative;
|
|
||||||
top: 0.1em;
|
|
||||||
text-decoration: none;
|
|
||||||
color: ` + style.htmlColor + `;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
td, th {
|
|
||||||
border: 1px solid silver;
|
|
||||||
padding: .5em 1em .5em 1em;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
border: 1px solid ` + style.htmlDividerColor + `;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
let counter = -1;
|
|
||||||
while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) {
|
|
||||||
body = body.replace(/- \[(X| )\]/, function(v, p1) {
|
|
||||||
let s = p1 == ' ' ? 'NOTICK' : 'TICK';
|
|
||||||
counter++;
|
|
||||||
return '°°JOP°CHECKBOX°' + s + '°' + counter + '°°';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderer = new marked.Renderer();
|
|
||||||
|
|
||||||
renderer.link = function (href, title, text) {
|
|
||||||
if (Resource.isResourceUrl(href)) {
|
|
||||||
return '[Resource not yet supported: ' + htmlentities(text) + ']';
|
|
||||||
} else {
|
|
||||||
const js = "postMessage(" + JSON.stringify(href) + "); return false;";
|
|
||||||
let output = "<a title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>" + htmlentities(text) + '</a>';
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.image = (href, title, text) => {
|
|
||||||
const resourceId = Resource.urlToId(href);
|
|
||||||
if (!this.state.resources[resourceId]) {
|
|
||||||
this.loadResource(resourceId);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const r = this.state.resources[resourceId];
|
|
||||||
if (r.mime == 'image/png' || r.mime == 'image/jpg' || r.mime == 'image/gif') {
|
|
||||||
const src = 'data:' + r.mime + ';base64,' + r.base64;
|
|
||||||
let output = '<img title="' + htmlentities(title) + '" src="' + src + '"/>';
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '[Image: ' + htmlentities(r.title) + '(' + htmlentities(r.mime) + ')]';
|
|
||||||
}
|
|
||||||
|
|
||||||
let html = note ? '<style>' + normalizeCss + "\n" + css + '</style>' + marked(body, { gfm: true, breaks: true, renderer: renderer }) : '';
|
|
||||||
|
|
||||||
let elementId = 1;
|
|
||||||
while (html.indexOf('°°JOP°') >= 0) {
|
|
||||||
html = html.replace(/°°JOP°CHECKBOX°([A-Z]+)°(\d+)°°/, function(v, type, index) {
|
|
||||||
const js = "postMessage('checkboxclick:" + type + ':' + index + "'); this.textContent = this.textContent == '☐' ? '☑' : '☐'; return false;";
|
|
||||||
return '<a href="#" onclick="' + js + '" class="checkbox">' + (type == 'NOTICK' ? '☐' : '☑') + '</a>';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let scriptHtml = '<script>document.body.scrollTop = ' + this.bodyScrollTop_ + ';</script>';
|
|
||||||
|
|
||||||
html = '<body onscroll="postMessage(\'bodyscroll:\' + document.body.scrollTop);">' + html + scriptHtml + '</body>';
|
|
||||||
|
|
||||||
console.info(html);
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyComponent = (
|
|
||||||
<View style={styles.bodyViewContainer}>
|
|
||||||
<WebView
|
|
||||||
source={{ html: markdownToHtml(note.body, globalStyle) }}
|
|
||||||
onMessage={(event) => {
|
|
||||||
let msg = event.nativeEvent.data;
|
|
||||||
|
|
||||||
//reg.logger().info('postMessage received: ' + msg);
|
|
||||||
|
|
||||||
if (msg.indexOf('checkboxclick:') === 0) {
|
|
||||||
msg = msg.split(':');
|
|
||||||
let index = Number(msg[msg.length - 1]);
|
|
||||||
let currentState = msg[msg.length - 2]; // Not really needed but keep it anyway
|
|
||||||
const newBody = toggleTickAt(note.body, index);
|
|
||||||
this.saveOneProperty('body', newBody);
|
this.saveOneProperty('body', newBody);
|
||||||
} else if (msg.indexOf('bodyscroll:') === 0) {
|
};
|
||||||
msg = msg.split(':');
|
|
||||||
this.bodyScrollTop_ = Number(msg[1]);
|
bodyComponent = <NoteBodyViewer style={styles.noteBodyViewer} note={note} onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}/>
|
||||||
} else {
|
|
||||||
Linking.openURL(msg);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const focusBody = !isNew && !!note.title;
|
const focusBody = !isNew && !!note.title;
|
||||||
bodyComponent = (
|
bodyComponent = (
|
||||||
|
|||||||
@@ -267,15 +267,17 @@ class Note extends BaseItem {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleIsTodo(noteId) {
|
static toggleIsTodo(note) {
|
||||||
let note = await Note.load(noteId);
|
if (!('is_todo' in note)) throw new Error('Missing "is_todo" property');
|
||||||
const isTodo = !note.is_todo ? 1 : 0;
|
|
||||||
note.is_todo = isTodo;
|
let output = Object.assign({}, note);
|
||||||
if (!note.is_todo) {
|
output.is_todo = output.is_todo ? 0 : 1;
|
||||||
note.todo_due = 0;
|
if (!output.is_todo) {
|
||||||
note.todo_completed = 0;
|
output.todo_due = 0;
|
||||||
|
output.todo_completed = 0;
|
||||||
}
|
}
|
||||||
return note;
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async duplicate(noteId, options = null) {
|
static async duplicate(noteId, options = null) {
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ class Synchronizer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.state_ = 'in_progress';
|
||||||
|
|
||||||
this.onProgress_ = options.onProgress ? options.onProgress : function(o) {};
|
this.onProgress_ = options.onProgress ? options.onProgress : function(o) {};
|
||||||
this.progressReport_ = { errors: [] };
|
this.progressReport_ = { errors: [] };
|
||||||
|
|
||||||
@@ -175,7 +177,6 @@ class Synchronizer {
|
|||||||
|
|
||||||
let outputContext = Object.assign({}, lastContext);
|
let outputContext = Object.assign({}, lastContext);
|
||||||
|
|
||||||
this.state_ = 'in_progress';
|
|
||||||
|
|
||||||
this.dispatch({ type: 'SYNC_STARTED' });
|
this.dispatch({ type: 'SYNC_STARTED' });
|
||||||
|
|
||||||
@@ -463,7 +464,6 @@ class Synchronizer {
|
|||||||
this.cancelling_ = false;
|
this.cancelling_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state_ = 'idle';
|
|
||||||
|
|
||||||
this.progressReport_.completedTime = time.unixMs();
|
this.progressReport_.completedTime = time.unixMs();
|
||||||
|
|
||||||
@@ -476,6 +476,8 @@ class Synchronizer {
|
|||||||
|
|
||||||
this.dispatch({ type: 'SYNC_COMPLETED' });
|
this.dispatch({ type: 'SYNC_COMPLETED' });
|
||||||
|
|
||||||
|
this.state_ = 'idle';
|
||||||
|
|
||||||
return outputContext;
|
return outputContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user