1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Resolves #9450: Make the beta markdown editor the default (#10796)

This commit is contained in:
Henry Heino 2024-08-02 06:47:26 -07:00 committed by GitHub
parent ff6d700499
commit e5c8b4bb66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 189 additions and 32 deletions

View File

@ -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
View File

@ -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

View File

@ -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'],

View File

@ -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.')}
&nbsp;&nbsp;<a onClick={onRichTextReadMoreLinkClick} style={styles.warningBannerLink} href="#">[ {_('Read more about it')} ]</a>
&nbsp;&nbsp;<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,

View File

@ -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}
&nbsp;&nbsp;<a onClick={props.onAccept} className='warning-banner-link' href="#">[ {props.acceptMessage} ]</a>
&nbsp;&nbsp;<a onClick={props.onDismiss} className='warning-banner-link' href="#">[ {_('Dismiss')} ]</a>
</div>;
};
export default BannerContent;

View File

@ -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);

View File

@ -0,0 +1,3 @@
@use "./styles/warning-banner.scss";
@use "./styles/warning-banner-link.scss";

View File

@ -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;
}

View File

@ -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;
}

View File

@ -50,7 +50,6 @@ export interface NoteEditorProps {
plugins: PluginStates;
toolbarButtonInfos: ToolbarButtonInfo[];
setTagsToolbarButtonInfo: ToolbarButtonInfo;
richTextBannerDismissed: boolean;
contentMaxWidth: number;
isSafeMode: boolean;
useCustomPdfViewer: boolean;

View File

@ -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"]');

View 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;

View File

@ -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,
},