Skip to content

Commit

Permalink
Desktop: Resolves #9450: Move the beta editor out of beta
Browse files Browse the repository at this point in the history
  • Loading branch information
personalizedrefrigerator committed Jul 23, 2024
1 parent afcd2d2 commit 6238498
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions packages/app-desktop/gui/MainScreen/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ interface Props {
startupPluginsLoaded: boolean;
shareInvitations: ShareInvitation[];
isSafeMode: boolean;
enableBetaMarkdownEditor: boolean;
enableLegacyMarkdownEditor: boolean;
needApiAuth: boolean;
processingShareInvitationResponse: boolean;
isResettingLayout: boolean;
Expand Down Expand Up @@ -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} />;
},
Expand Down Expand Up @@ -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.markdown-legacy'],
needApiAuth: state.needApiAuth,
isResettingLayout: state.isResettingLayout,
listRendererId: state.settings['notes.listRendererId'],
Expand Down
23 changes: 3 additions & 20 deletions packages/app-desktop/gui/NoteEditor/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 = [
Expand Down Expand Up @@ -434,30 +434,14 @@ 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}/>;
} else {
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);
}, []);
Expand Down Expand Up @@ -612,7 +596,7 @@ function NoteEditor(props: NoteEditorProps) {
{renderTagButton()}
{renderTagBar()}
</div>
{wysiwygBanner}
<WarningBanner bodyEditor={props.bodyEditor}/>
</div>
</div>
);
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import { _ } from '@joplin/lib/locale';

interface Props {
children: React.ReactElement|string;
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
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.markdown-legacy', true);
};

const incompatiblePluginIds = [
// cSpell:disable
'com.septemberhx.Joplin.Enhancement',
'ylc395.noteLinkSystem',
'outline',
'joplin.plugin.cmoptions',
'plugin.calebjohn.MathMode',
// cSpell:enable
];

const onDismissLegacyEditorPrompt = () => {
Setting.setValue('editor.pluginCompatibilityBannerDismissedFor', [...PluginService.instance().pluginIds]);
};

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 showMarkdownPluginBanner = useMemo(() => {
if (props.bodyEditor === 'CodeMirror5') {
return false;
}

const runningPluginIds = Object.keys(props.plugins);

return runningPluginIds.some(id => {
if (props.pluginCompatibilityBannerDismissedFor.includes(id)) {
return false;
}

return incompatiblePluginIds.includes(id);
});
}, [props.bodyEditor, props.plugins, props.pluginCompatibilityBannerDismissedFor]);

const markdownPluginBanner = (
<BannerContent
acceptMessage={_('Switch to the legacy editor')}
onAccept={onSwitchToLegacyEditor}
onDismiss={onDismissLegacyEditorPrompt}
visible={showMarkdownPluginBanner}
>
{_('One or more installed plugins does not support the current markdown editor.')}
</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 changes: 3 additions & 0 deletions packages/app-desktop/gui/NoteEditor/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

@use "./styles/warning-banner.scss";
@use "./styles/warning-banner-link.scss";
Original file line number Diff line number Diff line change
@@ -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;
}
10 changes: 10 additions & 0 deletions packages/app-desktop/gui/NoteEditor/styles/warning-banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

.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;
}
1 change: 0 additions & 1 deletion packages/app-desktop/gui/NoteEditor/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export interface NoteEditorProps {
plugins: PluginStates;
toolbarButtonInfos: ToolbarButtonInfo[];
setTagsToolbarButtonInfo: ToolbarButtonInfo;
richTextBannerDismissed: boolean;
contentMaxWidth: number;
isSafeMode: boolean;
useCustomPdfViewer: boolean;
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
@use 'gui/NoteListHeader/style.scss' as note-list-header;
@use 'gui/TrashNotification/style.scss' as trash-notification;
@use 'gui/Sidebar/style.scss' as sidebar-styles;
@use 'gui/NoteEditor/style.scss';
@use 'main.scss' as main;
28 changes: 24 additions & 4 deletions packages/lib/models/settings/builtInMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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.markdown-legacy': {
advanced: true,
value: false,
type: SettingItemType.Bool,
section: 'general',
public: true,
appTypes: [AppType.Desktop],
label: () => 'Use the legacy Markdown editor',
description: () => 'Switch to the legacy Markdown editor. Some plugins require this editor to function.',
storage: SettingStorage.File,
isGlobal: true,
},
Expand Down

0 comments on commit 6238498

Please sign in to comment.