mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Electron: Resolves #612: Allow duplicating a note
This commit is contained in:
parent
7d7e1e1637
commit
310afb0ad6
@ -59,7 +59,7 @@ class ImportScreenComponent extends React.Component {
|
||||
|
||||
async doImport() {
|
||||
const filePath = this.props.filePath;
|
||||
const folderTitle = await Folder.findUniqueFolderTitle(filename(filePath));
|
||||
const folderTitle = await Folder.findUniqueItemTitle(filename(filePath));
|
||||
const messages = this.state.messages.slice();
|
||||
|
||||
this.addMessage('start', _('New notebook "%s" will be created and file "%s" will be imported into it', folderTitle, basename(filePath)));
|
||||
|
@ -88,6 +88,15 @@ class NoteListComponent extends React.Component {
|
||||
});
|
||||
}}));
|
||||
|
||||
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),
|
||||
});
|
||||
}
|
||||
}}));
|
||||
|
||||
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]);
|
||||
|
@ -29,6 +29,19 @@ class BaseItem extends BaseModel {
|
||||
throw new Error('Invalid class name: ' + className);
|
||||
}
|
||||
|
||||
static async findUniqueItemTitle(title) {
|
||||
let counter = 1;
|
||||
let titleToTry = title;
|
||||
while (true) {
|
||||
const item = await this.loadByField('title', titleToTry);
|
||||
if (!item) return titleToTry;
|
||||
titleToTry = title + ' (' + counter + ')';
|
||||
counter++;
|
||||
if (counter >= 100) titleToTry = title + ' (' + ((new Date()).getTime()) + ')';
|
||||
if (counter >= 1000) throw new Error('Cannot find unique title');
|
||||
}
|
||||
}
|
||||
|
||||
// Need to dynamically load the classes like this to avoid circular dependencies
|
||||
static getClass(name) {
|
||||
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
|
||||
|
@ -33,19 +33,6 @@ class Folder extends BaseItem {
|
||||
}
|
||||
}
|
||||
|
||||
static async findUniqueFolderTitle(title) {
|
||||
let counter = 1;
|
||||
let titleToTry = title;
|
||||
while (true) {
|
||||
const folder = await this.loadByField('title', titleToTry);
|
||||
if (!folder) return titleToTry;
|
||||
titleToTry = title + ' (' + counter + ')';
|
||||
counter++;
|
||||
if (counter >= 100) titleToTry = title + ' (' + ((new Date()).getTime()) + ')';
|
||||
if (counter >= 1000) throw new Error('Cannot find unique title');
|
||||
}
|
||||
}
|
||||
|
||||
static noteIds(parentId) {
|
||||
return this.db().selectAll('SELECT id FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]).then((rows) => {
|
||||
let output = [];
|
||||
|
@ -420,6 +420,7 @@ class Note extends BaseItem {
|
||||
|
||||
static async duplicate(noteId, options = null) {
|
||||
const changes = options && options.changes;
|
||||
const uniqueTitle = options && options.uniqueTitle;
|
||||
|
||||
const originalNote = await Note.load(noteId);
|
||||
if (!originalNote) throw new Error('Unknown note: ' + noteId);
|
||||
@ -432,6 +433,11 @@ class Note extends BaseItem {
|
||||
newNote[n] = changes[n];
|
||||
}
|
||||
|
||||
if (uniqueTitle) {
|
||||
const title = await Note.findUniqueItemTitle(uniqueTitle);
|
||||
newNote.title = title;
|
||||
}
|
||||
|
||||
return this.save(newNote);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class InteropService_Importer_Enex extends InteropService_Importer_Base {
|
||||
let folder = this.options_.destinationFolder;
|
||||
|
||||
if (!folder) {
|
||||
const folderTitle = await Folder.findUniqueFolderTitle(filename(this.sourcePath_));
|
||||
const folderTitle = await Folder.findUniqueItemTitle(filename(this.sourcePath_));
|
||||
folder = await Folder.save({ title: folderTitle });
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ class InteropService_Importer_Md extends InteropService_Importer_Base {
|
||||
}
|
||||
|
||||
if (!this.options_.destinationFolder) {
|
||||
const folderTitle = await Folder.findUniqueFolderTitle(basename(rtrimSlashes(this.sourcePath_)));
|
||||
const folderTitle = await Folder.findUniqueItemTitle(basename(rtrimSlashes(this.sourcePath_)));
|
||||
const folder = await Folder.save({ title: folderTitle });
|
||||
parentFolderId = folder.id;
|
||||
} else {
|
||||
|
@ -51,7 +51,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
|
||||
let defaultFolder_ = null;
|
||||
const defaultFolder = async () => {
|
||||
if (defaultFolder_) return defaultFolder_;
|
||||
const folderTitle = await Folder.findUniqueFolderTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported');
|
||||
const folderTitle = await Folder.findUniqueItemTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported');
|
||||
defaultFolder_ = await Folder.save({ title: folderTitle });
|
||||
return defaultFolder_;
|
||||
}
|
||||
@ -100,7 +100,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
|
||||
|
||||
if (!itemIdMap[item.id]) itemIdMap[item.id] = uuid.create();
|
||||
item.id = itemIdMap[item.id];
|
||||
item.title = await Folder.findUniqueFolderTitle(item.title);
|
||||
item.title = await Folder.findUniqueItemTitle(item.title);
|
||||
|
||||
if (item.parent_id) {
|
||||
await setFolderToImportTo(item.parent_id);
|
||||
|
Loading…
Reference in New Issue
Block a user