mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-14 00:24:33 +08:00
fix(*) Small visual tweaks and fixes - see summary
Summary: ThreadStore should be done loading as soon as threads are available SearchSuggestionStore should use ContactsStore for contact results Contact Store should not "filter all, take 10" it should only filter until it has 10. It should also check against "Ben Gotow" as well as "Ben" and "Gotow", so I can type "Ben Go" Sometimes participants are "Ben Gotow <ben@g.com>", "ben@g.com". If we get zero contacts after removing Me, put "Me" back in... Fix "Update Available" notification, broken reference to `atom.views.getView(atom.workspace)` A bit more debugging around cursors. Need to handle this case soon. Only use atomWorkspace if it exists. Fix for dragging next to / around toolbar window controls Consolidate the display of Contacts in menus into a single MenuItem subclass Update Template Popover styling fetchFromCache should only remove thread loading indicator *IF* it found results in the cache. Doh... Give the thread list "Name" column a fixed width (mg) Better styling of message list collapsed mode, rage against user selection and cursor: pointer Occasionally admin.inboxapp.com returns bogus data Sebaastian feedback on thread list Test Plan: Run tests Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1350
This commit is contained in:
parent
c04d8a6a93
commit
dff5465931
27 changed files with 242 additions and 126 deletions
|
@ -49,7 +49,7 @@
|
|||
|
||||
&:hover {
|
||||
background: darken(@source-list-bg, 5%);
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ _ = require 'underscore-plus'
|
|||
|
||||
{Contact,
|
||||
ContactStore} = require 'inbox-exports'
|
||||
{TokenizingTextField} = require 'ui-components'
|
||||
{TokenizingTextField, Menu} = require 'ui-components'
|
||||
|
||||
module.exports =
|
||||
ParticipantsTextField = React.createClass
|
||||
|
@ -58,15 +58,7 @@ ParticipantsTextField = React.createClass
|
|||
focus: -> @refs.textField.focus()
|
||||
|
||||
_completionContent: (p) ->
|
||||
if p.name?.length > 0 and p.name isnt p.email
|
||||
<div className="completion-participant">
|
||||
<span className="participant-name">{p.name}</span>
|
||||
<span className="participant-email">({p.email})</span>
|
||||
</div>
|
||||
else
|
||||
<div className="completion-participant">
|
||||
<span className="participant-name">{p.email}</span>
|
||||
</div>
|
||||
<Menu.NameEmailItem name={p.name} email={p.email} />
|
||||
|
||||
_componentForParticipant: (p) ->
|
||||
if p.name?.length > 0 and p.name isnt p.email
|
||||
|
|
|
@ -280,24 +280,6 @@ body.is-blurred .composer-inner-wrap .tokenizing-field .token {
|
|||
z-index: 2;
|
||||
padding: 5px @spacing-standard 0 @spacing-standard;
|
||||
|
||||
.completion-participant {
|
||||
.participant-name, .participant-email {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.participant-email {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.participant {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
.activity-bar {
|
||||
-webkit-font-smoothing: auto;
|
||||
-webkit-user-select:none;
|
||||
background-color: rgba(80,80,80,1);
|
||||
border-top:1px solid rgba(0,0,0,0.7);
|
||||
color:white;
|
||||
|
@ -116,6 +117,7 @@
|
|||
overflow-y: scroll;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
font-family: monospace;
|
||||
-webkit-user-select:auto;
|
||||
|
||||
&.queue {
|
||||
padding: 0;
|
||||
|
|
|
@ -174,8 +174,13 @@ MessageList = React.createClass
|
|||
collapsed={collapsed}
|
||||
thread_participants={@_threadParticipants()} />
|
||||
|
||||
unless idx is @state.messages.length - 1
|
||||
components.push <hr className="message-item-divider" />
|
||||
if idx < @state.messages.length - 1
|
||||
next = @state.messages[idx + 1]
|
||||
nextCollapsed = next and !@state.messagesExpandedState[next.id]
|
||||
if collapsed and nextCollapsed
|
||||
components.push <hr className="message-item-divider collapsed" />
|
||||
else
|
||||
components.push <hr className="message-item-divider" />
|
||||
|
||||
components
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@
|
|||
.messages-wrap {
|
||||
flex: 4;
|
||||
overflow-y: auto;
|
||||
// position: absolute;
|
||||
// top:0; left:0; right:0; bottom:0;
|
||||
-webkit-user-select:none;
|
||||
opacity:0;
|
||||
transition: opacity 0s;
|
||||
&.has-reply-area {
|
||||
|
@ -97,15 +96,32 @@
|
|||
|
||||
margin: 0 auto;
|
||||
padding: @spacing-standard 0 @spacing-double 0;
|
||||
-webkit-user-select:none;
|
||||
|
||||
&:first-child { padding-top: 0; }
|
||||
|
||||
&.collapsed {
|
||||
padding: 0;
|
||||
background-color: @background-secondary;
|
||||
padding-bottom: 0;
|
||||
background-color: darken(@background-primary, 3%);
|
||||
-webkit-user-select:none;
|
||||
.message-header {
|
||||
padding-top: 0;
|
||||
padding-bottom: 5px;
|
||||
-webkit-user-select:none;
|
||||
}
|
||||
.snippet {
|
||||
cursor:default;
|
||||
cursor: default;
|
||||
padding-top: @spacing-half;
|
||||
color: @text-color-subtle;
|
||||
color: @text-color-very-subtle;
|
||||
}
|
||||
.collapse-control,
|
||||
.participant-label.to-label,
|
||||
.participant-label.cc-label,
|
||||
.participant-label.bcc-label,
|
||||
.participant-name.to-contact,
|
||||
.participant-name.cc-contact,
|
||||
.participant-name.bcc-contact {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,12 +129,17 @@
|
|||
}
|
||||
|
||||
.message-item-divider {
|
||||
border:0; // remove default hr border left, right
|
||||
border-top: 2px solid @border-secondary-bg;
|
||||
height: 3px;
|
||||
background: @background-secondary;
|
||||
border-bottom: 1px solid @border-primary-bg;
|
||||
margin: 0;
|
||||
margin-right: 2px;
|
||||
|
||||
&.collapsed {
|
||||
height:0px;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-header {
|
||||
|
@ -127,6 +148,7 @@
|
|||
border-bottom: 1px solid @border-color-divider;
|
||||
padding-bottom: @spacing-standard;
|
||||
padding-top: 5px;
|
||||
-webkit-user-select:auto;
|
||||
|
||||
.message-actions-wrap {
|
||||
width: 100%;
|
||||
|
@ -139,6 +161,7 @@
|
|||
height: 22px;
|
||||
border: 1px solid @border-color-divider;
|
||||
border-radius: 11px;
|
||||
-webkit-user-select:none;
|
||||
|
||||
z-index: 4;
|
||||
margin-top: 0.35em;
|
||||
|
@ -208,8 +231,6 @@
|
|||
.collapse-control.inactive { display: block; }
|
||||
}
|
||||
|
||||
.collapse-control:hover {cursor: pointer;}
|
||||
|
||||
.footer-reply-area-wrap {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
@ -218,6 +239,7 @@
|
|||
color: @text-color-very-subtle;
|
||||
border-top: 1px solid @border-color-divider;
|
||||
background: @background-primary;
|
||||
-webkit-user-select:none;
|
||||
z-index: 10;
|
||||
|
||||
&:hover {
|
||||
|
@ -249,7 +271,7 @@
|
|||
///////////////////////////////
|
||||
.message-participants {
|
||||
|
||||
&.collapsed:hover {cursor: pointer;}
|
||||
&.collapsed:hover {cursor: default;}
|
||||
|
||||
.from-contact {
|
||||
font-weight: @headings-font-weight;
|
||||
|
@ -316,6 +338,7 @@
|
|||
padding: @spacing-standard;
|
||||
order: 2;
|
||||
flex-shrink: 0;
|
||||
cursor: default;
|
||||
|
||||
.other-contact {
|
||||
color: @text-color-subtle;
|
||||
|
@ -323,7 +346,6 @@
|
|||
&.selected {
|
||||
font-weight: @font-weight-semi-bold;
|
||||
}
|
||||
&:hover {cursor: pointer;}
|
||||
}
|
||||
|
||||
// TODO: DRY
|
||||
|
|
|
@ -12,15 +12,13 @@ module.exports =
|
|||
if updater.getState() is 'update-available'
|
||||
@displayNotification(updater.releaseVersion)
|
||||
|
||||
# Watch for state changes via a command the auto-update manager fires.
|
||||
# This is necessary because binding callbacks through `remote` is dangerous
|
||||
@_command = atom.commands.add 'atom-workspace', 'window:update-available', (event, version, releaseNotes) =>
|
||||
@displayNotification(version)
|
||||
atom.onUpdateAvailable ({releaseVersion, releaseNotes} = {}) =>
|
||||
@displayNotification(releaseVersion)
|
||||
|
||||
displayNotification: (version) ->
|
||||
version = if version then "(#{version})" else ''
|
||||
Actions.postNotification
|
||||
type: 'success',
|
||||
type: 'info',
|
||||
sticky: true
|
||||
message: "An update to Edgehill is available #{version} - Restart now to update!",
|
||||
icon: 'fa-flag',
|
||||
|
@ -30,7 +28,6 @@ module.exports =
|
|||
}]
|
||||
|
||||
deactivate: ->
|
||||
@_command.dispose()
|
||||
@_unlisten()
|
||||
|
||||
_onNotificationActionTaken: ({notification, action}) ->
|
||||
|
|
|
@ -41,12 +41,9 @@ describe "NotificationUpdateAvailable", ->
|
|||
expect(@package.displayNotification).not.toHaveBeenCalled()
|
||||
|
||||
it "should listen for `window:update-available`", ->
|
||||
spyOn(atom.commands, 'add').andCallThrough()
|
||||
spyOn(atom, 'onUpdateAvailable').andCallThrough()
|
||||
@package.activate()
|
||||
|
||||
args = atom.commands.add.mostRecentCall.args
|
||||
expect(args[0]).toEqual('atom-workspace')
|
||||
expect(args[1]).toEqual('window:update-available')
|
||||
expect(atom.onUpdateAvailable).toHaveBeenCalled()
|
||||
|
||||
describe "displayNotification", ->
|
||||
beforeEach ->
|
||||
|
|
|
@ -56,6 +56,8 @@ SearchBar = React.createClass
|
|||
itemContentFunc = (item) ->
|
||||
if item.divider
|
||||
<Menu.Item divider={item.divider} />
|
||||
else if item.contact
|
||||
<Menu.NameEmailItem name={item.contact.name} email={item.contact.email} />
|
||||
else
|
||||
item.label
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
Reflux = require 'reflux'
|
||||
{DatabaseStore, Actions, Contact} = require 'inbox-exports'
|
||||
{Actions,
|
||||
Contact,
|
||||
ContactStore} = require 'inbox-exports'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
# Stores should closely match the needs of a particular part of the front end.
|
||||
|
@ -9,7 +11,6 @@ _ = require 'underscore-plus'
|
|||
|
||||
SearchSuggestionStore = Reflux.createStore
|
||||
init: ->
|
||||
@_all = []
|
||||
@_suggestions = []
|
||||
@_searchConstants = {"from": 4, "subject": 2}
|
||||
@_query = ""
|
||||
|
@ -19,14 +20,6 @@ SearchSuggestionStore = Reflux.createStore
|
|||
@listenTo Actions.searchQueryChanged, @onSearchQueryChanged
|
||||
@listenTo Actions.searchQueryCommitted, @onSearchQueryCommitted
|
||||
@listenTo Actions.searchBlurred, @onSearchBlurred
|
||||
@listenTo DatabaseStore, @onDataChanged
|
||||
@onDataChanged()
|
||||
|
||||
onDataChanged: (change) ->
|
||||
return if change && change.objectClass != Contact.name
|
||||
DatabaseStore.findAll(Contact).then (contacts) =>
|
||||
@_all = contacts
|
||||
@repopulate()
|
||||
|
||||
onSearchQueryChanged: (query) ->
|
||||
@_query = query
|
||||
|
@ -56,12 +49,7 @@ SearchSuggestionStore = Reflux.createStore
|
|||
val = term[key]?.toLowerCase()
|
||||
return @trigger(@) unless val
|
||||
|
||||
contactResults = []
|
||||
for contact in @_all
|
||||
if contact.name?.toLowerCase().indexOf(val) == 0 or contact.email?.toLowerCase().indexOf(val) == 0
|
||||
contactResults.push(contact)
|
||||
if contactResults.length is 10
|
||||
break
|
||||
contactResults = ContactStore.searchContacts(val, limit:10)
|
||||
|
||||
@_suggestions.push
|
||||
label: "Message Contains: #{val}"
|
||||
|
@ -72,13 +60,8 @@ SearchSuggestionStore = Reflux.createStore
|
|||
divider: 'People'
|
||||
|
||||
_.each contactResults, (contact) =>
|
||||
if contact.name
|
||||
label = "#{contact.name} <#{contact.email}>"
|
||||
else
|
||||
label = contact.email
|
||||
|
||||
@_suggestions.push
|
||||
label: label
|
||||
contact: contact
|
||||
value: [{"participants": contact.email}]
|
||||
|
||||
@trigger(@)
|
||||
|
|
|
@ -59,7 +59,11 @@ FullContactStore = Reflux.createStore
|
|||
@_error = err
|
||||
else
|
||||
@_error = null
|
||||
@_accountCache = JSON.parse(data)
|
||||
try
|
||||
@_accountCache = JSON.parse(data)
|
||||
catch err
|
||||
@_error = err
|
||||
@_accountCache = null
|
||||
@trigger(@)
|
||||
|
||||
# Swap the url's to see real data
|
||||
|
@ -68,5 +72,9 @@ FullContactStore = Reflux.createStore
|
|||
@_error = err
|
||||
else
|
||||
@_error = null
|
||||
@_applicationCache = JSON.parse(data)
|
||||
try
|
||||
@_applicationCache = JSON.parse(data)
|
||||
catch err
|
||||
@_error = err
|
||||
@_applicationCache = null
|
||||
@trigger(@)
|
|
@ -54,8 +54,13 @@ ThreadListParticipants = React.createClass
|
|||
list = @props.thread.participants
|
||||
return [] unless list and list instanceof Array
|
||||
me = NamespaceStore.current().emailAddress
|
||||
if list.length > 1
|
||||
list = _.reject list, (p) -> p.email is me
|
||||
list = _.reject list, (p) -> p.email is me
|
||||
|
||||
# Removing "Me" may remove "Me" several times due to the way
|
||||
# participants is created. If we're left with an empty array,
|
||||
# put one a "Me" back in.
|
||||
if list.length is 0
|
||||
list.push(@props.thread.participants[0])
|
||||
|
||||
# We only ever want to show three. Ben...Kevin... Marty
|
||||
# But we want the *right* three.
|
||||
|
|
|
@ -79,7 +79,7 @@ ThreadList = React.createClass
|
|||
|
||||
c2 = new ListTabular.Column
|
||||
name: "Name"
|
||||
flex: 1
|
||||
width: 200
|
||||
resolver: (thread) ->
|
||||
<ThreadListParticipants thread={thread} />
|
||||
|
||||
|
|
|
@ -10,10 +10,14 @@
|
|||
flex: 1;
|
||||
overflow: auto;
|
||||
-webkit-user-select: none;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
position: relative;
|
||||
|
||||
.list-item {
|
||||
background-color: @background-secondary;
|
||||
background-color: darken(@background-primary, 3%);
|
||||
}
|
||||
.list-column {
|
||||
border-bottom: 1px solid fade(@list-border, 60%);
|
||||
}
|
||||
|
||||
.message-count {
|
||||
|
@ -71,13 +75,25 @@
|
|||
}
|
||||
|
||||
.unread:not(.selected) {
|
||||
background-color: white;
|
||||
background-color: @background-primary;
|
||||
&:hover {
|
||||
background: darken(@background-primary, 2%);
|
||||
}
|
||||
.list-column {
|
||||
border-bottom: 1px solid @list-border;
|
||||
}
|
||||
.subject {
|
||||
font-weight: @font-weight-semi-bold;
|
||||
}
|
||||
.snippet {
|
||||
color: @text-color-subtle;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
// subpixel antialiasing looks awful against dark background colors
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
.participants {
|
||||
color: @text-color-inverse;
|
||||
.unread-true {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
border-radius: @border-radius-base;
|
||||
color: @text-color;
|
||||
|
||||
font-weight: @font-weight-medium;
|
||||
font-weight: @font-weight-normal;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ _ = require 'underscore-plus'
|
|||
React = require 'react'
|
||||
|
||||
class ListColumn
|
||||
constructor: ({@name, @resolver, @flex}) ->
|
||||
constructor: ({@name, @resolver, @flex, @width}) ->
|
||||
|
||||
ListTabularItem = React.createClass
|
||||
displayName: 'ListTabularItem'
|
||||
|
@ -29,7 +29,7 @@ ListTabularItem = React.createClass
|
|||
for column in (@props.columns ? [])
|
||||
<div key={column.name}
|
||||
displayName={column.name}
|
||||
style={flex: column.flex}
|
||||
style={_.pick(column, ['flex', 'width'])}
|
||||
className="list-column">
|
||||
{column.resolver(@props.item, @)}
|
||||
</div>
|
||||
|
|
|
@ -63,6 +63,7 @@ Events
|
|||
|
||||
|
||||
MenuItem = React.createClass
|
||||
displayName: 'MenuItem'
|
||||
render: ->
|
||||
if @props.divider
|
||||
<div className="divider">{@props.divider}</div>
|
||||
|
@ -71,6 +72,22 @@ MenuItem = React.createClass
|
|||
className += " selected" if @props.selected
|
||||
<div className={className} key={@props.key} onMouseDown={@props.onMouseDown}>{@props.content}</div>
|
||||
|
||||
MenuNameEmailItem = React.createClass
|
||||
displayName: 'MenuNameEmailItem'
|
||||
propTypes:
|
||||
name: React.PropTypes.string
|
||||
email: React.PropTypes.string
|
||||
|
||||
render: ->
|
||||
if @props.name?.length > 0 and @props.name isnt @props.email
|
||||
<span>
|
||||
<span className="primary">{@props.name}</span>
|
||||
<span className="secondary">{"(#{@props.email})"}</span>
|
||||
</span>
|
||||
else
|
||||
<span className="primary">{@props.email}</span>
|
||||
|
||||
|
||||
|
||||
Menu = React.createClass
|
||||
|
||||
|
@ -175,5 +192,6 @@ Menu = React.createClass
|
|||
|
||||
|
||||
Menu.Item = MenuItem
|
||||
Menu.NameEmailItem = MenuNameEmailItem
|
||||
|
||||
module.exports = Menu
|
||||
|
|
|
@ -78,7 +78,10 @@ Popover = React.createClass
|
|||
|
||||
popoverComponent = []
|
||||
if @state.showing
|
||||
popoverComponent = <div ref="popover" className="popover">{@props.children}</div>
|
||||
popoverComponent = <div ref="popover" className="popover">
|
||||
{@props.children}
|
||||
<div className="popover-pointer"></div>
|
||||
</div>
|
||||
|
||||
<div className={"popover-container "+@props.className} onBlur={@_onBlur} ref="container">
|
||||
{wrappedButtonComponent}
|
||||
|
|
|
@ -47,6 +47,8 @@ class InboxLongConnection
|
|||
success: (json) =>
|
||||
@setCursor(json['cursor'])
|
||||
callback(json['cursor'])
|
||||
console.log("Retrieved cursor #{json['cursor']} from \
|
||||
`generate_cursor` with timestamp: #{stamp}")
|
||||
|
||||
setCursor: (cursor) ->
|
||||
atom.config.set(@_cursorKey, cursor)
|
||||
|
@ -104,7 +106,15 @@ class InboxLongConnection
|
|||
lib = require 'https'
|
||||
|
||||
req = lib.request options, (res) =>
|
||||
return @retry() unless res.statusCode == 200
|
||||
if res.statusCode isnt 200
|
||||
res.on 'data', (chunk) =>
|
||||
if chunk.toString().indexOf('Invalid cursor') > 0
|
||||
console.log('Long Polling Connection: Cursor is invalid. Need to blow away local cache.')
|
||||
# TODO THIS!
|
||||
else
|
||||
@retry()
|
||||
return
|
||||
|
||||
@_buffer = ''
|
||||
res.setEncoding('utf8')
|
||||
processBufferThrottled = _.throttle(@onProcessBuffer, 400, {leading: false})
|
||||
|
|
|
@ -18,17 +18,32 @@ module.exports = ContactStore = Reflux.createStore
|
|||
@_refreshCacheFromDB()
|
||||
|
||||
searchContacts: (search, {limit}={}) ->
|
||||
return [] if not search or search.length is 0
|
||||
|
||||
limit ?= 5
|
||||
limit = Math.max(limit, 0)
|
||||
return [] if not search or search.length is 0
|
||||
search = search.toLowerCase()
|
||||
matches = _.filter @_contactCache, (contact) ->
|
||||
|
||||
matchFunction = (contact) ->
|
||||
# For a given contact, check:
|
||||
# - email (bengotow@gmail.com)
|
||||
# - name parts (Ben, Go)
|
||||
# - name full (Ben Gotow)
|
||||
# (necessary so user can type more than first name ie: "Ben Go")
|
||||
return true if contact.email?.toLowerCase().indexOf(search) is 0
|
||||
return true if contact.name?.toLowerCase().indexOf(search) is 0
|
||||
name = contact.name?.toLowerCase() ? ""
|
||||
for namePart in name.split(/\s/)
|
||||
return true if namePart.indexOf(search) is 0
|
||||
false
|
||||
matches = matches[0..limit-1] if matches.length > limit
|
||||
|
||||
matches = []
|
||||
for contact in @_contactCache
|
||||
if matchFunction(contact)
|
||||
matches.push(contact)
|
||||
if matches.length is limit
|
||||
break
|
||||
|
||||
matches
|
||||
|
||||
_refreshCacheFromDB: ->
|
||||
|
|
|
@ -80,25 +80,31 @@ ThreadStore = Reflux.createStore
|
|||
Actions.selectThreadId(newSelectedId)
|
||||
|
||||
console.log("Fetching data for thread list took #{Date.now() - start} msec")
|
||||
|
||||
# If we've loaded threads, remove the loading indicator.
|
||||
# If there are no results, wait for the API query to finish
|
||||
if @_items.length > 0
|
||||
@_itemsLoading = false
|
||||
|
||||
@trigger()
|
||||
|
||||
fetchFromAPI: ->
|
||||
return unless @_namespaceId
|
||||
|
||||
@_itemsLoading = true
|
||||
@trigger()
|
||||
|
||||
doneLoading = =>
|
||||
return unless @_itemsLoading
|
||||
@_itemsLoading = false
|
||||
@trigger()
|
||||
|
||||
if @_searchQuery
|
||||
atom.inbox.getThreadsForSearch @_namespaceId, @_searchQuery, (items) =>
|
||||
@_items = items
|
||||
@_itemsLoading = false
|
||||
@trigger()
|
||||
doneLoading()
|
||||
else
|
||||
success = =>
|
||||
@_itemsLoading = false
|
||||
@trigger()
|
||||
error = =>
|
||||
@_itemsLoading = false
|
||||
@trigger()
|
||||
atom.inbox.getThreads(@_namespaceId, {tag: @_tagId}, {success: success, error: error})
|
||||
@trigger()
|
||||
atom.inbox.getThreads(@_namespaceId, {tag: @_tagId}, {success: doneLoading, error: doneLoading})
|
||||
|
||||
# Inbound Events
|
||||
|
||||
|
|
|
@ -31,16 +31,10 @@ class WindowEventHandler
|
|||
when 'update-available'
|
||||
atom.updateAvailable(detail)
|
||||
|
||||
# FIXME: Remove this when deprecations are removed
|
||||
{releaseVersion, releaseNotes} = detail
|
||||
detail = [releaseVersion, releaseNotes]
|
||||
if workspaceElement = atom.views.getView(atom.workspace)
|
||||
atom.commands.dispatch workspaceElement, "window:update-available", detail
|
||||
|
||||
@subscribe ipc, 'command', (command, args...) ->
|
||||
activeElement = document.activeElement
|
||||
# Use the workspace element view if body has focus
|
||||
if activeElement is document.body and workspaceElement = atom.views.getView(atom.workspace)
|
||||
if activeElement is document.body and workspaceElement = document.getElementById("atom-workspace")
|
||||
activeElement = workspaceElement
|
||||
atom.commands.dispatch(activeElement, command, args[0])
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
.list-item {
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-computed;
|
||||
line-height: @line-height-computed * 1.15;
|
||||
color: @text-color;
|
||||
background: @list-bg;
|
||||
|
||||
&:hover {
|
||||
background: @list-hover-bg;
|
||||
background: darken(@list-bg, 5%);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
position: relative;
|
||||
input.search {
|
||||
border:1px solid darken(@background-color-secondary, 10%);
|
||||
background-color:white;
|
||||
background-color: white;
|
||||
border-radius:12px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
@ -27,17 +27,39 @@
|
|||
|
||||
.item {
|
||||
display: block;
|
||||
padding: 0.5em;
|
||||
padding-left: @padding-base-horizontal;
|
||||
padding-top: @padding-base-vertical;
|
||||
padding-bottom: @padding-base-vertical;
|
||||
cursor: pointer;
|
||||
color: @text-color;
|
||||
background: @background-primary;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.primary {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.secondary {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding-left:5px;
|
||||
color:@text-color-very-subtle;
|
||||
}
|
||||
}
|
||||
|
||||
.item.selected, .item:hover {
|
||||
text-decoration: none;
|
||||
background: @accent-primary;
|
||||
color: @text-color-inverse;
|
||||
.primary {
|
||||
color: @text-color-inverse;
|
||||
}
|
||||
.secondary {
|
||||
color: @text-color-inverse-very-subtle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,54 @@
|
|||
@import "ui-variables";
|
||||
|
||||
.popover-container {
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.popover {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 50%;
|
||||
width: 250px;
|
||||
max-height:400px;
|
||||
background-color: @background-color;
|
||||
transform: translate(-50%,-100%);
|
||||
box-shadow: 0px 4px 30px rgba(0,0,0,0.19), inset 0px 0px 1px rgba(0,0,0,0.5);
|
||||
border-radius: @border-radius-base;
|
||||
z-index: 40;
|
||||
|
||||
.menu {
|
||||
z-index:1;
|
||||
position: relative;
|
||||
.content-container {
|
||||
background: none;
|
||||
}
|
||||
.header-container {
|
||||
border-top-left-radius: @border-radius-base;
|
||||
border-top-right-radius: @border-radius-base;
|
||||
background: none;
|
||||
}
|
||||
.footer-container {
|
||||
border-bottom-left-radius: @border-radius-base;
|
||||
border-bottom-right-radius: @border-radius-base;
|
||||
background: none;
|
||||
|
||||
.item:last-child:hover {
|
||||
border-bottom-left-radius: @border-radius-base;
|
||||
border-bottom-right-radius: @border-radius-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popover-pointer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
max-height:400px;
|
||||
background-color: @background-color;
|
||||
transform: translateY(-100%);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
||||
z-index: 40;
|
||||
width: 22.5px;
|
||||
height: 11px;
|
||||
background: transparent url('images/tooltip/tooltip-bg-pointer@2x.png') no-repeat;
|
||||
background-size: 22.5px 10.5px;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index:0;
|
||||
bottom: -10px;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
border: 1px solid @border-secondary-bg;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
border-radius: @border-radius-small;
|
||||
background-color: @background-color;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,11 +58,11 @@ atom-workspace {
|
|||
}
|
||||
|
||||
.toolbar-window-controls {
|
||||
padding-top:14px;
|
||||
padding-left:@spacing-half;
|
||||
margin-top:14px;
|
||||
margin-left:@spacing-half;
|
||||
order: -1000;
|
||||
min-width: 102px;
|
||||
width: 102px;
|
||||
min-width: 72px;
|
||||
width: 72px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue