2020-11-07 17:59:37 +02:00
|
|
|
import { utils as pluginUtils, PluginStates } from '@joplin/lib/services/plugins/reducer';
|
|
|
|
import CommandService from '@joplin/lib/services/CommandService';
|
|
|
|
import InteropService from '@joplin/lib/services/interop/InteropService';
|
|
|
|
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
|
2020-10-09 19:35:46 +02:00
|
|
|
import InteropServiceHelper from '../../InteropServiceHelper';
|
2020-11-07 17:59:37 +02:00
|
|
|
import { _ } from '@joplin/lib/locale';
|
|
|
|
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
|
2021-10-16 11:07:41 +02:00
|
|
|
import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
2023-08-21 17:01:20 +02:00
|
|
|
import bridge from '../../services/bridge';
|
2021-01-22 19:41:11 +02:00
|
|
|
import BaseModel from '@joplin/lib/BaseModel';
|
|
|
|
import Note from '@joplin/lib/models/Note';
|
2021-01-29 20:45:11 +02:00
|
|
|
import Setting from '@joplin/lib/models/Setting';
|
2021-10-16 11:07:41 +02:00
|
|
|
const { clipboard } = require('electron');
|
2023-08-21 17:01:20 +02:00
|
|
|
import { Dispatch } from 'redux';
|
2024-03-02 16:25:27 +02:00
|
|
|
import { NoteEntity } from '@joplin/lib/services/database/types';
|
2023-08-21 17:01:20 +02:00
|
|
|
|
|
|
|
const Menu = bridge().Menu;
|
|
|
|
const MenuItem = bridge().MenuItem;
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
interface ContextMenuProps {
|
2024-03-02 16:25:27 +02:00
|
|
|
notes: NoteEntity[];
|
2023-08-21 17:01:20 +02:00
|
|
|
dispatch: Dispatch;
|
2020-11-12 21:29:22 +02:00
|
|
|
watchedNoteFiles: string[];
|
|
|
|
plugins: PluginStates;
|
2020-11-17 13:50:46 +02:00
|
|
|
inConflictFolder: boolean;
|
2021-05-19 15:00:16 +02:00
|
|
|
customCss: string;
|
2020-10-09 19:35:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export default class NoteListUtils {
|
2023-03-06 16:22:01 +02:00
|
|
|
public static makeContextMenu(noteIds: string[], props: ContextMenuProps) {
|
2020-07-03 23:32:39 +02:00
|
|
|
const cmdService = CommandService.instance();
|
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
const menuUtils = new MenuUtils(cmdService);
|
|
|
|
|
2024-09-21 13:53:16 +02:00
|
|
|
const notes: NoteEntity[] = BaseModel.modelsByIds(props.notes, noteIds);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-10 14:32:30 +02:00
|
|
|
const singleNoteId = noteIds.length === 1 ? noteIds[0] : null;
|
|
|
|
|
2024-03-02 16:25:27 +02:00
|
|
|
const includeDeletedNotes = notes.find(n => !!n.deleted_time);
|
|
|
|
const includeEncryptedNotes = notes.find(n => !!n.encryption_applied);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
const menu = new Menu();
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2024-03-02 16:25:27 +02:00
|
|
|
if (!includeEncryptedNotes && !includeDeletedNotes) {
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('setTags', noteIds) as any),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
|
|
|
|
2020-04-04 19:03:09 +02:00
|
|
|
menu.append(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('moveToFolder', noteIds) as any),
|
2020-04-04 19:03:09 +02:00
|
|
|
);
|
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('duplicateNote', noteIds) as any),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-10 14:32:30 +02:00
|
|
|
if (singleNoteId) {
|
2024-11-08 17:32:05 +02:00
|
|
|
const editInMenu = new Menu();
|
|
|
|
|
2020-10-10 14:32:30 +02:00
|
|
|
const cmd = props.watchedNoteFiles.includes(singleNoteId) ? 'stopExternalEditing' : 'startExternalEditing';
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2024-11-08 17:32:05 +02:00
|
|
|
editInMenu.append(new MenuItem(menuUtils.commandToStatefulMenuItem(cmd, singleNoteId) as any));
|
|
|
|
editInMenu.append(
|
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('openNoteInNewWindow', singleNoteId)),
|
|
|
|
);
|
|
|
|
|
|
|
|
menu.append(new MenuItem({ label: _('Edit in...'), submenu: editInMenu }));
|
2020-01-06 23:16:39 +02:00
|
|
|
}
|
|
|
|
|
2024-11-08 17:32:05 +02:00
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
if (noteIds.length <= 1) {
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
2023-07-16 18:42:42 +02:00
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('toggleNoteType', noteIds) as any,
|
|
|
|
),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
} else {
|
2020-11-12 21:13:28 +02:00
|
|
|
const switchNoteType = async (noteIds: string[], type: string) => {
|
2019-01-29 20:02:34 +02:00
|
|
|
for (let i = 0; i < noteIds.length; i++) {
|
|
|
|
const note = await Note.load(noteIds[i]);
|
|
|
|
const newNote = Note.changeNoteType(note, type);
|
|
|
|
if (newNote === note) continue;
|
2024-05-30 09:31:54 +02:00
|
|
|
await Note.save(newNote, { userSideValidation: true });
|
2019-01-29 20:02:34 +02:00
|
|
|
}
|
2019-07-29 14:13:23 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Switch to note type'),
|
|
|
|
click: async () => {
|
|
|
|
await switchNoteType(noteIds, 'note');
|
|
|
|
},
|
2023-08-22 12:58:53 +02:00
|
|
|
}),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Switch to to-do type'),
|
|
|
|
click: async () => {
|
|
|
|
await switchNoteType(noteIds, 'todo');
|
|
|
|
},
|
2023-08-22 12:58:53 +02:00
|
|
|
}),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
}
|
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Copy Markdown link'),
|
|
|
|
click: async () => {
|
|
|
|
const links = [];
|
|
|
|
for (let i = 0; i < noteIds.length; i++) {
|
|
|
|
const note = await Note.load(noteIds[i]);
|
|
|
|
links.push(Note.markdownTag(note));
|
|
|
|
}
|
|
|
|
clipboard.writeText(links.join(' '));
|
|
|
|
},
|
2023-08-22 12:58:53 +02:00
|
|
|
}),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2021-10-16 11:07:41 +02:00
|
|
|
if (noteIds.length === 1) {
|
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Copy external link'),
|
|
|
|
click: () => {
|
|
|
|
clipboard.writeText(getNoteCallbackUrl(noteIds[0]));
|
|
|
|
},
|
2023-08-22 12:58:53 +02:00
|
|
|
}),
|
2021-10-16 11:07:41 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-06 19:14:12 +02:00
|
|
|
if ([9, 10].includes(Setting.value('sync.target'))) {
|
2021-01-29 20:45:11 +02:00
|
|
|
menu.append(
|
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('showShareNoteDialog', noteIds.slice()) as any,
|
|
|
|
),
|
2021-01-29 20:45:11 +02:00
|
|
|
);
|
|
|
|
}
|
2019-12-13 03:16:34 +02:00
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
const exportMenu = new Menu();
|
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
const ioService = InteropService.instance();
|
2019-01-29 20:02:34 +02:00
|
|
|
const ioModules = ioService.modules();
|
|
|
|
for (let i = 0; i < ioModules.length; i++) {
|
|
|
|
const module = ioModules[i];
|
|
|
|
if (module.type !== 'exporter') continue;
|
2020-10-09 19:35:46 +02:00
|
|
|
if (noteIds.length > 1 && module.isNoteArchive === false) continue;
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
exportMenu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: module.fullLabel(),
|
|
|
|
click: async () => {
|
2020-11-17 13:50:46 +02:00
|
|
|
await InteropServiceHelper.export(props.dispatch.bind(this), module, {
|
|
|
|
sourceNoteIds: noteIds,
|
|
|
|
includeConflicts: props.inConflictFolder,
|
2020-12-19 19:42:18 +02:00
|
|
|
plugins: props.plugins,
|
2021-05-19 15:00:16 +02:00
|
|
|
customCss: props.customCss,
|
2020-11-17 13:50:46 +02:00
|
|
|
});
|
2019-07-29 14:13:23 +02:00
|
|
|
},
|
2023-08-22 12:58:53 +02:00
|
|
|
}),
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
}
|
|
|
|
|
2020-02-11 16:14:04 +02:00
|
|
|
exportMenu.append(
|
2020-07-03 23:32:39 +02:00
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('exportPdf', noteIds) as any,
|
|
|
|
),
|
2020-02-11 16:14:04 +02:00
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
const exportMenuItem = new MenuItem({ label: _('Export'), submenu: exportMenu });
|
2019-01-29 20:02:34 +02:00
|
|
|
|
|
|
|
menu.append(exportMenuItem);
|
|
|
|
}
|
|
|
|
|
2024-03-02 16:25:27 +02:00
|
|
|
if (includeDeletedNotes) {
|
|
|
|
menu.append(
|
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2024-03-02 16:25:27 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('restoreNote', noteIds) as any,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2024-03-02 16:25:27 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('permanentlyDeleteNote', noteIds) as any,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
menu.append(
|
|
|
|
new MenuItem(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2024-03-02 16:25:27 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('deleteNote', noteIds) as any,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
const pluginViewInfos = pluginUtils.viewInfosByType(props.plugins, 'menuItem');
|
|
|
|
|
|
|
|
for (const info of pluginViewInfos) {
|
|
|
|
const location = info.view.location;
|
2020-11-14 02:02:17 +02:00
|
|
|
if (location !== MenuItemLocation.Context && location !== MenuItemLocation.NoteListContextMenu) continue;
|
2020-10-09 19:35:46 +02:00
|
|
|
|
2021-08-09 20:04:40 +02:00
|
|
|
if (cmdService.isEnabled(info.view.commandName)) {
|
|
|
|
menu.append(
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-08-22 12:58:53 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName, noteIds) as any),
|
2021-08-09 20:04:40 +02:00
|
|
|
);
|
|
|
|
}
|
2020-10-09 19:35:46 +02:00
|
|
|
}
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
return menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|