mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 07:46:06 +08:00
fix(prefs): Move to a sheet rather than a window, use configSchema
Summary: This diff moves the preferences interface to a sheet in the main window, with the following benefits: - We can put any sort of React control in it (no ReactRemote) - It's not strange for the interface to scroll - Since it can scroll, it's safe to auto-generate preferences for plugins based on their package config schema. The general tab is now mostly based on the config schema, with the exception of the "Workspace" and "Layout" bits. The other tabs are still manual, and should be polished more. Test Plan: No new tests Reviewers: evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D2278
This commit is contained in:
parent
971089aeb0
commit
78dd69290d
|
@ -119,7 +119,9 @@ class AccountSwitcher extends React.Component
|
|||
@setState(showing: false)
|
||||
|
||||
_onManageAccounts: =>
|
||||
Actions.openPreferences({tab: 'Accounts'})
|
||||
Actions.switchPreferencesSection('Accounts')
|
||||
Actions.openPreferences()
|
||||
|
||||
@setState(showing: false)
|
||||
|
||||
_getStateFromStores: =>
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
{PreferencesSectionStore} = require 'nylas-exports'
|
||||
{PreferencesSectionStore,
|
||||
Actions,
|
||||
WorkspaceStore,
|
||||
ComponentRegistry} = require 'nylas-exports'
|
||||
|
||||
module.exports =
|
||||
activate: (@state={}) ->
|
||||
|
||||
activate: ->
|
||||
ipc = require 'ipc'
|
||||
React = require 'react'
|
||||
{Actions} = require('nylas-exports')
|
||||
|
||||
Cfg = PreferencesSectionStore.SectionConfig
|
||||
|
||||
|
@ -29,38 +32,20 @@ module.exports =
|
|||
component: require './tabs/preferences-keymaps'
|
||||
order: 3
|
||||
})
|
||||
PreferencesSectionStore.registerPreferenceSection(new Cfg {
|
||||
icon: 'ic-settings-notifications.png'
|
||||
sectionId: 'Notifications'
|
||||
displayName: 'Notifications'
|
||||
component: require './tabs/preferences-notifications'
|
||||
order: 4
|
||||
})
|
||||
PreferencesSectionStore.registerPreferenceSection(new Cfg {
|
||||
icon: 'ic-settings-appearance.png'
|
||||
sectionId: 'Appearance'
|
||||
displayName: 'Appearance'
|
||||
component: require './tabs/preferences-appearance'
|
||||
order: 5
|
||||
})
|
||||
|
||||
WorkspaceStore.defineSheet 'Preferences', {},
|
||||
split: ['Preferences']
|
||||
list: ['Preferences']
|
||||
|
||||
PreferencesRoot = require('./preferences-root')
|
||||
ComponentRegistry.register PreferencesRoot,
|
||||
location: WorkspaceStore.Location.Preferences
|
||||
|
||||
Actions.openPreferences.listen(@_openPreferences)
|
||||
ipc.on 'open-preferences', => @_openPreferences()
|
||||
|
||||
_openPreferences: ({tab} = {}) ->
|
||||
{ReactRemote} = require('nylas-exports')
|
||||
Preferences = require('./preferences')
|
||||
ReactRemote.openWindowForComponent(Preferences, {
|
||||
tag: 'preferences'
|
||||
title: "Preferences"
|
||||
width: 520
|
||||
resizable: false
|
||||
autosize: true
|
||||
stylesheetRegex: /(preferences|nylas\-fonts)/
|
||||
props: {
|
||||
initialTab: tab
|
||||
}
|
||||
})
|
||||
_openPreferences: ->
|
||||
Actions.pushSheet(WorkspaceStore.Sheet.Preferences)
|
||||
|
||||
deactivate: ->
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
|
||||
class PreferencesHeader extends React.Component
|
||||
@displayName: 'PreferencesHeader'
|
||||
|
||||
@propTypes:
|
||||
tabs: React.PropTypes.array.isRequired
|
||||
changeActiveTab: React.PropTypes.func.isRequired
|
||||
activeTab: React.PropTypes.object
|
||||
|
||||
render: =>
|
||||
if process.platform is "win32"
|
||||
imgMode = RetinaImg.Mode.ContentIsMask
|
||||
else
|
||||
imgMode = RetinaImg.Mode.ContentPreserve
|
||||
|
||||
<div className="preference-header">
|
||||
{ @props.tabs.map (sectionConfig) =>
|
||||
classname = "preference-header-item"
|
||||
classname += " active" if sectionConfig is @props.activeTab
|
||||
|
||||
<div className={classname} onClick={ => @props.changeActiveTab(sectionConfig) } key={sectionConfig.sectionId}>
|
||||
<div className="phi-container">
|
||||
<div className="icon">
|
||||
<RetinaImg mode={imgMode} {...sectionConfig.nameOrUrl()} />
|
||||
</div>
|
||||
<div className="name">
|
||||
{sectionConfig.displayName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
module.exports = PreferencesHeader
|
43
internal_packages/preferences/lib/preferences-root.cjsx
Normal file
43
internal_packages/preferences/lib/preferences-root.cjsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox, ConfigPropContainer, ScrollRegion} = require 'nylas-component-kit'
|
||||
{PreferencesSectionStore} = require 'nylas-exports'
|
||||
|
||||
PreferencesSidebar = require './preferences-sidebar'
|
||||
|
||||
class PreferencesRoot extends React.Component
|
||||
@displayName: 'PreferencesRoot'
|
||||
@containerRequired: false
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = @getStateFromStores()
|
||||
|
||||
componentDidMount: =>
|
||||
@unlisteners = []
|
||||
@unlisteners.push PreferencesSectionStore.listen =>
|
||||
@setState(@getStateFromStores())
|
||||
|
||||
componentWillUnmount: =>
|
||||
unlisten() for unlisten in @unlisteners
|
||||
|
||||
getStateFromStores: =>
|
||||
sections: PreferencesSectionStore.sections()
|
||||
activeSectionId: PreferencesSectionStore.activeSectionId()
|
||||
|
||||
render: =>
|
||||
section = _.find @state.sections, ({sectionId}) => sectionId is @state.activeSectionId
|
||||
|
||||
if section
|
||||
bodyElement = <section.component />
|
||||
else
|
||||
bodyElement = <div>No Section Active</div>
|
||||
|
||||
<Flexbox direction="row" className="preferences-wrap">
|
||||
<PreferencesSidebar sections={@state.sections}
|
||||
activeSectionId={@state.activeSectionId} />
|
||||
<ScrollRegion className="preferences-content">
|
||||
<ConfigPropContainer>{bodyElement}</ConfigPropContainer>
|
||||
</ScrollRegion>
|
||||
</Flexbox>
|
||||
|
||||
module.exports = PreferencesRoot
|
30
internal_packages/preferences/lib/preferences-sidebar.cjsx
Normal file
30
internal_packages/preferences/lib/preferences-sidebar.cjsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
{Actions} = require 'nylas-exports'
|
||||
|
||||
class PreferencesSidebar extends React.Component
|
||||
@displayName: 'PreferencesSidebar'
|
||||
|
||||
@propTypes:
|
||||
sections: React.PropTypes.array.isRequired
|
||||
activeSectionId: React.PropTypes.string
|
||||
|
||||
render: =>
|
||||
<div className="preferences-sidebar">
|
||||
{ @props.sections.map ({sectionId, displayName}) =>
|
||||
classname = "item"
|
||||
classname += " active" if sectionId is @props.activeSectionId
|
||||
|
||||
<div key={sectionId}
|
||||
className={classname}
|
||||
onClick={ => Actions.switchPreferencesSection(sectionId) }>
|
||||
<div className="name">
|
||||
{displayName}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
module.exports = PreferencesSidebar
|
|
@ -1,53 +0,0 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox, ConfigPropContainer} = require 'nylas-component-kit'
|
||||
{PreferencesSectionStore} = require 'nylas-exports'
|
||||
|
||||
PreferencesHeader = require './preferences-header'
|
||||
|
||||
class Preferences extends React.Component
|
||||
@displayName: 'Preferences'
|
||||
|
||||
constructor: (@props) ->
|
||||
tabs = PreferencesSectionStore.sections()
|
||||
if @props.initialTab
|
||||
activeTab = _.find tabs, (t) => t.name is @props.initialTab
|
||||
activeTab ||= tabs[0]
|
||||
|
||||
@state = _.extend(@getStateFromStores(), {activeTab})
|
||||
|
||||
componentDidMount: =>
|
||||
@unlisteners = []
|
||||
@unlisteners.push PreferencesSectionStore.listen =>
|
||||
@setState(@getStateFromStores())
|
||||
|
||||
componentWillUnmount: =>
|
||||
unlisten() for unlisten in @unlisteners
|
||||
|
||||
componentDidUpdate: =>
|
||||
if @state.tabs.length > 0 and not @state.activeTab
|
||||
@setState(activeTab: @state.tabs[0])
|
||||
|
||||
getStateFromStores: =>
|
||||
tabs: PreferencesSectionStore.sections()
|
||||
|
||||
render: =>
|
||||
if @state.activeTab
|
||||
bodyElement = <@state.activeTab.component config={@state.config} />
|
||||
else
|
||||
bodyElement = <div>No Tab Active</div>
|
||||
|
||||
<div className="preferences-wrap">
|
||||
<PreferencesHeader tabs={@state.tabs}
|
||||
activeTab={@state.activeTab}
|
||||
changeActiveTab={@_onChangeActiveTab}/>
|
||||
<ConfigPropContainer>
|
||||
{bodyElement}
|
||||
</ConfigPropContainer>
|
||||
<div style={clear:'both'}></div>
|
||||
</div>
|
||||
|
||||
_onChangeActiveTab: (tab) =>
|
||||
@setState(activeTab: tab)
|
||||
|
||||
module.exports = Preferences
|
|
@ -0,0 +1,63 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
_str = require 'underscore.string'
|
||||
|
||||
###
|
||||
This component renders input controls for a subtree of the N1 config-schema
|
||||
and reads/writes current values using the `config` prop, which is expected to
|
||||
be an instance of the config provided by `ConfigPropContainer`.
|
||||
|
||||
The config schema follows the JSON Schema standard: http://json-schema.org/
|
||||
###
|
||||
class ConfigSchemaItem extends React.Component
|
||||
@displayName: 'ConfigSchemaItem'
|
||||
@propTypes:
|
||||
config: React.PropTypes.object
|
||||
configSchema: React.PropTypes.object
|
||||
keyPath: React.PropTypes.string
|
||||
|
||||
render: ->
|
||||
return false unless @_appliesToPlatform()
|
||||
if @props.configSchema.type is 'object'
|
||||
<section>
|
||||
<h2>{_str.humanize(@props.keyName)}</h2>
|
||||
{_.pairs(@props.configSchema.properties).map ([key, value]) =>
|
||||
<ConfigSchemaItem
|
||||
keyName={key}
|
||||
keyPath={"#{@props.keyPath}.#{key}"}
|
||||
configSchema={value}
|
||||
config={@props.config}
|
||||
/>
|
||||
}
|
||||
</section>
|
||||
|
||||
else if @props.configSchema['enum']?
|
||||
<div className="item">
|
||||
<label htmlFor={@props.keyPath}>{@props.configSchema.title}:</label>
|
||||
<select onChange={@_onChangeValue}>
|
||||
{_.zip(@props.configSchema.enum, @props.configSchema.enumLabels).map ([value, label]) =>
|
||||
<option value={value}>{label}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
else if @props.configSchema.type is 'boolean'
|
||||
<div className="item">
|
||||
<input id={@props.keyPath} type="checkbox" onChange={@_onChangeChecked} checked={ @props.config.get(@props.keyPath) }/>
|
||||
<label htmlFor={@props.keyPath}>{@props.configSchema.title}</label>
|
||||
</div>
|
||||
else
|
||||
<span></span>
|
||||
|
||||
_appliesToPlatform: =>
|
||||
return true if not @props.configSchema.platforms?
|
||||
return true if process.platform in @props.configSchema.platforms
|
||||
return false
|
||||
|
||||
_onChangeChecked: =>
|
||||
@props.config.toggle(@props.keyPath)
|
||||
|
||||
_onChangeValue: (event) =>
|
||||
@props.config.set(@props.keyPath, event.target.value)
|
||||
|
||||
module.exports = ConfigSchemaItem
|
|
@ -16,22 +16,20 @@ class PreferencesAccounts extends React.Component
|
|||
@unsubscribe?()
|
||||
|
||||
render: =>
|
||||
<div className="container-accounts">
|
||||
<section className="container-accounts">
|
||||
<h2>Accounts</h2>
|
||||
{@_renderAccounts()}
|
||||
<div style={textAlign:"right", marginTop: '20'}>
|
||||
<button className="btn btn-large" onClick={@_onAddAccount}>Add Account...</button>
|
||||
</div>
|
||||
|
||||
{@_renderLinkedAccounts()}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
_renderAccounts: =>
|
||||
return false unless @state.accounts
|
||||
|
||||
<div>
|
||||
<div className="section-header">
|
||||
Accounts:
|
||||
</div>
|
||||
{ @state.accounts.map (account) =>
|
||||
<div className="well large" style={marginBottom:10} key={account.id}>
|
||||
<Flexbox direction="row" style={alignItems: 'middle'}>
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
|
||||
class AppearanceModeOption extends React.Component
|
||||
@propTypes:
|
||||
mode: React.PropTypes.string.isRequired
|
||||
active: React.PropTypes.bool
|
||||
onClick: React.PropTypes.func
|
||||
|
||||
constructor: (@props) ->
|
||||
|
||||
render: =>
|
||||
classname = "appearance-mode"
|
||||
classname += " active" if @props.active
|
||||
<div className={classname} onClick={@props.onClick}>
|
||||
<RetinaImg name={"appearance-mode-#{@props.mode}.png"} mode={RetinaImg.Mode.ContentIsMask}/>
|
||||
<div>{@props.mode} View</div>
|
||||
</div>
|
||||
|
||||
class PreferencesAppearance extends React.Component
|
||||
@displayName: 'PreferencesAppearance'
|
||||
@propTypes:
|
||||
config: React.PropTypes.object
|
||||
|
||||
render: =>
|
||||
<div className="container-appearance">
|
||||
<div className="section">
|
||||
<div className="section-header">
|
||||
Layout and theme:
|
||||
</div>
|
||||
<div className="section-body section-appearance">
|
||||
<Flexbox direction="row" style={alignItems: "center"}>
|
||||
{['list', 'split'].map (mode) =>
|
||||
<AppearanceModeOption
|
||||
mode={mode} key={mode}
|
||||
active={@props.config.get('core.workspace.mode') is mode}
|
||||
onClick={ => @props.config.set('core.workspace.mode', mode)} />
|
||||
}
|
||||
</Flexbox>
|
||||
|
||||
<div className="section-header">
|
||||
<input type="checkbox"
|
||||
id="dark"
|
||||
checked={@props.config.contains('core.themes','ui-dark')}
|
||||
onChange={ => @props.config.toggleContains('core.themes', 'ui-dark')}
|
||||
/>
|
||||
<label htmlFor="dark">Use dark color scheme</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
module.exports = PreferencesAppearance
|
|
@ -1,109 +1,43 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
{LaunchServices, AccountStore} = require 'nylas-exports'
|
||||
{AccountStore} = require 'nylas-exports'
|
||||
|
||||
ConfigSchemaItem = require './config-schema-item'
|
||||
WorkspaceSection = require './workspace-section'
|
||||
|
||||
class PreferencesGeneral extends React.Component
|
||||
@displayName: 'PreferencesGeneral'
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = {}
|
||||
|
||||
@_services = new LaunchServices()
|
||||
if @_services.available()
|
||||
@_services.isRegisteredForURLScheme 'mailto', (registered) =>
|
||||
@setState(defaultClient: registered)
|
||||
|
||||
toggleDefaultMailClient: =>
|
||||
if @state.defaultClient is true
|
||||
@setState(defaultClient: false)
|
||||
@_services.resetURLScheme('mailto')
|
||||
else
|
||||
@setState(defaultClient: true)
|
||||
@_services.registerForURLScheme('mailto')
|
||||
|
||||
toggleShowImportant: (event) =>
|
||||
@props.config.toggle('core.showImportant')
|
||||
event.preventDefault()
|
||||
|
||||
toggleAutoloadImages: (event) =>
|
||||
@props.config.toggle('core.reading.autoloadImages')
|
||||
event.preventDefault()
|
||||
|
||||
toggleShowSystemTrayIcon: (event) =>
|
||||
@props.config.toggle('core.showSystemTray')
|
||||
event.preventDefault()
|
||||
|
||||
_renderImportanceOptionElement: =>
|
||||
return false unless AccountStore.current()?.usesImportantFlag()
|
||||
importanceOptionElement = <div className="section-header">
|
||||
<input type="checkbox" id="show-important"
|
||||
checked={@props.config.get('core.showImportant')}
|
||||
onChange={@toggleShowImportant}/>
|
||||
<label htmlFor="show-important">Show Gmail-style important markers</label>
|
||||
</div>
|
||||
|
||||
render: =>
|
||||
<div className="container-notifications">
|
||||
<div className="section">
|
||||
<div className="section-header platform-darwin-only">
|
||||
<input type="checkbox" id="default-client" checked={@state.defaultClient} onChange={@toggleDefaultMailClient}/>
|
||||
<label htmlFor="default-client">Use Nylas as my default mail client</label>
|
||||
</div>
|
||||
<div className="container" style={maxWidth:600}>
|
||||
|
||||
<div className="section-header platform-darwin-only">
|
||||
<input type="checkbox" id="show-system-tray"
|
||||
checked={@props.config.get('core.showSystemTray')}
|
||||
onChange={@toggleShowSystemTrayIcon}/>
|
||||
<label htmlFor="show-system-tray">Show N1 icon in the menu bar</label>
|
||||
</div>
|
||||
<WorkspaceSection config={@props.config} configSchema={@props.configSchema} />
|
||||
|
||||
{@_renderImportanceOptionElement()}
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.notifications}
|
||||
keyName="Notifications"
|
||||
keyPath="core.notifications"
|
||||
config={@props.config} />
|
||||
|
||||
<div className="section-header">
|
||||
<input type="checkbox" id="autoload-images"
|
||||
checked={@props.config.get('core.reading.autoloadImages')}
|
||||
onChange={@toggleAutoloadImages}/>
|
||||
<label htmlFor="autoload-images">Automatically load images in viewed messages</label>
|
||||
</div>
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.reading}
|
||||
keyName="Reading"
|
||||
keyPath="core.reading"
|
||||
config={@props.config} />
|
||||
|
||||
<div className="section-header" style={marginTop:30}>
|
||||
Delay for marking messages as read:
|
||||
<select value={@props.config.get('core.reading.markAsReadDelay')}
|
||||
onChange={ (event) => @props.config.set('core.reading.markAsReadDelay', event.target.value) }>
|
||||
<option value={0}>Instantly</option>
|
||||
<option value={500}>½ Second</option>
|
||||
<option value={2000}>2 Seconds</option>
|
||||
</select>
|
||||
</div>
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.sending}
|
||||
keyName="Sending"
|
||||
keyPath="core.sending"
|
||||
config={@props.config} />
|
||||
|
||||
<div className="section-header">
|
||||
Download attachments for new mail:
|
||||
<select value={@props.config.get('core.attachments.downloadPolicy')}
|
||||
onChange={ (event) => @props.config.set('core.attachments.downloadPolicy', event.target.value) }>
|
||||
<option value="on-receive">When Received</option>
|
||||
<option value="on-read">When Reading</option>
|
||||
<option value="manually">Manually</option>
|
||||
</select>
|
||||
</div>
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.attachments}
|
||||
keyName="Attachments"
|
||||
keyPath="core.attachments"
|
||||
config={@props.config} />
|
||||
|
||||
<div className="section-header">
|
||||
Default reply behavior:
|
||||
<div style={float:'right', width:138}>
|
||||
<input type="radio"
|
||||
id="core.sending.defaultReplyType.reply"
|
||||
checked={@props.config.get('core.sending.defaultReplyType') == 'reply'}
|
||||
onChange={ => @props.config.set('core.sending.defaultReplyType', 'reply') }/>
|
||||
<label htmlFor="core.sending.defaultReplyType.reply">Reply</label>
|
||||
<br/>
|
||||
<input type="radio"
|
||||
id="core.sending.defaultReplyType.replyAll"
|
||||
checked={@props.config.get('core.sending.defaultReplyType') == 'reply-all'}
|
||||
onChange={ => @props.config.set('core.sending.defaultReplyType', 'reply-all') }/>
|
||||
<label htmlFor="core.sending.defaultReplyType.replyAll">Reply all</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
module.exports = PreferencesGeneral
|
||||
|
|
|
@ -49,24 +49,28 @@ class PreferencesKeymaps extends React.Component
|
|||
|
||||
render: =>
|
||||
<div className="container-keymaps">
|
||||
<Flexbox className="shortcut shortcut-select">
|
||||
<div className="shortcut-name">Keyboard shortcut set:</div>
|
||||
<div className="shortcut-value">
|
||||
<select
|
||||
style={margin:0}
|
||||
value={@props.config.get('core.keymapTemplate')}
|
||||
onChange={ (event) => @props.config.set('core.keymapTemplate', event.target.value) }>
|
||||
{ @state.templates.map (template) =>
|
||||
<option key={template} value={template}>{template}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</Flexbox>
|
||||
{@_renderBindings()}
|
||||
|
||||
<div className="shortcuts-extras">
|
||||
<section>
|
||||
<h2>Shortcuts</h2>
|
||||
<Flexbox className="shortcut shortcut-select">
|
||||
<div className="shortcut-name">Keyboard shortcut set:</div>
|
||||
<div className="shortcut-value">
|
||||
<select
|
||||
style={margin:0}
|
||||
value={@props.config.get('core.keymapTemplate')}
|
||||
onChange={ (event) => @props.config.set('core.keymapTemplate', event.target.value) }>
|
||||
{ @state.templates.map (template) =>
|
||||
<option key={template} value={template}>{template}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</Flexbox>
|
||||
{@_renderBindings()}
|
||||
</section>
|
||||
<section>
|
||||
<h2>Customization</h2>
|
||||
<p>Define additional shortcuts by adding them to your shortcuts file.</p>
|
||||
<button className="btn" onClick={@_onShowUserKeymaps}>Edit custom shortcuts</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
_renderBindingFor: ([command, label]) =>
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
|
||||
class PreferencesNotifications extends React.Component
|
||||
@displayName: 'PreferencesNotifications'
|
||||
|
||||
@propTypes:
|
||||
config: React.PropTypes.object.isRequired
|
||||
|
||||
render: =>
|
||||
<div className="container-notifications">
|
||||
<div className="section">
|
||||
<div className="section-header">
|
||||
Notifications:
|
||||
</div>
|
||||
<div className="section-body">
|
||||
<p className="platform-darwin-only">
|
||||
<input type="checkbox"
|
||||
id="core.showUnreadBadge"
|
||||
checked={@props.config.get('core.showUnreadBadge')}
|
||||
onChange={ => @props.config.toggle('core.showUnreadBadge')}/>
|
||||
<label htmlFor="core.showUnreadBadge">Badge dock icon with unread message count</label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox"
|
||||
id="core.notifications.enabled"
|
||||
checked={@props.config.get('core.notifications.enabled')}
|
||||
onChange={ => @props.config.toggle('core.notifications.enabled')}/>
|
||||
<label htmlFor="core.notifications.enabled">Show notifications for new unread messages</label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="section-header">
|
||||
Sounds:
|
||||
</div>
|
||||
<div className="section-body">
|
||||
<p>
|
||||
<input type="checkbox"
|
||||
id="core.notifications.sounds"
|
||||
checked={@props.config.get('core.notifications.sounds')}
|
||||
onChange={ => @props.config.toggle('core.notifications.sounds')}/>
|
||||
<label htmlFor="core.notifications.sounds">Play sound when receiving new mail</label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox"
|
||||
id="core.sending.sounds"
|
||||
checked={@props.config.get('core.sending.sounds')}
|
||||
onChange={ => @props.config.toggle('core.sending.sounds')}/>
|
||||
<label htmlFor="core.sending.sounds">Play sound when a message is sent</label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
module.exports = PreferencesNotifications
|
122
internal_packages/preferences/lib/tabs/workspace-section.cjsx
Normal file
122
internal_packages/preferences/lib/tabs/workspace-section.cjsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
React = require 'react'
|
||||
{RetinaImg, Flexbox} = require 'nylas-component-kit'
|
||||
{LaunchServices, AccountStore} = require 'nylas-exports'
|
||||
ConfigSchemaItem = require './config-schema-item'
|
||||
|
||||
class DefaultMailClientItem extends React.Component
|
||||
constructor: (@props) ->
|
||||
@state = {}
|
||||
@_services = new LaunchServices()
|
||||
if @_services.available()
|
||||
@_services.isRegisteredForURLScheme 'mailto', (registered) =>
|
||||
@setState(defaultClient: registered)
|
||||
|
||||
render: =>
|
||||
return false unless process.platform is 'darwin'
|
||||
<div className="item">
|
||||
<input type="checkbox" id="default-client" checked={@state.defaultClient} onChange={@toggleDefaultMailClient}/>
|
||||
<label htmlFor="default-client">Use Nylas as my default mail client</label>
|
||||
</div>
|
||||
|
||||
toggleDefaultMailClient: =>
|
||||
if @state.defaultClient is true
|
||||
@setState(defaultClient: false)
|
||||
@_services.resetURLScheme('mailto')
|
||||
else
|
||||
@setState(defaultClient: true)
|
||||
@_services.registerForURLScheme('mailto')
|
||||
|
||||
class AppearanceModeSwitch extends React.Component
|
||||
@displayName: 'AppearanceModeSwitch'
|
||||
@propTypes:
|
||||
config: React.PropTypes.object.isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = {
|
||||
value: @props.config.get('core.workspace.mode')
|
||||
}
|
||||
|
||||
componentWillReceiveProps: (nextProps) ->
|
||||
@setState({
|
||||
value: nextProps.config.get('core.workspace.mode')
|
||||
})
|
||||
|
||||
render: ->
|
||||
hasChanges = @state.value isnt @props.config.get('core.workspace.mode')
|
||||
applyChangesClass = "btn btn-small"
|
||||
applyChangesClass += " btn-disabled" unless hasChanges
|
||||
|
||||
<div className="appearance-mode-switch">
|
||||
<Flexbox
|
||||
direction="row"
|
||||
style={alignItems: "center"}
|
||||
className="item">
|
||||
{@_renderModeOptions()}
|
||||
</Flexbox>
|
||||
<div className={applyChangesClass} onClick={@_onApplyChanges}>Apply Changes</div>
|
||||
</div>
|
||||
|
||||
_renderModeOptions: ->
|
||||
['list', 'split'].map (mode) =>
|
||||
<AppearanceModeOption
|
||||
mode={mode}
|
||||
key={mode}
|
||||
active={@state.value is mode}
|
||||
onClick={ => @setState(value: mode) } />
|
||||
|
||||
_onApplyChanges: =>
|
||||
@props.config.set('core.workspace.mode', @state.value)
|
||||
|
||||
|
||||
class AppearanceModeOption extends React.Component
|
||||
@propTypes:
|
||||
mode: React.PropTypes.string.isRequired
|
||||
active: React.PropTypes.bool
|
||||
onClick: React.PropTypes.func
|
||||
|
||||
constructor: (@props) ->
|
||||
|
||||
render: =>
|
||||
classname = "appearance-mode"
|
||||
classname += " active" if @props.active
|
||||
<div className={classname} onClick={@props.onClick}>
|
||||
<RetinaImg name={"appearance-mode-#{@props.mode}.png"} mode={RetinaImg.Mode.ContentIsMask}/>
|
||||
<div>{@props.mode} View</div>
|
||||
</div>
|
||||
|
||||
class WorkspaceSection extends React.Component
|
||||
@displayName: 'WorkspaceSection'
|
||||
@propTypes:
|
||||
config: React.PropTypes.object
|
||||
configSchema: React.PropTypes.object
|
||||
|
||||
render: =>
|
||||
<section>
|
||||
<h2>Workspace</h2>
|
||||
<DefaultMailClientItem />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.workspace.properties.systemTray}
|
||||
keyPath="core.workspace.systemTray"
|
||||
config={@props.config} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={@props.configSchema.properties.workspace.properties.showImportant}
|
||||
keyPath="core.workspace.showImportant"
|
||||
config={@props.config} />
|
||||
|
||||
<div className="item">
|
||||
<input type="checkbox"
|
||||
id="dark"
|
||||
checked={@props.config.contains('core.themes','ui-dark')}
|
||||
onChange={ => @props.config.toggleContains('core.themes', 'ui-dark')}
|
||||
/>
|
||||
<label htmlFor="dark">Use dark color scheme</label>
|
||||
</div>
|
||||
|
||||
<h2>Layout</h2>
|
||||
|
||||
<AppearanceModeSwitch config={@props.config} />
|
||||
</section>
|
||||
|
||||
module.exports = WorkspaceSection
|
|
@ -1,61 +1,56 @@
|
|||
@import "ui-variables";
|
||||
@import "ui-mixins";
|
||||
|
||||
body.window-type-react-remote {
|
||||
margin:0;
|
||||
|
||||
.sheet-toolbar {
|
||||
border-bottom: none;
|
||||
height:30px;
|
||||
min-height:30px;
|
||||
max-height:30px;
|
||||
display: none;
|
||||
|
||||
.toolbar-window-controls {
|
||||
margin-top:3px;
|
||||
margin-left:3px;
|
||||
}
|
||||
.window-title {
|
||||
line-height:30px;
|
||||
}
|
||||
}
|
||||
.sheet {
|
||||
background: @background-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
body.platform-darwin.window-type-react-remote {
|
||||
.sheet-toolbar {
|
||||
display:inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// Preferences Specific
|
||||
|
||||
body.is-blurred {
|
||||
.preference-header {
|
||||
background-image: -webkit-linear-gradient(top, lighten(@toolbar-background-color, 14%), lighten(@toolbar-background-color, 14%));
|
||||
}
|
||||
}
|
||||
|
||||
body.platform-darwin {
|
||||
.preferences-wrap .platform-darwin-only {
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.preferences-wrap {
|
||||
input[type=checkbox] {margin: 0 7px 0 0; position: relative; top: -1px; }
|
||||
input[type=radio] {margin: 0 7px 0 0; position: relative; top: -1px; }
|
||||
select { margin: 4px 0 0 8px; }
|
||||
|
||||
padding-bottom:50px;
|
||||
|
||||
height: 100%;
|
||||
background-color: @background-primary;
|
||||
color: @text-color;
|
||||
|
||||
.platform-darwin-only {
|
||||
display: none;
|
||||
section:first-child h2:first-child {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
section section h2 {
|
||||
font-size:120%;
|
||||
}
|
||||
|
||||
section {
|
||||
padding-bottom: @padding-base-vertical;
|
||||
.item {
|
||||
padding-top: @padding-small-vertical;
|
||||
padding-bottom: @padding-small-vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.preferences-sidebar {
|
||||
background: @background-secondary;
|
||||
border-right: 1px solid @border-color-divider;
|
||||
flex: 1;
|
||||
max-width:350px;
|
||||
min-width:200px;
|
||||
height: 100%;
|
||||
|
||||
.item {
|
||||
padding: @padding-large-vertical @padding-large-horizontal;
|
||||
border-bottom: 1px solid @border-color-divider;
|
||||
cursor: default;
|
||||
}
|
||||
.item.active {
|
||||
background: @background-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.preferences-content {
|
||||
flex: 4;
|
||||
.scroll-region-content {
|
||||
padding: @padding-large-vertical*3 @padding-large-horizontal * 3;
|
||||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
|
@ -72,105 +67,68 @@ body.platform-darwin {
|
|||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top:25px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
line-height: 2em;
|
||||
margin-top:7px;
|
||||
margin-bottom:2px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
padding:5px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.section-body-list {
|
||||
flex: 1;
|
||||
img { width:100%; padding:15px;}
|
||||
}
|
||||
}
|
||||
|
||||
.container-signatures {
|
||||
width:95%;
|
||||
margin-left: 2.5%;
|
||||
margin-right: 2.5%;
|
||||
.contenteditable-container {
|
||||
border: 1px solid @input-border-color;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.contenteditable-container {
|
||||
border: 1px solid @input-border-color;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
.section-body {
|
||||
padding: 10px 0 0 0;
|
||||
|
||||
.menu {
|
||||
border: solid thin #CCC;
|
||||
margin-right: 5px;
|
||||
min-height: 200px;
|
||||
.menu-items {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style: none;
|
||||
|
||||
li { padding: 6px; }
|
||||
}
|
||||
}
|
||||
.menu-horizontal {
|
||||
height: 100%;
|
||||
.menu-items {
|
||||
height:100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
li {
|
||||
text-align:center;
|
||||
width:40px;
|
||||
display:inline-block;
|
||||
padding:8px 16px 8px 16px;
|
||||
border-right: solid thin #CCC;
|
||||
}
|
||||
}
|
||||
}
|
||||
.signature-area {
|
||||
border: solid thin #CCC;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
padding: 10px 0 0 0;
|
||||
|
||||
.menu {
|
||||
border: solid thin #CCC;
|
||||
margin-right: 5px;
|
||||
min-height: 200px;
|
||||
.menu-items {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style: none;
|
||||
|
||||
li { padding: 6px; }
|
||||
}
|
||||
}
|
||||
.menu-horizontal {
|
||||
height: 100%;
|
||||
.menu-items {
|
||||
height:100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
li {
|
||||
text-align:center;
|
||||
width:40px;
|
||||
display:inline-block;
|
||||
padding:8px 16px 8px 16px;
|
||||
border-right: solid thin #CCC;
|
||||
}
|
||||
}
|
||||
}
|
||||
.signature-area {
|
||||
border: solid thin #CCC;
|
||||
min-height: 200px;
|
||||
}
|
||||
.menu-footer {
|
||||
border: solid thin #CCC;
|
||||
overflow: auto;
|
||||
}
|
||||
.signature-footer {
|
||||
border: solid thin #CCC;
|
||||
overflow: auto;
|
||||
|
||||
.edit-html-button {
|
||||
float: right;
|
||||
margin: 6px;
|
||||
}
|
||||
}
|
||||
.menu-footer {
|
||||
border: solid thin #CCC;
|
||||
overflow: auto;
|
||||
}
|
||||
.signature-footer {
|
||||
border: solid thin #CCC;
|
||||
overflow: auto;
|
||||
|
||||
}
|
||||
|
||||
.container-notifications {
|
||||
width: 75%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.container-appearance {
|
||||
width:70%;
|
||||
margin: auto;
|
||||
|
||||
.section-appearance {
|
||||
padding-left: 0;
|
||||
.edit-html-button {
|
||||
float: right;
|
||||
margin: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.appearance-mode-switch {
|
||||
max-width:400px;
|
||||
text-align: right;
|
||||
|
||||
.appearance-mode {
|
||||
background-color: @background-off-primary;;
|
||||
|
@ -181,6 +139,7 @@ body.platform-darwin {
|
|||
padding:25px;
|
||||
padding-bottom:9px;
|
||||
margin-right:10px;
|
||||
margin-bottom:7px;
|
||||
margin-top:0;
|
||||
img {
|
||||
background-color: @background-tertiary;
|
||||
|
@ -190,6 +149,9 @@ body.platform-darwin {
|
|||
text-transform: capitalize;
|
||||
cursor: default;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right:0;
|
||||
}
|
||||
}
|
||||
.appearance-mode.active {
|
||||
border:1px solid @component-active-color;
|
||||
|
@ -199,129 +161,43 @@ body.platform-darwin {
|
|||
}
|
||||
|
||||
.container-keymaps {
|
||||
width:75%;
|
||||
margin-left:12.5%;
|
||||
margin-right:12.5%;
|
||||
|
||||
.shortcut {
|
||||
padding: 3px 0;
|
||||
&.shortcut-select {
|
||||
padding: 5px 0 20px 0;
|
||||
select {
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-name {
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.shortcut-value {
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
}
|
||||
.shortcut {
|
||||
padding: 3px 0;
|
||||
&.shortcut-select {
|
||||
padding: 5px 0 20px 0;
|
||||
select {
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
.shortcuts-extras {
|
||||
text-align:center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.shortcut-name {
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.shortcut-value {
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-accounts {
|
||||
margin-left:20px;
|
||||
margin-right:20px;
|
||||
|
||||
.account-name {
|
||||
font-size: @font-size-large;
|
||||
cursor:default;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: @font-size-large;
|
||||
cursor:default;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.account-subtext {
|
||||
font-size: @font-size-small;
|
||||
cursor:default;
|
||||
font-size: @font-size-small;
|
||||
cursor:default;
|
||||
}
|
||||
}
|
||||
|
||||
.preference-header {
|
||||
margin-bottom:15px;
|
||||
border-bottom: 1px solid darken(@toolbar-background-color, 9%);
|
||||
background-image: -webkit-linear-gradient(top, @toolbar-background-color, darken(@toolbar-background-color, 2%));
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
|
||||
.preference-header-item {
|
||||
margin-left:3px;
|
||||
margin-right:3px;
|
||||
padding-left:3px;
|
||||
padding-right:3px;
|
||||
-webkit-app-region: no-drag;
|
||||
display: inline-block;
|
||||
|
||||
.phi-container {
|
||||
padding-left:5px;
|
||||
padding-right:5px;
|
||||
padding-top: 3px;
|
||||
text-align: center;
|
||||
height:58px;
|
||||
|
||||
.icon {
|
||||
height:33px;
|
||||
}
|
||||
.name {
|
||||
font-size:12px;
|
||||
line-height:22px;
|
||||
white-space:nowrap;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
background-color:rgba(0,0,0, 0.12);
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.platform-win32.is-blurred {
|
||||
.preference-header {
|
||||
background: lighten(@toolbar-background-color, 14%);
|
||||
}
|
||||
}
|
||||
|
||||
body.platform-win32 {
|
||||
.preference-header {
|
||||
background: #f2f2f2;
|
||||
|
||||
.preference-header-item {
|
||||
margin-left: 0;
|
||||
margin-right: 1px;
|
||||
width: 80px;
|
||||
padding-top: 3px;
|
||||
.windows-btn-bg;
|
||||
height: 63px;
|
||||
|
||||
color: @text-color;
|
||||
.icon img.content-mask { background: @text-color; }
|
||||
|
||||
&:hover {
|
||||
background: #e5e5e5;
|
||||
}
|
||||
&.active {
|
||||
background: #f2f2f2;
|
||||
border-radius: 0 0 0 0;
|
||||
border-bottom: 2px solid @accent-primary;
|
||||
color: @accent-primary;
|
||||
.icon img.content-mask { background: @accent-primary; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preferences-wrap {
|
||||
.well {
|
||||
border-radius: 0;
|
||||
|
|
|
@ -21,8 +21,8 @@ export function activate() {
|
|||
}
|
||||
};
|
||||
|
||||
unsubConfig = NylasEnv.config.onDidChange('core.showSystemTray', onSystemTrayToggle).dispose;
|
||||
if (NylasEnv.config.get('core.showSystemTray')) {
|
||||
unsubConfig = NylasEnv.config.onDidChange('core.workspace.systemTray', onSystemTrayToggle).dispose;
|
||||
if (NylasEnv.config.get('core.workspace.systemTray')) {
|
||||
systemTray = new SystemTray(platform);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ class ConfigPropContainer extends React.Component
|
|||
}
|
||||
|
||||
render: =>
|
||||
React.cloneElement(@props.children, {config: @state.config})
|
||||
React.cloneElement(@props.children, {
|
||||
config: @state.config,
|
||||
configSchema: NylasEnv.config.getSchema('core')
|
||||
})
|
||||
|
||||
module.exports = ConfigPropContainer
|
||||
|
|
|
@ -9,15 +9,15 @@ module.exports =
|
|||
type: 'string'
|
||||
default: 'list'
|
||||
enum: ['split', 'list']
|
||||
showUnreadBadge:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
showImportant:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
showSystemTray:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
systemTray:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Show icon in Mac OS X menu bar"
|
||||
platforms: ['darwin']
|
||||
showImportant:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Show Gmail-style important markers (Gmail Only)"
|
||||
disabledPackages:
|
||||
type: 'array'
|
||||
default: []
|
||||
|
@ -38,31 +38,47 @@ module.exports =
|
|||
type: 'string'
|
||||
default: 'on-read'
|
||||
enum: ['on-receive', 'on-read', 'manually']
|
||||
enumLabels: ['When Received', 'When Read', 'Manually']
|
||||
title: "Download attachments for new mail"
|
||||
reading:
|
||||
type: 'object'
|
||||
properties:
|
||||
markAsReadDelay:
|
||||
type: 'integer'
|
||||
default: 500
|
||||
enum: [0, 500, 2000]
|
||||
enumLabels: ['Instantly', '½ Second', '2 Seconds']
|
||||
title: "Delay for marking messages as read"
|
||||
autoloadImages:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Automatically load images in viewed messages"
|
||||
sending:
|
||||
type: 'object'
|
||||
properties:
|
||||
sounds:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Play sound when a message is sent"
|
||||
defaultReplyType:
|
||||
type: 'string'
|
||||
default: 'reply-all'
|
||||
enum: ['reply', 'reply-all']
|
||||
enumLabels: ['Reply', 'Reply All']
|
||||
title: "Default reply behavior"
|
||||
notifications:
|
||||
type: 'object'
|
||||
properties:
|
||||
enabled:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Show notifications for new unread messages"
|
||||
sounds:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Play sound when receiving new mail"
|
||||
unreadBadge:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: "Show badge on the app icon"
|
||||
platforms: ['darwin']
|
||||
|
|
|
@ -126,19 +126,18 @@ class Actions
|
|||
@retryInitialSync: ActionScopeWorkWindow
|
||||
|
||||
###
|
||||
Public: Open the preferences window. Pass an object with a tab name
|
||||
(ie: `{tab: 'Accounts'}`) to open a specific panel.
|
||||
Public: Open the preferences view.
|
||||
|
||||
*Scope: Window*
|
||||
###
|
||||
@openPreferences: ActionScopeWindow
|
||||
|
||||
###
|
||||
Public: Register a preferences tab, usually applied in Preferences window
|
||||
Public: Switch to the preferences tab with the specific name
|
||||
|
||||
*Scope: Window*
|
||||
###
|
||||
@registerPreferencesTab: ActionScopeWindow
|
||||
@switchPreferencesSection: ActionScopeWindow
|
||||
|
||||
###
|
||||
Public: Clear the developer console for the current window.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
_ = require 'underscore'
|
||||
NylasStore = require 'nylas-store'
|
||||
Actions = require '../actions'
|
||||
|
||||
class SectionConfig
|
||||
constructor: (opts={}) ->
|
||||
|
@ -20,21 +21,21 @@ class SectionConfig
|
|||
class PreferencesSectionStore extends NylasStore
|
||||
constructor: ->
|
||||
@_sectionConfigs = []
|
||||
@_activeSectionId = null
|
||||
@_accumulateAndTrigger ?= _.debounce(( => @trigger()), 20)
|
||||
|
||||
@Section = {}
|
||||
@SectionConfig = SectionConfig
|
||||
|
||||
@listenTo Actions.switchPreferencesSection, (sectionName) =>
|
||||
@_activeSectionId = sectionName
|
||||
@trigger()
|
||||
|
||||
sections: =>
|
||||
@_sectionConfigs
|
||||
|
||||
# TODO: Use our <GeneratedForm /> Class
|
||||
# TODO: Add in a "richtext" input type in addition to standard input
|
||||
# types.
|
||||
registerPreferences: (packageId, config) ->
|
||||
throw new Error("Not implemented yet")
|
||||
|
||||
unregisterPreferences: (packageId) ->
|
||||
throw new Error("Not implemented yet")
|
||||
activeSectionId: =>
|
||||
@_activeSectionId
|
||||
|
||||
###
|
||||
Public: Register a new top-level section to preferences
|
||||
|
@ -64,6 +65,10 @@ class PreferencesSectionStore extends NylasStore
|
|||
@Section[sectionConfig.sectionId] = sectionConfig.sectionId
|
||||
@_sectionConfigs.push(sectionConfig)
|
||||
@_sectionConfigs = _.sortBy(@_sectionConfigs, "order")
|
||||
|
||||
if @_sectionConfigs.length is 1
|
||||
@_activeSectionId = sectionConfig.sectionId
|
||||
|
||||
@_accumulateAndTrigger()
|
||||
|
||||
unregisterPreferenceSection: (sectionId) ->
|
||||
|
|
|
@ -19,7 +19,7 @@ UnreadCountStore = Reflux.createStore
|
|||
@listenTo AccountStore, @_onAccountChanged
|
||||
@listenTo DatabaseStore, @_onDataChanged
|
||||
|
||||
NylasEnv.config.observe 'core.showUnreadBadge', (val) =>
|
||||
NylasEnv.config.observe 'core.notifications.unreadBadge', (val) =>
|
||||
if val is true
|
||||
@_updateBadgeForCount()
|
||||
else
|
||||
|
@ -75,7 +75,7 @@ UnreadCountStore = Reflux.createStore
|
|||
|
||||
_updateBadgeForCount: (count) ->
|
||||
return unless NylasEnv.isMainWindow()
|
||||
return if NylasEnv.config.get('core.showUnreadBadge') is false
|
||||
return if NylasEnv.config.get('core.notifications.unreadBadge') is false
|
||||
if count > 999
|
||||
@_setBadge("999+")
|
||||
else if count > 0
|
||||
|
|
|
@ -34,6 +34,7 @@ class WorkspaceStore extends NylasStore
|
|||
@listenTo Actions.toggleWorkspaceLocationHidden, @_onToggleLocationHidden
|
||||
|
||||
@listenTo Actions.popSheet, @popSheet
|
||||
@listenTo Actions.pushSheet, @pushSheet
|
||||
@listenTo Actions.searchQueryCommitted, @popToRootSheet
|
||||
|
||||
@_preferredLayoutMode = NylasEnv.config.get('core.workspace.mode')
|
||||
|
|
|
@ -157,6 +157,7 @@ body.platform-win32 {
|
|||
background: -webkit-gradient(linear, left top, left bottom, from(darken(@btn-default-bg-color, 9%)), to(darken(@btn-default-bg-color, 13.5%)));
|
||||
}
|
||||
.btn-disabled {
|
||||
color: fadeout(@btn-default-text-color, 40%);
|
||||
background: fadeout(@btn-default-bg-color, 15%);
|
||||
box-shadow: 0 0.5px 0 rgba(0,0,0,0.15), 0 -0.5px 0 rgba(0,0,0,0.15), 0.5px 0 0 rgba(0,0,0,0.15), -0.5px 0 0 rgba(0,0,0,0.15);
|
||||
&:active {
|
||||
|
|
Loading…
Reference in a new issue