mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
This commit is contained in:
parent
ff6d700499
commit
e5c8b4bb66
@ -296,6 +296,8 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/index.js
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -275,6 +275,8 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/index.js
|
||||
|
@ -85,7 +85,7 @@ interface Props {
|
||||
startupPluginsLoaded: boolean;
|
||||
shareInvitations: ShareInvitation[];
|
||||
isSafeMode: boolean;
|
||||
enableBetaMarkdownEditor: boolean;
|
||||
enableLegacyMarkdownEditor: boolean;
|
||||
needApiAuth: boolean;
|
||||
processingShareInvitationResponse: boolean;
|
||||
isResettingLayout: boolean;
|
||||
@ -783,12 +783,12 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
},
|
||||
|
||||
editor: () => {
|
||||
let bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
||||
let bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror6' : 'TinyMCE';
|
||||
|
||||
if (this.props.isSafeMode) {
|
||||
bodyEditor = 'PlainText';
|
||||
} else if (this.props.settingEditorCodeView && this.props.enableBetaMarkdownEditor) {
|
||||
bodyEditor = 'CodeMirror6';
|
||||
} else if (this.props.settingEditorCodeView && this.props.enableLegacyMarkdownEditor) {
|
||||
bodyEditor = 'CodeMirror5';
|
||||
}
|
||||
return <NoteEditor key={key} bodyEditor={bodyEditor} />;
|
||||
},
|
||||
@ -969,7 +969,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
shareInvitations: state.shareService.shareInvitations,
|
||||
processingShareInvitationResponse: state.shareService.processingShareInvitationResponse,
|
||||
isSafeMode: state.settings.isSafeMode,
|
||||
enableBetaMarkdownEditor: state.settings['editor.beta'],
|
||||
enableLegacyMarkdownEditor: state.settings['editor.legacyMarkdown'],
|
||||
needApiAuth: state.needApiAuth,
|
||||
isResettingLayout: state.isResettingLayout,
|
||||
listRendererId: state.settings['notes.listRendererId'],
|
||||
|
@ -35,7 +35,6 @@ import NoteSearchBar from '../NoteSearchBar';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import bridge from '../../services/bridge';
|
||||
import NoteRevisionViewer from '../NoteRevisionViewer';
|
||||
import { parseShareCache } from '@joplin/lib/services/share/reducer';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
@ -51,6 +50,7 @@ import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginS
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
|
||||
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
|
||||
import WarningBanner from './WarningBanner/WarningBanner';
|
||||
const debounce = require('debounce');
|
||||
|
||||
const commands = [
|
||||
@ -434,7 +434,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
editor = <TinyMCE {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'PlainText') {
|
||||
editor = <PlainEditor {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'CodeMirror') {
|
||||
} else if (props.bodyEditor === 'CodeMirror5') {
|
||||
editor = <CodeMirror5 {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'CodeMirror6') {
|
||||
editor = <CodeMirror6 {...editorProps}/>;
|
||||
@ -442,22 +442,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
throw new Error(`Invalid editor: ${props.bodyEditor}`);
|
||||
}
|
||||
|
||||
const onRichTextReadMoreLinkClick = useCallback(() => {
|
||||
void bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
|
||||
}, []);
|
||||
|
||||
const onRichTextDismissLinkClick = useCallback(() => {
|
||||
Setting.setValue('richTextBannerDismissed', true);
|
||||
}, []);
|
||||
|
||||
const wysiwygBanner = props.bodyEditor !== 'TinyMCE' || props.richTextBannerDismissed ? null : (
|
||||
<div style={styles.warningBanner}>
|
||||
{_('This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.')}
|
||||
<a onClick={onRichTextReadMoreLinkClick} style={styles.warningBannerLink} href="#">[ {_('Read more about it')} ]</a>
|
||||
<a onClick={onRichTextDismissLinkClick} style={styles.warningBannerLink} href="#">[ {_('Dismiss')} ]</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
const noteRevisionViewer_onBack = useCallback(() => {
|
||||
setShowRevisions(false);
|
||||
}, []);
|
||||
@ -612,7 +596,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
{renderTagButton()}
|
||||
{renderTagBar()}
|
||||
</div>
|
||||
{wysiwygBanner}
|
||||
<WarningBanner bodyEditor={props.bodyEditor}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -636,7 +620,6 @@ const mapStateToProps = (state: AppState) => {
|
||||
syncStarted: state.syncStarted,
|
||||
decryptionStarted: state.decryptionWorker?.state !== 'idle',
|
||||
themeId: state.settings.theme,
|
||||
richTextBannerDismissed: state.settings.richTextBannerDismissed,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedNoteTags: state.selectedNoteTags,
|
||||
|
@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
acceptMessage: string;
|
||||
onAccept: ()=> void;
|
||||
onDismiss: ()=> void;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const BannerContent: React.FC<Props> = props => {
|
||||
if (!props.visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div className='warning-banner'>
|
||||
{props.children}
|
||||
<a onClick={props.onAccept} className='warning-banner-link' href="#">[ {props.acceptMessage} ]</a>
|
||||
<a onClick={props.onDismiss} className='warning-banner-link' href="#">[ {_('Dismiss')} ]</a>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default BannerContent;
|
@ -0,0 +1,104 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import BannerContent from './BannerContent';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import bridge from '../../../services/bridge';
|
||||
import { useMemo } from 'react';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
|
||||
interface Props {
|
||||
bodyEditor: string;
|
||||
richTextBannerDismissed: boolean;
|
||||
pluginCompatibilityBannerDismissedFor: string[];
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
const onRichTextDismissLinkClick = () => {
|
||||
Setting.setValue('richTextBannerDismissed', true);
|
||||
};
|
||||
|
||||
const onRichTextReadMoreLinkClick = () => {
|
||||
void bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
|
||||
};
|
||||
|
||||
const onSwitchToLegacyEditor = () => {
|
||||
Setting.setValue('editor.legacyMarkdown', true);
|
||||
};
|
||||
|
||||
const onDismissLegacyEditorPrompt = () => {
|
||||
Setting.setValue('editor.pluginCompatibilityBannerDismissedFor', [...PluginService.instance().pluginIds]);
|
||||
};
|
||||
|
||||
const incompatiblePluginIds = [
|
||||
// cSpell:disable
|
||||
'com.septemberhx.Joplin.Enhancement',
|
||||
'ylc395.noteLinkSystem',
|
||||
'outline',
|
||||
'joplin.plugin.cmoptions',
|
||||
'plugin.calebjohn.MathMode',
|
||||
'com.ckant.joplin-plugin-better-code-blocks',
|
||||
// cSpell:enable
|
||||
];
|
||||
|
||||
const WarningBanner: React.FC<Props> = props => {
|
||||
const wysiwygBanner = (
|
||||
<BannerContent
|
||||
acceptMessage={_('Read more about it')}
|
||||
onAccept={onRichTextReadMoreLinkClick}
|
||||
onDismiss={onRichTextDismissLinkClick}
|
||||
visible={props.bodyEditor === 'TinyMCE' && !props.richTextBannerDismissed}
|
||||
>
|
||||
{_('This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.')}
|
||||
</BannerContent>
|
||||
);
|
||||
|
||||
const incompatiblePluginNames = useMemo(() => {
|
||||
if (props.bodyEditor !== 'CodeMirror6') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const runningPluginIds = Object.keys(props.plugins);
|
||||
|
||||
return runningPluginIds.map((id): string|string[] => {
|
||||
if (props.pluginCompatibilityBannerDismissedFor?.includes(id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (incompatiblePluginIds.includes(id)) {
|
||||
return PluginService.instance().pluginById(id).manifest.name;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}).flat();
|
||||
}, [props.bodyEditor, props.plugins, props.pluginCompatibilityBannerDismissedFor]);
|
||||
|
||||
const markdownPluginBanner = (
|
||||
<BannerContent
|
||||
acceptMessage={_('Switch to the legacy editor')}
|
||||
onAccept={onSwitchToLegacyEditor}
|
||||
onDismiss={onDismissLegacyEditorPrompt}
|
||||
visible={incompatiblePluginNames.length > 0}
|
||||
>
|
||||
{_('The following plugins may not support the current markdown editor:')}
|
||||
<ul>
|
||||
{incompatiblePluginNames.map((name, index) => <li key={index}>{name}</li>)}
|
||||
</ul>
|
||||
</BannerContent>
|
||||
);
|
||||
|
||||
return <>
|
||||
{wysiwygBanner}
|
||||
{markdownPluginBanner}
|
||||
</>;
|
||||
};
|
||||
|
||||
export default connect((state: AppState) => {
|
||||
return {
|
||||
richTextBannerDismissed: state.settings.richTextBannerDismissed,
|
||||
pluginCompatibilityBannerDismissedFor: state.settings['editor.pluginCompatibilityBannerDismissedFor'],
|
||||
plugins: state.pluginService.plugins,
|
||||
};
|
||||
})(WarningBanner);
|
3
packages/app-desktop/gui/NoteEditor/style.scss
Normal file
3
packages/app-desktop/gui/NoteEditor/style.scss
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
@use "./styles/warning-banner.scss";
|
||||
@use "./styles/warning-banner-link.scss";
|
@ -0,0 +1,6 @@
|
||||
.warning-banner-link {
|
||||
color: var(--joplin-color);
|
||||
font-family: var(--joplin-font-family);
|
||||
font-size: var(--joplin-font-siize);
|
||||
font-weight: bold;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
|
||||
.warning-banner {
|
||||
background: var(--joplin-warning-background-color);
|
||||
font-family: var(--joplin-font-family);
|
||||
padding: 10px;
|
||||
font-size: var(--joplin-font-size);
|
||||
line-height: 1.6em;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
max-height: 25vh;
|
||||
overflow-y: auto;
|
||||
}
|
@ -50,7 +50,6 @@ export interface NoteEditorProps {
|
||||
plugins: PluginStates;
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
richTextBannerDismissed: boolean;
|
||||
contentMaxWidth: number;
|
||||
isSafeMode: boolean;
|
||||
useCustomPdfViewer: boolean;
|
||||
|
@ -11,7 +11,7 @@ export default class NoteEditorPage {
|
||||
|
||||
public constructor(private readonly page: Page) {
|
||||
this.containerLocator = page.locator('.rli-editor');
|
||||
this.codeMirrorEditor = this.containerLocator.locator('.codeMirrorEditor');
|
||||
this.codeMirrorEditor = this.containerLocator.locator('.cm-editor');
|
||||
this.richTextEditor = this.containerLocator.locator('iframe[title="Rich Text Area"]');
|
||||
this.noteTitleInput = this.containerLocator.locator('.title-input');
|
||||
this.attachFileButton = this.containerLocator.locator('[title^="Attach file"]');
|
||||
|
@ -11,4 +11,5 @@
|
||||
@use 'gui/TrashNotification/style.scss' as trash-notification;
|
||||
@use 'gui/Sidebar/style.scss' as sidebar-styles;
|
||||
@use 'gui/styles/index.scss';
|
||||
@use 'main.scss' as main;
|
||||
@use 'gui/NoteEditor/style.scss';
|
||||
@use 'main.scss' as main;
|
||||
|
@ -423,6 +423,13 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
notesParent: { value: '', type: SettingItemType.String, public: false },
|
||||
|
||||
richTextBannerDismissed: { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, public: false },
|
||||
'editor.pluginCompatibilityBannerDismissedFor': {
|
||||
value: [] as string[], // List of plugin IDs
|
||||
type: SettingItemType.Array,
|
||||
storage: SettingStorage.File,
|
||||
isGlobal: true,
|
||||
public: false,
|
||||
},
|
||||
|
||||
firstStart: { value: true, type: SettingItemType.Bool, public: false },
|
||||
locale: {
|
||||
@ -1195,8 +1202,8 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
type: SettingItemType.Bool,
|
||||
public: true,
|
||||
appTypes: [AppType.Desktop],
|
||||
label: () => 'Enable spell checking in Markdown editor? (WARNING BETA feature)',
|
||||
description: () => 'Spell checker in the Markdown editor was previously unstable (cursor location was not stable, sometimes edits would not be saved or reflected in the viewer, etc.) however it appears to be more reliable now. If you notice any issue, please report it on GitHub or the Joplin Forum (Help -> Joplin Forum)',
|
||||
label: () => _('Enable spell checking in Markdown editor?'),
|
||||
description: () => _('Checks spelling in most non-code regions of the Markdown editor.'),
|
||||
storage: SettingStorage.File,
|
||||
isGlobal: true,
|
||||
},
|
||||
@ -1225,10 +1232,23 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
value: false,
|
||||
type: SettingItemType.Bool,
|
||||
section: 'general',
|
||||
public: true,
|
||||
public: false,
|
||||
appTypes: [AppType.Desktop],
|
||||
label: () => 'Opt-in to the editor beta',
|
||||
description: () => 'This beta adds improved accessibility and plugin API compatibility with the mobile editor. If you find bugs, please report them in the Discourse forum.',
|
||||
description: () => 'Currently unused',
|
||||
storage: SettingStorage.File,
|
||||
isGlobal: true,
|
||||
},
|
||||
|
||||
'editor.legacyMarkdown': {
|
||||
advanced: true,
|
||||
value: false,
|
||||
type: SettingItemType.Bool,
|
||||
section: 'general',
|
||||
public: true,
|
||||
appTypes: [AppType.Desktop],
|
||||
label: () => _('Use the legacy Markdown editor'),
|
||||
description: () => _('Enable the the legacy Markdown editor. Some plugins require this editor to function. However, it has accessibility issues and other plugins will not work.'),
|
||||
storage: SettingStorage.File,
|
||||
isGlobal: true,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user