mirror of
https://github.com/laurent22/joplin.git
synced 2025-04-26 12:02:59 +02:00
Electron app: handle tags
This commit is contained in:
parent
e649670bfe
commit
eda3be066d
CliClient/app
ElectronClient/app
app.js
gui
ImportScreen.jsxItemList.jsxMainScreen.jsxNoteList.jsxPromptDialog.jsxRoot.jsxSideBar.jsx
package.jsonstyle.csstheme.jsnote-viewer
ReactNativeClient/lib/models
@ -28,7 +28,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
if (command == 'add') {
|
if (command == 'add') {
|
||||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
||||||
if (!tag) tag = await Tag.save({ title: args.tag });
|
if (!tag) tag = await Tag.save({ title: args.tag }, { userSideValidation: true });
|
||||||
for (let i = 0; i < notes.length; i++) {
|
for (let i = 0; i < notes.length; i++) {
|
||||||
await Tag.addNote(tag.id, notes[i].id);
|
await Tag.addNote(tag.id, notes[i].id);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,9 @@ class Application extends BaseApplication {
|
|||||||
case 'WINDOW_COMMAND':
|
case 'WINDOW_COMMAND':
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
newState = Object.assign({}, state);
|
||||||
newState.windowCommand = { name: action.name };
|
let command = Object.assign({}, action);
|
||||||
|
delete command.type;
|
||||||
|
newState.windowCommand = command;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,9 @@ class ImportScreenComponent extends React.Component {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// const folder = await Folder.save({ title: folderTitle });
|
const folder = await Folder.save({ title: folderTitle });
|
||||||
|
|
||||||
// await importEnex(folder.id, filePath, options);
|
await importEnex(folder.id, filePath, options);
|
||||||
|
|
||||||
this.addMessage('done', _('The notes have been imported: %s', lastProgress));
|
this.addMessage('done', _('The notes have been imported: %s', lastProgress));
|
||||||
this.setState({ doImport: false });
|
this.setState({ doImport: false });
|
||||||
|
@ -38,6 +38,10 @@ class ItemList extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const items = this.props.items;
|
const items = this.props.items;
|
||||||
|
const style = Object.assign({}, this.props.style, {
|
||||||
|
overflowX: 'hidden',
|
||||||
|
overflowY: 'auto',
|
||||||
|
});
|
||||||
|
|
||||||
if (!this.props.itemHeight) throw new Error('itemHeight is required');
|
if (!this.props.itemHeight) throw new Error('itemHeight is required');
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ class ItemList extends React.Component {
|
|||||||
const that = this;
|
const that = this;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.join(' ')} style={this.props.style} onScroll={ (event) => { this.onScroll(event.target.scrollTop) }}>
|
<div className={classes.join(' ')} style={style} onScroll={ (event) => { this.onScroll(event.target.scrollTop) }}>
|
||||||
{ itemComps }
|
{ itemComps }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ const { NoteList } = require('./NoteList.min.js');
|
|||||||
const { NoteText } = require('./NoteText.min.js');
|
const { NoteText } = require('./NoteText.min.js');
|
||||||
const { PromptDialog } = require('./PromptDialog.min.js');
|
const { PromptDialog } = require('./PromptDialog.min.js');
|
||||||
const { Setting } = require('lib/models/setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
|
const { Tag } = require('lib/models/tag.js');
|
||||||
const { Note } = require('lib/models/note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { Folder } = require('lib/models/folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
@ -17,8 +18,6 @@ class MainScreenComponent extends React.Component {
|
|||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
newNotePromptVisible: false,
|
|
||||||
newFolderPromptVisible: false,
|
|
||||||
promptOptions: null,
|
promptOptions: null,
|
||||||
noteVisiblePanes: ['editor', 'viewer'],
|
noteVisiblePanes: ['editor', 'viewer'],
|
||||||
});
|
});
|
||||||
@ -43,7 +42,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
this.setState({ noteVisiblePanes: panes });
|
this.setState({ noteVisiblePanes: panes });
|
||||||
}
|
}
|
||||||
|
|
||||||
doCommand(command) {
|
async doCommand(command) {
|
||||||
if (!command) return;
|
if (!command) return;
|
||||||
|
|
||||||
const createNewNote = async (title, isTodo) => {
|
const createNewNote = async (title, isTodo) => {
|
||||||
@ -68,7 +67,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
if (command.name === 'newNote') {
|
if (command.name === 'newNote') {
|
||||||
this.setState({
|
this.setState({
|
||||||
promptOptions: {
|
promptOptions: {
|
||||||
message: _('Note title:'),
|
label: _('Note title:'),
|
||||||
onClose: async (answer) => {
|
onClose: async (answer) => {
|
||||||
if (answer) await createNewNote(answer, false);
|
if (answer) await createNewNote(answer, false);
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
@ -78,7 +77,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
} else if (command.name === 'newTodo') {
|
} else if (command.name === 'newTodo') {
|
||||||
this.setState({
|
this.setState({
|
||||||
promptOptions: {
|
promptOptions: {
|
||||||
message: _('To-do title:'),
|
label: _('To-do title:'),
|
||||||
onClose: async (answer) => {
|
onClose: async (answer) => {
|
||||||
if (answer) await createNewNote(answer, true);
|
if (answer) await createNewNote(answer, true);
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
@ -88,7 +87,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
} else if (command.name === 'newNotebook') {
|
} else if (command.name === 'newNotebook') {
|
||||||
this.setState({
|
this.setState({
|
||||||
promptOptions: {
|
promptOptions: {
|
||||||
message: _('Notebook title:'),
|
label: _('Notebook title:'),
|
||||||
onClose: async (answer) => {
|
onClose: async (answer) => {
|
||||||
if (answer) {
|
if (answer) {
|
||||||
let folder = null;
|
let folder = null;
|
||||||
@ -105,6 +104,24 @@ class MainScreenComponent extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({ promptOptions: null });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (command.name === 'setTags') {
|
||||||
|
const tags = await Tag.tagsByNoteId(command.noteId);
|
||||||
|
const tagTitles = tags.map((a) => { return a.title });
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
promptOptions: {
|
||||||
|
label: _('Add or remove tags:'),
|
||||||
|
description: _('Separate each tag by a comma.'),
|
||||||
|
value: tagTitles.join(', '),
|
||||||
|
onClose: async (answer) => {
|
||||||
|
if (answer !== null) {
|
||||||
|
const tagTitles = answer.split(',').map((a) => { return a.trim() });
|
||||||
|
await Tag.setNoteTagsByTitles(command.noteId, tagTitles);
|
||||||
|
}
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -133,14 +150,14 @@ class MainScreenComponent extends React.Component {
|
|||||||
const rowHeight = style.height - theme.headerHeight;
|
const rowHeight = style.height - theme.headerHeight;
|
||||||
|
|
||||||
const sideBarStyle = {
|
const sideBarStyle = {
|
||||||
width: Math.floor(layoutUtils.size(style.width * .2, 100, 300)),
|
width: Math.floor(layoutUtils.size(style.width * .2, 150, 300)),
|
||||||
height: rowHeight,
|
height: rowHeight,
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
};
|
};
|
||||||
|
|
||||||
const noteListStyle = {
|
const noteListStyle = {
|
||||||
width: Math.floor(layoutUtils.size(style.width * .2, 100, 300)),
|
width: Math.floor(layoutUtils.size(style.width * .2, 150, 300)),
|
||||||
height: rowHeight,
|
height: rowHeight,
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
@ -188,7 +205,14 @@ class MainScreenComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
<PromptDialog theme={this.props.theme} style={promptStyle} onClose={(answer) => promptOptions.onClose(answer)} message={promptOptions ? promptOptions.message : ''} visible={!!this.state.promptOptions}/>
|
<PromptDialog
|
||||||
|
value={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
||||||
|
theme={this.props.theme}
|
||||||
|
style={promptStyle}
|
||||||
|
onClose={(answer) => promptOptions.onClose(answer)}
|
||||||
|
label={promptOptions ? promptOptions.label : ''}
|
||||||
|
description={promptOptions ? promptOptions.description : null}
|
||||||
|
visible={!!this.state.promptOptions} />
|
||||||
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
||||||
<SideBar style={sideBarStyle} />
|
<SideBar style={sideBarStyle} />
|
||||||
<NoteList itemHeight={40} style={noteListStyle} />
|
<NoteList itemHeight={40} style={noteListStyle} />
|
||||||
|
@ -57,16 +57,24 @@ class NoteListComponent extends React.Component {
|
|||||||
|
|
||||||
const menu = new Menu()
|
const menu = new Menu()
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Delete'), click: async () => {
|
menu.append(new MenuItem({label: _('Add or remove tags'), click: async () => {
|
||||||
const ok = bridge().showConfirmMessageBox(_('Delete note?'));
|
this.props.dispatch({
|
||||||
if (!ok) return;
|
type: 'WINDOW_COMMAND',
|
||||||
await Note.delete(noteId);
|
name: 'setTags',
|
||||||
|
noteId: noteId,
|
||||||
|
});
|
||||||
}}));
|
}}));
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Switch between note and to-do'), click: async () => {
|
menu.append(new MenuItem({label: _('Switch between note and to-do'), click: async () => {
|
||||||
const note = await Note.load(noteId);
|
const note = await Note.load(noteId);
|
||||||
await Note.save(Note.toggleIsTodo(note));
|
await Note.save(Note.toggleIsTodo(note));
|
||||||
}}))
|
}}));
|
||||||
|
|
||||||
|
menu.append(new MenuItem({label: _('Delete'), click: async () => {
|
||||||
|
const ok = bridge().showConfirmMessageBox(_('Delete note?'));
|
||||||
|
if (!ok) return;
|
||||||
|
await Note.delete(noteId);
|
||||||
|
}}));
|
||||||
|
|
||||||
menu.popup(bridge().window());
|
menu.popup(bridge().window());
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ class PromptDialog extends React.Component {
|
|||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
visible: false,
|
visible: false,
|
||||||
answer: '',
|
answer: this.props.value ? this.props.value : '',
|
||||||
});
|
});
|
||||||
this.focusInput_ = true;
|
this.focusInput_ = true;
|
||||||
}
|
}
|
||||||
@ -18,6 +18,10 @@ class PromptDialog extends React.Component {
|
|||||||
this.setState({ visible: newProps.visible });
|
this.setState({ visible: newProps.visible });
|
||||||
if (newProps.visible) this.focusInput_ = true;
|
if (newProps.visible) this.focusInput_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('value' in newProps) {
|
||||||
|
this.setState({ answer: newProps.value });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@ -60,6 +64,7 @@ class PromptDialog extends React.Component {
|
|||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
fontFamily: theme.fontFamily,
|
fontFamily: theme.fontFamily,
|
||||||
|
verticalAlign: 'top',
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputStyle = {
|
const inputStyle = {
|
||||||
@ -67,6 +72,10 @@ class PromptDialog extends React.Component {
|
|||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const descStyle = Object.assign({}, theme.textStyle, {
|
||||||
|
marginTop: 10,
|
||||||
|
});
|
||||||
|
|
||||||
const onClose = (accept) => {
|
const onClose = (accept) => {
|
||||||
if (this.props.onClose) this.props.onClose(accept ? this.state.answer : null);
|
if (this.props.onClose) this.props.onClose(accept ? this.state.answer : null);
|
||||||
this.setState({ visible: false, answer: '' });
|
this.setState({ visible: false, answer: '' });
|
||||||
@ -84,17 +93,22 @@ class PromptDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const descComp = this.props.description ? <div style={descStyle}>{this.props.description}</div> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={modalLayerStyle}>
|
<div style={modalLayerStyle}>
|
||||||
<div style={promptDialogStyle}>
|
<div style={promptDialogStyle}>
|
||||||
<label style={labelStyle}>{this.props.message ? this.props.message : ''}</label>
|
<label style={labelStyle}>{this.props.label ? this.props.label : ''}</label>
|
||||||
<input
|
<div style={{display: 'inline-block'}}>
|
||||||
style={inputStyle}
|
<input
|
||||||
ref={input => this.answerInput_ = input}
|
style={inputStyle}
|
||||||
value={this.state.answer}
|
ref={input => this.answerInput_ = input}
|
||||||
type="text"
|
value={this.state.answer}
|
||||||
onChange={(event) => onChange(event)}
|
type="text"
|
||||||
onKeyDown={(event) => onKeyDown(event)} />
|
onChange={(event) => onChange(event)}
|
||||||
|
onKeyDown={(event) => onKeyDown(event)} />
|
||||||
|
{descComp}
|
||||||
|
</div>
|
||||||
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
||||||
<button style={buttonStyle} onClick={() => onClose(true)}>OK</button>
|
<button style={buttonStyle} onClick={() => onClose(true)}>OK</button>
|
||||||
<button style={buttonStyle} onClick={() => onClose(false)}>Cancel</button>
|
<button style={buttonStyle} onClick={() => onClose(false)}>Cancel</button>
|
||||||
|
@ -13,11 +13,18 @@ const { app } = require('../app');
|
|||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
async function initialize(dispatch) {
|
async function initialize(dispatch) {
|
||||||
|
this.wcsTimeoutId_ = null;
|
||||||
|
|
||||||
bridge().window().on('resize', function() {
|
bridge().window().on('resize', function() {
|
||||||
store.dispatch({
|
if (this.wcsTimeoutId_) clearTimeout(this.wcsTimeoutId_);
|
||||||
type: 'WINDOW_CONTENT_SIZE_SET',
|
|
||||||
size: bridge().windowContentSize(),
|
this.wcsTimeoutId_ = setTimeout(() => {
|
||||||
});
|
store.dispatch({
|
||||||
|
type: 'WINDOW_CONTENT_SIZE_SET',
|
||||||
|
size: bridge().windowContentSize(),
|
||||||
|
});
|
||||||
|
this.wcsTimeoutId_ = null;
|
||||||
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
|
@ -75,6 +75,7 @@ class SideBarComponent extends React.Component {
|
|||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
marginLeft: 5,
|
marginLeft: 5,
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
|
minHeight: 70,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,7 +158,10 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
const style = Object.assign({}, this.style().root, this.props.style);
|
const style = Object.assign({}, this.style().root, this.props.style, {
|
||||||
|
overflowX: 'hidden',
|
||||||
|
overflowY: 'auto',
|
||||||
|
});
|
||||||
|
|
||||||
let items = [];
|
let items = [];
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "joplin-desktop",
|
"name": "joplin-desktop",
|
||||||
"version": "0.0.1",
|
"version": "0.10.0",
|
||||||
"description": "Joplin for Desktop",
|
"description": "Joplin for Desktop",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -6,37 +6,13 @@ body, textarea {
|
|||||||
|
|
||||||
#react-root {
|
#react-root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*.item-list {
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-list .item {
|
|
||||||
height: 40px; This must match NoteList.itemHeight
|
|
||||||
vertical-align: middle;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-list .item.odd {
|
|
||||||
background-color: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-list .selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
.note-list .list-item:hover {
|
.note-list .list-item:hover {
|
||||||
background-color: rgba(0,160,255,0.1) !important;
|
background-color: rgba(0,160,255,0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-bar .selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-bar .list-item:hover,
|
.side-bar .list-item:hover,
|
||||||
.side-bar .synchronize-button:hover {
|
.side-bar .synchronize-button:hover {
|
||||||
background-color: #533d7d;
|
background-color: #533d7d;
|
||||||
|
@ -56,6 +56,16 @@ globalStyle.lineInput = {
|
|||||||
backgroundColor: globalStyle.backgroundColor,
|
backgroundColor: globalStyle.backgroundColor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
globalStyle.textStyle = {
|
||||||
|
color: globalStyle.color,
|
||||||
|
fontFamily: globalStyle.fontFamily,
|
||||||
|
fontSize: globalStyle.fontSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, {
|
||||||
|
color: globalStyle.color2,
|
||||||
|
});
|
||||||
|
|
||||||
let themeCache_ = {};
|
let themeCache_ = {};
|
||||||
|
|
||||||
function themeStyle(theme) {
|
function themeStyle(theme) {
|
||||||
|
@ -23,6 +23,15 @@ class NoteTag extends BaseItem {
|
|||||||
return this.modelSelectAll('SELECT * FROM note_tags WHERE note_id IN ("' + noteIds.join('","') + '")');
|
return this.modelSelectAll('SELECT * FROM note_tags WHERE note_id IN ("' + noteIds.join('","') + '")');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async tagIdsByNoteId(noteId) {
|
||||||
|
let rows = await this.db().selectAll('SELECT tag_id FROM note_tags WHERE note_id = ?', [noteId]);
|
||||||
|
let output = [];
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
output.push(rows[i].tag_id);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { NoteTag };
|
module.exports = { NoteTag };
|
@ -98,7 +98,38 @@ class Tag extends BaseItem {
|
|||||||
return await Tag.modelSelectAll('SELECT * FROM tags WHERE id IN (SELECT DISTINCT tag_id FROM note_tags)');
|
return await Tag.modelSelectAll('SELECT * FROM tags WHERE id IN (SELECT DISTINCT tag_id FROM note_tags)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async tagsByNoteId(noteId) {
|
||||||
|
const tagIds = await NoteTag.tagIdsByNoteId(noteId);
|
||||||
|
return this.modelSelectAll('SELECT * FROM tags WHERE id IN ("' + tagIds.join('","') + '")');
|
||||||
|
}
|
||||||
|
|
||||||
|
static async setNoteTagsByTitles(noteId, tagTitles) {
|
||||||
|
const previousTags = await this.tagsByNoteId(noteId);
|
||||||
|
const addedTitles = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tagTitles.length; i++) {
|
||||||
|
const title = tagTitles[i].trim().toLowerCase();
|
||||||
|
if (!title) continue;
|
||||||
|
let tag = await this.loadByField('title', title);
|
||||||
|
if (!tag) tag = await Tag.save({ title: title }, { userSideValidation: true });
|
||||||
|
await this.addNote(tag.id, noteId);
|
||||||
|
addedTitles.push(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < previousTags.length; i++) {
|
||||||
|
if (addedTitles.indexOf(previousTags[i].title) < 0) {
|
||||||
|
await this.removeNote(previousTags[i].id, noteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async save(o, options = null) {
|
static async save(o, options = null) {
|
||||||
|
if (options.userSideValidation) {
|
||||||
|
if ('title' in o) {
|
||||||
|
o.title = o.title.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return super.save(o, options).then((tag) => {
|
return super.save(o, options).then((tag) => {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'TAG_UPDATE_ONE',
|
type: 'TAG_UPDATE_ONE',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user