diff --git a/internal_packages/account-sidebar/lib/account-sidebar.cjsx b/internal_packages/account-sidebar/lib/account-sidebar.cjsx
index 5e86502a6..13425d5bc 100644
--- a/internal_packages/account-sidebar/lib/account-sidebar.cjsx
+++ b/internal_packages/account-sidebar/lib/account-sidebar.cjsx
@@ -1,10 +1,13 @@
React = require 'react'
-{Actions, MailViewFilter} = require("nylas-exports")
+{Actions, MailViewFilter, AccountStore} = require("nylas-exports")
{ScrollRegion} = require("nylas-component-kit")
SidebarDividerItem = require("./account-sidebar-divider-item")
SidebarSheetItem = require("./account-sidebar-sheet-item")
AccountSidebarStore = require ("./account-sidebar-store")
AccountSidebarMailViewItem = require("./account-sidebar-mail-view-item")
+crypto = require 'crypto'
+{RetinaImg} = require 'nylas-component-kit'
+classNames = require 'classnames'
class AccountSidebar extends React.Component
@displayName: 'AccountSidebar'
@@ -16,10 +19,12 @@ class AccountSidebar extends React.Component
constructor: (@props) ->
@state = @_getStateFromStores()
+ @state.showing = false
componentDidMount: =>
@unsubscribers = []
@unsubscribers.push AccountSidebarStore.listen @_onStoreChange
+ @unsubscribers.push AccountStore.listen @_onStoreChange
# It's important that every React class explicitly stops listening to
# atom events before it unmounts. Thank you event-kit
@@ -29,11 +34,105 @@ class AccountSidebar extends React.Component
render: =>
+ _accountSwitcher: =>
+ return undefined if @state.accounts.length < 1
+
+
+ {@_renderAccount @state.account, true}
+ {@_renderDropdown()}
+
+
+ _renderAccount: (account, isPrimaryItem) =>
+ classes = classNames
+ "account": true
+ "item": true
+ "dropdown-item-padding": not isPrimaryItem
+ "active": account is @state.account
+ "bg-color-hover": not isPrimaryItem
+ "primary-item": isPrimaryItem
+ "account-option": not isPrimaryItem
+
+ email = account.emailAddress.trim().toLowerCase()
+
+ if isPrimaryItem
+ dropdownClasses = classNames
+ "account-switcher-dropdown": true,
+ "account-switcher-dropdown-hidden": @state.showing
+
+ dropdownArrow =
+
+
+
+ onClick = @_toggleDropdown
+
+ else
+ onClick = =>
+ @_onSwitchAccount account
+
+
+
+ {dropdownArrow}
+
+ {email}
+
+
+
+
+
+ _renderNewAccountOption: =>
+
+
+
+
+
+ Add account…
+
+
+
+
+
+ _renderDropdown: =>
+ display = if @state.showing then "block" else "none"
+ # display = "block"
+
+ accounts = @state.accounts.map (a) =>
+ @_renderAccount(a)
+
+
+ {accounts}
+ {@_renderNewAccountOption()}
+
+
+ _toggleDropdown: =>
+ @setState showing: !@state.showing
+
+ _gravatarUrl: (email) =>
+ hash = crypto.createHash('md5').update(email, 'utf8').digest('hex')
+
+ "url(http://www.gravatar.com/avatar/#{hash}?d=blank&s=56)"
+
_sections: =>
return @state.sections.map (section) =>
@@ -64,12 +163,25 @@ class AccountSidebar extends React.Component
_onStoreChange: =>
@setState @_getStateFromStores()
+ _onBlur: (e) =>
+ target = e.nativeEvent.relatedTarget
+ if target? and React.findDOMNode(@refs.button).contains(target)
+ return
+ @setState(showing: false)
+
_onSwitchAccount: (account) =>
Actions.selectAccountId(account.id)
+ @setState(showing: false)
+
+ _onAddAccount: =>
+ require('remote').getGlobal('application').windowManager.newOnboardingWindow()
+ @setState showing: false
_getStateFromStores: =>
sections: AccountSidebarStore.sections()
selected: AccountSidebarStore.selected()
+ accounts: AccountStore.items()
+ account: AccountStore.current()
module.exports = AccountSidebar
diff --git a/internal_packages/account-sidebar/lib/account-switcher.cjsx b/internal_packages/account-sidebar/lib/account-switcher.cjsx
deleted file mode 100644
index e8bcfde40..000000000
--- a/internal_packages/account-sidebar/lib/account-switcher.cjsx
+++ /dev/null
@@ -1,65 +0,0 @@
-React = require 'react'
-{Actions, AccountStore} = require("nylas-exports")
-{RetinaImg} = require('nylas-component-kit')
-crypto = require 'crypto'
-classNames = require 'classnames'
-
-class AccountSwitcher extends React.Component
- @displayName: 'AccountSwitcher'
-
- @containerRequired: false
- @containerStyles:
- minWidth: 64
- maxWidth: 64
-
- constructor: (@props) ->
- @state = @_getStateFromStores()
-
- componentDidMount: =>
- @unsubscribers = []
- @unsubscribers.push AccountStore.listen @_onStoreChange
-
- # It's important that every React class explicitly stops listening to
- # atom events before it unmounts. Thank you event-kit
- # This can be fixed via a Reflux mixin
- componentWillUnmount: =>
- unsubscribe() for unsubscribe in @unsubscribers
-
- render: =>
- if @state.accounts.length is 0
- return
-
-
- {@_accounts()}
-
-
- _accounts: =>
- return @state.accounts.map (account) =>
- hash = account.emailAddress.trim().toLowerCase()
- hash = crypto.createHash('md5').update(hash, 'utf8').digest('hex')
-
- classnames = classNames
- 'account': true
- 'active': account is @state.account
-
- gravatarUrl = "http://www.gravatar.com/avatar/#{hash}?d=blank&s=44"
-
- @_onSwitchAccount(account) }>
-
-
-
-
- _onStoreChange: =>
- @setState @_getStateFromStores()
-
- _onSwitchAccount: (account) =>
- Actions.selectAccountId(account.id)
-
- _getStateFromStores: =>
- accounts: AccountStore.items()
- account: AccountStore.current()
-
-module.exports = AccountSwitcher
diff --git a/internal_packages/account-sidebar/lib/main.cjsx b/internal_packages/account-sidebar/lib/main.cjsx
index 0d57b5528..48bc24ef7 100644
--- a/internal_packages/account-sidebar/lib/main.cjsx
+++ b/internal_packages/account-sidebar/lib/main.cjsx
@@ -1,15 +1,11 @@
React = require "react"
AccountSidebar = require "./account-sidebar"
-AccountSwitcher = require "./account-switcher"
{ComponentRegistry, WorkspaceStore} = require "nylas-exports"
module.exports =
item: null # The DOM item the main React component renders into
activate: (@state) ->
- ComponentRegistry.register AccountSwitcher,
- location: WorkspaceStore.Location.RootSwitcher
-
ComponentRegistry.register AccountSidebar,
location: WorkspaceStore.Location.RootSidebar
diff --git a/internal_packages/account-sidebar/spec/account-sidebar-spec.cjsx b/internal_packages/account-sidebar/spec/account-sidebar-spec.cjsx
new file mode 100644
index 000000000..5ae935903
--- /dev/null
+++ b/internal_packages/account-sidebar/spec/account-sidebar-spec.cjsx
@@ -0,0 +1,60 @@
+React = require 'react/addons'
+TestUtils = React.addons.TestUtils
+AccountSidebar = require './../lib/account-sidebar'
+{AccountStore} = require 'nylas-exports'
+
+describe "AccountSidebar", ->
+ describe "account switcher", ->
+ sidebar = null
+
+ beforeEach ->
+ spyOn(AccountStore, "items").andCallFake ->
+ [
+ AccountStore.current(),
+ {
+ emailAddress: "dillon@nylas.com",
+ provider: "exchange"
+ }
+ ]
+
+ sidebar = TestUtils.renderIntoDocument(
+
+ )
+
+ it "doesn't render the dropdown if nothing clicked", ->
+ dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
+ dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
+
+ expect(dropdownNode.style.display).toBe "none"
+
+ it "renders the dropdown if clicking the arrow btn", ->
+ toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
+ TestUtils.Simulate.click toggler
+ dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
+ dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
+
+ expect(dropdownNode.style.display).toBe "block"
+
+ it "removes the dropdown when clicking elsewhere", ->
+ toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
+ TestUtils.Simulate.blur toggler
+ dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
+ dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
+
+ expect(dropdownNode.style.display).toBe "none"
+
+ it "shows all the accounts in the dropdown", ->
+ toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
+ TestUtils.Simulate.click toggler
+ dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
+ accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "account-option"
+
+ expect(accounts.length).toBe 2
+
+ it "shows the 'Add Account' button too", ->
+ toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
+ TestUtils.Simulate.click toggler
+ dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
+ accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "new-account-option"
+
+ expect(accounts.length).toBe 1
diff --git a/internal_packages/account-sidebar/stylesheets/account-sidebar.less b/internal_packages/account-sidebar/stylesheets/account-sidebar.less
index 1598e346c..8f0b03abf 100644
--- a/internal_packages/account-sidebar/stylesheets/account-sidebar.less
+++ b/internal_packages/account-sidebar/stylesheets/account-sidebar.less
@@ -1,45 +1,7 @@
@import "ui-variables";
@import "ui-mixins";
-#account-switcher {
- background-color: #212831;
- flex: 1;
- .account {
- position: relative;
- margin:15px 10px;
- margin-bottom: 0;
- display:block;
- border-radius: 8px;
- opacity: 0.7;
- img {
- -webkit-filter: ~"saturate(0%)";
- }
- .gravatar {
- width: 44px;
- height: 44px;
- position: absolute;
- z-index: 2;
- border-radius: 7px;
- }
- }
- .account.active {
- opacity: 1.0;
- img {
- -webkit-filter: ~"saturate(100%)";
- }
- }
- .account.active::after {
- box-shadow: inset 0 0 1px 2px @source-list-active-color;
- border-radius: 7px;
- width:100%;
- height:100%;
- content: ' ';
- left: 0;
- position:absolute;
- z-index: 3;
- }
-}
-
+#account-switcher,
#account-sidebar {
order: 1;
height: 100%;
@@ -79,7 +41,6 @@
float: left;
}
.name {
- text-transform: capitalize;
padding-left: @padding-small-horizontal;
position:relative;
top:1px;
@@ -110,3 +71,85 @@
padding-bottom: 0.25em;
}
}
+
+#account-sidebar {
+ .name {
+ text-transform: capitalize;
+ }
+}
+
+#account-switcher {
+ padding-top: @padding-large-vertical;
+ padding-bottom: @padding-base-vertical;
+ border-bottom: 1px solid @border-color-divider;
+
+ .account {
+ position: relative;
+ margin-bottom: 0;
+ display:block;
+ .gravatar {
+ background-size: 28px 28px;
+ width: 28px;
+ height: 28px;
+ position: absolute;
+ z-index: 2;
+ border-radius: 7px;
+ top: -2px;
+ }
+ }
+ .name {
+ text-transform: lowercase;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .account-switcher-dropdown {
+ margin-top: -7px;
+ transform: scale(1, -1);
+
+ &.account-switcher-dropdown-hidden {
+ transform: scale(1, 1);
+ }
+ }
+
+ .dropdown-positioning {
+ display: block;
+ position: absolute;
+ top: 45px;
+ width: 100%;
+ z-index: 999;
+
+ .account .gravatar {
+ top: 6px;
+ }
+ }
+
+ .dropdown-colors {
+ background: lighten(@source-list-bg, 5%);
+ border: 1px solid @border-color-divider;
+ border-radius: @border-radius-base;
+ box-shadow: @standard-shadow;
+ }
+
+ .dropdown-item-padding {
+ padding: 6px 5px 0 14px;
+
+ &:first-child {
+ padding-top: 8px;
+ border-top-left-radius: @list-border-radius;
+ border-top-right-radius: @list-border-radius;
+ }
+ &:last-child {
+ padding-bottom: 2px;
+ border-bottom-left-radius: @list-border-radius;
+ border-bottom-right-radius: @list-border-radius;
+ }
+ }
+
+ .bg-color-hover {
+
+ &:hover {
+ background: @list-hover-bg;
+ }
+ }
+}
diff --git a/internal_packages/settings/lib/main.cjsx b/internal_packages/settings/lib/main.cjsx
index 3b8fe4e26..b6937cf5b 100644
--- a/internal_packages/settings/lib/main.cjsx
+++ b/internal_packages/settings/lib/main.cjsx
@@ -8,7 +8,7 @@ module.exports =
activate: (@state={}) ->
WorkspaceStore.defineSheet 'Settings', {root: true, supportedModes: ['list'], name: 'Plugins'},
- list: ['RootSwitcher', 'RootSidebar', 'SettingsSidebar', 'Settings']
+ list: ['RootSidebar', 'SettingsSidebar', 'Settings']
ComponentRegistry.register SettingsTabsView,
location: WorkspaceStore.Location.SettingsSidebar
diff --git a/internal_packages/thread-list/lib/main.cjsx b/internal_packages/thread-list/lib/main.cjsx
index 2d6e1cd89..d86953a4c 100644
--- a/internal_packages/thread-list/lib/main.cjsx
+++ b/internal_packages/thread-list/lib/main.cjsx
@@ -14,7 +14,7 @@ DraftList = require './draft-list'
module.exports =
activate: (@state={}) ->
WorkspaceStore.defineSheet 'Drafts', {root: true, name: 'Drafts', sidebarComponent: DraftListSidebarItem},
- list: ['RootSwitcher', 'RootSidebar', 'DraftList']
+ list: ['RootSidebar', 'DraftList']
ComponentRegistry.register ThreadList,
location: WorkspaceStore.Location.ThreadList
diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee
index 3c61b8c32..f7ced57c7 100644
--- a/src/flux/stores/workspace-store.coffee
+++ b/src/flux/stores/workspace-store.coffee
@@ -46,8 +46,8 @@ class WorkspaceStore extends NylasStore
if atom.isMainWindow()
@defineSheet 'Global'
@defineSheet 'Threads', {root: true},
- list: ['RootSwitcher', 'RootSidebar', 'ThreadList']
- split: ['RootSwitcher', 'RootSidebar', 'ThreadList', 'MessageList', 'MessageListSidebar']
+ list: ['RootSidebar', 'ThreadList']
+ split: ['RootSidebar', 'ThreadList', 'MessageList', 'MessageListSidebar']
@defineSheet 'Thread', {},
list: ['MessageList', 'MessageListSidebar']
else
diff --git a/static/images/sidebar/account-switcher-dropdown@2x.png b/static/images/sidebar/account-switcher-dropdown@2x.png
new file mode 100644
index 000000000..3424240a3
Binary files /dev/null and b/static/images/sidebar/account-switcher-dropdown@2x.png differ
diff --git a/static/images/source-list/icon-accounts-addnew@2x.png b/static/images/source-list/icon-accounts-addnew@2x.png
new file mode 100644
index 000000000..e2afaa2cc
Binary files /dev/null and b/static/images/source-list/icon-accounts-addnew@2x.png differ