From 9f252ea67383ca10e52e440387e44707a7f23790 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 6 Feb 2022 16:42:00 +0000 Subject: [PATCH] Desktop: Add support for custom notebook icons (#6110) --- .eslintignore | 3 ++ .gitignore | 3 ++ packages/app-desktop/bridge.ts | 11 +++++-- .../gui/EditFolderDialog/Dialog.tsx | 30 +++++++++++++++++-- .../gui/EditFolderDialog/IconSelector.tsx | 22 ++++++++++---- .../gui/EditFolderDialog/style.scss | 5 ++++ packages/app-desktop/gui/FolderIconBox.tsx | 17 +++++++++++ packages/app-desktop/gui/Sidebar/Sidebar.tsx | 13 +++++--- .../components/side-menu-content.js | 21 ++++++++++--- packages/lib/models/Folder.ts | 8 +++-- packages/lib/services/database/types.ts | 17 +++++++++++ packages/lib/shim-init-node.js | 17 +++++++++++ packages/lib/shim.ts | 4 +++ 13 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 packages/app-desktop/gui/FolderIconBox.tsx diff --git a/.eslintignore b/.eslintignore index 22f33b046..f6f4ba55d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -235,6 +235,9 @@ packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map packages/app-desktop/gui/ErrorBoundary.d.ts packages/app-desktop/gui/ErrorBoundary.js packages/app-desktop/gui/ErrorBoundary.js.map +packages/app-desktop/gui/FolderIconBox.d.ts +packages/app-desktop/gui/FolderIconBox.js +packages/app-desktop/gui/FolderIconBox.js.map packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.d.ts packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js.map diff --git a/.gitignore b/.gitignore index 3f8358219..e63cef53b 100644 --- a/.gitignore +++ b/.gitignore @@ -225,6 +225,9 @@ packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map packages/app-desktop/gui/ErrorBoundary.d.ts packages/app-desktop/gui/ErrorBoundary.js packages/app-desktop/gui/ErrorBoundary.js.map +packages/app-desktop/gui/FolderIconBox.d.ts +packages/app-desktop/gui/FolderIconBox.js +packages/app-desktop/gui/FolderIconBox.js.map packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.d.ts packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js.map diff --git a/packages/app-desktop/bridge.ts b/packages/app-desktop/bridge.ts index 48634e0f7..ddd972007 100644 --- a/packages/app-desktop/bridge.ts +++ b/packages/app-desktop/bridge.ts @@ -9,6 +9,13 @@ interface LastSelectedPath { directory: string; } +interface OpenDialogOptions { + properties?: string[]; + defaultPath?: string; + createDirectory?: boolean; + filters?: any[]; +} + export class Bridge { private electronWrapper_: ElectronAppWrapper; @@ -155,14 +162,14 @@ export class Bridge { return filePath; } - async showOpenDialog(options: any = null) { + async showOpenDialog(options: OpenDialogOptions = null) { const { dialog } = require('electron'); if (!options) options = {}; let fileType = 'file'; if (options.properties && options.properties.includes('openDirectory')) fileType = 'directory'; if (!('defaultPath' in options) && (this.lastSelectedPaths_ as any)[fileType]) options.defaultPath = (this.lastSelectedPaths_ as any)[fileType]; if (!('createDirectory' in options)) options.createDirectory = true; - const { filePaths } = await dialog.showOpenDialog(this.window(), options); + const { filePaths } = await dialog.showOpenDialog(this.window(), options as any); if (filePaths && filePaths.length) { (this.lastSelectedPaths_ as any)[fileType] = dirname(filePaths[0]); } diff --git a/packages/app-desktop/gui/EditFolderDialog/Dialog.tsx b/packages/app-desktop/gui/EditFolderDialog/Dialog.tsx index cb73c1921..9852be10f 100644 --- a/packages/app-desktop/gui/EditFolderDialog/Dialog.tsx +++ b/packages/app-desktop/gui/EditFolderDialog/Dialog.tsx @@ -8,9 +8,11 @@ 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 { FolderEntity, FolderIcon } from '@joplin/lib/services/database/types'; +import { FolderEntity, FolderIcon, FolderIconType } from '@joplin/lib/services/database/types'; import Button from '../Button/Button'; import bridge from '../../services/bridge'; +import shim from '@joplin/lib/shim'; +import FolderIconBox from '../FolderIconBox'; interface Props { themeId: number; @@ -93,6 +95,27 @@ export default function(props: Props) { setFolderIcon(null); }, []); + const onBrowseClick = useCallback(async () => { + const filePaths = await bridge().showOpenDialog(); + if (filePaths.length !== 1) return; + const filePath = filePaths[0]; + + try { + const dataUrl = await shim.imageToDataUrl(filePath, 256); + setFolderIcon(icon => { + return { + ...icon, + emoji: '', + name: '', + type: FolderIconType.DataUrl, + dataUrl, + }; + }); + } catch (error) { + await bridge().showErrorMessageBox(error.message); + } + }, []); + function renderForm() { return (
@@ -105,11 +128,14 @@ export default function(props: Props) {
+ { folderIcon &&
} -
diff --git a/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx b/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx index 8c2979a20..756df1677 100644 --- a/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx +++ b/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx @@ -3,7 +3,7 @@ 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'; +import { FolderIcon, FolderIconType } from '@joplin/lib/services/database/types'; import bridge from '../../services/bridge'; export interface ChangeEvent { @@ -15,6 +15,7 @@ type ChangeHandler = (event: ChangeEvent)=> void; interface Props { onChange: ChangeHandler; icon: FolderIcon | null; + title: string; } export const IconSelector = (props: Props) => { @@ -62,7 +63,7 @@ export const IconSelector = (props: Props) => { }); const onEmoji = (selection: FolderIcon) => { - props.onChange({ value: selection }); + props.onChange({ value: { ...selection, type: FolderIconType.Emoji } }); }; p.on('emoji', onEmoji); @@ -78,16 +79,25 @@ export const IconSelector = (props: Props) => { picker.togglePicker(buttonRef.current); }, [picker]); - const buttonText = props.icon ? props.icon.emoji : '...'; + // const buttonText = props.icon ? props.icon.emoji : '...'; return (