2016-04-02 05:30:43 +08:00
import _ from 'underscore' ;
import { Contenteditable , RetinaImg } from 'nylas-component-kit' ;
import { React } from 'nylas-exports' ;
import TemplateStore from './template-store' ;
import TemplateEditor from './template-editor' ;
class PreferencesTemplates extends React . Component {
static displayName = 'PreferencesTemplates' ;
constructor ( ) {
super ( ) ;
this . _templateSaveQueue = { } ;
const { templates , selectedTemplate , selectedTemplateName } = this . _getStateFromStores ( ) ;
this . state = {
editAsHTML : false ,
editState : templates . length === 0 ? "new" : null ,
templates : templates ,
selectedTemplate : selectedTemplate ,
selectedTemplateName : selectedTemplateName ,
contents : null ,
} ;
}
componentDidMount ( ) {
this . unsub = TemplateStore . listen ( this . _onChange ) ;
}
componentWillUnmount ( ) {
this . unsub ( ) ;
if ( this . state . selectedTemplate ) {
this . _saveTemplateNow ( this . state . selectedTemplate . name , this . state . contents ) ;
}
}
// SAVING AND LOADING TEMPLATES
_loadTemplateContents = ( template ) => {
if ( template ) {
TemplateStore . getTemplateContents ( template . id , ( contents ) => {
this . setState ( { contents : contents } ) ;
} ) ;
}
}
_saveTemplateNow ( name , contents , callback ) {
TemplateStore . saveTemplate ( name , contents , callback ) ;
}
_saveTemplateSoon ( name , contents ) {
this . _templateSaveQueue [ name ] = contents ;
this . _saveTemplatesFromCache ( ) ;
}
_ _saveTemplatesFromCache ( ) {
2016-05-07 05:36:11 +08:00
for ( const name of Object . keys ( this . _templateSaveQueue ) ) {
this . _saveTemplateNow ( name , this . _templateSaveQueue [ name ] ) ;
2016-04-02 05:30:43 +08:00
}
this . _templateSaveQueue = { } ;
}
_saveTemplatesFromCache = _ . debounce ( PreferencesTemplates . prototype . _ _saveTemplatesFromCache , 500 ) ;
// OVERALL STATE HANDLING
_onChange = ( ) => {
this . setState ( this . _getStateFromStores ( ) ) ;
}
_getStateFromStores ( ) {
const templates = TemplateStore . items ( ) ;
let selectedTemplate = this . state ? this . state . selectedTemplate : null ;
2016-05-14 08:14:04 +08:00
if ( selectedTemplate && ! _ . pluck ( templates , "id" ) . includes ( selectedTemplate . id ) ) {
2016-04-02 05:30:43 +08:00
selectedTemplate = null ;
} else if ( ! selectedTemplate ) {
selectedTemplate = templates . length > 0 ? templates [ 0 ] : null ;
}
this . _loadTemplateContents ( selectedTemplate ) ;
let selectedTemplateName = null ;
if ( selectedTemplate ) {
selectedTemplateName = this . state ? this . state . selectedTemplateName : selectedTemplate . name ;
}
return { templates , selectedTemplate , selectedTemplateName } ;
}
// TEMPLATE CONTENT EDITING
_onEditTemplate = ( event ) => {
const html = event . target . value ;
this . setState ( { contents : html } ) ;
if ( this . state . selectedTemplate ) {
this . _saveTemplateSoon ( this . state . selectedTemplate . name , html ) ;
}
}
_onSelectTemplate = ( event ) => {
if ( this . state . selectedTemplate ) {
this . _saveTemplateNow ( this . state . selectedTemplate . name , this . state . contents ) ;
}
2016-05-14 08:14:04 +08:00
const selectedId = event . target . value ;
const selectedTemplate = this . state . templates . find ( ( template ) =>
template . id === selectedId
) ;
2016-04-02 05:30:43 +08:00
this . setState ( {
selectedTemplate : selectedTemplate ,
selectedTemplateName : selectedTemplate ? selectedTemplate . name : null ,
contents : null ,
} ) ;
this . _loadTemplateContents ( selectedTemplate ) ;
}
_renderTemplatePicker ( ) {
const options = this . state . templates . map ( ( template ) => {
return < option value = { template . id } key = { template . id } > { template . name } < / option >
} ) ;
return (
< select value = { this . state . selectedTemplate ? this . state . selectedTemplate . id : null } onChange = { this . _onSelectTemplate } >
{ options }
< / select >
) ;
}
_renderEditableTemplate ( ) {
return (
< Contenteditable
2016-05-07 05:36:11 +08:00
ref = "templateInput"
value = { this . state . contents || "" }
onChange = { this . _onEditTemplate }
extensions = { [ TemplateEditor ] }
spellcheck = { false }
/ >
2016-04-02 05:30:43 +08:00
) ;
}
_renderHTMLTemplate ( ) {
return (
2016-05-07 05:36:11 +08:00
< textarea
ref = "templateHTMLInput"
value = { this . state . contents || "" }
onChange = { this . _onEditTemplate }
/ >
2016-04-02 05:30:43 +08:00
) ;
}
_renderModeToggle ( ) {
if ( this . state . editAsHTML ) {
2016-05-07 05:36:11 +08:00
return ( < a onClick = { ( ) => { this . setState ( { editAsHTML : false } ) ; } } > Edit live preview < / a > ) ;
2016-04-02 05:30:43 +08:00
}
2016-05-07 05:36:11 +08:00
return ( < a onClick = { ( ) => { this . setState ( { editAsHTML : true } ) ; } } > Edit raw HTML < / a > ) ;
2016-04-02 05:30:43 +08:00
}
2016-06-10 06:09:16 +08:00
_onEnter ( action ) {
return ( event ) => {
if ( event . key === "Enter" ) {
action ( )
}
}
}
2016-04-02 05:30:43 +08:00
// TEMPLATE NAME EDITING
_renderEditName ( ) {
return (
< div className = "section-title" >
2016-06-10 06:09:16 +08:00
Template Name : < input type = "text" className = "template-name-input" value = { this . state . selectedTemplateName } onChange = { this . _onEditName } onKeyDown = { this . _onEnter ( this . _saveName ) } / >
2016-04-02 05:30:43 +08:00
< button className = "btn template-name-btn" onClick = { this . _saveName } > Save Name < / button >
< button className = "btn template-name-btn" onClick = { this . _cancelEditName } > Cancel < / button >
< / div >
) ;
}
_renderName ( ) {
const rawText = this . state . editAsHTML ? "Raw HTML " : "" ;
return (
< div className = "section-title" >
{ rawText } Template : { this . _renderTemplatePicker ( ) }
< button className = "btn template-name-btn" title = "New template" onClick = { this . _startNewTemplate } > New < / button >
2016-05-07 05:36:11 +08:00
< button className = "btn template-name-btn" onClick = { ( ) => { this . setState ( { editState : "name" } ) ; } } > Rename < / button >
2016-04-02 05:30:43 +08:00
< / div >
) ;
}
_onEditName = ( event ) => {
this . setState ( { selectedTemplateName : event . target . value } ) ;
}
_cancelEditName = ( ) => {
this . setState ( {
selectedTemplateName : this . state . selectedTemplate ? this . state . selectedTemplate . name : null ,
editState : null ,
} ) ;
}
_saveName = ( ) => {
if ( this . state . selectedTemplate && this . state . selectedTemplate . name !== this . state . selectedTemplateName ) {
TemplateStore . renameTemplate ( this . state . selectedTemplate . name , this . state . selectedTemplateName , ( renamedTemplate ) => {
this . setState ( {
selectedTemplate : renamedTemplate ,
editState : null ,
} ) ;
} ) ;
} else {
this . setState ( {
editState : null ,
} ) ;
}
}
// DELETE AND NEW
_deleteTemplate = ( ) => {
const numTemplates = this . state . templates . length ;
if ( this . state . selectedTemplate ) {
TemplateStore . deleteTemplate ( this . state . selectedTemplate . name ) ;
}
if ( numTemplates === 1 ) {
this . setState ( {
editState : "new" ,
selectedTemplate : null ,
selectedTemplateName : "" ,
contents : "" ,
} ) ;
}
}
_startNewTemplate = ( ) => {
this . setState ( {
editState : "new" ,
selectedTemplate : null ,
selectedTemplateName : "" ,
contents : "" ,
} ) ;
}
_saveNewTemplate = ( ) => {
2016-06-10 06:20:52 +08:00
this . setState ( { contents : "" } )
TemplateStore . saveNewTemplate ( this . state . selectedTemplateName , "" , ( template ) => {
2016-04-02 05:30:43 +08:00
this . setState ( {
selectedTemplate : template ,
editState : null ,
} ) ;
} ) ;
}
_cancelNewTemplate = ( ) => {
const template = this . state . templates . length > 0 ? this . state . templates [ 0 ] : null ;
this . setState ( {
selectedTemplate : template ,
selectedTemplateName : template ? template . name : null ,
editState : null ,
} ) ;
this . _loadTemplateContents ( template ) ;
}
_renderCreateNew ( ) {
const cancel = ( < button className = "btn template-name-btn" onClick = { this . _cancelNewTemplate } > Cancel < / button > ) ;
return (
< div className = "section-title" >
2016-06-10 06:09:16 +08:00
Template Name : < input type = "text" className = "template-name-input" value = { this . state . selectedTemplateName } onChange = { this . _onEditName } onKeyDown = { this . _onEnter ( this . _saveNewTemplate ) } / >
2016-04-02 05:30:43 +08:00
< button className = "btn btn-emphasis template-name-btn" onClick = { this . _saveNewTemplate } > Save < / button >
{ this . state . templates . length ? cancel : null }
< / div >
) ;
}
// MAIN RENDER
render ( ) {
const deleteBtn = (
< button className = "btn" title = "Delete template" onClick = { this . _deleteTemplate } >
< RetinaImg name = "icon-composer-trash.png" mode = { RetinaImg . Mode . ContentIsMask } / >
< / button >
) ;
const editor = (
< div >
< div className = "template-wrap" >
{ this . state . editAsHTML ? this . _renderHTMLTemplate ( ) : this . _renderEditableTemplate ( ) }
< / div >
< div style = { { marginTop : "5px" } } >
< span className = "editor-note" >
2016-05-14 08:14:04 +08:00
{ _ . size ( this . _templateSaveQueue ) === 0 ? "Changes saved." : "" }
& nbsp ;
2016-04-02 05:30:43 +08:00
< / span >
2016-05-07 05:36:11 +08:00
< span style = { { "float" : "right" } } > { this . state . editState === null ? deleteBtn : "" } < / span >
2016-04-02 05:30:43 +08:00
< / div >
< div className = "toggle-mode" style = { { marginTop : "1em" } } >
{ this . _renderModeToggle ( ) }
< / div >
< / div >
) ;
let editContainer = this . _renderName ( ) ;
if ( this . state . editState === "name" ) {
editContainer = this . _renderEditName ( ) ;
} else if ( this . state . editState === "new" ) {
editContainer = this . _renderCreateNew ( ) ;
}
const noTemplatesMessage = (
< div className = "template-status-bar no-templates-message" >
{ ` You don't have any templates! Enter a template name and press save to create one. ` }
< / div >
) ;
return (
< div className = "container-templates" >
< section style = { this . state . editState === "new" ? { marginBottom : 50 } : null } >
{ editContainer }
{ this . state . editState !== "new" ? editor : null }
{ this . state . templates . length === 0 ? noTemplatesMessage : null }
< / section >
< section className = "templates-instructions" >
< p >
2016-05-19 06:30:11 +08:00
{ ` To create a variable, type a set of double curly
brackets wrapping the variable ' s name , like this ` }: <strong>{"{{"}variable_name{"}}"}</strong>. The highlighting in the variable regions will be removed before the message is
sent .
2016-04-02 05:30:43 +08:00
< / p >
< p >
2017-01-14 10:54:37 +08:00
Reply templates are saved as HTML files in the < strong > ~ / . n y l a s - m a i l / t e m p l a t e s < / s t r o n g > d i r e c t o r y o n y o u r c o m p u t e r . I n r a w H T M L , v a r i a b l e s a r e d e f i n e d a s H T M L & l t ; c o d e & g t ; t a g s w i t h c l a s s & q u o t ; v a r e m p t y & q u o t ; .
2016-04-02 05:30:43 +08:00
< / p >
< / section >
< / div >
) ;
}
}
export default PreferencesTemplates ;