WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,7 @@ packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteEditor/WarningBanner/onRichTextDismissLinkClick.js
packages/lib/components/shared/NoteEditor/WarningBanner/onRichTextReadMoreLinkClick.js
packages/lib/components/shared/NoteEditor/WarningBanner/useConvertToMarkdownBanner.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
packages/lib/components/shared/NoteRevisionViewer/getHelpMessage.js
packages/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick.js
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,7 @@ packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteEditor/WarningBanner/onRichTextDismissLinkClick.js
packages/lib/components/shared/NoteEditor/WarningBanner/onRichTextReadMoreLinkClick.js
packages/lib/components/shared/NoteEditor/WarningBanner/useConvertToMarkdownBanner.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
packages/lib/components/shared/NoteRevisionViewer/getHelpMessage.js
packages/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick.js
Expand Down
25 changes: 9 additions & 16 deletions packages/app-desktop/gui/NoteEditor/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import useConnectToEditorPlugin from './utils/useConnectToEditorPlugin';
import getResourceBaseUrl from './utils/getResourceBaseUrl';
import useInitialCursorLocation from './utils/useInitialCursorLocation';
import NotePositionService, { EditorCursorLocations } from '@joplin/lib/services/NotePositionService';
import useConvertToMarkdownBanner from '@joplin/lib/components/shared/NoteEditor/WarningBanner/useConvertToMarkdownBanner';

const debounce = require('debounce');

Expand Down Expand Up @@ -496,16 +497,12 @@ function NoteEditorContent(props: NoteEditorProps) {
setShowRevisions(false);
}, []);

const onBannerConvertItToMarkdown = useCallback(async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
if (!props.selectedNoteIds || props.selectedNoteIds.length === 0) return;
await CommandService.instance().execute('convertNoteToMarkdown', props.selectedNoteIds[0]);
}, [props.selectedNoteIds]);

const onHideBannerConvertItToMarkdown = async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
Setting.setValue('editor.enableHtmlToMarkdownBanner', false);
};
const convertToMarkdownBannerData = useConvertToMarkdownBanner({
note: formNote,
readOnly: isReadOnly,
dismissed: !props.enableHtmlToMarkdownBanner,
});

const onBannerResourceClick = useCallback(async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
Expand Down Expand Up @@ -652,20 +649,16 @@ function NoteEditorContent(props: NoteEditorProps) {
const theme = themeStyle(props.themeId);

function renderConvertHtmlToMarkdown(): React.ReactNode {
if (!props.enableHtmlToMarkdownBanner) return null;

const note = props.notes.find(n => n.id === props.selectedNoteIds[0]);
if (!note) return null;
if (note.markup_language !== MarkupLanguage.Html) return null;
if (!convertToMarkdownBannerData.enabled) return null;

return (
<div style={styles.resourceWatchBanner}>
<p style={styles.resourceWatchBannerLine}>
{_('This note is in HTML format. Convert it to Markdown to edit it more easily.')}
&nbsp;
<a href="#" style={styles.resourceWatchBannerAction} onClick={onBannerConvertItToMarkdown}>{`${_('Convert it')}`}</a>
<button className='link-button' style={styles.resourceWatchBannerAction} onClick={convertToMarkdownBannerData.convert.onPress}>{convertToMarkdownBannerData.convert.label}</button>
{' / '}
<a href="#" style={styles.resourceWatchBannerAction} onClick={onHideBannerConvertItToMarkdown}>{_('Don\'t show this message again')}</a>
<button className='link-button' style={styles.resourceWatchBannerAction} onClick={convertToMarkdownBannerData.dismiss.onPress}>{convertToMarkdownBannerData.dismiss.label}</button>
Comment on lines -666 to +661
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to link-buttons allows using the onPress callbacks directly (without the .preventDefaults).

</p>
</div>
);
Expand Down
66 changes: 66 additions & 0 deletions packages/app-mobile/components/NoteEditor/NoteEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { Store } from 'redux';
import { AppState } from '../../utils/types';
import { MarkupLanguage } from '@joplin/renderer';
import { EditorType } from './types';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import shim from '@joplin/lib/shim';

let store: Store<AppState>;
let registeredRuntime: RegisteredRuntime;
Expand Down Expand Up @@ -56,6 +59,7 @@ describe('NoteEditor', () => {
store = createMockReduxStore();
setupGlobalStore(store);
registeredRuntime = mockCommandRuntimes(store);
shim.showMessageBox = jest.fn();
});

afterEach(() => {
Expand Down Expand Up @@ -132,4 +136,66 @@ describe('NoteEditor', () => {

wrappedNoteEditor.unmount();
});

it.each([
{ readOnly: false, label: 'should show a warning banner when opening an HTML-format note' },
{ readOnly: true, label: 'should not show a warning banner when opening a read-only HTML note' },
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This case may not have been handled before this pull request. (On desktop, the conversion banner seems to have been previously shown for read-only notes).

])('$label', async ({ readOnly }) => {
const note = await Note.save({
parent_id: '', title: 'Test', body: '<p>Test</p>', markup_language: MarkupLanguage.Html,
});
const wrappedNoteEditor = render(
<TestProviderStack store={store}>
<NoteEditor
ref={undefined}
{...defaultEditorProps}
noteId={note.id}
markupLanguage={note.markup_language}
readOnly={readOnly}
mode={EditorType.Markdown}
/>
</TestProviderStack>,
);

const warningBannerQuery = /This note is in HTML format. Convert it to Markdown to edit it more easily.*/;
const warning = screen.queryByText(warningBannerQuery);
if (readOnly) {
expect(warning).toBeNull();
} else {
expect(warning).toBeVisible();
}

wrappedNoteEditor.unmount();
});

it('should convert an HTML-format note to Markdown from the conversion banner', async () => {
const parent = await Folder.save({ title: 'Test' });
const note = await Note.save({
parent_id: parent.id, title: 'Test', body: '<p><strong>Test</strong></p>', markup_language: MarkupLanguage.Html,
});
const wrappedNoteEditor = render(
<TestProviderStack store={store}>
<NoteEditor
ref={undefined}
{...defaultEditorProps}
noteId={note.id}
markupLanguage={note.markup_language}
mode={EditorType.Markdown}
/>
</TestProviderStack>,
);

const convertButton = screen.getByRole('button', { name: 'Convert it' });
fireEvent.press(convertButton);

// Should be converted to Markdown
await waitFor(async () => {
const newNotes = await Note.previews(parent.id, { fields: ['title', 'body', 'id'] });
expect(newNotes).toMatchObject([
{ title: 'Test', body: '**Test**' },
]);
});

wrappedNoteEditor.unmount();
});
});
7 changes: 6 additions & 1 deletion packages/app-mobile/components/NoteEditor/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,12 @@ function NoteEditor(props: Props) {
/>
</View>

<WarningBanner editorType={props.mode}/>
<WarningBanner
editorType={props.mode}
noteId={props.noteId}
markupLanguage={props.markupLanguage}
readOnly={props.readOnly}
/>

<SearchPanel
editorSettings={editorSettings}
Expand Down
68 changes: 53 additions & 15 deletions packages/app-mobile/components/NoteEditor/WarningBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,79 @@ import { AppState } from '../../utils/types';
import { EditorType } from './types';
import { Banner } from 'react-native-paper';
import { useMemo } from 'react';
import useConvertToMarkdownBanner from '@joplin/lib/components/shared/NoteEditor/WarningBanner/useConvertToMarkdownBanner';
import { MarkupLanguage } from '@joplin/renderer/types';

interface Props {
editorType: EditorType;
richTextBannerDismissed: boolean;
convertToMarkdownBannerDismissed: boolean;

markupLanguage: MarkupLanguage;
noteId: string;
readOnly: boolean;
}

const useBanner = ({ editorType, readOnly, richTextBannerDismissed, convertToMarkdownBannerDismissed, noteId, markupLanguage }: Props) => {
const convertToMarkdownBanner = useConvertToMarkdownBanner({
note: { markup_language: markupLanguage, id: noteId },
dismissed: convertToMarkdownBannerDismissed,
readOnly,
});

return useMemo(() => {
if (editorType === EditorType.RichText && !richTextBannerDismissed) {
return {
label: _('This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.'),
actions: [
{
label: _('Read more'),
onPress: onRichTextReadMoreLinkClick,
},
{
label: _('Dismiss'),
accessibilityHint: _('Hides warning'),
onPress: onRichTextDismissLinkClick,
},
],
};
}

if (convertToMarkdownBanner.enabled) {
return {
label: convertToMarkdownBanner.label,
actions: [
convertToMarkdownBanner.dismiss,
convertToMarkdownBanner.convert,
],
};
}

return null;
}, [editorType, richTextBannerDismissed, convertToMarkdownBanner]);
};

const WarningBanner: React.FC<Props> = props => {
const actions = useMemo(() => [
{
label: _('Read more'),
onPress: onRichTextReadMoreLinkClick,
},
{
label: _('Dismiss'),
accessibilityHint: _('Hides warning'),
onPress: onRichTextDismissLinkClick,
},
], []);

if (props.editorType !== EditorType.RichText || props.richTextBannerDismissed) return null;
const banner = useBanner(props);

if (!banner) return null;
return (
<Banner
icon='alert-outline'
actions={actions}
actions={banner.actions}
// Avoid hiding with react-native-paper's "visible" prop to avoid potential accessibility issues
// related to how react-native-paper hides the banner.
visible={true}
>
{_('This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.')}
{banner.label}
</Banner>
);
};

export default connect((state: AppState) => {
return {
richTextBannerDismissed: state.settings.richTextBannerDismissed,
convertToMarkdownBannerDismissed: !state.settings['editor.enableHtmlToMarkdownBanner'],
selectedNoteIds: state.selectedNoteIds,
};
})(WarningBanner);
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MarkupLanguage } from '@joplin/renderer';
import { _ } from '../../../../locale';
import Setting from '../../../../models/Setting';
import CommandService from '../../../../services/CommandService';
import shim from '../../../../shim';

type NoteSlice = {
id: string;
markup_language: MarkupLanguage;
};

interface Props {
note: NoteSlice;
readOnly: boolean;
dismissed: boolean;
}

const useConvertToMarkdownBanner = ({ note, readOnly, dismissed }: Props) => {
const React = shim.react();
const noteId = note.id;
const enabled = !readOnly && !dismissed && note?.markup_language === MarkupLanguage.Html;

return React.useMemo(() => {
return {
enabled,
label: _('This note is in HTML format. Convert it to Markdown to edit it more easily.'),
dismiss: {
label: _('Don\'t show this message again'),
onPress: () => {
Setting.setValue('editor.enableHtmlToMarkdownBanner', false);
},
},
convert: {
label: _('Convert it'),
onPress: async () => {
if (!noteId) return;
await CommandService.instance().execute('convertNoteToMarkdown', noteId);
},
},
};
}, [noteId, enabled]);
};

export default useConvertToMarkdownBanner;
3 changes: 2 additions & 1 deletion packages/lib/models/settings/builtInMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -761,8 +761,9 @@ const builtInMetadata = (Setting: typeof SettingType) => {
type: SettingItemType.Bool,
public: true,
section: 'note',
appTypes: [AppType.Desktop],
appTypes: [AppType.Desktop, AppType.Mobile],
label: () => _('Enable HTML-to-Markdown conversion banner'),
description: () => _('If enabled, opening an HTML note displays a prompt to convert the note to Markdown.'),
storage: SettingStorage.File,
isGlobal: true,
},
Expand Down