mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-06 06:09:43 -06:00
Desktop: Support converting multiple notes from HTML to Markdown at once (#13802)
This commit is contained in:
@@ -165,8 +165,6 @@ packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/bridge.js
|
||||
packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/convertNoteToMarkdown.test.js
|
||||
packages/app-desktop/commands/convertNoteToMarkdown.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
packages/app-desktop/commands/copyToClipboard.js
|
||||
packages/app-desktop/commands/editProfileConfig.js
|
||||
@@ -207,7 +205,6 @@ packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
|
||||
packages/app-desktop/gui/ConversionNotification/ConversionNotification.js
|
||||
packages/app-desktop/gui/Dialog.js
|
||||
packages/app-desktop/gui/DialogButtonRow.js
|
||||
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
|
||||
@@ -1245,6 +1242,8 @@ packages/lib/callbackUrlUtils.js
|
||||
packages/lib/clipperUtils.js
|
||||
packages/lib/commands/convertHtmlToMarkdown.test.js
|
||||
packages/lib/commands/convertHtmlToMarkdown.js
|
||||
packages/lib/commands/convertNoteToMarkdown.test.js
|
||||
packages/lib/commands/convertNoteToMarkdown.js
|
||||
packages/lib/commands/deleteNote.js
|
||||
packages/lib/commands/historyBackward.js
|
||||
packages/lib/commands/historyForward.js
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -137,8 +137,6 @@ packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/bridge.js
|
||||
packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/convertNoteToMarkdown.test.js
|
||||
packages/app-desktop/commands/convertNoteToMarkdown.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
packages/app-desktop/commands/copyToClipboard.js
|
||||
packages/app-desktop/commands/editProfileConfig.js
|
||||
@@ -179,7 +177,6 @@ packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
|
||||
packages/app-desktop/gui/ConversionNotification/ConversionNotification.js
|
||||
packages/app-desktop/gui/Dialog.js
|
||||
packages/app-desktop/gui/DialogButtonRow.js
|
||||
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
|
||||
@@ -1217,6 +1214,8 @@ packages/lib/callbackUrlUtils.js
|
||||
packages/lib/clipperUtils.js
|
||||
packages/lib/commands/convertHtmlToMarkdown.test.js
|
||||
packages/lib/commands/convertHtmlToMarkdown.js
|
||||
packages/lib/commands/convertNoteToMarkdown.test.js
|
||||
packages/lib/commands/convertNoteToMarkdown.js
|
||||
packages/lib/commands/deleteNote.js
|
||||
packages/lib/commands/historyBackward.js
|
||||
packages/lib/commands/historyForward.js
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { runtime as convertHtmlToMarkdown } from '@joplin/lib/commands/convertHtmlToMarkdown';
|
||||
import bridge from '../services/bridge';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'convertNoteToMarkdown',
|
||||
label: () => _('Convert note to Markdown'),
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
const note = await Note.load(noteId);
|
||||
|
||||
if (!note) return;
|
||||
|
||||
try {
|
||||
const markdownBody = await convertHtmlToMarkdown().execute(context, note.body);
|
||||
|
||||
const newNote = await Note.duplicate(note.id);
|
||||
|
||||
newNote.body = markdownBody;
|
||||
newNote.markup_language = MarkupLanguage.Markdown;
|
||||
|
||||
await Note.save(newNote);
|
||||
|
||||
await Note.delete(note.id, { toTrash: true });
|
||||
|
||||
context.dispatch({
|
||||
type: 'NOTE_HTML_TO_MARKDOWN_DONE',
|
||||
value: note.id,
|
||||
});
|
||||
|
||||
context.dispatch({
|
||||
type: 'NOTE_SELECT',
|
||||
id: newNote.id,
|
||||
});
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(_('Could not convert note to Markdown: %s', error.message));
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected && noteIsHtml && !noteIsReadOnly',
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
// AUTO-GENERATED using `gulp buildScriptIndexes`
|
||||
import * as convertNoteToMarkdown from './convertNoteToMarkdown';
|
||||
import * as copyDevCommand from './copyDevCommand';
|
||||
import * as copyToClipboard from './copyToClipboard';
|
||||
import * as editProfileConfig from './editProfileConfig';
|
||||
@@ -25,7 +24,6 @@ import * as toggleSafeMode from './toggleSafeMode';
|
||||
import * as toggleTabMovesFocus from './toggleTabMovesFocus';
|
||||
|
||||
const index: any[] = [
|
||||
convertNoteToMarkdown,
|
||||
copyDevCommand,
|
||||
copyToClipboard,
|
||||
editProfileConfig,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { Dispatch } from 'redux';
|
||||
import { PopupNotificationContext } from '../PopupNotification/PopupNotificationProvider';
|
||||
import { NotificationType } from '../PopupNotification/types';
|
||||
|
||||
interface Props {
|
||||
noteId: string;
|
||||
dispatch: Dispatch;
|
||||
}
|
||||
|
||||
export default (props: Props) => {
|
||||
const popupManager = useContext(PopupNotificationContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.noteId || props.noteId === '') return;
|
||||
|
||||
props.dispatch({ type: 'NOTE_HTML_TO_MARKDOWN_DONE', value: '' });
|
||||
|
||||
const notification = popupManager.createPopup(() => (
|
||||
<div>{_('The note has been converted to Markdown and the original note has been moved to the trash')}</div>
|
||||
), { type: NotificationType.Success });
|
||||
notification.scheduleDismiss();
|
||||
}, [props.dispatch, popupManager, props.noteId]);
|
||||
|
||||
return <div style={{ display: 'none' }}/>;
|
||||
};
|
||||
@@ -38,14 +38,12 @@ import restart from '../services/restart';
|
||||
import { connect } from 'react-redux';
|
||||
import { NoteListColumns } from '@joplin/lib/services/plugins/api/noteListType';
|
||||
import validateColumns from './NoteListHeader/utils/validateColumns';
|
||||
import ConversionNotification from './ConversionNotification/ConversionNotification';
|
||||
import TrashNotification from './TrashNotification/TrashNotification';
|
||||
import UpdateNotification from './UpdateNotification/UpdateNotification';
|
||||
import NoteEditor from './NoteEditor/NoteEditor';
|
||||
import PluginNotification from './PluginNotification/PluginNotification';
|
||||
import { Toast } from '@joplin/lib/services/plugins/api/types';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
@@ -86,7 +84,6 @@ interface Props {
|
||||
showInvalidJoplinCloudCredential: boolean;
|
||||
toast: Toast;
|
||||
shouldSwitchToAppleSiliconVersion: boolean;
|
||||
noteHtmlToMarkdownDone: string;
|
||||
}
|
||||
|
||||
interface ShareFolderDialogOptions {
|
||||
@@ -800,10 +797,6 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<ConversionNotification
|
||||
noteId={this.props.noteHtmlToMarkdownDone}
|
||||
dispatch={this.props.dispatch as Dispatch}
|
||||
/>
|
||||
<TrashNotification
|
||||
lastDeletion={this.props.lastDeletion}
|
||||
lastDeletionNotificationTime={this.props.lastDeletionNotificationTime}
|
||||
@@ -860,7 +853,6 @@ const mapStateToProps = (state: AppState) => {
|
||||
showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate,
|
||||
toast: state.toast,
|
||||
shouldSwitchToAppleSiliconVersion: shim.isAppleSilicon() && process.arch !== 'arm64',
|
||||
noteHtmlToMarkdownDone: state.noteHtmlToMarkdownDone,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { createContext, useMemo, useRef, useState } from 'react';
|
||||
import { createContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { NotificationType, PopupHandle, PopupControl as PopupManager } from './types';
|
||||
import { Hour, msleep } from '@joplin/utils/time';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
export const PopupNotificationContext = createContext<PopupManager|null>(null);
|
||||
export const VisibleNotificationsContext = createContext<PopupSpec[]>([]);
|
||||
@@ -112,6 +113,18 @@ const PopupNotificationProvider: React.FC<Props> = props => {
|
||||
return manager;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const defaultShowToast = shim.showToast;
|
||||
shim.showToast = async (message: string, options) => {
|
||||
const popup = popupManager.createPopup(() => message, { type: options?.type ?? NotificationType.Info });
|
||||
popup.scheduleDismiss();
|
||||
};
|
||||
|
||||
return () => {
|
||||
shim.showToast = defaultShowToast;
|
||||
};
|
||||
}, [popupManager]);
|
||||
|
||||
return <PopupNotificationContext.Provider value={popupManager}>
|
||||
<VisibleNotificationsContext.Provider value={popupSpecs}>
|
||||
{props.children}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ToastType } from '@joplin/lib/shim';
|
||||
import * as React from 'react';
|
||||
|
||||
export type PopupHandle = {
|
||||
@@ -5,14 +6,13 @@ export type PopupHandle = {
|
||||
scheduleDismiss(delay?: number): void;
|
||||
};
|
||||
|
||||
export enum NotificationType {
|
||||
Info = 'info',
|
||||
Success = 'success',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
export type NotificationContentCallback = ()=> React.ReactNode;
|
||||
|
||||
// NotificationType is an alias for ToastType
|
||||
export type NotificationType = ToastType;
|
||||
// eslint-disable-next-line no-redeclare -- export const is necessary for creating an alias, this is not a redeclaration.
|
||||
export const NotificationType = ToastType;
|
||||
|
||||
export interface PopupOptions {
|
||||
type?: NotificationType;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import Setting from '@joplin/lib/models/Setting';
|
||||
const { clipboard } = require('electron');
|
||||
import { Dispatch } from 'redux';
|
||||
import { NoteEntity } from '@joplin/lib/services/database/types';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
@@ -143,6 +144,16 @@ export default class NoteListUtils {
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
|
||||
const includesHtmlNotes = notes.some(n => n.markup_language === MarkupLanguage.Html);
|
||||
if (includesHtmlNotes) {
|
||||
menu.append(
|
||||
new MenuItem(
|
||||
menuUtils.commandToStatefulMenuItem('convertNoteToMarkdown', noteIds),
|
||||
),
|
||||
);
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
}
|
||||
|
||||
if ([9, 10, 11].includes(Setting.value('sync.target'))) {
|
||||
menu.append(
|
||||
new MenuItem(
|
||||
@@ -204,7 +215,6 @@ export default class NoteListUtils {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const pluginViewInfos = pluginUtils.viewInfosByType(props.plugins, 'menuItem');
|
||||
|
||||
for (const info of pluginViewInfos) {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import * as convertHtmlToMarkdown from './convertNoteToMarkdown';
|
||||
import { AppState, createAppDefaultState } from '../app.reducer';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import { defaultState, State } from '../reducer';
|
||||
import Note from '../models/Note';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import { NoteEntity } from '@joplin/lib/services/database/types';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '../testing/test-utils';
|
||||
import Folder from '../models/Folder';
|
||||
import { NoteEntity } from '../services/database/types';
|
||||
import shim from '../shim';
|
||||
|
||||
describe('convertNoteToMarkdown', () => {
|
||||
let state: AppState = undefined;
|
||||
let state: State = undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
state = createAppDefaultState({});
|
||||
state = defaultState;
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
shim.showToast = jest.fn();
|
||||
});
|
||||
|
||||
it('should set the original note to be trashed', async () => {
|
||||
@@ -29,13 +31,6 @@ describe('convertNoteToMarkdown', () => {
|
||||
});
|
||||
|
||||
it('should recreate a new note that is a clone of the original', async () => {
|
||||
let noteConvertedToMarkdownId = '';
|
||||
const dispatchFn = jest.fn()
|
||||
.mockImplementationOnce(() => {})
|
||||
.mockImplementationOnce(action => {
|
||||
noteConvertedToMarkdownId = action.id;
|
||||
});
|
||||
|
||||
const folder = await Folder.save({ title: 'test_folder' });
|
||||
const htmlNoteProperties = {
|
||||
title: 'test',
|
||||
@@ -49,10 +44,11 @@ describe('convertNoteToMarkdown', () => {
|
||||
const htmlNote = await Note.save(htmlNoteProperties);
|
||||
state.selectedNoteIds = [htmlNote.id];
|
||||
|
||||
await convertHtmlToMarkdown.runtime().execute({ state, dispatch: dispatchFn });
|
||||
await convertHtmlToMarkdown.runtime().execute({ state, dispatch: jest.fn() });
|
||||
|
||||
expect(dispatchFn).toHaveBeenCalledTimes(2);
|
||||
expect(noteConvertedToMarkdownId).not.toBe('');
|
||||
const notes = await Note.previews(folder.id);
|
||||
expect(notes).toHaveLength(1);
|
||||
const noteConvertedToMarkdownId = notes[0].id;
|
||||
|
||||
const markdownNote = await Note.load(noteConvertedToMarkdownId);
|
||||
|
||||
@@ -63,15 +59,6 @@ describe('convertNoteToMarkdown', () => {
|
||||
});
|
||||
|
||||
it('should generate action to trigger notification', async () => {
|
||||
let originalHtmlNoteId = '';
|
||||
let actionType = '';
|
||||
const dispatchFn = jest.fn()
|
||||
.mockImplementationOnce(action => {
|
||||
originalHtmlNoteId = action.value;
|
||||
actionType = action.type;
|
||||
})
|
||||
.mockImplementationOnce(() => {});
|
||||
|
||||
const folder = await Folder.save({ title: 'test_folder' });
|
||||
const htmlNoteProperties = {
|
||||
title: 'test',
|
||||
@@ -85,12 +72,9 @@ describe('convertNoteToMarkdown', () => {
|
||||
const htmlNote = await Note.save(htmlNoteProperties);
|
||||
state.selectedNoteIds = [htmlNote.id];
|
||||
|
||||
await convertHtmlToMarkdown.runtime().execute({ state, dispatch: dispatchFn });
|
||||
await convertHtmlToMarkdown.runtime().execute({ state, dispatch: jest.fn() });
|
||||
|
||||
expect(dispatchFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(originalHtmlNoteId).toBe(htmlNote.id);
|
||||
expect(actionType).toBe('NOTE_HTML_TO_MARKDOWN_DONE');
|
||||
expect(shim.showToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
75
packages/lib/commands/convertNoteToMarkdown.ts
Normal file
75
packages/lib/commands/convertNoteToMarkdown.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { _, _n } from '../locale';
|
||||
import Note from '../models/Note';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '../services/CommandService';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { runtime as convertHtmlToMarkdown } from './convertHtmlToMarkdown';
|
||||
import shim, { ToastType } from '../shim';
|
||||
import { NoteEntity } from '../services/database/types';
|
||||
import { itemIsReadOnly } from '../models/utils/readOnly';
|
||||
import { ModelType } from '../BaseModel';
|
||||
import ItemChange from '../models/ItemChange';
|
||||
import Setting from '../models/Setting';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
|
||||
const logger = Logger.create('convertNoteToMarkdown');
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'convertNoteToMarkdown',
|
||||
label: () => _('Convert to Markdown'),
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, noteIds: string|string[] = []) => {
|
||||
if (typeof noteIds === 'string') {
|
||||
noteIds = [noteIds];
|
||||
}
|
||||
if (noteIds.length === 0) {
|
||||
noteIds = context.state.selectedNoteIds;
|
||||
}
|
||||
|
||||
const notes: NoteEntity[] = await Note.loadItemsByIdsOrFail(noteIds);
|
||||
|
||||
try {
|
||||
let isFirst = true;
|
||||
let processedCount = 0;
|
||||
for (const note of notes) {
|
||||
if (note.markup_language === MarkupLanguage.Markdown) {
|
||||
logger.warn('Skipping item: Already Markdown.');
|
||||
continue;
|
||||
}
|
||||
if (await itemIsReadOnly(Note, ModelType.Note, ItemChange.SOURCE_UNSPECIFIED, note.id, Setting.value('sync.userId'), context.state.shareService)) {
|
||||
throw new Error(_('Cannot convert read-only item: "%s"', note.title));
|
||||
}
|
||||
|
||||
const markdownBody = await convertHtmlToMarkdown().execute(context, note.body);
|
||||
const newNote = await Note.duplicate(note.id);
|
||||
|
||||
newNote.body = markdownBody;
|
||||
newNote.markup_language = MarkupLanguage.Markdown;
|
||||
|
||||
await Note.save(newNote);
|
||||
await Note.delete(note.id, { toTrash: true });
|
||||
processedCount ++;
|
||||
|
||||
if (isFirst) {
|
||||
context.dispatch({
|
||||
type: 'NOTE_SELECT',
|
||||
id: newNote.id,
|
||||
});
|
||||
isFirst = false;
|
||||
}
|
||||
}
|
||||
|
||||
void shim.showToast(_n(
|
||||
'The note has been converted to Markdown and the original note has been moved to the trash',
|
||||
'The notes have been converted to Markdown and the original notes have been moved to the trash',
|
||||
processedCount,
|
||||
), { type: ToastType.Success });
|
||||
} catch (error) {
|
||||
await shim.showErrorDialog(_('Could not convert notes to Markdown: %s', error.message));
|
||||
}
|
||||
},
|
||||
enabledCondition: 'selectionIncludesHtmlNotes && (multipleNotesSelected || !noteIsReadOnly)',
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
// AUTO-GENERATED using `gulp buildScriptIndexes`
|
||||
import * as convertHtmlToMarkdown from './convertHtmlToMarkdown';
|
||||
import * as convertNoteToMarkdown from './convertNoteToMarkdown';
|
||||
import * as deleteNote from './deleteNote';
|
||||
import * as historyBackward from './historyBackward';
|
||||
import * as historyForward from './historyForward';
|
||||
@@ -14,6 +15,7 @@ import * as toggleEditorPlugin from './toggleEditorPlugin';
|
||||
|
||||
const index: any[] = [
|
||||
convertHtmlToMarkdown,
|
||||
convertNoteToMarkdown,
|
||||
deleteNote,
|
||||
historyBackward,
|
||||
historyForward,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { itemIsReadOnlySync, ItemSlice } from '../../models/utils/readOnly';
|
||||
import ItemChange from '../../models/ItemChange';
|
||||
import { getTrashFolderId } from '../trash';
|
||||
import getActivePluginEditorView from '../plugins/utils/getActivePluginEditorView';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
|
||||
export interface WhenClauseContextOptions {
|
||||
commandFolderId?: string;
|
||||
@@ -18,6 +19,7 @@ export interface WhenClauseContextOptions {
|
||||
|
||||
export interface WhenClauseContext {
|
||||
allSelectedNotesAreDeleted: boolean;
|
||||
selectionIncludesHtmlNotes: boolean;
|
||||
foldersAreDeleted: boolean;
|
||||
foldersIncludeReadOnly: boolean;
|
||||
folderIsDeleted: boolean;
|
||||
@@ -88,6 +90,7 @@ export default function stateToWhenClauseContext(state: State, options: WhenClau
|
||||
|
||||
// Selected notes properties
|
||||
allSelectedNotesAreDeleted: !selectedNotes.find(n => !n.deleted_time),
|
||||
selectionIncludesHtmlNotes: selectedNotes.some(n => n.markup_language === MarkupLanguage.Html),
|
||||
|
||||
// Note history
|
||||
historyhasBackwardNotes: windowState.backwardHistoryNotes && windowState.backwardHistoryNotes.length > 0,
|
||||
|
||||
@@ -54,6 +54,16 @@ export interface ShowMessageBoxOptions {
|
||||
cancelId?: number;
|
||||
}
|
||||
|
||||
export enum ToastType {
|
||||
Info = 'info',
|
||||
Error = 'error',
|
||||
Success = 'success',
|
||||
}
|
||||
|
||||
export interface ShowToastOptions {
|
||||
type: ToastType;
|
||||
}
|
||||
|
||||
export enum MobilePlatform {
|
||||
None = '',
|
||||
Android = 'android',
|
||||
@@ -458,6 +468,11 @@ const shim = {
|
||||
return await shim.showMessageBox(message, { type: MessageBoxType.Confirm }) === 0;
|
||||
},
|
||||
|
||||
showToast: async (message: string, { type = ToastType.Info }: ShowToastOptions = null): Promise<void> => {
|
||||
// Should usually be overridden by implementers
|
||||
await shim.showMessageBox(message, { type: type === ToastType.Error ? MessageBoxType.Error : MessageBoxType.Info });
|
||||
},
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
writeImageToFile: (_image: any, _format: any, _filePath: string): void => {
|
||||
throw new Error('Not implemented');
|
||||
|
||||
Reference in New Issue
Block a user