You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop, Mobile: Added support for notebook icons
This commit is contained in:
		| @@ -216,6 +216,12 @@ packages/app-desktop/gui/DialogTitle.js.map | ||||
| packages/app-desktop/gui/DropboxLoginScreen.d.ts | ||||
| packages/app-desktop/gui/DropboxLoginScreen.js | ||||
| packages/app-desktop/gui/DropboxLoginScreen.js.map | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.d.ts | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.js | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.js.map | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.d.ts | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.js | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.js.map | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.d.ts | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map | ||||
| @@ -282,6 +288,9 @@ packages/app-desktop/gui/MainScreen/commands/newTodo.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.js.map | ||||
| @@ -699,6 +708,9 @@ packages/app-desktop/gui/utils/SyncScrollMap.js.map | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.js | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map | ||||
| packages/app-desktop/gui/utils/loadScript.d.ts | ||||
| packages/app-desktop/gui/utils/loadScript.js | ||||
| packages/app-desktop/gui/utils/loadScript.js.map | ||||
| packages/app-desktop/plugins/GotoAnything.d.ts | ||||
| packages/app-desktop/plugins/GotoAnything.js | ||||
| packages/app-desktop/plugins/GotoAnything.js.map | ||||
|   | ||||
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -199,6 +199,12 @@ packages/app-desktop/gui/DialogTitle.js.map | ||||
| packages/app-desktop/gui/DropboxLoginScreen.d.ts | ||||
| packages/app-desktop/gui/DropboxLoginScreen.js | ||||
| packages/app-desktop/gui/DropboxLoginScreen.js.map | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.d.ts | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.js | ||||
| packages/app-desktop/gui/EditFolderDialog/Dialog.js.map | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.d.ts | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.js | ||||
| packages/app-desktop/gui/EditFolderDialog/IconSelector.js.map | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.d.ts | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js | ||||
| packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map | ||||
| @@ -265,6 +271,9 @@ packages/app-desktop/gui/MainScreen/commands/newTodo.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolder.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openFolderDialog.js.map | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.d.ts | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.js | ||||
| packages/app-desktop/gui/MainScreen/commands/openNote.js.map | ||||
| @@ -682,6 +691,9 @@ packages/app-desktop/gui/utils/SyncScrollMap.js.map | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.js | ||||
| packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map | ||||
| packages/app-desktop/gui/utils/loadScript.d.ts | ||||
| packages/app-desktop/gui/utils/loadScript.js | ||||
| packages/app-desktop/gui/utils/loadScript.js.map | ||||
| packages/app-desktop/plugins/GotoAnything.d.ts | ||||
| packages/app-desktop/plugins/GotoAnything.js | ||||
| packages/app-desktop/plugins/GotoAnything.js.map | ||||
|   | ||||
| @@ -18,6 +18,7 @@ export enum AppStateDialogName { | ||||
|  | ||||
| export interface AppStateDialog { | ||||
| 	name: AppStateDialogName; | ||||
| 	props: Record<string, any>; | ||||
| } | ||||
|  | ||||
| export interface AppState extends State { | ||||
| @@ -287,6 +288,7 @@ export default function(state: AppState, action: any) { | ||||
|  | ||||
| 					newDialogs.push({ | ||||
| 						name: action.name, | ||||
| 						props: action.props || {}, | ||||
| 					}); | ||||
|  | ||||
| 					newState.dialogs = newDialogs; | ||||
|   | ||||
| @@ -564,7 +564,8 @@ class Application extends BaseApplication { | ||||
| 		// setTimeout(() => { | ||||
| 		// 	this.dispatch({ | ||||
| 		// 		type: 'DIALOG_OPEN', | ||||
| 		// 		name: 'masterPassword', | ||||
| 		// 		name: 'editFolder', | ||||
| 		// 		props: { folderId: '3d90f7da26b947dc9c8c6c65e86cd231' }, | ||||
| 		// 	}); | ||||
| 		// }, 2000); | ||||
|  | ||||
|   | ||||
| @@ -27,6 +27,9 @@ interface Props { | ||||
| 	disabled?: boolean; | ||||
| 	style?: any; | ||||
| 	size?: ButtonSize; | ||||
| 	isSquare?: boolean; | ||||
| 	iconOnly?: boolean; | ||||
| 	fontSize?: number; | ||||
| } | ||||
|  | ||||
| const StyledTitle = styled.span` | ||||
| @@ -41,6 +44,10 @@ export const buttonSizePx = (props: Props) => { | ||||
| 	throw new Error(`Unknown size: ${props.size}`); | ||||
| }; | ||||
|  | ||||
| const isSquare = (props: Props) => { | ||||
| 	return props.iconOnly || props.isSquare; | ||||
| }; | ||||
|  | ||||
| const StyledButtonBase = styled.button` | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| @@ -48,19 +55,19 @@ const StyledButtonBase = styled.button` | ||||
| 	height: ${(props: Props) => buttonSizePx(props)}px; | ||||
| 	min-height: ${(props: Props) => buttonSizePx(props)}px; | ||||
| 	max-height: ${(props: Props) => buttonSizePx(props)}px; | ||||
| 	width: ${(props: any) => props.iconOnly ? `${buttonSizePx}px` : 'auto'}; | ||||
| 	${(props: any) => props.iconOnly ? `min-width: ${buttonSizePx}px;` : ''} | ||||
| 	${(props: any) => !props.iconOnly ? 'min-width: 100px;' : ''} | ||||
| 	${(props: any) => props.iconOnly ? `max-width: ${buttonSizePx}px;` : ''} | ||||
| 	width: ${(props: Props) => isSquare(props) ? `${buttonSizePx(props)}px` : 'auto'}; | ||||
| 	${(props: Props) => isSquare(props) ? `min-width: ${buttonSizePx(props)}px;` : ''} | ||||
| 	${(props: Props) => !isSquare(props) ? 'min-width: 100px;' : ''} | ||||
| 	${(props: Props) => isSquare(props) ? `max-width: ${buttonSizePx(props)}px;` : ''} | ||||
| 	box-sizing: border-box; | ||||
| 	border-radius: 3px; | ||||
| 	border-style: solid; | ||||
| 	border-width: 1px; | ||||
| 	/*font-size: ${(props: any) => props.theme.fontSize}px; */ | ||||
| 	padding: 0 ${(props: any) => props.iconOnly ? 4 : 14}px; | ||||
| 	padding: 0 ${(props: Props) => isSquare(props) ? 4 : 14}px; | ||||
| 	justify-content: center; | ||||
| 	opacity: ${(props: any) => props.disabled ? 0.5 : 1}; | ||||
| 	opacity: ${(props: Props) => props.disabled ? 0.5 : 1}; | ||||
| 	user-select: none; | ||||
| 	${(props: Props) => props.fontSize ? `font-size: ${props.fontSize}px;` : ''} | ||||
| `; | ||||
|  | ||||
| const StyledIcon = styled(styled.span(space))` | ||||
| @@ -200,7 +207,7 @@ function buttonClass(level: ButtonLevel) { | ||||
| 	return StyledButtonSecondary; | ||||
| } | ||||
|  | ||||
| function Button(props: Props) { | ||||
| const Button = React.forwardRef((props: Props, ref: any) => { | ||||
| 	const iconOnly = props.iconName && !props.title; | ||||
|  | ||||
| 	const StyledButton = buttonClass(props.level); | ||||
| @@ -221,11 +228,11 @@ function Button(props: Props) { | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<StyledButton size={props.size} style={props.style} disabled={props.disabled} title={props.tooltip} className={props.className} iconOnly={iconOnly} onClick={onClick}> | ||||
| 		<StyledButton ref={ref} fontSize={props.fontSize} isSquare={props.isSquare} size={props.size} style={props.style} disabled={props.disabled} title={props.tooltip} className={props.className} iconOnly={iconOnly} onClick={onClick}> | ||||
| 			{renderIcon()} | ||||
| 			{renderTitle()} | ||||
| 		</StyledButton> | ||||
| 	); | ||||
| } | ||||
| }); | ||||
|  | ||||
| export default styled(Button)`${space}`; | ||||
|   | ||||
							
								
								
									
										115
									
								
								packages/app-desktop/gui/EditFolderDialog/Dialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								packages/app-desktop/gui/EditFolderDialog/Dialog.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| import * as React from 'react'; | ||||
| import { useCallback, useState } from 'react'; | ||||
| import { _ } from '@joplin/lib/locale'; | ||||
| import DialogButtonRow, { ClickEvent } from '../DialogButtonRow'; | ||||
| import Dialog from '../Dialog'; | ||||
| import DialogTitle from '../DialogTitle'; | ||||
| import StyledInput from '../style/StyledInput'; | ||||
| import { IconSelector, ChangeEvent } from './IconSelector'; | ||||
| import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; | ||||
| import Folder from '@joplin/lib/models/Folder'; | ||||
| import { FolderIcon } from '@joplin/lib/services/database/types'; | ||||
| import Button from '../Button/Button'; | ||||
|  | ||||
| interface Props { | ||||
| 	themeId: number; | ||||
| 	dispatch: Function; | ||||
| 	folderId: string; | ||||
| } | ||||
|  | ||||
| export default function(props: Props) { | ||||
| 	const [folderTitle, setFolderTitle] = useState(''); | ||||
| 	const [folderIcon, setFolderIcon] = useState<FolderIcon>(); | ||||
|  | ||||
| 	useAsyncEffect(async (event: AsyncEffectEvent) => { | ||||
| 		const folder = await Folder.load(props.folderId); | ||||
| 		if (event.cancelled) return; | ||||
| 		setFolderTitle(folder.title); | ||||
| 		setFolderIcon(Folder.unserializeIcon(folder.icon)); | ||||
| 	}, [props.folderId]); | ||||
|  | ||||
| 	const onClose = useCallback(() => { | ||||
| 		props.dispatch({ | ||||
| 			type: 'DIALOG_CLOSE', | ||||
| 			name: 'editFolder', | ||||
| 		}); | ||||
| 	}, [props.dispatch]); | ||||
|  | ||||
| 	const onButtonRowClick = useCallback(async (event: ClickEvent) => { | ||||
| 		if (event.buttonName === 'cancel') { | ||||
| 			onClose(); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		if (event.buttonName === 'ok') { | ||||
| 			await Folder.save({ | ||||
| 				id: props.folderId, | ||||
| 				title: folderTitle, | ||||
| 				icon: Folder.serializeIcon(folderIcon), | ||||
| 			}); | ||||
| 			onClose(); | ||||
| 			return; | ||||
| 		} | ||||
| 	}, [onClose, folderTitle, folderIcon, props.folderId]); | ||||
|  | ||||
| 	const onFolderTitleChange = useCallback((event: any) => { | ||||
| 		setFolderTitle(event.target.value); | ||||
| 	}, []); | ||||
|  | ||||
| 	const onFolderIconChange = useCallback((event: ChangeEvent) => { | ||||
| 		setFolderIcon(event.value); | ||||
| 	}, []); | ||||
|  | ||||
| 	const onClearClick = useCallback(() => { | ||||
| 		setFolderIcon(null); | ||||
| 	}, []); | ||||
|  | ||||
| 	function renderForm() { | ||||
| 		return ( | ||||
| 			<div> | ||||
| 				<div className="form"> | ||||
| 					<div className="form-input-group"> | ||||
| 						<label>{_('Title')}</label> | ||||
| 						<StyledInput type="text" value={folderTitle} onChange={onFolderTitleChange}/> | ||||
| 					</div> | ||||
|  | ||||
| 					<div className="form-input-group"> | ||||
| 						<label>{_('Icon')}</label> | ||||
| 						<div className="icon-selector-row"> | ||||
| 							<IconSelector | ||||
| 								icon={folderIcon} | ||||
| 								onChange={onFolderIconChange} | ||||
| 							/> | ||||
| 							<Button ml={1} title={_('Clear')} onClick={onClearClick}/> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	function renderContent() { | ||||
| 		return ( | ||||
| 			<div className="dialog-content"> | ||||
| 				{renderForm()} | ||||
| 			</div> | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	function renderDialogWrapper() { | ||||
| 		return ( | ||||
| 			<div className="dialog-root"> | ||||
| 				<DialogTitle title={_('Edit notebook')}/> | ||||
| 				{renderContent()} | ||||
| 				<DialogButtonRow | ||||
| 					themeId={props.themeId} | ||||
| 					onClick={onButtonRowClick} | ||||
| 				/> | ||||
| 			</div> | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<Dialog onClose={onClose} className="master-password-dialog" renderContent={renderDialogWrapper}/> | ||||
| 	); | ||||
| } | ||||
							
								
								
									
										92
									
								
								packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| import { EmojiButton } from '@joeattardi/emoji-button'; | ||||
| import { useEffect, useState, useCallback, useRef } from 'react'; | ||||
| import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; | ||||
| import { loadScript } from '../utils/loadScript'; | ||||
| import Button from '../Button/Button'; | ||||
| import { FolderIcon } from '@joplin/lib/services/database/types'; | ||||
|  | ||||
| export interface ChangeEvent { | ||||
| 	value: FolderIcon; | ||||
| } | ||||
|  | ||||
| type ChangeHandler = (event: ChangeEvent)=> void; | ||||
|  | ||||
| interface Props { | ||||
| 	onChange: ChangeHandler; | ||||
| 	icon: FolderIcon | null; | ||||
| } | ||||
|  | ||||
| export const IconSelector = (props: Props) => { | ||||
| 	const [emojiButtonClassReady, setEmojiButtonClassReady] = useState<boolean>(false); | ||||
| 	const [picker, setPicker] = useState<EmojiButton>(); | ||||
| 	const buttonRef = useRef(null); | ||||
|  | ||||
| 	useAsyncEffect(async (event: AsyncEffectEvent) => { | ||||
| 		const loadScripts = async () => { | ||||
| 			// The emoji-button lib is annoying to load as it only comes as an | ||||
| 			// ES module. So we first need to load the lib, then load a loader | ||||
| 			// script, which will copy the class to the window object. | ||||
|  | ||||
| 			await loadScript({ | ||||
| 				id: 'emoji-button-lib', | ||||
| 				src: 'node_modules/@joeattardi/emoji-button/dist/index.js', | ||||
| 				attrs: { | ||||
| 					type: 'module', | ||||
| 				}, | ||||
| 			}); | ||||
|  | ||||
| 			if (event.cancelled) return; | ||||
|  | ||||
| 			await loadScript({ | ||||
| 				id: 'emoji-button-lib-loader', | ||||
| 				src: 'gui/EditFolderDialog/loadEmojiLib.js', | ||||
| 				attrs: { | ||||
| 					type: 'module', | ||||
| 				}, | ||||
| 			}); | ||||
|  | ||||
| 			if (event.cancelled) return; | ||||
|  | ||||
| 			setEmojiButtonClassReady(true); | ||||
| 		}; | ||||
|  | ||||
| 		void loadScripts(); | ||||
| 	}, []); | ||||
|  | ||||
| 	useEffect(() => { | ||||
| 		if (!emojiButtonClassReady) return () => {}; | ||||
|  | ||||
| 		const p: EmojiButton = new (window as any).EmojiButton({ | ||||
| 			zIndex: 10000, | ||||
| 		}); | ||||
|  | ||||
| 		const onEmoji = (selection: FolderIcon) => { | ||||
| 			props.onChange({ value: selection }); | ||||
| 		}; | ||||
|  | ||||
| 		p.on('emoji', onEmoji); | ||||
|  | ||||
| 		setPicker(p); | ||||
|  | ||||
| 		return () => { | ||||
| 			p.off('emoji', onEmoji); | ||||
| 		}; | ||||
| 	}, [emojiButtonClassReady, props.onChange]); | ||||
|  | ||||
| 	const onClick = useCallback(() => { | ||||
| 		picker.togglePicker(buttonRef.current); | ||||
| 	}, [picker]); | ||||
|  | ||||
| 	const buttonText = props.icon ? props.icon.emoji : '...'; | ||||
|  | ||||
| 	return ( | ||||
| 		<Button | ||||
| 			disabled={!picker} | ||||
| 			ref={buttonRef} | ||||
| 			onClick={onClick} | ||||
| 			title={buttonText} | ||||
| 			isSquare={true} | ||||
| 			fontSize={20} | ||||
| 		/> | ||||
| 	); | ||||
| }; | ||||
| @@ -0,0 +1,2 @@ | ||||
| import { EmojiButton } from '../../node_modules/@joeattardi/emoji-button/dist/index.js'; | ||||
| window.EmojiButton = EmojiButton; | ||||
							
								
								
									
										4
									
								
								packages/app-desktop/gui/EditFolderDialog/style.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/app-desktop/gui/EditFolderDialog/style.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| .icon-selector-row { | ||||
| 	display: flex; | ||||
| 	flex-direction: row; | ||||
| } | ||||
| @@ -11,6 +11,7 @@ import * as newNote from './newNote'; | ||||
| import * as newSubFolder from './newSubFolder'; | ||||
| import * as newTodo from './newTodo'; | ||||
| import * as openFolder from './openFolder'; | ||||
| import * as openFolderDialog from './openFolderDialog'; | ||||
| import * as openNote from './openNote'; | ||||
| import * as openTag from './openTag'; | ||||
| import * as print from './print'; | ||||
| @@ -47,6 +48,7 @@ const index:any[] = [ | ||||
| 	newSubFolder, | ||||
| 	newTodo, | ||||
| 	openFolder, | ||||
| 	openFolderDialog, | ||||
| 	openNote, | ||||
| 	openTag, | ||||
| 	print, | ||||
|   | ||||
| @@ -0,0 +1,22 @@ | ||||
| import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; | ||||
| import { _ } from '@joplin/lib/locale'; | ||||
|  | ||||
| export const declaration: CommandDeclaration = { | ||||
| 	name: 'openFolderDialog', | ||||
| 	label: () => _('Edit'), | ||||
| }; | ||||
|  | ||||
| export const runtime = (): CommandRuntime => { | ||||
| 	return { | ||||
| 		execute: async (context: CommandContext, folderId: string) => { | ||||
| 			context.dispatch({ | ||||
| 				type: 'DIALOG_OPEN', | ||||
| 				name: 'editFolder', | ||||
| 				isOpen: true, | ||||
| 				props: { | ||||
| 					folderId, | ||||
| 				}, | ||||
| 			}); | ||||
| 		}, | ||||
| 	}; | ||||
| }; | ||||
| @@ -22,6 +22,7 @@ import { plainTextToHtml } from '@joplin/lib/htmlUtils'; | ||||
| import openEditDialog from './utils/openEditDialog'; | ||||
| import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml'; | ||||
| import { themeStyle } from '@joplin/lib/theme'; | ||||
| import { loadScript } from '../../../utils/loadScript'; | ||||
| const { clipboard } = require('electron'); | ||||
| const supportedLocales = require('./supportedLocales'); | ||||
|  | ||||
| @@ -286,32 +287,32 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { | ||||
| 	// module would not load these extra files. | ||||
| 	// ----------------------------------------------------------------------------------------- | ||||
|  | ||||
| 	const loadScript = async (script: any) => { | ||||
| 		return new Promise((resolve) => { | ||||
| 			let element: any = document.createElement('script'); | ||||
| 			if (script.src.indexOf('.css') >= 0) { | ||||
| 				element = document.createElement('link'); | ||||
| 				element.rel = 'stylesheet'; | ||||
| 				element.href = script.src; | ||||
| 			} else { | ||||
| 				element.src = script.src; | ||||
| 	// const loadScript = async (script: any) => { | ||||
| 	// 	return new Promise((resolve) => { | ||||
| 	// 		let element: any = document.createElement('script'); | ||||
| 	// 		if (script.src.indexOf('.css') >= 0) { | ||||
| 	// 			element = document.createElement('link'); | ||||
| 	// 			element.rel = 'stylesheet'; | ||||
| 	// 			element.href = script.src; | ||||
| 	// 		} else { | ||||
| 	// 			element.src = script.src; | ||||
|  | ||||
| 				if (script.attrs) { | ||||
| 					for (const attr in script.attrs) { | ||||
| 						element[attr] = script.attrs[attr]; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 	// 			if (script.attrs) { | ||||
| 	// 				for (const attr in script.attrs) { | ||||
| 	// 					element[attr] = script.attrs[attr]; | ||||
| 	// 				} | ||||
| 	// 			} | ||||
| 	// 		} | ||||
|  | ||||
| 			element.id = script.id; | ||||
| 	// 		element.id = script.id; | ||||
|  | ||||
| 			element.onload = () => { | ||||
| 				resolve(null); | ||||
| 			}; | ||||
| 	// 		element.onload = () => { | ||||
| 	// 			resolve(null); | ||||
| 	// 		}; | ||||
|  | ||||
| 			document.getElementsByTagName('head')[0].appendChild(element); | ||||
| 		}); | ||||
| 	}; | ||||
| 	// 		document.getElementsByTagName('head')[0].appendChild(element); | ||||
| 	// 	}); | ||||
| 	// }; | ||||
|  | ||||
| 	useEffect(() => { | ||||
| 		let cancelled = false; | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './Di | ||||
| import Dialog from './Dialog'; | ||||
| import SyncWizardDialog from './SyncWizard/Dialog'; | ||||
| import MasterPasswordDialog from './MasterPasswordDialog/Dialog'; | ||||
| import EditFolderDialog from './EditFolderDialog/Dialog'; | ||||
| import StyleSheetContainer from './StyleSheets/StyleSheetContainer'; | ||||
| const { ImportScreen } = require('./ImportScreen.min.js'); | ||||
| const { ResourceScreen } = require('./ResourceScreen.js'); | ||||
| @@ -36,7 +37,7 @@ interface Props { | ||||
| 	size: Size; | ||||
| 	zoomFactor: number; | ||||
| 	needApiAuth: boolean; | ||||
| 	dialogs: AppStateDialog; | ||||
| 	dialogs: AppStateDialog[]; | ||||
| } | ||||
|  | ||||
| interface ModalDialogProps { | ||||
| @@ -53,19 +54,25 @@ interface RegisteredDialogProps { | ||||
| } | ||||
|  | ||||
| interface RegisteredDialog { | ||||
| 	render: (props: RegisteredDialogProps)=> any; | ||||
| 	render: (props: RegisteredDialogProps, customProps: any)=> any; | ||||
| } | ||||
|  | ||||
| const registeredDialogs: Record<string, RegisteredDialog> = { | ||||
| 	syncWizard: { | ||||
| 		render: (props: RegisteredDialogProps) => { | ||||
| 			return <SyncWizardDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId}/>; | ||||
| 		render: (props: RegisteredDialogProps, customProps: any) => { | ||||
| 			return <SyncWizardDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	masterPassword: { | ||||
| 		render: (props: RegisteredDialogProps) => { | ||||
| 			return <MasterPasswordDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId}/>; | ||||
| 		render: (props: RegisteredDialogProps, customProps: any) => { | ||||
| 			return <MasterPasswordDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	editFolder: { | ||||
| 		render: (props: RegisteredDialogProps, customProps: any) => { | ||||
| 			return <EditFolderDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>; | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
| @@ -180,17 +187,19 @@ class RootComponent extends React.Component<Props, any> { | ||||
| 	} | ||||
|  | ||||
| 	private renderDialogs() { | ||||
| 		if (!this.props.dialogs.length) return null; | ||||
| 		const props: Props = this.props; | ||||
|  | ||||
| 		if (!props.dialogs.length) return null; | ||||
|  | ||||
| 		const output: any[] = []; | ||||
| 		for (const dialog of this.props.dialogs) { | ||||
| 		for (const dialog of props.dialogs) { | ||||
| 			const md = registeredDialogs[dialog.name]; | ||||
| 			if (!md) throw new Error(`Unknown dialog: ${dialog.name}`); | ||||
| 			output.push(md.render({ | ||||
| 				key: dialog.name, | ||||
| 				themeId: this.props.themeId, | ||||
| 				dispatch: this.props.dispatch, | ||||
| 			})); | ||||
| 				themeId: props.themeId, | ||||
| 				dispatch: props.dispatch, | ||||
| 			}, dialog.props)); | ||||
| 		} | ||||
| 		return output; | ||||
| 	} | ||||
|   | ||||
| @@ -78,12 +78,14 @@ function ExpandLink(props: any) { | ||||
| } | ||||
|  | ||||
| function FolderItem(props: any) { | ||||
| 	const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props; | ||||
| 	const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, folderIcon, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props; | ||||
|  | ||||
| 	const noteCountComp = noteCount ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null; | ||||
|  | ||||
| 	const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null; | ||||
|  | ||||
| 	const icon = folderIcon ? <span style={{ fontSize: 20, marginRight: 5 }}>{folderIcon.emoji}</span> : null; | ||||
|  | ||||
| 	return ( | ||||
| 		<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth} ${selected ? 'selected' : ''}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}> | ||||
| 			<ExpandLink themeId={props.themeId} hasChildren={hasChildren} folderId={folderId} onClick={onFolderToggleClick_} isExpanded={isExpanded}/> | ||||
| @@ -103,7 +105,7 @@ function FolderItem(props: any) { | ||||
| 				}} | ||||
| 				onDoubleClick={onFolderToggleClick_} | ||||
| 			> | ||||
| 				<span className="title">{folderTitle}</span> | ||||
| 				{icon}<span className="title" style={{ lineHeight: 0 }}>{folderTitle}</span> | ||||
| 				{shareIcon} {noteCountComp} | ||||
| 			</StyledListItemAnchor> | ||||
| 		</StyledListItem> | ||||
| @@ -292,7 +294,7 @@ class SidebarComponent extends React.Component<Props, State> { | ||||
| 		); | ||||
|  | ||||
| 		if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) { | ||||
| 			menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('renameFolder', itemId))); | ||||
| 			menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('openFolderDialog', itemId))); | ||||
|  | ||||
| 			menu.append(new MenuItem({ type: 'separator' })); | ||||
|  | ||||
| @@ -464,6 +466,7 @@ class SidebarComponent extends React.Component<Props, State> { | ||||
| 			key={folder.id} | ||||
| 			folderId={folder.id} | ||||
| 			folderTitle={Folder.displayTitle(folder)} | ||||
| 			folderIcon={Folder.unserializeIcon(folder.icon)} | ||||
| 			themeId={this.props.themeId} | ||||
| 			depth={depth} | ||||
| 			selected={selected} | ||||
|   | ||||
| @@ -46,7 +46,7 @@ export const StyledHeaderLabel = styled.span` | ||||
|  | ||||
| export const StyledListItem = styled.div` | ||||
| 	box-sizing: border-box; | ||||
| 	height: 25px; | ||||
| 	height: 30px; | ||||
| 	display: flex; | ||||
| 	flex-direction: row; | ||||
| 	align-items: center; | ||||
|   | ||||
							
								
								
									
										48
									
								
								packages/app-desktop/gui/utils/loadScript.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								packages/app-desktop/gui/utils/loadScript.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import Logger from '@joplin/lib/Logger'; | ||||
|  | ||||
| const logger = Logger.create('loadScript'); | ||||
|  | ||||
| export interface Script { | ||||
| 	id: string; | ||||
| 	src: string; | ||||
| 	attrs?: Record<string, any>; | ||||
| } | ||||
|  | ||||
| export const loadScript = async (script: Script) => { | ||||
| 	return new Promise((resolve) => { | ||||
| 		let element: any = document.getElementById(script.id); | ||||
|  | ||||
| 		if (element) { | ||||
| 			if (element.href === script.src || element.src === script.src) { | ||||
| 				logger.info(`Trying to load a script that has already been loaded: ${JSON.stringify(script)} - skipping it`); | ||||
| 				resolve(null); | ||||
| 			} else { | ||||
| 				logger.info(`Source of script has changed - reloading it: ${JSON.stringify(script)}`); | ||||
| 				element.parentNode.removeChild(element); | ||||
| 				element = null; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (script.src.indexOf('.css') >= 0) { | ||||
| 			element = document.createElement('link'); | ||||
| 			element.rel = 'stylesheet'; | ||||
| 			element.href = script.src; | ||||
| 		} else { | ||||
| 			element = document.createElement('script'); | ||||
| 			element.src = script.src; | ||||
| 			if (script.attrs) { | ||||
| 				for (const attr in script.attrs) { | ||||
| 					element[attr] = script.attrs[attr]; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		element.id = script.id; | ||||
|  | ||||
| 		element.onload = () => { | ||||
| 			resolve(null); | ||||
| 		}; | ||||
|  | ||||
| 		document.getElementsByTagName('head')[0].appendChild(element); | ||||
| 	}); | ||||
| }; | ||||
							
								
								
									
										303
									
								
								packages/app-desktop/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										303
									
								
								packages/app-desktop/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -11,6 +11,7 @@ | ||||
| 			"dependencies": { | ||||
| 				"@electron/remote": "^2.0.1", | ||||
| 				"@fortawesome/fontawesome-free": "^5.13.0", | ||||
| 				"@joeattardi/emoji-button": "^4.6.0", | ||||
| 				"async-mutex": "^0.1.3", | ||||
| 				"codemirror": "^5.56.0", | ||||
| 				"color": "^3.1.2", | ||||
| @@ -1530,6 +1531,15 @@ | ||||
| 			"version": "0.8.2", | ||||
| 			"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw==" | ||||
| 		}, | ||||
| 		"node_modules/@fortawesome/fontawesome-common-types": { | ||||
| 			"version": "0.2.36", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", | ||||
| 			"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", | ||||
| 			"hasInstallScript": true, | ||||
| 			"engines": { | ||||
| 				"node": ">=6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@fortawesome/fontawesome-free": { | ||||
| 			"version": "5.13.0", | ||||
| 			"integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==", | ||||
| @@ -1537,6 +1547,42 @@ | ||||
| 				"node": ">=6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@fortawesome/fontawesome-svg-core": { | ||||
| 			"version": "1.2.36", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", | ||||
| 			"integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", | ||||
| 			"hasInstallScript": true, | ||||
| 			"dependencies": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			}, | ||||
| 			"engines": { | ||||
| 				"node": ">=6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@fortawesome/free-regular-svg-icons": { | ||||
| 			"version": "5.15.4", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", | ||||
| 			"integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", | ||||
| 			"hasInstallScript": true, | ||||
| 			"dependencies": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			}, | ||||
| 			"engines": { | ||||
| 				"node": ">=6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@fortawesome/free-solid-svg-icons": { | ||||
| 			"version": "5.15.4", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", | ||||
| 			"integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", | ||||
| 			"hasInstallScript": true, | ||||
| 			"dependencies": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			}, | ||||
| 			"engines": { | ||||
| 				"node": ">=6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@gar/promisify": { | ||||
| 			"version": "1.1.2", | ||||
| 			"integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", | ||||
| @@ -2250,6 +2296,23 @@ | ||||
| 				"node": ">=8" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@joeattardi/emoji-button": { | ||||
| 			"version": "4.6.0", | ||||
| 			"resolved": "https://registry.npmjs.org/@joeattardi/emoji-button/-/emoji-button-4.6.0.tgz", | ||||
| 			"integrity": "sha512-KwOE1j+YxX47JmT0pXNCa+9Ai4Wf2fmABtvuxy6JBJ5QV0HdoThRKjL6CxAreVwwLbNQ/PDoR36xpc5QJjLXPA==", | ||||
| 			"dependencies": { | ||||
| 				"@fortawesome/fontawesome-svg-core": "^1.2.28", | ||||
| 				"@fortawesome/free-regular-svg-icons": "^5.13.0", | ||||
| 				"@fortawesome/free-solid-svg-icons": "^5.13.0", | ||||
| 				"@popperjs/core": "^2.4.0", | ||||
| 				"@types/twemoji": "^12.1.1", | ||||
| 				"focus-trap": "^5.1.0", | ||||
| 				"fuzzysort": "^1.1.4", | ||||
| 				"tiny-emitter": "^2.1.0", | ||||
| 				"tslib": "^2.0.0", | ||||
| 				"twemoji": "^13.0.0" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@malept/cross-spawn-promise": { | ||||
| 			"version": "1.1.1", | ||||
| 			"integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", | ||||
| @@ -2439,6 +2502,15 @@ | ||||
| 				"node": ">=10" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@popperjs/core": { | ||||
| 			"version": "2.10.2", | ||||
| 			"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", | ||||
| 			"integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==", | ||||
| 			"funding": { | ||||
| 				"type": "opencollective", | ||||
| 				"url": "https://opencollective.com/popperjs" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@sindresorhus/is": { | ||||
| 			"version": "0.14.0", | ||||
| 			"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", | ||||
| @@ -2896,6 +2968,11 @@ | ||||
| 				"@types/react-test-renderer": "*" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/@types/twemoji": { | ||||
| 			"version": "12.1.2", | ||||
| 			"resolved": "https://registry.npmjs.org/@types/twemoji/-/twemoji-12.1.2.tgz", | ||||
| 			"integrity": "sha512-3eMyKenMi0R1CeKzBYtk/Z2JIHsTMQrIrTah0q54o45pHTpWVNofU2oHx0jS8tqsDRhis2TbB6238WP9oh2l2w==" | ||||
| 		}, | ||||
| 		"node_modules/@types/verror": { | ||||
| 			"version": "1.10.5", | ||||
| 			"integrity": "sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw==", | ||||
| @@ -7916,6 +7993,15 @@ | ||||
| 				"safe-buffer": "~5.1.0" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/focus-trap": { | ||||
| 			"version": "5.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-5.1.0.tgz", | ||||
| 			"integrity": "sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ==", | ||||
| 			"dependencies": { | ||||
| 				"tabbable": "^4.0.0", | ||||
| 				"xtend": "^4.0.1" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/for-in": { | ||||
| 			"version": "1.0.2", | ||||
| 			"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", | ||||
| @@ -8060,6 +8146,11 @@ | ||||
| 			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"node_modules/fuzzysort": { | ||||
| 			"version": "1.1.4", | ||||
| 			"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.1.4.tgz", | ||||
| 			"integrity": "sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==" | ||||
| 		}, | ||||
| 		"node_modules/gauge": { | ||||
| 			"version": "2.7.4", | ||||
| 			"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", | ||||
| @@ -16602,6 +16693,11 @@ | ||||
| 			"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"node_modules/tabbable": { | ||||
| 			"version": "4.0.0", | ||||
| 			"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz", | ||||
| 			"integrity": "sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==" | ||||
| 		}, | ||||
| 		"node_modules/taboverride": { | ||||
| 			"version": "4.0.3", | ||||
| 			"integrity": "sha1-M5JAEqLzr17mCcXzDhvSanX75qk=" | ||||
| @@ -16829,6 +16925,11 @@ | ||||
| 				"node": ">=0.10.0" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/tiny-emitter": { | ||||
| 			"version": "2.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", | ||||
| 			"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" | ||||
| 		}, | ||||
| 		"node_modules/tinymce": { | ||||
| 			"version": "5.8.0", | ||||
| 			"integrity": "sha512-1bOI3k+1D76rVjAJC3XkHezXJVghurnKBDREF1STHBLTQUY17XTbaDNJUxNgJqJHa2xg1udd5I1bzdfSd77DGw==" | ||||
| @@ -17008,6 +17109,11 @@ | ||||
| 				"utf8-byte-length": "^1.0.1" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/tslib": { | ||||
| 			"version": "2.3.1", | ||||
| 			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", | ||||
| 			"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" | ||||
| 		}, | ||||
| 		"node_modules/tunnel": { | ||||
| 			"version": "0.0.6", | ||||
| 			"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", | ||||
| @@ -17032,6 +17138,54 @@ | ||||
| 			"dev": true, | ||||
| 			"optional": true | ||||
| 		}, | ||||
| 		"node_modules/twemoji": { | ||||
| 			"version": "13.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.1.0.tgz", | ||||
| 			"integrity": "sha512-e3fZRl2S9UQQdBFLYXtTBT6o4vidJMnpWUAhJA+yLGR+kaUTZAt3PixC0cGvvxWSuq2MSz/o0rJraOXrWw/4Ew==", | ||||
| 			"dependencies": { | ||||
| 				"fs-extra": "^8.0.1", | ||||
| 				"jsonfile": "^5.0.0", | ||||
| 				"twemoji-parser": "13.1.0", | ||||
| 				"universalify": "^0.1.2" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/twemoji-parser": { | ||||
| 			"version": "13.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.1.0.tgz", | ||||
| 			"integrity": "sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==" | ||||
| 		}, | ||||
| 		"node_modules/twemoji/node_modules/fs-extra": { | ||||
| 			"version": "8.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", | ||||
| 			"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", | ||||
| 			"dependencies": { | ||||
| 				"graceful-fs": "^4.2.0", | ||||
| 				"jsonfile": "^4.0.0", | ||||
| 				"universalify": "^0.1.0" | ||||
| 			}, | ||||
| 			"engines": { | ||||
| 				"node": ">=6 <7 || >=8" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/twemoji/node_modules/fs-extra/node_modules/jsonfile": { | ||||
| 			"version": "4.0.0", | ||||
| 			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", | ||||
| 			"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", | ||||
| 			"optionalDependencies": { | ||||
| 				"graceful-fs": "^4.1.6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/twemoji/node_modules/jsonfile": { | ||||
| 			"version": "5.0.0", | ||||
| 			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", | ||||
| 			"integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", | ||||
| 			"dependencies": { | ||||
| 				"universalify": "^0.1.2" | ||||
| 			}, | ||||
| 			"optionalDependencies": { | ||||
| 				"graceful-fs": "^4.1.6" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/type": { | ||||
| 			"version": "1.2.0", | ||||
| 			"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", | ||||
| @@ -17174,8 +17328,12 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/universalify": { | ||||
| 			"version": "0.1.1", | ||||
| 			"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" | ||||
| 			"version": "0.1.2", | ||||
| 			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", | ||||
| 			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", | ||||
| 			"engines": { | ||||
| 				"node": ">= 4.0.0" | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/unset-value": { | ||||
| 			"version": "1.0.0", | ||||
| @@ -17847,7 +18005,6 @@ | ||||
| 		"node_modules/xtend": { | ||||
| 			"version": "4.0.2", | ||||
| 			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", | ||||
| 			"dev": true, | ||||
| 			"engines": { | ||||
| 				"node": ">=0.4" | ||||
| 			} | ||||
| @@ -18995,10 +19152,39 @@ | ||||
| 			"version": "0.8.2", | ||||
| 			"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw==" | ||||
| 		}, | ||||
| 		"@fortawesome/fontawesome-common-types": { | ||||
| 			"version": "0.2.36", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", | ||||
| 			"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" | ||||
| 		}, | ||||
| 		"@fortawesome/fontawesome-free": { | ||||
| 			"version": "5.13.0", | ||||
| 			"integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==" | ||||
| 		}, | ||||
| 		"@fortawesome/fontawesome-svg-core": { | ||||
| 			"version": "1.2.36", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", | ||||
| 			"integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", | ||||
| 			"requires": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			} | ||||
| 		}, | ||||
| 		"@fortawesome/free-regular-svg-icons": { | ||||
| 			"version": "5.15.4", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", | ||||
| 			"integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", | ||||
| 			"requires": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			} | ||||
| 		}, | ||||
| 		"@fortawesome/free-solid-svg-icons": { | ||||
| 			"version": "5.15.4", | ||||
| 			"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", | ||||
| 			"integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", | ||||
| 			"requires": { | ||||
| 				"@fortawesome/fontawesome-common-types": "^0.2.36" | ||||
| 			} | ||||
| 		}, | ||||
| 		"@gar/promisify": { | ||||
| 			"version": "1.1.2", | ||||
| 			"integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", | ||||
| @@ -19532,6 +19718,23 @@ | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		"@joeattardi/emoji-button": { | ||||
| 			"version": "4.6.0", | ||||
| 			"resolved": "https://registry.npmjs.org/@joeattardi/emoji-button/-/emoji-button-4.6.0.tgz", | ||||
| 			"integrity": "sha512-KwOE1j+YxX47JmT0pXNCa+9Ai4Wf2fmABtvuxy6JBJ5QV0HdoThRKjL6CxAreVwwLbNQ/PDoR36xpc5QJjLXPA==", | ||||
| 			"requires": { | ||||
| 				"@fortawesome/fontawesome-svg-core": "^1.2.28", | ||||
| 				"@fortawesome/free-regular-svg-icons": "^5.13.0", | ||||
| 				"@fortawesome/free-solid-svg-icons": "^5.13.0", | ||||
| 				"@popperjs/core": "^2.4.0", | ||||
| 				"@types/twemoji": "^12.1.1", | ||||
| 				"focus-trap": "^5.1.0", | ||||
| 				"fuzzysort": "^1.1.4", | ||||
| 				"tiny-emitter": "^2.1.0", | ||||
| 				"tslib": "^2.0.0", | ||||
| 				"twemoji": "^13.0.0" | ||||
| 			} | ||||
| 		}, | ||||
| 		"@malept/cross-spawn-promise": { | ||||
| 			"version": "1.1.1", | ||||
| 			"integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", | ||||
| @@ -19664,6 +19867,11 @@ | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		"@popperjs/core": { | ||||
| 			"version": "2.10.2", | ||||
| 			"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", | ||||
| 			"integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" | ||||
| 		}, | ||||
| 		"@sindresorhus/is": { | ||||
| 			"version": "0.14.0", | ||||
| 			"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" | ||||
| @@ -20112,6 +20320,11 @@ | ||||
| 				"@types/react-test-renderer": "*" | ||||
| 			} | ||||
| 		}, | ||||
| 		"@types/twemoji": { | ||||
| 			"version": "12.1.2", | ||||
| 			"resolved": "https://registry.npmjs.org/@types/twemoji/-/twemoji-12.1.2.tgz", | ||||
| 			"integrity": "sha512-3eMyKenMi0R1CeKzBYtk/Z2JIHsTMQrIrTah0q54o45pHTpWVNofU2oHx0jS8tqsDRhis2TbB6238WP9oh2l2w==" | ||||
| 		}, | ||||
| 		"@types/verror": { | ||||
| 			"version": "1.10.5", | ||||
| 			"integrity": "sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw==", | ||||
| @@ -24029,6 +24242,15 @@ | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		"focus-trap": { | ||||
| 			"version": "5.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-5.1.0.tgz", | ||||
| 			"integrity": "sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ==", | ||||
| 			"requires": { | ||||
| 				"tabbable": "^4.0.0", | ||||
| 				"xtend": "^4.0.1" | ||||
| 			} | ||||
| 		}, | ||||
| 		"for-in": { | ||||
| 			"version": "1.0.2", | ||||
| 			"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", | ||||
| @@ -24141,6 +24363,11 @@ | ||||
| 			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"fuzzysort": { | ||||
| 			"version": "1.1.4", | ||||
| 			"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.1.4.tgz", | ||||
| 			"integrity": "sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==" | ||||
| 		}, | ||||
| 		"gauge": { | ||||
| 			"version": "2.7.4", | ||||
| 			"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", | ||||
| @@ -30633,6 +30860,11 @@ | ||||
| 			"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"tabbable": { | ||||
| 			"version": "4.0.0", | ||||
| 			"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz", | ||||
| 			"integrity": "sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==" | ||||
| 		}, | ||||
| 		"taboverride": { | ||||
| 			"version": "4.0.3", | ||||
| 			"integrity": "sha1-M5JAEqLzr17mCcXzDhvSanX75qk=" | ||||
| @@ -30816,6 +31048,11 @@ | ||||
| 			"integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"tiny-emitter": { | ||||
| 			"version": "2.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", | ||||
| 			"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" | ||||
| 		}, | ||||
| 		"tinymce": { | ||||
| 			"version": "5.8.0", | ||||
| 			"integrity": "sha512-1bOI3k+1D76rVjAJC3XkHezXJVghurnKBDREF1STHBLTQUY17XTbaDNJUxNgJqJHa2xg1udd5I1bzdfSd77DGw==" | ||||
| @@ -30951,6 +31188,11 @@ | ||||
| 				"utf8-byte-length": "^1.0.1" | ||||
| 			} | ||||
| 		}, | ||||
| 		"tslib": { | ||||
| 			"version": "2.3.1", | ||||
| 			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", | ||||
| 			"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" | ||||
| 		}, | ||||
| 		"tunnel": { | ||||
| 			"version": "0.0.6", | ||||
| 			"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", | ||||
| @@ -30969,6 +31211,53 @@ | ||||
| 			"dev": true, | ||||
| 			"optional": true | ||||
| 		}, | ||||
| 		"twemoji": { | ||||
| 			"version": "13.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.1.0.tgz", | ||||
| 			"integrity": "sha512-e3fZRl2S9UQQdBFLYXtTBT6o4vidJMnpWUAhJA+yLGR+kaUTZAt3PixC0cGvvxWSuq2MSz/o0rJraOXrWw/4Ew==", | ||||
| 			"requires": { | ||||
| 				"fs-extra": "^8.0.1", | ||||
| 				"jsonfile": "^5.0.0", | ||||
| 				"twemoji-parser": "13.1.0", | ||||
| 				"universalify": "^0.1.2" | ||||
| 			}, | ||||
| 			"dependencies": { | ||||
| 				"fs-extra": { | ||||
| 					"version": "8.1.0", | ||||
| 					"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", | ||||
| 					"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", | ||||
| 					"requires": { | ||||
| 						"graceful-fs": "^4.2.0", | ||||
| 						"jsonfile": "^4.0.0", | ||||
| 						"universalify": "^0.1.0" | ||||
| 					}, | ||||
| 					"dependencies": { | ||||
| 						"jsonfile": { | ||||
| 							"version": "4.0.0", | ||||
| 							"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", | ||||
| 							"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", | ||||
| 							"requires": { | ||||
| 								"graceful-fs": "^4.1.6" | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				"jsonfile": { | ||||
| 					"version": "5.0.0", | ||||
| 					"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", | ||||
| 					"integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", | ||||
| 					"requires": { | ||||
| 						"graceful-fs": "^4.1.6", | ||||
| 						"universalify": "^0.1.2" | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		"twemoji-parser": { | ||||
| 			"version": "13.1.0", | ||||
| 			"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.1.0.tgz", | ||||
| 			"integrity": "sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==" | ||||
| 		}, | ||||
| 		"type": { | ||||
| 			"version": "1.2.0", | ||||
| 			"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", | ||||
| @@ -31080,8 +31369,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"universalify": { | ||||
| 			"version": "0.1.1", | ||||
| 			"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" | ||||
| 			"version": "0.1.2", | ||||
| 			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", | ||||
| 			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" | ||||
| 		}, | ||||
| 		"unset-value": { | ||||
| 			"version": "1.0.0", | ||||
| @@ -31599,8 +31889,7 @@ | ||||
| 		}, | ||||
| 		"xtend": { | ||||
| 			"version": "4.0.2", | ||||
| 			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", | ||||
| 			"dev": true | ||||
| 			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" | ||||
| 		}, | ||||
| 		"y18n": { | ||||
| 			"version": "4.0.3", | ||||
|   | ||||
| @@ -136,6 +136,7 @@ | ||||
|   "dependencies": { | ||||
|     "@electron/remote": "^2.0.1", | ||||
|     "@fortawesome/fontawesome-free": "^5.13.0", | ||||
|     "@joeattardi/emoji-button": "^4.6.0", | ||||
|     "@joplin/lib": "~2.6", | ||||
|     "@joplin/renderer": "~2.6", | ||||
|     "async-mutex": "^0.1.3", | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| @use 'main.scss' as main; | ||||
| @use 'gui/ConfigScreen/style.scss' as config-screen; | ||||
| @use 'gui/EditFolderDialog/style.scss' as edit-folder-dialog; | ||||
| @use 'gui/EncryptionConfigScreen/style.scss' as encryption-config-screen; | ||||
| @use 'gui/PasswordInput/style.scss' as password-input; | ||||
| @use 'gui/ConfigScreen/style.scss' as config-screen; | ||||
| @use 'main.scss' as main; | ||||
| @@ -385,7 +385,9 @@ class ScreenHeaderComponent extends React.PureComponent { | ||||
|  | ||||
| 					for (let i = 0; i < folders.length; i++) { | ||||
| 						const f = folders[i]; | ||||
| 						pickerItems.push({ label: `${'      '.repeat(indent)} ${Folder.displayTitle(f)}`, value: f.id }); | ||||
| 						const icon = Folder.unserializeIcon(f.icon); | ||||
| 						const iconString = icon ? `${icon.emoji} ` : ''; | ||||
| 						pickerItems.push({ label: `${'      '.repeat(indent)} ${iconString + Folder.displayTitle(f)}`, value: f.id }); | ||||
| 						pickerItems = addFolderChildren(f.children, pickerItems, indent + 1); | ||||
| 					} | ||||
|  | ||||
|   | ||||
| @@ -227,6 +227,9 @@ class NotesScreenComponent extends BaseScreenComponent { | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		const icon = Folder.unserializeIcon(parent.icon); | ||||
| 		const iconString = icon ? `${icon.emoji} ` : ''; | ||||
|  | ||||
| 		let buttonFolderId = this.props.selectedFolderId != Folder.conflictFolderId() ? this.props.selectedFolderId : null; | ||||
| 		if (!buttonFolderId) buttonFolderId = this.props.activeFolderId; | ||||
|  | ||||
| @@ -236,7 +239,7 @@ class NotesScreenComponent extends BaseScreenComponent { | ||||
|  | ||||
| 		return ( | ||||
| 			<View style={rootStyle}> | ||||
| 				<ScreenHeader title={title} showBackButton={false} parentComponent={thisComp} sortButton_press={this.sortButton_press} folderPickerOptions={this.folderPickerOptions()} showSearchButton={true} showSideMenuButton={true} /> | ||||
| 				<ScreenHeader title={iconString + title} showBackButton={false} parentComponent={thisComp} sortButton_press={this.sortButton_press} folderPickerOptions={this.folderPickerOptions()} showSearchButton={true} showSideMenuButton={true} /> | ||||
| 				<NoteList style={this.styles().noteList} /> | ||||
| 				{actionButtonComp} | ||||
| 				<DialogBox | ||||
|   | ||||
| @@ -252,6 +252,9 @@ class SideMenuContentComponent extends Component { | ||||
| 			</TouchableOpacity> | ||||
| 		); | ||||
|  | ||||
| 		const folderIcon = Folder.unserializeIcon(folder.icon); | ||||
| 		const icon = folderIcon ? `${folderIcon.emoji} ` : ''; | ||||
|  | ||||
| 		return ( | ||||
| 			<View key={folder.id} style={{ flex: 1, flexDirection: 'row' }}> | ||||
| 				<TouchableOpacity | ||||
| @@ -265,7 +268,7 @@ class SideMenuContentComponent extends Component { | ||||
| 				> | ||||
| 					<View style={folderButtonStyle}> | ||||
| 						<Text numberOfLines={1} style={this.styles().folderButtonText}> | ||||
| 							{Folder.displayTitle(folder)} | ||||
| 							{icon + Folder.displayTitle(folder)} | ||||
| 						</Text> | ||||
| 					</View> | ||||
| 				</TouchableOpacity> | ||||
|   | ||||
| @@ -351,7 +351,7 @@ export default class JoplinDatabase extends Database { | ||||
| 		// must be set in the synchronizer too. | ||||
|  | ||||
| 		// Note: v16 and v17 don't do anything. They were used to debug an issue. | ||||
| 		const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]; | ||||
| 		const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]; | ||||
|  | ||||
| 		let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion); | ||||
|  | ||||
| @@ -906,6 +906,10 @@ export default class JoplinDatabase extends Database { | ||||
| 				queries.push('ALTER TABLE `resources` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""'); | ||||
| 			} | ||||
|  | ||||
| 			if (targetVersion == 41) { | ||||
| 				queries.push('ALTER TABLE `folders` ADD COLUMN icon TEXT NOT NULL DEFAULT ""'); | ||||
| 			} | ||||
|  | ||||
| 			const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] }; | ||||
|  | ||||
| 			queries.push(updateVersionQuery); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { FolderEntity } from '../services/database/types'; | ||||
| import { FolderEntity, FolderIcon } from '../services/database/types'; | ||||
| import BaseModel, { DeleteOptions } from '../BaseModel'; | ||||
| import time from '../time'; | ||||
| import { _ } from '../locale'; | ||||
| @@ -666,4 +666,13 @@ export default class Folder extends BaseItem { | ||||
| 			return folder; | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public static serializeIcon(icon: FolderIcon): string { | ||||
| 		return icon ? JSON.stringify(icon) : ''; | ||||
| 	} | ||||
|  | ||||
| 	public static unserializeIcon(icon: string): FolderIcon { | ||||
| 		return icon ? JSON.parse(icon) : null; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,14 @@ export interface BaseItemEntity { | ||||
|   created_time?: number; | ||||
| } | ||||
|  | ||||
| export interface FolderIcon { | ||||
| 	emoji: string; | ||||
| 	name: string; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -60,6 +68,7 @@ export interface FolderEntity { | ||||
|   "is_shared"?: number  | ||||
|   "share_id"?: string  | ||||
|   "master_key_id"?: string  | ||||
|   "icon"?: string  | ||||
|   "type_"?: number  | ||||
| } | ||||
| export interface ItemChangeEntity { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user