mirror of
https://github.com/laurent22/joplin.git
synced 2025-03-23 21:09:30 +02:00
Desktop, Mobile: Added support for notebook icons
This commit is contained in:
parent
79d97f2ba7
commit
e97bb78ce4
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user