You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Clipper: More consistent REST API
This commit is contained in:
		| @@ -1,15 +1,25 @@ | ||||
| let browser_ = null; | ||||
| let browserName_ = null; | ||||
| if (typeof browser !== 'undefined') { | ||||
| 	browser_ = browser; | ||||
| 	browserSupportsPromises_ = true; | ||||
| 	browserName_ = 'firefox'; | ||||
| } else if (typeof chrome !== 'undefined') { | ||||
| 	browser_ = chrome; | ||||
| 	browserSupportsPromises_ = false; | ||||
| 	browserName_ = 'chrome'; | ||||
| } | ||||
|  | ||||
| function env() { | ||||
| 	return 'prod'; | ||||
| 	return !('update_url' in browser_.runtime.getManifest()) ? 'dev' : 'prod'; | ||||
| let env_ = null; | ||||
|  | ||||
| // Make this function global so that it can be accessed | ||||
| // from the popup too. | ||||
| // https://stackoverflow.com/questions/6323184/communication-between-background-page-and-popup-page-in-a-chrome-extension | ||||
| window.joplinEnv = function() { | ||||
| 	if (env_) return env_; | ||||
|  | ||||
| 	env_ = !('update_url' in browser_.runtime.getManifest()) ? 'dev' : 'prod'; | ||||
| 	return env_; | ||||
| } | ||||
|  | ||||
| async function browserCaptureVisibleTabs(windowId, options) { | ||||
| @@ -22,8 +32,19 @@ async function browserCaptureVisibleTabs(windowId, options) { | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| browser_.runtime.onInstalled.addListener(function() { | ||||
| 	if (env() === 'dev') { | ||||
| browser_.runtime.onInstalled.addListener(function(details) { | ||||
| 	if (details && details.temporary) { | ||||
| 		// In Firefox - https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onInstalled | ||||
| 		env_ = 'dev'; | ||||
| 	} else if (browserName_ === 'chrome') { | ||||
| 		// In Chrome | ||||
| 		env_ = !('update_url' in browser_.runtime.getManifest()) ? 'dev' : 'prod'; | ||||
| 	} else { | ||||
| 		// If we don't know, be safe and default to prod | ||||
| 		env_ = 'prod'; | ||||
| 	} | ||||
|  | ||||
| 	if (window.joplinEnv() === 'dev') { | ||||
| 		browser_.browserAction.setIcon({ | ||||
| 			path: 'icons/32-dev.png', | ||||
| 		}); | ||||
| @@ -34,9 +55,9 @@ browser_.runtime.onMessage.addListener((command) => { | ||||
| 	if (command.name === 'screenshotArea') { | ||||
| 		browserCaptureVisibleTabs(null, { format: 'jpeg' }).then((imageDataUrl) => { | ||||
| 			content = Object.assign({}, command.content); | ||||
| 			content.imageDataUrl = imageDataUrl; | ||||
| 			content.image_data_url = imageDataUrl; | ||||
|  | ||||
| 			fetch(command.apiBaseUrl + "/notes", { | ||||
| 			fetch(command.api_base_url + "/notes", { | ||||
| 				method: "POST", | ||||
| 				headers: { | ||||
| 					'Accept': 'application/json', | ||||
|   | ||||
| @@ -92,9 +92,9 @@ | ||||
| 				name: 'clippedContent', | ||||
| 				html: article.body, | ||||
| 				title: article.title, | ||||
| 				baseUrl: baseUrl(), | ||||
| 				base_url: baseUrl(), | ||||
| 				url: location.origin + location.pathname, | ||||
| 				parentId: command.parentId, | ||||
| 				parent_id: command.parent_id, | ||||
| 			}; | ||||
|  | ||||
| 		} else if (command.name === "completePageHtml") { | ||||
| @@ -106,9 +106,9 @@ | ||||
| 				name: 'clippedContent', | ||||
| 				html: cleanDocument.innerHTML, | ||||
| 				title: pageTitle(), | ||||
| 				baseUrl: baseUrl(), | ||||
| 				base_url: baseUrl(), | ||||
| 				url: location.origin + location.pathname, | ||||
| 				parentId: command.parentId, | ||||
| 				parent_id: command.parent_id, | ||||
| 			}; | ||||
|  | ||||
| 		} else if (command.name === 'screenshot') { | ||||
| @@ -207,15 +207,15 @@ | ||||
| 				setTimeout(() => { | ||||
| 					const content = { | ||||
| 						title: pageTitle(), | ||||
| 						cropRect: selectionArea, | ||||
| 						crop_rect: selectionArea, | ||||
| 						url: location.origin + location.pathname, | ||||
| 						parentId: command.parentId, | ||||
| 						parent_id: command.parent_id, | ||||
| 					}; | ||||
|  | ||||
| 					browser_.runtime.sendMessage({ | ||||
| 						name: 'screenshotArea', | ||||
| 						content: content, | ||||
| 						apiBaseUrl: command.apiBaseUrl, | ||||
| 						api_base_url: command.api_base_url, | ||||
| 					}); | ||||
| 				}, 100); | ||||
| 			} | ||||
|   | ||||
| @@ -30,14 +30,14 @@ class AppComponent extends Component { | ||||
| 		this.clipSimplified_click = () => { | ||||
| 			bridge().sendCommandToActiveTab({ | ||||
| 				name: 'simplifiedPageHtml', | ||||
| 				parentId: this.props.selectedFolderId, | ||||
| 				parent_id: this.props.selectedFolderId, | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		this.clipComplete_click = () => { | ||||
| 			bridge().sendCommandToActiveTab({ | ||||
| 				name: 'completePageHtml', | ||||
| 				parentId: this.props.selectedFolderId, | ||||
| 				parent_id: this.props.selectedFolderId, | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| @@ -47,8 +47,8 @@ class AppComponent extends Component { | ||||
|  | ||||
| 				await bridge().sendCommandToActiveTab({ | ||||
| 					name: 'screenshot', | ||||
| 					apiBaseUrl: baseUrl, | ||||
| 					parentId: this.props.selectedFolderId, | ||||
| 					api_base_url: baseUrl, | ||||
| 					parent_id: this.props.selectedFolderId, | ||||
| 				}); | ||||
|  | ||||
| 				window.close(); | ||||
| @@ -118,7 +118,7 @@ class AppComponent extends Component { | ||||
| 					<div className="Preview"> | ||||
| 						<input className={"Title"} value={content.title} onChange={this.contentTitle_change}/> | ||||
| 						<div className={"BodyWrapper"}> | ||||
| 							<div className={"Body"} dangerouslySetInnerHTML={{__html: content.bodyHtml}}></div> | ||||
| 							<div className={"Body"} dangerouslySetInnerHTML={{__html: content.body_html}}></div> | ||||
| 						</div> | ||||
| 						<a className={"Confirm Button"} onClick={this.confirm_click}>Confirm</a> | ||||
| 					</div> | ||||
| @@ -182,7 +182,7 @@ class AppComponent extends Component { | ||||
| 			return ( | ||||
| 				<div className="Folders"> | ||||
| 					<label>In notebook: </label> | ||||
| 					<select value={this.props.selectedFolderId} onChange={this.folderSelect_change}> | ||||
| 					<select value={this.props.selectedFolderId || ''} onChange={this.folderSelect_change}> | ||||
| 						{ optionComps } | ||||
| 					</select> | ||||
| 				</div> | ||||
|   | ||||
| @@ -12,7 +12,7 @@ class Bridge { | ||||
| 		this.clipperServerPortStatus_ = 'searching'; | ||||
|  | ||||
| 		this.browser_notify = async (command) => { | ||||
| 			console.info('Popup: Got command: ' + command.name); | ||||
| 			console.info('Popup: Got command:', command); | ||||
| 			 | ||||
| 			if (command.warning) { | ||||
| 				console.warn('Popup: Got warning: ' + command.warning); | ||||
| @@ -24,10 +24,10 @@ class Bridge { | ||||
| 			if (command.name === 'clippedContent') { | ||||
| 				const content = { | ||||
| 					title: command.title, | ||||
| 					bodyHtml: command.html, | ||||
| 					baseUrl: command.baseUrl, | ||||
| 					url: command.url, | ||||
| 					parentId: command.parentId, | ||||
| 					body_html: command.html, | ||||
| 					base_url: command.base_url, | ||||
| 					source_url: command.url, | ||||
| 					parent_id: command.parent_id, | ||||
| 				}; | ||||
|  | ||||
| 				this.dispatch({ type: 'CLIPPED_CONTENT_SET', content: content }); | ||||
| @@ -36,14 +36,21 @@ class Bridge { | ||||
|  | ||||
| 		this.browser_.runtime.onMessage.addListener(this.browser_notify); | ||||
|  | ||||
| 		console.info('Popup: Env: ', this.env()); | ||||
| 		const backgroundPage = this.browser_.extension.getBackgroundPage(); | ||||
| 		this.env_ = backgroundPage.joplinEnv(); | ||||
|  | ||||
| 		console.info('Popup: Env:', this.env()); | ||||
|  | ||||
| 		this.dispatch({ | ||||
| 			type: 'ENV_SET', | ||||
| 			env: this.env(), | ||||
| 		}); | ||||
|  | ||||
| 		this.findClipperServerPort(); | ||||
| 	} | ||||
|  | ||||
| 	env() { | ||||
| 		return 'prod'; | ||||
| 		return !('update_url' in this.browser().runtime.getManifest()) ? 'dev' : 'prod'; | ||||
| 		return this.env_; | ||||
| 	} | ||||
|  | ||||
| 	browser() { | ||||
| @@ -149,6 +156,12 @@ class Bridge { | ||||
|  | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			this.browser().tabs.executeScript(options, () => { | ||||
| 				const e = this.browser().runtime.lastError; | ||||
| 				if (e) { | ||||
| 					const msg = ['tabsExecuteScript: Cannot load ' + JSON.stringify(options)] | ||||
| 					if (e.message) msg.push(e.message); | ||||
| 					reject(new Error(msg.join(': '))); | ||||
| 				} | ||||
| 				resolve(); | ||||
| 			}); | ||||
| 		}) | ||||
|   | ||||
| @@ -17,6 +17,7 @@ const defaultState = { | ||||
| 	}, | ||||
| 	folders: [], | ||||
| 	selectedFolderId: null, | ||||
| 	env: 'prod', | ||||
| }; | ||||
|  | ||||
| const reduxMiddleware = store => next => async (action) => { | ||||
| @@ -60,6 +61,10 @@ function reducer(state = defaultState, action) { | ||||
| 		newState = Object.assign({}, state); | ||||
| 		newState.folders = action.folders; | ||||
|  | ||||
| 		if (!newState.selectedFolderId && action.folders.length) { | ||||
| 			newState.selectedFolderId = action.folders[0].id; | ||||
| 		} | ||||
|  | ||||
| 	} else if (action.type === 'SELECTED_FOLDER_SET') { | ||||
|  | ||||
| 		newState = Object.assign({}, state); | ||||
| @@ -73,6 +78,11 @@ function reducer(state = defaultState, action) { | ||||
| 		if ('port' in action) clipperServer.port = action.port; | ||||
| 		newState.clipperServer = clipperServer; | ||||
|  | ||||
| 	} else if (action.type === 'ENV_SET') { | ||||
|  | ||||
| 		newState = Object.assign({}, state); | ||||
| 		newState.env = action.env; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return newState; | ||||
|   | ||||
| @@ -76,24 +76,24 @@ class ClipperServer { | ||||
| 			body: requestNote.body ? requestNote.body : '', | ||||
| 		}; | ||||
|  | ||||
| 		if (requestNote.bodyHtml) { | ||||
| 		if (requestNote.body_html) { | ||||
| 			// Parsing will not work if the HTML is not wrapped in a top level tag, which is not guaranteed | ||||
| 			// when getting the content from elsewhere. So here wrap it - it won't change anything to the final | ||||
| 			// rendering but it makes sure everything will be parsed. | ||||
| 			output.body = await this.htmlToMdParser().parse('<div>' + requestNote.bodyHtml + '</div>', { | ||||
| 				baseUrl: requestNote.baseUrl ? requestNote.baseUrl : '', | ||||
| 			output.body = await this.htmlToMdParser().parse('<div>' + requestNote.body_html + '</div>', { | ||||
| 				baseUrl: requestNote.base_url ? requestNote.base_url : '', | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		if (requestNote.parentId) { | ||||
| 			output.parent_id = requestNote.parentId; | ||||
| 		if (requestNote.parent_id) { | ||||
| 			output.parent_id = requestNote.parent_id; | ||||
| 		} else { | ||||
| 			const folder = await Folder.defaultFolder(); | ||||
| 			if (!folder) throw new Error('Cannot find folder for note'); | ||||
| 			output.parent_id = folder.id; | ||||
| 		} | ||||
|  | ||||
| 		if (requestNote.url) output.source_url = requestNote.url; | ||||
| 		if (requestNote.source_url) output.source_url = requestNote.source_url; | ||||
|  | ||||
| 		return output; | ||||
| 	} | ||||
| @@ -277,8 +277,8 @@ class ClipperServer { | ||||
|  | ||||
| 							note = await Note.save(note); | ||||
|  | ||||
| 							if (requestNote.imageDataUrl) { | ||||
| 								await this.attachImageFromDataUrl_(note, requestNote.imageDataUrl, requestNote.cropRect); | ||||
| 							if (requestNote.image_data_url) { | ||||
| 								await this.attachImageFromDataUrl_(note, requestNote.image_data_url, requestNote.crop_rect); | ||||
| 							} | ||||
|  | ||||
| 							this.logger().info('Request (' + requestId + '): Created note ' + note.id); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user