From 20c522ed39f7c0230cd242e21d795d3df3d838c4 Mon Sep 17 00:00:00 2001 From: Drew Regitsky Date: Wed, 2 Dec 2015 21:28:13 -0800 Subject: [PATCH] feat(templates-plugin): Add prefs page with template editor, fix bugs. [Composer Templates / Quick Reply plugin example] Add a page to preferences with a basic editor for quick reply templates, allowing add, delete, and edit (HTML and rendered text), with instructions for how to add variable regions. Add methods to TemplatesStore to enable these. Fix issues in plugin with quoted text handling, name conflicts, `this` scoping. --- examples/N1-Composer-Templates/lib/main.es6 | 9 +- .../lib/preferences-templates.cjsx | 238 ++++++++++++++++++ .../lib/template-picker.jsx | 8 +- .../lib/template-store.es6 | 93 +++++-- .../stylesheets/message-templates.less | 93 ++++++- 5 files changed, 419 insertions(+), 22 deletions(-) create mode 100644 examples/N1-Composer-Templates/lib/preferences-templates.cjsx diff --git a/examples/N1-Composer-Templates/lib/main.es6 b/examples/N1-Composer-Templates/lib/main.es6 index a90e1e531..43d762891 100644 --- a/examples/N1-Composer-Templates/lib/main.es6 +++ b/examples/N1-Composer-Templates/lib/main.es6 @@ -1,4 +1,4 @@ -import {ComponentRegistry, DraftStore} from 'nylas-exports'; +import {PreferencesUIStore, ComponentRegistry, DraftStore} from 'nylas-exports'; import TemplatePicker from './template-picker'; import TemplateStatusBar from './template-status-bar'; import Extension from './template-draft-extension'; @@ -8,14 +8,21 @@ module.exports = { activate(state = {}) { this.state = state; + this.preferencesTab = new PreferencesUIStore.TabItem({ + tabId: "Quick Replies", + displayName: "Quick Replies", + component: require("./preferences-templates"), + }); ComponentRegistry.register(TemplatePicker, {role: 'Composer:ActionButton'}); ComponentRegistry.register(TemplateStatusBar, {role: 'Composer:Footer'}); + PreferencesUIStore.registerPreferencesTab(this.preferencesTab); return DraftStore.registerExtension(Extension); }, deactivate() { ComponentRegistry.unregister(TemplatePicker); ComponentRegistry.unregister(TemplateStatusBar); + PreferencesUIStore.unregisterPreferencesTab(this.preferencesTab.sectionId); return DraftStore.unregisterExtension(Extension); }, diff --git a/examples/N1-Composer-Templates/lib/preferences-templates.cjsx b/examples/N1-Composer-Templates/lib/preferences-templates.cjsx new file mode 100644 index 000000000..8cfffe576 --- /dev/null +++ b/examples/N1-Composer-Templates/lib/preferences-templates.cjsx @@ -0,0 +1,238 @@ +_ = require 'underscore' +{Contenteditable, RetinaImg, Flexbox} = require 'nylas-component-kit' +{AccountStore, Utils, React} = require 'nylas-exports' +TemplateStore = require './template-store'; + +class PreferencesTemplates extends React.Component + @displayName: 'PreferencesTemplates' + + constructor: (@props) -> + TemplateStore.init(); + @_templateSaveQueue = {} + + @state = + editAsHTML: false + editState: null + templates: [] + selectedTemplate: null + selectedTemplateName: null + contents: null + + componentDidMount: -> + @usub = TemplateStore.listen @_onChange + + componentWillUnmount: -> + @usub() + if @state.selectedTemplate? + @_saveTemplateNow(@state.selectedTemplate.name, @state.contents) + + + + #SAVING AND LOADING TEMPLATES + _loadTemplateContents: (template) => + if template + TemplateStore.getTemplateContents(template.id, (contents) => + @setState({contents: contents}) + ) + + _saveTemplateNow: (name, contents, callback) => + TemplateStore.saveTemplate(name, contents, false, callback) + + _saveTemplateSoon: (name, contents) => + @_templateSaveQueue[name] = contents + @_saveTemplatesFromCache() + + __saveTemplatesFromCache: => + for name, contents of @_templateSaveQueue + @_saveTemplateNow(name, contents) + + @_templateSaveQueue = {} + + _saveTemplatesFromCache: _.debounce(PreferencesTemplates::__saveTemplatesFromCache, 500) + + + + # OVERALL STATE HANDLING + _onChange: => + @setState @_getStateFromStores() + + _getStateFromStores: -> + templates = TemplateStore.items() + selectedTemplate = @state.selectedTemplate + if selectedTemplate? and selectedTemplate.id not in _.pluck(templates, "id") + selectedTemplate = null + else if not selectedTemplate? + selectedTemplate = if templates.length > 0 then templates[0] else null + @_loadTemplateContents(selectedTemplate) + if selectedTemplate? + selectedTemplateName = @state.selectedTemplateName || selectedTemplate.name + return {templates, selectedTemplate, selectedTemplateName} + + + + # TEMPLATE CONTENT EDITING + _onEditTemplate: (event) => + html = event.target.value + @setState contents: html + if @state.selectedTemplate? + @_saveTemplateSoon(@state.selectedTemplate.name, html) + + _onSelectTemplate: (event) => + if @state.selectedTemplate? + @_saveTemplateNow(@state.selectedTemplate.name, @state.contents) + selectedTemplate = null + for template in @state.templates + if template.id == event.target.value + selectedTemplate = template + @setState + selectedTemplate: selectedTemplate + selectedTemplateName: selectedTemplate?.name + contents: null + @_loadTemplateContents(selectedTemplate) + + _renderTemplatePicker: -> + options = @state.templates.map (template) -> + + + + + _renderEditableTemplate: -> + + + _renderHTMLTemplate: -> +