You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Various fixes
This commit is contained in:
		| @@ -36,7 +36,6 @@ class Command extends BaseCommand { | ||||
|  | ||||
| 	async action(args) { | ||||
| 		let pattern = args['pattern']; | ||||
| 		let suffix = ''; | ||||
| 		let items = []; | ||||
| 		let options = args.options; | ||||
|  | ||||
| @@ -59,7 +58,6 @@ class Command extends BaseCommand { | ||||
| 		if (pattern == '/' || !app().currentFolder()) { | ||||
| 			queryOptions.includeConflictFolder = true; | ||||
| 			items = await Folder.all(queryOptions); | ||||
| 			suffix = '/'; | ||||
| 			modelType = Folder.modelType(); | ||||
| 		} else { | ||||
| 			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)); | ||||
| 				} | ||||
|  | ||||
| 				let title = item.title + suffix; | ||||
| 				let title = item.title; | ||||
| 				if (!shortIdShown && (seenTitles.indexOf(item.title) >= 0 || !item.title)) { | ||||
| 					title += ' (' + BaseModel.shortId(item.id) + ')'; | ||||
| 				} else { | ||||
|   | ||||
| @@ -318,6 +318,11 @@ msgstr "" | ||||
| msgid "Please open this URL in your browser to authenticate the application:" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "Cannot refresh token: authentication data is missing. Starting the " | ||||
| "synchronisation again may fix the problem." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "Please set the \"sync.2.path\" config value to the desired synchronisation " | ||||
| "destination." | ||||
|   | ||||
| @@ -14,6 +14,8 @@ msgstr "" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
| "Content-Transfer-Encoding: 8bit\n" | ||||
| "X-Generator: Poedit 2.0.3\n" | ||||
| "POT-Creation-Date: \n" | ||||
| "PO-Revision-Date: \n" | ||||
|  | ||||
| msgid "No notebook selected." | ||||
| msgstr "Aucun carnet n'est sélectionné." | ||||
| @@ -353,6 +355,11 @@ msgstr "" | ||||
| "Veuillez ouvrir cette URL dans votre navigateur internet pour autoriser le " | ||||
| "logiciel :" | ||||
|  | ||||
| msgid "" | ||||
| "Cannot refresh token: authentication data is missing. Starting the " | ||||
| "synchronisation again may fix the problem." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "Please set the \"sync.2.path\" config value to the desired synchronisation " | ||||
| "destination." | ||||
| @@ -512,7 +519,7 @@ msgstr "Nouveau carnet" | ||||
|  | ||||
| msgid "There are currently no notes. Create one by clicking on the (+) button." | ||||
| 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" | ||||
| @@ -583,8 +590,8 @@ msgid "" | ||||
| "Click on the (+) button to create a new note or notebook. Click on the side " | ||||
| "menu to access your existing notebooks." | ||||
| msgstr "" | ||||
| "Cliquez sur le bouton (+) pour une nouvelle note ou carnet. Ouvrez le menu " | ||||
| "latéral pour accéder à vos carnets." | ||||
| "Appuyez sur le bouton (+) pour créer une nouvelle note ou carnet. Ouvrez le " | ||||
| "menu latéral pour accéder à vos carnets." | ||||
|  | ||||
| msgid "You currently have no notebook. Create one by clicking on (+) button." | ||||
| msgstr "" | ||||
|   | ||||
| @@ -318,6 +318,11 @@ msgstr "" | ||||
| msgid "Please open this URL in your browser to authenticate the application:" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "Cannot refresh token: authentication data is missing. Starting the " | ||||
| "synchronisation again may fix the problem." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "Please set the \"sync.2.path\" config value to the desired synchronisation " | ||||
| "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, | ||||
| 		alignItems: 'center', | ||||
| 		paddingLeft: globalStyle.marginLeft, | ||||
| 		paddingRight: globalStyle.marginRight, | ||||
| 		backgroundColor: globalStyle.backgroundColor, | ||||
| 	}, | ||||
| 	listItemText: { | ||||
| 		flex: 1, | ||||
| 		color: globalStyle.color, | ||||
| 	}, | ||||
| }; | ||||
| @@ -70,7 +72,7 @@ class NoteItemComponent extends Component { | ||||
| 		const listItemStyle = !!Number(note.is_todo) && checkboxChecked ? styles.listItemFadded : styles.listItem; | ||||
|  | ||||
| 		return ( | ||||
| 			<TouchableHighlight onPress={() => this.onPress()} underlayColor="#0066FF"> | ||||
| 			<TouchableHighlight style={{borderWidth:1, borderColor:'red'}} onPress={() => this.onPress()} underlayColor="#0066FF"> | ||||
| 				<View style={ listItemStyle }> | ||||
| 					<Checkbox | ||||
| 						style={checkboxStyle} | ||||
|   | ||||
| @@ -12,15 +12,13 @@ import { ScreenHeader } from 'lib/components/screen-header.js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
| import { Checkbox } from 'lib/components/checkbox.js' | ||||
| import { _ } from 'lib/locale.js'; | ||||
| import marked from 'lib/marked.js'; | ||||
| import { reg } from 'lib/registry.js'; | ||||
| import { shim } from 'lib/shim.js'; | ||||
| import { BaseScreenComponent } from 'lib/components/base-screen.js'; | ||||
| import { dialogs } from 'lib/dialogs.js'; | ||||
| import { globalStyle } from 'lib/components/global-style.js'; | ||||
| import DialogBox from 'react-native-dialogbox'; | ||||
| const Entities = require('html-entities').AllHtmlEntities; | ||||
| const htmlentities = (new Entities()).encode; | ||||
| import { NoteBodyViewer } from 'lib/components/note-body-viewer.js'; | ||||
|  | ||||
| const styleObject = { | ||||
| 	titleTextInput: { | ||||
| @@ -37,7 +35,7 @@ const styleObject = { | ||||
| 		color: globalStyle.color, | ||||
| 		backgroundColor: globalStyle.backgroundColor, | ||||
| 	}, | ||||
| 	bodyViewContainer: { | ||||
| 	noteBodyViewer: { | ||||
| 		flex: 1, | ||||
| 		paddingLeft: globalStyle.marginLeft, | ||||
| 		paddingRight: globalStyle.marginRight, | ||||
| @@ -78,8 +76,6 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 			resources: {}, | ||||
| 		}; | ||||
|  | ||||
| 		this.bodyScrollTop_ = 0; | ||||
|  | ||||
| 		this.saveButtonHasBeenShown_ = false; | ||||
|  | ||||
| 		this.backHandler = () => { | ||||
| @@ -237,10 +233,10 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	async toggleIsTodo_onPress() { | ||||
| 		let note = await Note.toggleIsTodo(this.state.note.id); | ||||
| 		let newState = { note: note }; | ||||
| 		if (!note.id) newState.lastSavedNote = Object.assign({}, note); | ||||
| 	toggleIsTodo_onPress() { | ||||
| 		let newNote = Note.toggleIsTodo(this.state.note); | ||||
| 		let newState = { note: newNote }; | ||||
| 		//if (!newNote.id) newState.lastSavedNote = Object.assign({}, newNote); | ||||
| 		this.setState(newState); | ||||
| 	} | ||||
|  | ||||
| @@ -249,15 +245,6 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 		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() { | ||||
| 		if (!this.state.note.id) return; | ||||
|  | ||||
| @@ -302,158 +289,11 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
|  | ||||
| 		let bodyComponent = null; | ||||
| 		if (this.state.mode == 'view') { | ||||
| 			function toggleTickAt(body, index) { | ||||
| 				let counter = -1; | ||||
| 				while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) { | ||||
| 					counter++; | ||||
| 			const onCheckboxChange = (newBody) => { | ||||
| 				this.saveOneProperty('body', newBody); | ||||
| 			}; | ||||
|  | ||||
| 					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); | ||||
| 							} else if (msg.indexOf('bodyscroll:') === 0) { | ||||
| 								msg = msg.split(':'); | ||||
| 								this.bodyScrollTop_ = Number(msg[1]); | ||||
| 							} else { | ||||
| 								Linking.openURL(msg); | ||||
| 							} | ||||
| 						}} | ||||
| 					/> | ||||
| 				</View> | ||||
| 			); | ||||
| 			bodyComponent = <NoteBodyViewer style={styles.noteBodyViewer} note={note} onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}/> | ||||
| 		} else { | ||||
| 			const focusBody = !isNew && !!note.title; | ||||
| 			bodyComponent = ( | ||||
|   | ||||
| @@ -267,15 +267,17 @@ class Note extends BaseItem { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	static async toggleIsTodo(noteId) { | ||||
| 		let note = await Note.load(noteId); | ||||
| 		const isTodo = !note.is_todo ? 1 : 0; | ||||
| 		note.is_todo = isTodo; | ||||
| 		if (!note.is_todo) { | ||||
| 			note.todo_due = 0; | ||||
| 			note.todo_completed = 0; | ||||
| 	static toggleIsTodo(note) { | ||||
| 		if (!('is_todo' in note)) throw new Error('Missing "is_todo" property'); | ||||
|  | ||||
| 		let output = Object.assign({}, note); | ||||
| 		output.is_todo = output.is_todo ? 0 : 1; | ||||
| 		if (!output.is_todo) { | ||||
| 			output.todo_due = 0; | ||||
| 			output.todo_completed = 0; | ||||
| 		} | ||||
| 		return note; | ||||
|  | ||||
| 		return output; | ||||
| 	} | ||||
|  | ||||
| 	static async duplicate(noteId, options = null) { | ||||
|   | ||||
| @@ -154,7 +154,9 @@ class Synchronizer { | ||||
| 			error.code = 'alreadyStarted'; | ||||
| 			throw error; | ||||
| 			return; | ||||
| 		}	 | ||||
| 		} | ||||
|  | ||||
| 		this.state_ = 'in_progress'; | ||||
|  | ||||
| 		this.onProgress_ = options.onProgress ? options.onProgress : function(o) {}; | ||||
| 		this.progressReport_ = { errors: [] }; | ||||
| @@ -175,7 +177,6 @@ class Synchronizer { | ||||
|  | ||||
| 		let outputContext = Object.assign({}, lastContext); | ||||
| 		 | ||||
| 		this.state_ = 'in_progress'; | ||||
|  | ||||
| 		this.dispatch({ type: 'SYNC_STARTED' }); | ||||
|  | ||||
| @@ -463,7 +464,6 @@ class Synchronizer { | ||||
| 			this.cancelling_ = false; | ||||
| 		} | ||||
|  | ||||
| 		this.state_ = 'idle'; | ||||
|  | ||||
| 		this.progressReport_.completedTime = time.unixMs(); | ||||
|  | ||||
| @@ -475,6 +475,8 @@ class Synchronizer { | ||||
| 		this.progressReport_ = {}; | ||||
|  | ||||
| 		this.dispatch({ type: 'SYNC_COMPLETED' }); | ||||
| 		 | ||||
| 		this.state_ = 'idle'; | ||||
|  | ||||
| 		return outputContext; | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user