mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-06 08:08:10 +08:00
fix(send-action): Make dropdown trigger secondary send actions
We can come up with a new UX for this later, but for now this is important for consistency with the Reply/Reply-All/Forward picker and others in the app that perform actions rather than changing selection. Also makes it possible to choose to "Send and Archive" /without/ making it the future default, which will be nice when there are many you may want infrequently.
This commit is contained in:
parent
b29b087a9f
commit
8b7c1aaa01
2 changed files with 47 additions and 58 deletions
|
@ -15,32 +15,31 @@ class SendActionButton extends React.Component
|
|||
constructor: (@props) ->
|
||||
@state =
|
||||
actionConfigs: @_actionConfigs(@props)
|
||||
selectedSendType: NylasEnv.config.get(SendActionButton.CONFIG_KEY) ? @_defaultActionConfig().configKey
|
||||
|
||||
componentDidMount: ->
|
||||
componentDidMount: =>
|
||||
@unsub = ExtensionRegistry.Composer.listen(@_onExtensionsChanged)
|
||||
|
||||
componentWillReceiveProps: (newProps) ->
|
||||
componentWillReceiveProps: (newProps) =>
|
||||
@setState actionConfigs: @_actionConfigs(newProps)
|
||||
|
||||
componentWillUnmount: ->
|
||||
componentWillUnmount: =>
|
||||
@unsub()
|
||||
|
||||
primaryClick: => @_onPrimaryClick()
|
||||
|
||||
_configKeyFromTitle: (title) ->
|
||||
_configKeyFromTitle: (title) =>
|
||||
return _str.dasherize(title.toLowerCase())
|
||||
|
||||
_onExtensionsChanged: =>
|
||||
@setState actionConfigs: @_actionConfigs(@props)
|
||||
|
||||
_defaultActionConfig: ->
|
||||
_defaultActionConfig: =>
|
||||
title: "Send"
|
||||
iconUrl: null
|
||||
onSend: ({draft}) -> Actions.sendDraft(draft.clientId)
|
||||
configKey: "send"
|
||||
|
||||
_actionConfigs: (props) ->
|
||||
_actionConfigs: (props) =>
|
||||
return [] unless props.draft
|
||||
actionConfigs = [@_defaultActionConfig()]
|
||||
|
||||
|
@ -56,7 +55,7 @@ class SendActionButton extends React.Component
|
|||
|
||||
return actionConfigs
|
||||
|
||||
_verifyConfig: (config={}, extension) ->
|
||||
_verifyConfig: (config={}, extension) =>
|
||||
name = extension.name
|
||||
if not _.isString(config.title)
|
||||
throw new Error("#{name}.sendActionConfig must return a string `title`")
|
||||
|
@ -66,49 +65,50 @@ class SendActionButton extends React.Component
|
|||
|
||||
return true
|
||||
|
||||
render: ->
|
||||
return false if not @props.draft
|
||||
render: =>
|
||||
return false unless @props.draft
|
||||
if @state.actionConfigs.length is 1
|
||||
@_renderSingleDefaultButton()
|
||||
else
|
||||
@_renderSendDropdown()
|
||||
|
||||
_onPrimaryClick: =>
|
||||
actionConfigs = @_orderedActionConfigs()
|
||||
@_sendWithAction(actionConfigs[0].onSend)
|
||||
{preferred} = @_orderedActionConfigs()
|
||||
@_sendWithAction(preferred)
|
||||
|
||||
_renderSingleDefaultButton: ->
|
||||
classes = "btn btn-toolbar btn-normal btn-emphasis btn-text btn-send"
|
||||
iconUrl = @state.actionConfigs[0].iconUrl
|
||||
<button className={classes}
|
||||
style={order: -100}
|
||||
onClick={@_onPrimaryClick}>{@_sendContent(iconUrl)}</button>
|
||||
_renderSingleDefaultButton: =>
|
||||
<button
|
||||
className={"btn btn-toolbar btn-normal btn-emphasis btn-text btn-send"}
|
||||
style={order: -100}
|
||||
onClick={@_onPrimaryClick}>
|
||||
{@_contentForAction(@state.actionConfigs[0])}
|
||||
</button>
|
||||
|
||||
_renderSendDropdown: =>
|
||||
{preferred, rest} = @_orderedActionConfigs()
|
||||
|
||||
_renderSendDropdown: ->
|
||||
actionConfigs = @_orderedActionConfigs()
|
||||
<ButtonDropdown
|
||||
className={"btn-send btn-emphasis btn-text"}
|
||||
style={order: -100}
|
||||
primaryItem={@_sendContent(actionConfigs[0].iconUrl)}
|
||||
primaryTitle={actionConfigs[0].title}
|
||||
primaryItem={@_contentForAction(preferred)}
|
||||
primaryTitle={preferred.title}
|
||||
primaryClick={@_onPrimaryClick}
|
||||
closeOnMenuClick={true}
|
||||
menu={@_dropdownMenu(actionConfigs[1..-1])}/>
|
||||
menu={@_dropdownMenu(rest)}/>
|
||||
|
||||
_orderedActionConfigs: ->
|
||||
_orderedActionConfigs: =>
|
||||
configKeys = _.pluck(@state.actionConfigs, "configKey")
|
||||
if @state.selectedSendType not in configKeys
|
||||
selectedSendType = @_defaultActionConfig().configKey
|
||||
else
|
||||
selectedSendType = @state.selectedSendType
|
||||
preferredKey = NylasEnv.config.get(SendActionButton.CONFIG_KEY)
|
||||
|
||||
primary = _.findWhere(@state.actionConfigs, configKey: selectedSendType)
|
||||
rest = _.reject @state.actionConfigs, (config) ->
|
||||
config.configKey is selectedSendType
|
||||
if not preferredKey? or preferredKey not in configKeys
|
||||
preferredKey = @_defaultActionConfig().configKey
|
||||
|
||||
return [primary].concat(rest)
|
||||
preferred = _.findWhere(@state.actionConfigs, configKey: preferredKey)
|
||||
rest = _.without(@state.actionConfigs, preferred)
|
||||
|
||||
_sendWithAction: (onSend) ->
|
||||
{preferred, rest}
|
||||
|
||||
_sendWithAction: ({onSend}) =>
|
||||
isValidDraft = @props.isValidDraft()
|
||||
if isValidDraft
|
||||
try
|
||||
|
@ -116,19 +116,14 @@ class SendActionButton extends React.Component
|
|||
catch err
|
||||
NylasEnv.reportError(err)
|
||||
|
||||
_dropdownMenu: (actionConfigs) ->
|
||||
_dropdownMenu: (actionConfigs) =>
|
||||
<Menu items={actionConfigs}
|
||||
itemKey={ (actionConfig) -> actionConfig.configKey }
|
||||
itemContent={ (actionConfig) => @_sendContent(actionConfig.iconUrl) }
|
||||
onSelect={@_menuItemSelect}
|
||||
itemContent={@_contentForAction}
|
||||
onSelect={@_sendWithAction}
|
||||
/>
|
||||
|
||||
_menuItemSelect: (actionConfig) =>
|
||||
@setState selectedSendType: actionConfig.configKey
|
||||
|
||||
_sendContent: (iconUrl) ->
|
||||
sendIcon = "icon-composer-send.png"
|
||||
|
||||
_contentForAction: ({iconUrl}) =>
|
||||
if iconUrl
|
||||
plusHTML = <span> + </span>
|
||||
additionalImg = <RetinaImg url={iconUrl}
|
||||
|
@ -138,7 +133,7 @@ class SendActionButton extends React.Component
|
|||
additionalImg = ""
|
||||
|
||||
<span>
|
||||
<RetinaImg name={sendIcon} mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<RetinaImg name="icon-composer-send.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<span className="text">Send{plusHTML}</span>{additionalImg}
|
||||
</span>
|
||||
|
||||
|
|
|
@ -60,15 +60,15 @@ describe "SendActionButton", ->
|
|||
|
||||
it "has the correct primary item", ->
|
||||
spyOn(ExtensionRegistry.Composer, "extensions").andReturn [GoodExtension, SecondExtension]
|
||||
spyOn(NylasEnv.config, 'get').andReturn('second-extension')
|
||||
@sendActionButton = render(@draft)
|
||||
@sendActionButton.setState(selectedSendType: 'second-extension')
|
||||
dropdown = ReactTestUtils.findRenderedComponentWithType(@sendActionButton, ButtonDropdown)
|
||||
expect(dropdown.props.primaryTitle).toBe "Second Extension"
|
||||
|
||||
it "falls back to a default if the primary item can't be found", ->
|
||||
spyOn(ExtensionRegistry.Composer, "extensions").andReturn [GoodExtension, SecondExtension]
|
||||
spyOn(NylasEnv.config, 'get').andReturn('does-not-exist')
|
||||
@sendActionButton = render(@draft)
|
||||
@sendActionButton.setState(selectedSendType: 'does-not-exist')
|
||||
dropdown = ReactTestUtils.findRenderedComponentWithType(@sendActionButton, ButtonDropdown)
|
||||
expect(dropdown.props.primaryTitle).toBe "Send"
|
||||
|
||||
|
@ -104,8 +104,8 @@ describe "SendActionButton", ->
|
|||
onSend: ->
|
||||
|
||||
spyOn(ExtensionRegistry.Composer, "extensions").andReturn [NoIconUrl]
|
||||
spyOn(NylasEnv.config, 'get').andReturn('some-title')
|
||||
@sendActionButton = render(@draft)
|
||||
@sendActionButton.setState(selectedSendType: 'some-title')
|
||||
dropdowns = ReactTestUtils.scryRenderedComponentsWithType(@sendActionButton, ButtonDropdown)
|
||||
icons = ReactTestUtils.scryRenderedComponentsWithType(@sendActionButton, RetinaImg)
|
||||
buttons = ReactTestUtils.scryRenderedDOMComponentsWithTag(@sendActionButton, "button")
|
||||
|
@ -152,9 +152,9 @@ describe "SendActionButton", ->
|
|||
iconUrl: "nylas://foo/bar/baz"
|
||||
onSend: -> clicked = "onSend fired"
|
||||
spyOn(ExtensionRegistry.Composer, "extensions").andReturn [Click]
|
||||
spyOn(NylasEnv.config, 'get').andReturn('click')
|
||||
|
||||
@sendActionButton = render(@draft)
|
||||
@sendActionButton.setState(selectedSendType: 'click')
|
||||
|
||||
button = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(@sendActionButton, "primary-item"))
|
||||
ReactTestUtils.Simulate.click(button)
|
||||
|
@ -168,9 +168,9 @@ describe "SendActionButton", ->
|
|||
iconUrl: "nylas://foo/bar/baz"
|
||||
onSend: -> throw new Error("BOO")
|
||||
spyOn(ExtensionRegistry.Composer, "extensions").andReturn [Click]
|
||||
spyOn(NylasEnv.config, 'get').andReturn('click')
|
||||
|
||||
@sendActionButton = render(@draft)
|
||||
@sendActionButton.setState(selectedSendType: 'click')
|
||||
|
||||
button = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(@sendActionButton, "primary-item"))
|
||||
ReactTestUtils.Simulate.click(button)
|
||||
|
@ -178,14 +178,8 @@ describe "SendActionButton", ->
|
|||
expect(NylasEnv.reportError).toHaveBeenCalled()
|
||||
expect(NylasEnv.reportError.calls[0].args[0].message).toMatch /BOO/
|
||||
|
||||
it "initializes with the correct config item", ->
|
||||
spyOn(NylasEnv.config, "get").andReturn "test-state"
|
||||
it "initializes with the default and shows the standard Send option", ->
|
||||
spyOn(NylasEnv.config, 'get').andReturn(null)
|
||||
@sendActionButton = render(@draft)
|
||||
expect(@sendActionButton.state.selectedSendType).toBe "test-state"
|
||||
|
||||
it "initializes with the default key", ->
|
||||
spyOn(NylasEnv.config, "get").andReturn null
|
||||
@sendActionButton = render(@draft)
|
||||
expect(@sendActionButton.state.selectedSendType).toBe "send"
|
||||
|
||||
|
||||
button = React.findDOMNode(@sendActionButton)
|
||||
expect(button.innerText).toEqual('Send')
|
||||
|
|
Loading…
Reference in a new issue