mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 05:06:53 +08:00
fix(friday): Bugs and initial pass at the Today view without content (see summary)
Summary: Load unread counts from database again, not tags fix(multiselect-list): Clear selection on esc fix(onboarding): Make target=_blank links work in onboarding pages fix(workspace): Items in header and footer regions are in a single column fix(layout): Critical issue for things not 100% height fix(activity-bar): Show in dev mode so you know you're in dev mode fix(quoted-text): Support for #divRplyFwdMsg quoted text marker Test Plan: Run specs Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1484
This commit is contained in:
parent
a745681684
commit
0bd303865e
20 changed files with 350 additions and 69 deletions
|
@ -1,4 +1,5 @@
|
|||
React = require 'react'
|
||||
_ = require 'underscore-plus'
|
||||
classNames = require 'classnames'
|
||||
{Actions, Utils, WorkspaceStore} = require 'inbox-exports'
|
||||
{RetinaImg} = require 'ui-components'
|
||||
|
@ -7,12 +8,22 @@ class AccountSidebarSheetItem extends React.Component
|
|||
@displayName: 'AccountSidebarSheetItem'
|
||||
|
||||
render: =>
|
||||
classSet = classNames
|
||||
classSet = classNames
|
||||
'item': true
|
||||
'selected': @props.select
|
||||
|
||||
if @props.item.icon and @props.item.icon.displayName?
|
||||
component = @props.item.icon
|
||||
icon = <component selected={@props.select} />
|
||||
|
||||
else if _.isString(@props.item.icon)
|
||||
icon = <RetinaImg name={@props.item.icon} fallback="folder.png" colorfill={@props.select} />
|
||||
|
||||
else
|
||||
icon = <RetinaImg name={"folder.png"} colorfill={@props.select} />
|
||||
|
||||
<div className={classSet} onClick={@_onClick}>
|
||||
<RetinaImg name={"folder.png"} colorfill={@props.select} />
|
||||
{icon}
|
||||
<span className="name"> {@props.item.name}</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,9 +11,13 @@ _ = require 'underscore-plus'
|
|||
|
||||
AccountSidebarStore = Reflux.createStore
|
||||
init: ->
|
||||
@_inboxCount = null
|
||||
@_tags = []
|
||||
|
||||
@_setStoreDefaults()
|
||||
@_registerListeners()
|
||||
@_populate()
|
||||
@_populateInboxCount()
|
||||
|
||||
########### PUBLIC #####################################################
|
||||
|
||||
|
@ -42,55 +46,63 @@ AccountSidebarStore = Reflux.createStore
|
|||
return unless namespace
|
||||
|
||||
DatabaseStore.findAll(Tag, namespaceId: namespace.id).then (tags) =>
|
||||
# Collect the built-in tags we want to display, and the user tags
|
||||
# (which can be identified by having non-hardcoded IDs)
|
||||
|
||||
# We ignore the server drafts so we can use our own localDrafts
|
||||
tags = _.reject tags, (tag) -> tag.id is "drafts"
|
||||
|
||||
# We ignore the trash tag because you can't trash anything
|
||||
tags = _.reject tags, (tag) -> tag.id is "trash"
|
||||
|
||||
mainTagIDs = ['inbox', 'drafts', 'sent', 'archive']
|
||||
mainTags = _.filter tags, (tag) -> _.contains(mainTagIDs, tag.id)
|
||||
userTags = _.reject tags, (tag) -> _.contains(mainTagIDs, tag.id)
|
||||
|
||||
# Sort the main tags so they always appear in a standard order
|
||||
mainTags = _.sortBy mainTags, (tag) -> mainTagIDs.indexOf(tag.id)
|
||||
mainTags.push new Tag(name: 'All Mail', id: '*')
|
||||
|
||||
# Sort user tags by name
|
||||
userTags = _.sortBy(userTags, 'name')
|
||||
|
||||
# Find root views, add the Views section
|
||||
rootSheets = _.filter WorkspaceStore.Sheet, (sheet) -> sheet.root and sheet.name
|
||||
|
||||
lastSections = @_sections
|
||||
@_sections = [
|
||||
{ label: 'Mailboxes', items: mainTags, type: 'tag' },
|
||||
{ label: 'Views', items: rootSheets, type: 'sheet' },
|
||||
{ label: 'Tags', items: userTags, type: 'tag' },
|
||||
]
|
||||
|
||||
@trigger(@)
|
||||
@_tags = tags
|
||||
@_build()
|
||||
|
||||
_populateInboxCount: ->
|
||||
namespace = NamespaceStore.current()
|
||||
return unless namespace
|
||||
|
||||
# Make a web request for unread count
|
||||
atom.inbox.makeRequest
|
||||
method: 'GET'
|
||||
path: "/n/#{namespace.id}/tags/inbox"
|
||||
returnsModel: true
|
||||
DatabaseStore.count(Thread, [
|
||||
Thread.attributes.namespaceId.equal(namespace.id),
|
||||
Thread.attributes.unread.equal(true),
|
||||
Thread.attributes.tags.contains('inbox')
|
||||
]).then (count) =>
|
||||
if count isnt @_inboxCount
|
||||
@_inboxCount = count
|
||||
@_build()
|
||||
|
||||
_populateDraftCount: ->
|
||||
namespace = NamespaceStore.current()
|
||||
return unless namespace
|
||||
_build: ->
|
||||
tags = @_tags
|
||||
|
||||
DatabaseStore.count(Message, draft: true).then (count) =>
|
||||
#TODO: Save Draft Count
|
||||
@trigger(@)
|
||||
# Collect the built-in tags we want to display, and the user tags
|
||||
# (which can be identified by having non-hardcoded IDs)
|
||||
|
||||
# We ignore the server drafts so we can use our own localDrafts
|
||||
tags = _.reject tags, (tag) -> tag.id is "drafts"
|
||||
|
||||
# We ignore the trash tag because you can't trash anything
|
||||
tags = _.reject tags, (tag) -> tag.id is "trash"
|
||||
|
||||
mainTagIDs = ['inbox', 'drafts', 'sent', 'archive']
|
||||
mainTags = _.filter tags, (tag) -> _.contains(mainTagIDs, tag.id)
|
||||
userTags = _.reject tags, (tag) -> _.contains(mainTagIDs, tag.id)
|
||||
|
||||
# Sort the main tags so they always appear in a standard order
|
||||
mainTags = _.sortBy mainTags, (tag) -> mainTagIDs.indexOf(tag.id)
|
||||
mainTags.push new Tag(name: 'All Mail', id: '*')
|
||||
|
||||
inboxTag = _.find tags, (tag) -> tag.id is 'inbox'
|
||||
inboxTag?.unreadCount = @_inboxCount
|
||||
|
||||
# Sort user tags by name
|
||||
userTags = _.sortBy(userTags, 'name')
|
||||
|
||||
# Find root views, add the Views section
|
||||
featureSheets = _.filter WorkspaceStore.Sheet, (sheet) ->
|
||||
sheet.name in ['Today']
|
||||
extraSheets = _.filter WorkspaceStore.Sheet, (sheet) ->
|
||||
sheet.root and sheet.name and not (sheet in featureSheets)
|
||||
|
||||
lastSections = @_sections
|
||||
@_sections = [
|
||||
{ label: '', items: featureSheets, type: 'sheet' },
|
||||
{ label: 'Mailboxes', items: mainTags, type: 'tag' },
|
||||
{ label: 'Views', items: extraSheets, type: 'sheet' },
|
||||
{ label: 'Tags', items: userTags, type: 'tag' },
|
||||
]
|
||||
|
||||
@trigger(@)
|
||||
|
||||
_refetchFromAPI: ->
|
||||
namespace = NamespaceStore.current()
|
||||
|
@ -113,17 +125,11 @@ AccountSidebarStore = Reflux.createStore
|
|||
_onDataChanged: (change) ->
|
||||
@populateInboxCountDebounced ?= _.debounce =>
|
||||
@_populateInboxCount()
|
||||
, 1000
|
||||
@populateDraftCountDebounced ?= _.debounce =>
|
||||
@_populateDraftCount()
|
||||
, 1000
|
||||
, 5000
|
||||
|
||||
if change.objectClass is Tag.name
|
||||
@_populate()
|
||||
if change.objectClass is Thread.name
|
||||
@populateInboxCountDebounced()
|
||||
if change.objectClass is Message.name
|
||||
return unless _.some change.objects, (msg) -> msg.draft
|
||||
@populateDraftCountDebounced()
|
||||
|
||||
module.exports = AccountSidebarStore
|
||||
|
|
|
@ -24,7 +24,7 @@ ActivityBarStore = Reflux.createStore
|
|||
@_curlHistory = []
|
||||
@_longPollHistory = []
|
||||
@_longPollState = 'Unknown'
|
||||
@_visible = false
|
||||
@_visible = atom.inDevMode()
|
||||
|
||||
_registerListeners: ->
|
||||
@listenTo Actions.didMakeAPIRequest, @_onAPIRequest
|
||||
|
|
|
@ -93,12 +93,14 @@ EmailFixingStyles = """
|
|||
|
||||
.gmail_extra,
|
||||
.gmail_quote,
|
||||
#divRplyFwdMsg,
|
||||
blockquote {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.show-quoted-text .gmail_extra,
|
||||
.show-quoted-text .gmail_quote,
|
||||
.show-quoted-text #divRplyFwdMsg,
|
||||
.show-quoted-text blockquote {
|
||||
display:inherit;
|
||||
}
|
||||
|
@ -160,4 +162,4 @@ class EmailFrame extends React.Component
|
|||
Utils.stripQuotedText(email)
|
||||
|
||||
|
||||
module.exports = EmailFrame
|
||||
module.exports = EmailFrame
|
||||
|
|
|
@ -31,6 +31,8 @@ class ContainerView extends React.Component
|
|||
if webview
|
||||
node = React.findDOMNode(webview)
|
||||
if node.hasListeners is undefined
|
||||
node.addEventListener 'new-window', (e) ->
|
||||
require('shell').openExternal(e.url)
|
||||
node.addEventListener 'did-start-loading', (e) ->
|
||||
if node.hasMobileUserAgent is undefined
|
||||
node.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53")
|
||||
|
|
BIN
internal_packages/today/assets/HurmeGeometricSans4Thin.otf
Executable file
BIN
internal_packages/today/assets/HurmeGeometricSans4Thin.otf
Executable file
Binary file not shown.
BIN
internal_packages/today/assets/background.png
Normal file
BIN
internal_packages/today/assets/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 MiB |
16
internal_packages/today/lib/main.cjsx
Normal file
16
internal_packages/today/lib/main.cjsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
TodayView = require "./today-view"
|
||||
TodayIcon = require "./today-icon"
|
||||
{ComponentRegistry,
|
||||
WorkspaceStore} = require 'inbox-exports'
|
||||
|
||||
module.exports =
|
||||
|
||||
activate: (@state={}) ->
|
||||
WorkspaceStore.defineSheet 'Today', {root: true, supportedModes: ['list'], name: 'Today', icon: TodayIcon},
|
||||
list: ['RootSidebar', 'Today']
|
||||
|
||||
ComponentRegistry.register TodayView,
|
||||
location: WorkspaceStore.Location.Today
|
||||
|
||||
deactivate: ->
|
||||
ComponentRegistry.unregister(TodayView)
|
32
internal_packages/today/lib/today-icon.cjsx
Normal file
32
internal_packages/today/lib/today-icon.cjsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
React = require 'react'
|
||||
_ = require "underscore-plus"
|
||||
moment = require 'moment'
|
||||
classNames = require 'classnames'
|
||||
|
||||
class TodayIcon extends React.Component
|
||||
@displayName: 'TodayIcon'
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
moment: moment()
|
||||
|
||||
componentDidMount: =>
|
||||
@_setTimeState()
|
||||
|
||||
componentWillUnmount: =>
|
||||
clearInterval(@_timer)
|
||||
|
||||
render: =>
|
||||
classes = classNames
|
||||
'today-icon': true
|
||||
'selected': @props.selected
|
||||
|
||||
<div className={classes}>{@state.moment.format('D')}</div>
|
||||
|
||||
_setTimeState: =>
|
||||
timeTillNextSecond = (60 - (new Date).getSeconds()) * 1000
|
||||
@_timer = setTimeout(@_setTimeState, timeTillNextSecond)
|
||||
@setState(moment: moment())
|
||||
|
||||
|
||||
module.exports = TodayIcon
|
83
internal_packages/today/lib/today-view.cjsx
Normal file
83
internal_packages/today/lib/today-view.cjsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
React = require 'react'
|
||||
_ = require "underscore-plus"
|
||||
{Utils, Actions} = require 'inbox-exports'
|
||||
{Spinner, EventedIFrame} = require 'ui-components'
|
||||
moment = require 'moment'
|
||||
|
||||
class TodayViewDateTime extends React.Component
|
||||
@displayName: 'TodayViewDateTime'
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
moment: moment()
|
||||
|
||||
componentDidMount: =>
|
||||
@_setTimeState()
|
||||
|
||||
componentWillUnmount: =>
|
||||
clearInterval(@_timer)
|
||||
|
||||
render: =>
|
||||
<div className="centered">
|
||||
<div className="time">{@state.moment.format('h:mm')}</div>
|
||||
<div className="date">{@state.moment.format('dddd, MMM Do')}</div>
|
||||
</div>
|
||||
|
||||
_setTimeState: =>
|
||||
timeTillNextSecond = (60 - (new Date).getSeconds()) * 1000
|
||||
@_timer = setTimeout(@_setTimeState, timeTillNextSecond)
|
||||
|
||||
@setState(moment: moment())
|
||||
|
||||
|
||||
class TodayViewBox extends React.Component
|
||||
@displayName: 'TodayViewBox'
|
||||
|
||||
@propTypes:
|
||||
name: React.PropTypes.string.isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
|
||||
render: =>
|
||||
<div className="box">
|
||||
<h2>{@props.name}</h2>
|
||||
</div>
|
||||
|
||||
class TodayView extends React.Component
|
||||
@displayName: 'TodayView'
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = @_getStateFromStores()
|
||||
|
||||
render: =>
|
||||
<div className="today">
|
||||
<div className="inner">
|
||||
<TodayViewDateTime />
|
||||
<div className="boxes">
|
||||
<TodayViewBox name="Conversations">
|
||||
</TodayViewBox>
|
||||
<TodayViewBox name="Events">
|
||||
</TodayViewBox>
|
||||
<TodayViewBox name="Drafts">
|
||||
</TodayViewBox>
|
||||
</div>
|
||||
<div className="to-the-inbox">
|
||||
Inbox
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
componentDidMount: =>
|
||||
@_unsubscribers = []
|
||||
|
||||
componentWillUnmount: =>
|
||||
unsubscribe() for unsubscribe in @_unsubscribers
|
||||
|
||||
_getStateFromStores: =>
|
||||
{}
|
||||
|
||||
_onChange: =>
|
||||
@setState(@_getStateFromStores())
|
||||
|
||||
|
||||
module.exports = TodayView
|
14
internal_packages/today/package.json
Executable file
14
internal_packages/today/package.json
Executable file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "today",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main",
|
||||
"description": "Today View",
|
||||
"license": "Proprietary",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.8"
|
||||
}
|
||||
}
|
86
internal_packages/today/stylesheets/today-view.less
Normal file
86
internal_packages/today/stylesheets/today-view.less
Normal file
|
@ -0,0 +1,86 @@
|
|||
@import "ui-variables";
|
||||
@import "ui-mixins";
|
||||
|
||||
@font-face {
|
||||
font-family: 'Hurme';
|
||||
font-style: normal;
|
||||
src: url(nylas://today/assets/HurmeGeometricSans4Thin.otf);
|
||||
}
|
||||
|
||||
.today-icon {
|
||||
display:inline-block;
|
||||
overflow:hidden;
|
||||
width:16px;
|
||||
height:16px;
|
||||
color:@source-list-bg;
|
||||
text-align:center;
|
||||
font-weight:500;
|
||||
font-size:11px;
|
||||
line-height:19px;
|
||||
position:relative;
|
||||
top:5px;
|
||||
background-color:@text-color-very-subtle;
|
||||
|
||||
&.selected {
|
||||
background-color:@accent-primary;
|
||||
}
|
||||
}
|
||||
.today {
|
||||
background:url(nylas://today/assets/background.png) top center no-repeat;
|
||||
background-size:100%;
|
||||
overflow-y:scroll;
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
|
||||
.inner {
|
||||
|
||||
}
|
||||
|
||||
.to-the-inbox {
|
||||
opacity:0.3;
|
||||
position:absolute;
|
||||
width:100%;
|
||||
text-align:center;
|
||||
bottom:10px;
|
||||
font-weight:@font-weight-semi-bold;
|
||||
}
|
||||
.centered {
|
||||
text-align:center;
|
||||
opacity:0.6;
|
||||
.time {
|
||||
font-family: 'Hurme';
|
||||
margin-top:70px;
|
||||
font-size:100px;
|
||||
line-height:96px;
|
||||
}
|
||||
.date {
|
||||
font-family:@font-family-sans-serif;
|
||||
font-weight:@font-weight-normal;
|
||||
font-size:22px;
|
||||
}
|
||||
}
|
||||
.boxes {
|
||||
display: flex;
|
||||
flex-direction:row;
|
||||
padding:15px;
|
||||
position:absolute;
|
||||
bottom:20px;
|
||||
width:100%;
|
||||
.box {
|
||||
margin:15px;
|
||||
border-radius: @border-radius-large;
|
||||
background-color:white;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
flex:1;
|
||||
height:40vh;
|
||||
h2 {
|
||||
margin-top:4px;
|
||||
padding:12px;
|
||||
border-bottom:1px solid #ccc;
|
||||
font-size:15px;
|
||||
font-weight:@font-weight-semi-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +1,40 @@
|
|||
Reflux = require 'reflux'
|
||||
_ = require 'underscore-plus'
|
||||
{DatabaseStore, NamespaceStore, Actions, Tag} = require 'inbox-exports'
|
||||
{DatabaseStore, NamespaceStore, Actions, Thread} = require 'inbox-exports'
|
||||
remote = require 'remote'
|
||||
app = remote.require 'app'
|
||||
|
||||
AppUnreadCount = null
|
||||
|
||||
module.exports =
|
||||
AppUnreadBadgeStore = Reflux.createStore
|
||||
init: ->
|
||||
@listenTo NamespaceStore, @_onNamespaceChanged
|
||||
@listenTo DatabaseStore, @_onDataChanged
|
||||
@_fetchCount()
|
||||
|
||||
_onNamespaceChanged: ->
|
||||
@_onDataChanged()
|
||||
|
||||
_onDataChanged: (change) ->
|
||||
return if change && change.objectClass != Tag.name
|
||||
return app.dock?.setBadge?("") unless NamespaceStore.current()
|
||||
@_updateBadge()
|
||||
|
||||
_updateBadge: ->
|
||||
DatabaseStore.find(Tag, 'inbox').then (inbox) ->
|
||||
return unless inbox
|
||||
count = inbox.unreadCount
|
||||
if change && change.objectClass is Thread.name
|
||||
@_fetchCountDebounced ?= _.debounce(@_fetchCount, 5000)
|
||||
@_fetchCountDebounced()
|
||||
|
||||
_fetchCount: ->
|
||||
namespace = NamespaceStore.current()
|
||||
return unless namespace
|
||||
|
||||
DatabaseStore.count(Thread, [
|
||||
Thread.attributes.namespaceId.equal(namespace.id),
|
||||
Thread.attributes.unread.equal(true),
|
||||
Thread.attributes.tags.contains('inbox')
|
||||
]).then (count) ->
|
||||
return if AppUnreadCount is count
|
||||
AppUnreadCount = count
|
||||
|
||||
if count > 999
|
||||
app.dock?.setBadge?("\u221E")
|
||||
else if count > 0
|
||||
|
|
|
@ -68,6 +68,7 @@ class MultiselectList extends React.Component
|
|||
'core:previous-item': => @_onShift(-1)
|
||||
'core:select-down': => @_onShift(1, {select: true})
|
||||
'core:select-up': => @_onShift(-1, {select: true})
|
||||
'application:pop-sheet': => @_onDeselect()
|
||||
|
||||
Object.keys(props.commands).forEach (key) =>
|
||||
commands[key] = =>
|
||||
|
@ -156,6 +157,10 @@ class MultiselectList extends React.Component
|
|||
return unless id
|
||||
@state.dataView.selection.toggle(@state.dataView.getById(id))
|
||||
|
||||
_onDeselect: =>
|
||||
return unless @_visible()
|
||||
@state.dataView.selection.clear()
|
||||
|
||||
_onShift: (delta, options = {}) =>
|
||||
if @state.showKeyboardCursor and @_visible()
|
||||
id = @state.keyboardCursorId
|
||||
|
|
|
@ -35,7 +35,7 @@ class ResizableRegion extends React.Component
|
|||
|
||||
###
|
||||
Public: React `props` supported by ResizableRegion:
|
||||
|
||||
|
||||
- `handle` Provide a {ResizableHandle} to indicate which edge of the
|
||||
region should be draggable.
|
||||
- `onResize` A {Function} that will be called continuously as the region is resized.
|
||||
|
@ -58,6 +58,8 @@ class ResizableRegion extends React.Component
|
|||
minHeight: React.PropTypes.number
|
||||
maxHeight: React.PropTypes.number
|
||||
|
||||
style: React.PropTypes.object
|
||||
|
||||
constructor: (@props = {}) ->
|
||||
@props.handle ?= ResizableHandle.Right
|
||||
@state =
|
||||
|
@ -65,7 +67,7 @@ class ResizableRegion extends React.Component
|
|||
|
||||
render: =>
|
||||
if @props.handle.axis is 'horizontal'
|
||||
containerStyle =
|
||||
containerStyle = _.extend {}, @props.style,
|
||||
'minWidth': @props.minWidth
|
||||
'maxWidth': @props.maxWidth
|
||||
'position': 'relative'
|
||||
|
@ -76,7 +78,7 @@ class ResizableRegion extends React.Component
|
|||
containerStyle.flex = 1
|
||||
|
||||
else
|
||||
containerStyle =
|
||||
containerStyle = _.extend {}, @props.style,
|
||||
'minHeight': @props.minHeight
|
||||
'maxHeight': @props.maxHeight
|
||||
'position': 'relative'
|
||||
|
@ -90,7 +92,7 @@ class ResizableRegion extends React.Component
|
|||
containerStyle.flex = 1
|
||||
|
||||
otherProps = _.omit(@props, _.keys(@constructor.propTypes))
|
||||
|
||||
|
||||
<div style={containerStyle} {...otherProps}>
|
||||
{@props.children}
|
||||
<div className={@props.handle.className}
|
||||
|
@ -111,7 +113,7 @@ class ResizableRegion extends React.Component
|
|||
@setState(height: nextProps.initialHeight)
|
||||
if nextProps.handle.axis is 'horizontal' and nextProps.initialWidth != @props.initialWidth
|
||||
@setState(width: nextProps.initialWidth)
|
||||
|
||||
|
||||
componentWillUnmount: =>
|
||||
PriorityUICoordinator.endPriorityTask(@_taskId) if @_taskId
|
||||
@_taskId = null
|
||||
|
|
|
@ -15,8 +15,12 @@ about Tags on the Nylas Platform, read the
|
|||
API documentation for more information about what tags are read-only.
|
||||
|
||||
`unreadCount`: {AttributeNumber} The number of unread threads with the tag.
|
||||
Note: This attribute is only available when a single tag is fetched directly
|
||||
from the Nylas API, not when all tags are listed.
|
||||
|
||||
`threadCount`: {AttributeNumber} The number of threads with the tag.
|
||||
Note: This attribute is only available when a single tag is fetched directly
|
||||
from the Nylas API, not when all tags are listed.
|
||||
|
||||
###
|
||||
class Tag extends Model
|
||||
|
@ -34,4 +38,4 @@ class Tag extends Model
|
|||
modelKey: 'threadCount'
|
||||
jsonKey: 'thread_count'
|
||||
|
||||
module.exports = Tag
|
||||
module.exports = Tag
|
||||
|
|
|
@ -134,7 +134,7 @@ Utils =
|
|||
|
||||
tableNameForJoin: (primaryKlass, secondaryKlass) ->
|
||||
"#{primaryKlass.name}-#{secondaryKlass.name}"
|
||||
|
||||
|
||||
imageNamed: (resourcePath, fullname) ->
|
||||
[name, ext] = fullname.split('.')
|
||||
|
||||
|
@ -171,6 +171,7 @@ Utils =
|
|||
/<[br|p][ ]*>[\n]?[ ]*>/i, # HTML lines beginning with >
|
||||
/[\n|>]On .* wrote:[\n|<]/, #On ... wrote: on it's own line
|
||||
/.gmail_quote/ # gmail quote class class
|
||||
/divRplyFwdMsg/ # outlook?
|
||||
]
|
||||
|
||||
for regex in regexs
|
||||
|
|
|
@ -106,7 +106,7 @@ WorkspaceStore = Reflux.createStore
|
|||
###
|
||||
Managing Sheets
|
||||
###
|
||||
|
||||
|
||||
# * `id` {String} The ID of the Sheet being defined.
|
||||
# * `options` {Object} If the sheet should be listed in the left sidebar,
|
||||
# pass `{root: true, name: 'Label'}`.
|
||||
|
@ -126,6 +126,7 @@ WorkspaceStore = Reflux.createStore
|
|||
columns: columns
|
||||
supportedModes: Object.keys(columns)
|
||||
|
||||
icon: options.icon
|
||||
name: options.name
|
||||
root: options.root
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ class SheetContainer extends React.Component
|
|||
|
||||
<div name="Header" style={order:1, zIndex: 3}>
|
||||
<InjectedComponentSet matching={locations: [topSheet.Header, WorkspaceStore.Sheet.Global.Header]}
|
||||
direction="column"
|
||||
id={topSheet.id}/>
|
||||
</div>
|
||||
|
||||
|
@ -206,6 +207,7 @@ class SheetContainer extends React.Component
|
|||
|
||||
<div name="Footer" style={order:3, zIndex: 4}>
|
||||
<InjectedComponentSet matching={locations: [topSheet.Footer, WorkspaceStore.Sheet.Global.Footer]}
|
||||
direction="column"
|
||||
id={topSheet.id}/>
|
||||
</div>
|
||||
</Flexbox>
|
||||
|
|
|
@ -63,6 +63,7 @@ class Sheet extends React.Component
|
|||
<ResizableRegion key={"#{@props.data.id}:#{idx}"}
|
||||
name={"#{@props.data.id}:#{idx}"}
|
||||
className={"column-#{location.id}"}
|
||||
style={height:'100%'}
|
||||
data-column={idx}
|
||||
onResize={ => @props.onColumnSizeChanged(@) }
|
||||
minWidth={minWidth}
|
||||
|
@ -76,7 +77,7 @@ class Sheet extends React.Component
|
|||
name={"#{@props.data.id}:#{idx}"}
|
||||
className={"column-#{location.id}"}
|
||||
data-column={idx}
|
||||
style={flex: 1}
|
||||
style={flex: 1, height:'100%'}
|
||||
matching={location: location, mode: @state.mode}/>
|
||||
|
||||
_getStateFromStores: =>
|
||||
|
|
Loading…
Reference in a new issue