2020-10-09 19:35:46 +02:00
|
|
|
import { utils as pluginUtils, PluginStates } from 'lib/services/plugins/reducer';
|
|
|
|
import CommandService from 'lib/services/CommandService';
|
|
|
|
import eventManager from 'lib/eventManager';
|
|
|
|
import InteropService from 'lib/services/interop/InteropService';
|
|
|
|
import MenuUtils from 'lib/services/commands/MenuUtils';
|
|
|
|
import InteropServiceHelper from '../../InteropServiceHelper';
|
|
|
|
import { _ } from 'lib/locale';
|
|
|
|
import { MenuItemLocation } from 'lib/services/plugins/api/types';
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
const BaseModel = require('lib/BaseModel');
|
2020-10-09 19:35:46 +02:00
|
|
|
const bridge = require('electron').remote.require('./bridge').default;
|
2019-01-29 20:02:34 +02:00
|
|
|
const Menu = bridge().Menu;
|
|
|
|
const MenuItem = bridge().MenuItem;
|
2019-01-31 09:55:51 +02:00
|
|
|
const Note = require('lib/models/Note');
|
2019-07-29 14:13:23 +02:00
|
|
|
const { substrWithEllipsis } = require('lib/string-utils');
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
interface ContextMenuProps {
|
|
|
|
notes: any[],
|
|
|
|
dispatch: Function,
|
|
|
|
watchedNoteFiles: string[],
|
|
|
|
plugins: PluginStates,
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class NoteListUtils {
|
|
|
|
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);
|
|
|
|
|
2020-05-21 10:14:33 +02:00
|
|
|
const notes = noteIds.map(id => BaseModel.byId(props.notes, id));
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-10 14:32:30 +02:00
|
|
|
const singleNoteId = noteIds.length === 1 ? noteIds[0] : null;
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
let hasEncrypted = false;
|
|
|
|
for (let i = 0; i < notes.length; i++) {
|
2019-07-29 14:13:23 +02:00
|
|
|
if (notes[i].encryption_applied) hasEncrypted = true;
|
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
|
|
|
|
|
|
|
if (!hasEncrypted) {
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
2020-10-09 19:35:46 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('setTags', { noteIds }))
|
2019-07-29 14:13:23 +02:00
|
|
|
);
|
|
|
|
|
2020-04-04 19:03:09 +02:00
|
|
|
menu.append(
|
2020-10-16 18:44:04 +02:00
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem('moveToFolder', { noteIds }))
|
2020-04-04 19:03:09 +02:00
|
|
|
);
|
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Duplicate'),
|
|
|
|
click: async () => {
|
|
|
|
for (let i = 0; i < noteIds.length; i++) {
|
|
|
|
const note = await Note.load(noteIds[i]);
|
|
|
|
await Note.duplicate(noteIds[i], {
|
|
|
|
uniqueTitle: _('%s - Copy', note.title),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2020-10-10 14:32:30 +02:00
|
|
|
if (singleNoteId) {
|
|
|
|
const cmd = props.watchedNoteFiles.includes(singleNoteId) ? 'stopExternalEditing' : 'startExternalEditing';
|
|
|
|
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem(cmd, { noteId: singleNoteId })));
|
2020-01-06 23:16:39 +02:00
|
|
|
}
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
if (noteIds.length <= 1) {
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Switch between note and to-do type'),
|
|
|
|
click: async () => {
|
|
|
|
for (let i = 0; i < noteIds.length; i++) {
|
|
|
|
const note = await Note.load(noteIds[i]);
|
2020-05-02 17:41:07 +02:00
|
|
|
const newNote = await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
|
|
|
|
const eventNote = {
|
|
|
|
id: newNote.id,
|
|
|
|
is_todo: newNote.is_todo,
|
|
|
|
todo_due: newNote.todo_due,
|
|
|
|
todo_completed: newNote.todo_completed,
|
|
|
|
};
|
|
|
|
eventManager.emit('noteTypeToggle', { noteId: note.id, note: eventNote });
|
2019-07-29 14:13:23 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
} else {
|
2020-10-09 19:35:46 +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;
|
|
|
|
await Note.save(newNote, { userSideValidation: true });
|
|
|
|
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
|
|
|
}
|
2019-07-29 14:13:23 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Switch to note type'),
|
|
|
|
click: async () => {
|
|
|
|
await switchNoteType(noteIds, 'note');
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Switch to to-do type'),
|
|
|
|
click: async () => {
|
|
|
|
await switchNoteType(noteIds, 'todo');
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
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 { clipboard } = require('electron');
|
|
|
|
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(' '));
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
2019-01-29 20:02:34 +02:00
|
|
|
|
2019-12-13 03:16:34 +02:00
|
|
|
menu.append(
|
2020-07-03 23:32:39 +02:00
|
|
|
new MenuItem(
|
2020-10-09 19:35:46 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('showShareNoteDialog', { noteIds: noteIds.slice() })
|
2020-07-03 23:32:39 +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 () => {
|
|
|
|
await InteropServiceHelper.export(props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
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(
|
2020-10-09 19:35:46 +02:00
|
|
|
menuUtils.commandToStatefulMenuItem('exportPdf', { noteIds: noteIds })
|
2020-07-03 23:32:39 +02:00
|
|
|
)
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-29 14:13:23 +02:00
|
|
|
menu.append(
|
|
|
|
new MenuItem({
|
|
|
|
label: _('Delete'),
|
|
|
|
click: async () => {
|
|
|
|
await this.confirmDeleteNotes(noteIds);
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
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;
|
|
|
|
if (location !== MenuItemLocation.Context) continue;
|
|
|
|
|
|
|
|
menu.append(
|
|
|
|
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
return menu;
|
|
|
|
}
|
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
static async confirmDeleteNotes(noteIds:string[]) {
|
2019-01-29 20:02:34 +02:00
|
|
|
if (!noteIds.length) return;
|
2019-01-31 09:55:51 +02:00
|
|
|
|
|
|
|
let msg = '';
|
|
|
|
if (noteIds.length === 1) {
|
|
|
|
const note = await Note.load(noteIds[0]);
|
|
|
|
if (!note) return;
|
|
|
|
msg = _('Delete note "%s"?', substrWithEllipsis(note.title, 0, 32));
|
|
|
|
} else {
|
|
|
|
msg = _('Delete these %d notes?', noteIds.length);
|
|
|
|
}
|
|
|
|
|
2019-05-11 14:36:44 +02:00
|
|
|
const ok = bridge().showConfirmMessageBox(msg, {
|
|
|
|
buttons: [_('Delete'), _('Cancel')],
|
|
|
|
defaultId: 1,
|
|
|
|
});
|
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
if (!ok) return;
|
|
|
|
await Note.batchDelete(noteIds);
|
|
|
|
}
|
2020-01-06 23:16:39 +02:00
|
|
|
|
2019-01-29 20:02:34 +02:00
|
|
|
}
|