diff --git a/internal_packages/account-sidebar/lib/account-sidebar-store.coffee b/internal_packages/account-sidebar/lib/account-sidebar-store.coffee index 29af93421..a7ec5b3cd 100644 --- a/internal_packages/account-sidebar/lib/account-sidebar-store.coffee +++ b/internal_packages/account-sidebar/lib/account-sidebar-store.coffee @@ -44,11 +44,8 @@ class AccountSidebarStore extends NylasStore account = AccountStore.current() return unless account - viewFilterForCategory = (cat) -> - return MailViewFilter.forCategory(cat) - userCategories = CategoryStore.getUserCategories() - userCategoryViews = _.map(userCategories, viewFilterForCategory) + userCategoryItems = _.map(userCategories, @_sidebarItemForCategory) # Our drafts are displayed via the `DraftListSidebarItem` which # is loading into the `Drafts` Sheet. @@ -56,30 +53,33 @@ class AccountSidebarStore extends NylasStore standardCategories = _.reject standardCategories, (category) => category.name is "drafts" - standardViews = _.map(standardCategories, viewFilterForCategory) - - starredView = MailViewFilter.forStarred() - standardViews.splice(1, 0, starredView) + standardCategoryItems = _.map(standardCategories, @_sidebarItemForCategory) + starredItem = @_sidebarItemForMailView('starred', MailViewFilter.forStarred()) # Find root views and add them to the bottom of the list (Drafts, etc.) - _.each WorkspaceStore.Sheet, (sheet) -> - if sheet.root and sheet.name - standardViews.push(sheet) + standardItems = standardCategoryItems + standardItems.splice(1, 0, starredItem) + standardItems.push(WorkspaceStore.sidebarItems()...) @_sections = [] @_sections.push label: 'Mailboxes' - items: standardViews + items: standardItems type: 'mailboxes' @_sections.push label: CategoryStore.categoryLabel() - items: userCategoryViews + items: userCategoryItems type: 'category' @trigger() - _isStandardCategory: (category) => - category.name and category.name in CategoryStore.standardCategories + _sidebarItemForMailView: (id, filter) => + new WorkspaceStore.SidebarItem({id: id, name: filter.name, mailViewFilter: filter}) + + _sidebarItemForCategory: (category) => + filter = MailViewFilter.forCategory(category) + @_sidebarItemForMailView(category.id, filter) + module.exports = new AccountSidebarStore() diff --git a/internal_packages/account-sidebar/lib/account-sidebar.cjsx b/internal_packages/account-sidebar/lib/account-sidebar.cjsx index 13425d5bc..b08828890 100644 --- a/internal_packages/account-sidebar/lib/account-sidebar.cjsx +++ b/internal_packages/account-sidebar/lib/account-sidebar.cjsx @@ -1,13 +1,12 @@ React = require 'react' -{Actions, MailViewFilter, AccountStore} = require("nylas-exports") +{Actions, MailViewFilter, WorkspaceStore} = require("nylas-exports") {ScrollRegion} = require("nylas-component-kit") SidebarDividerItem = require("./account-sidebar-divider-item") SidebarSheetItem = require("./account-sidebar-sheet-item") +AccountSwitcher = require ("./account-switcher") 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' @@ -19,169 +18,64 @@ 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 - # This can be fixed via a Reflux mixin componentWillUnmount: => unsubscribe() for unsubscribe in @unsubscribers render: => - {@_accountSwitcher()} +
{@_sections()}
- _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) => + @state.sections.map (section) =>
{section.label}
{@_itemComponents(section)}
_itemComponents: (section) => - section.items?.map (item) => - return unless item - if item instanceof MailViewFilter - - else - if item.sidebarComponent - itemClass = item.sidebarComponent - else - itemClass = SidebarSheetItem + section.items.map (item) => + unless item instanceof WorkspaceStore.SidebarItem + throw new Error("AccountSidebar:_itemComponents: sections contained an \ + item which was not a SidebarItem") - + select={item.id is @state.selected?.id } /> + + else if item.mailViewFilter + + + else if item.sheet + + + else + throw new Error("AccountSidebar:_itemComponents: each item must have a \ + custom component, or a sheet or mailViewFilter") _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 new file mode 100644 index 000000000..c9634399d --- /dev/null +++ b/internal_packages/account-sidebar/lib/account-switcher.cjsx @@ -0,0 +1,136 @@ +React = require 'react' +{Actions, AccountStore} = require("nylas-exports") +{ScrollRegion} = require("nylas-component-kit") +crypto = require 'crypto' +{RetinaImg} = require 'nylas-component-kit' +classNames = require 'classnames' + +class AccountSwitcher extends React.Component + @displayName: 'AccountSwitcher' + + constructor: (@props) -> + @state = @_getStateFromStores() + @state.showing = false + + componentDidMount: => + @unsubscribers = [] + @unsubscribers.push AccountStore.listen @_onStoreChange + + componentWillUnmount: => + unsubscribe() for unsubscribe in @unsubscribers + + render: => + 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)" + + _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: => + accounts: AccountStore.items() + account: AccountStore.current() + + +module.exports = AccountSwitcher diff --git a/internal_packages/settings/lib/main.cjsx b/internal_packages/settings/lib/main.cjsx index b6937cf5b..ccd5a2104 100644 --- a/internal_packages/settings/lib/main.cjsx +++ b/internal_packages/settings/lib/main.cjsx @@ -7,7 +7,7 @@ SettingsTabsView = require "./settings-tabs-view" module.exports = activate: (@state={}) -> - WorkspaceStore.defineSheet 'Settings', {root: true, supportedModes: ['list'], name: 'Plugins'}, + WorkspaceStore.defineSheet 'Settings', {root: true, supportedModes: ['list']}, list: ['RootSidebar', 'SettingsSidebar', 'Settings'] ComponentRegistry.register SettingsTabsView, diff --git a/internal_packages/thread-list/lib/draft-list-sidebar-item.cjsx b/internal_packages/thread-list/lib/draft-list-sidebar-item.cjsx index 888e43fbc..f656afe0a 100644 --- a/internal_packages/thread-list/lib/draft-list-sidebar-item.cjsx +++ b/internal_packages/thread-list/lib/draft-list-sidebar-item.cjsx @@ -34,11 +34,11 @@ class DraftListSidebarItem extends React.Component
{unread}
-
{@props.item.name}
+
Drafts
_onClick: (event) => event.preventDefault() - Actions.selectRootSheet(@props.item) + Actions.selectRootSheet(WorkspaceStore.Sheet.Drafts) module.exports = DraftListSidebarItem diff --git a/internal_packages/thread-list/lib/main.cjsx b/internal_packages/thread-list/lib/main.cjsx index 29a7d3c00..8c4f0a6e4 100644 --- a/internal_packages/thread-list/lib/main.cjsx +++ b/internal_packages/thread-list/lib/main.cjsx @@ -1,6 +1,6 @@ _ = require 'underscore' React = require "react" -{ComponentRegistry, WorkspaceStore} = require "nylas-exports" +{MailViewFilter, ComponentRegistry, WorkspaceStore} = require "nylas-exports" {DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton} = require "./thread-buttons" {DraftDeleteButton} = require "./draft-buttons" @@ -13,9 +13,17 @@ DraftList = require './draft-list' module.exports = activate: (@state={}) -> - WorkspaceStore.defineSheet 'Drafts', {root: true, name: 'Drafts', sidebarComponent: DraftListSidebarItem}, + WorkspaceStore.defineSheet 'Drafts', {root: true}, list: ['RootSidebar', 'DraftList'] + @sidebarItem = new WorkspaceStore.SidebarItem + component: DraftListSidebarItem + sheet: WorkspaceStore.Sheet.Drafts + id: 'Drafts' + name: 'Drafts' + + WorkspaceStore.addSidebarItem(@sidebarItem) + ComponentRegistry.register ThreadList, location: WorkspaceStore.Location.ThreadList diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee index f7ced57c7..8a0d42a51 100644 --- a/src/flux/stores/workspace-store.coffee +++ b/src/flux/stores/workspace-store.coffee @@ -5,6 +5,13 @@ NylasStore = require 'nylas-store' Sheet = {} Location = {} +SidebarItems = {} + +class WorkspaceSidebarItem + constructor: ({@id, @component, @name, @sheet, @mailViewFilter}) -> + if not @sheet and not @mailViewFilter and not @component + throw new Error("WorkspaceSidebarItem: You must provide either a sheet \ + component, or a mailViewFilter for the sidebar item named #{@name}") ### Public: The WorkspaceStore manages Sheets and layout modes in the application. @@ -40,6 +47,9 @@ class WorkspaceStore extends NylasStore @Location = Location = {} @Sheet = Sheet = {} + @SidebarItem = WorkspaceSidebarItem + @SidebarItems = SidebarItems = {} + @_hiddenLocations = {} @_sheetStack = [] @@ -137,6 +147,20 @@ class WorkspaceStore extends NylasStore return false unless loc @_hiddenLocations[loc.id]? + + sidebarItems: => + _.values(@SidebarItems) + + addSidebarItem: (item) => + unless item instanceof WorkspaceSidebarItem + throw new Error("WorkspaceStore::addSidebarItem requires a `WorkspaceSidebarItem`") + @SidebarItems[item.id] = item + @triggerDebounced() + + removeSidebarItem: (item) => + delete @SidebarItems[item.id] + @triggerDebounced() + ### Managing Sheets ###