New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Desktop: Added options to set default templates #2857
Conversation
Couple of rookie mistakes on my end, as i expected :) Good points. There is one minor problem left regarding the deletion of templates. On delete -> refresh the dropdowns go to "None", but Setting.value('default****') still equals the value of the deleted template which means you're stuck with them on new note/todo. To get past that as a user you have to change to another template and apply. If you delete all your template when a default is set i think you'd need to load in new templates to stop them from showing. I've attempted a solution for this by matching defaultNote and defaultTodo with the templates array on refresh/startup, which seems to work. I'll push it in the coming commit. |
ElectronClient/app.js
Outdated
const templates = await TemplateUtils.loadTemplates(Setting.value('templateDir')); | ||
if (shim.isElectron()) Setting.setValue('availableTemplates', templates); | ||
Setting.setValue('availableTemplates', templates); | ||
|
||
setTimeout(() => { | ||
if (!templates.filter(i => i.value == Setting.value('defaultNote')).length) Setting.setValue('defaultNote', '0'); | ||
if (!templates.filter(i => i.value == Setting.value('defaultTodo')).length) Setting.setValue('defaultTodo', '0'); | ||
}, 2000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears twice. Would making it into a function inside templateUtils be ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think it should be pushed into MainScren.jsx That way it will happen right away when a user deletes a template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I think Array.find Might be a bit cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, didn't know about Array.find. I'll try it out.
I've tried putting this test inside Mainscreen createNewNote,. Problem is it seems to need a timeout of about 1-2 sec for the templates array not to turn up blank (which corrupts the test), and i want the test to determine if template should be null or not. I guess just moving the current tests to createNewNote would work. Drawback is the deleted template would still appear on the first new note/todo and then not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That 1-2 sec is really weird, and I suspect it won't be needed if you move the code to createNewNote. Keep in mind that you can access the templates in MainScreen by using
this.props.templates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not sure why it only worked with timeout at first. I might have messed up syntax when testing without or something. Anyway, seems to work as expected when put in createNewNote now!
I'm sorry to do this, but I'm afraid that I gave you a bad suggestion earlier. Using the template value as the Setting key doesn't work well because if the user edits a template then the default will be lost, which is not good. The better behavior would be to index the options with the label, since this will stay stable
then in createNewNote you will have to fetch the body from |
Good point and no worries, hadn't thought of that myself. Editing works without reset on latest commit. |
@CalebJohn Anything else i should do here? |
Hi @johodh Sorry for going dark on you here. This LGTM, but we have a semi-official feature freeze happening now. All the bug fixing pull requests are being priortized now, and this will have to come after that, sorry. Thanks for following up. |
Thank you for the pull request @johodh, I'm slowly trying to go through our backlog. The implementation looks good overall, but I have an issue with the So my suggestion would be to add a new method to TemplateUtils called |
@laurent22 Thanks for the feedback! That does seem like a better idea. I hope the last commit should do it. Btw, I was able to read TemplateUtils.availableTemplates_ in Setting as well, so i'm curious if the method is really needed. Also, can i do something about the failed checks? They seemed to be related to mustache and not my changes. |
const templates = TemplateUtils.availableTemplates(); | ||
const options = { 0: 'None' }; | ||
for (let i = 0; i < templates.length; i++) { | ||
options[templates[i].label] = templates[i].label; | ||
} | ||
return options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Identifying the template by array index is too fragile because if a template is added or deleted it will break. They should be identified by name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The templates are identified by label in the options array. Label is the same as file name (the index loop is just for iterating through all available template objects). I've played around with this a bit, and if a template file is deleted joplin defaults back to no template on next reload or joplin restart. Adding new template files also does not mess with the currently seleced template.
const templates = TemplateUtils.availableTemplates(); | ||
const options = { 0: 'None' }; | ||
for (let i = 0; i < templates.length; i++) { | ||
options[templates[i].label] = templates[i].label; | ||
} | ||
return options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please refactor to avoid the duplicate code. Maybe create a function defaultTemplateOptions()
somewhere
@@ -346,6 +347,40 @@ class Setting extends BaseModel { | |||
}; | |||
}, | |||
}, | |||
defaultNote: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename to editor.defaultNoteTemplate
and editor.defaultTodoTemplate
@@ -346,6 +347,40 @@ class Setting extends BaseModel { | |||
}; | |||
}, | |||
}, | |||
defaultNote: { | |||
value: '0', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defaults to empty string ''
isEnum: true, | ||
public: true, | ||
appTypes: ['desktop'], | ||
label: () => _('Choose default template for new note:'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'New note default template'
and 'New to-do default template'
ElectronClient/gui/MainScreen.jsx
Outdated
if (!template) { | ||
const templateType = isTodo ? 'defaultTodo' : 'defaultNote'; | ||
const settingsLabel = Setting.value(templateType); | ||
if (settingsLabel != 0) { | ||
const matchLabel = this.props.templates.find(i => i.label == settingsLabel); | ||
if (!matchLabel) { | ||
Setting.setValue(templateType, '0'); | ||
} else { | ||
template = matchLabel.value; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ought to be simpler:
if (!template) {
template = Setting.value(isTodo ? 'defaultTodo' : 'defaultNote');`
}
Please don't set any setting in here, as we don't want side-effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized the Setting.setValue isn't necessary. I put it there for safety, thinking the set defaultTodo/defaultNote options might remain after (deleting a template) and refreshing. They don't, so i'll remove it.
I'll also cut down line 103 and 104 to one line like you propose. Only the template filename is stored in Setting.value(default****), not the contents, so we still need to get the contents from props. This change was made as Caleb realized storing the template contents in options won't allow the user to edit a template without losing the default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, i was wrong about the first part. The settings do remain even after refresh, which leads to a problem when deleting templates and refreshing. I'll see what i can do.
By convention properties that end with "_" are private and should not be accessed from elsewhere. |
Also please could you add tests for this feature? |
Hmm, right I think we have a problem here because the Setting class is now loading the TemplateUtil class, even though that's not needed for the tests (or for the CLI and mobile app). I think there's some plumbing to do to allow the Setting class to access other classes properly, probably using dependency injection. So could you put this feature on hold for now please, until this is sorted out? Sorry about that, I didn't realise it would be a problem for this pull request. |
I'll look in to the requested changes, add tests and push a new commit for you to review. Please have a look at my response on your first comment. Should i do something in particular to put the feature on hold, or do i just leave it here as is until the plumbing is sorted out? (I'm new to github :)) |
const templateFileName = Setting.value(isTodo ? 'editor.defaultTodoTemplate' : 'editor.defaultNoteTemplate'); | ||
if (templateFileName) { | ||
if (typeof(this.props.templates.find(i => i.label == templateFileName)) != 'undefined') { | ||
template = this.props.templates.find(i => i.label == templateFileName).value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got rid of Setting.setValue(...).
After deleting a template and then refreshing templates the Setting.value('editor.default****Template') still returns the name of the deleted file, however the dropdown in settings will default back to 'None'. I made an if (typeof()) test to stop errors getting thrown on new note when a default template has been deleted.
This also means that if you re-add the deleted template it will be selected as default again, until you actively change to something else in the settings panel.
Can't seem to run any tests without running in to the Mustache-problem.
|
What I meant by plumbing is that certain core features are currently missing to be able to implement this, so for now I would prefer to close the pull request. When these core features are implemented we can look at it again. |
I see. I wish i could help you out with that, but i suspect i still have too much to learn at this point to get into coding core features. I'd be happy to finish this PR later on. |
@CalebJohn Here it is.
Known issues: