diff --git a/internal_packages/attachments/lib/image-attachment-component.cjsx b/internal_packages/attachments/lib/image-attachment-component.cjsx
index aaca8b951..a6ec1d330 100644
--- a/internal_packages/attachments/lib/image-attachment-component.cjsx
+++ b/internal_packages/attachments/lib/image-attachment-component.cjsx
@@ -14,12 +14,13 @@ class ImageAttachmentComponent extends AttachmentComponent
- {@_fileActions()}
+ {@_renderFileActions()}
-
-
{@props.file.filename}
+
+
{@props.file.filename}
+
{@_imgOrLoader()}
diff --git a/internal_packages/attachments/stylesheets/attachments.less b/internal_packages/attachments/stylesheets/attachments.less
index 102c8e685..fc08bcbd5 100644
--- a/internal_packages/attachments/stylesheets/attachments.less
+++ b/internal_packages/attachments/stylesheets/attachments.less
@@ -79,8 +79,11 @@
}
}
-.image-file-upload, .image-attachment-file-wrap, .attachment-file-wrap,
+.image-file-upload,
+.image-attachment-file-wrap,
+.attachment-file-wrap,
.attachment-inner-wrap {
+
.attachment-download-bar-wrap {
display: none;
}
@@ -128,10 +131,15 @@
}
}
-.image-attachment-file-wrap, .image-file-upload {
+.image-attachment-file-wrap,
+.image-file-upload {
+
position: relative;
- margin: 0 0 8px 0;
text-align: center;
+ display:inline-block;
+ vertical-align: top;
+ margin-bottom: @spacing-standard;
+ margin-right: @spacing-standard;
.attachment-download-progress,
.attachment-upload-progress {
@@ -147,11 +155,11 @@
}
&:hover {
- .attachment-file-actions, .attachment-name-bg, .attachment-name {
+ .attachment-file-actions, .attachment-name-container, .attachment-name {
display: block;
}
}
- .attachment-file-actions, .attachment-name-bg, .attachment-name {
+ .attachment-file-actions, .attachment-name-container, .attachment-name {
display: none;
}
@@ -167,30 +175,35 @@
.attachment-preview {
position: relative;
z-index: 1;
+ overflow: hidden;
- .attachment-name-bg {
+ .attachment-name-container {
position: absolute;
bottom: 0;
top: 0;
z-index: 2;
width: 100%;
- background: linear-gradient(to top, rgba(0,0,0,0.75) 0%,rgba(0,0,0,0) 23%)
- }
- .attachment-name {
- color: @white;
- left: 15px;
- bottom: 13px;
- position: absolute;
- z-index: 3;
+ height:100%;
+ min-height:300px;
+ background: linear-gradient(to top, rgba(0,0,0,0.75) 0%,rgba(0,0,0,0) 23%);
+ vertical-align:bottom;
+
+ .attachment-name {
+ color: @white;
+ left: @spacing-standard;
+ bottom: @spacing-standard;
+ position: absolute;
+ z-index: 3;
+ }
}
img {
position: relative;
z-index: 1;
max-width: 100%;
- background: @background-secondary;
+ background: url(../static/images/attachments/transparency-background.png) top left repeat;
+ background-size:8px;
}
}
}
-
diff --git a/internal_packages/composer/lib/image-file-upload.cjsx b/internal_packages/composer/lib/image-file-upload.cjsx
index 0a957f926..1cbaf76a4 100644
--- a/internal_packages/composer/lib/image-file-upload.cjsx
+++ b/internal_packages/composer/lib/image-file-upload.cjsx
@@ -18,8 +18,9 @@ class ImageFileUpload extends FileUpload
-
-
{@props.uploadData.fileName}
+
+
{@props.uploadData.fileName}
+
diff --git a/internal_packages/developer-bar/stylesheets/developer-bar.less b/internal_packages/developer-bar/stylesheets/developer-bar.less
index 13f1423c6..268259dcd 100755
--- a/internal_packages/developer-bar/stylesheets/developer-bar.less
+++ b/internal_packages/developer-bar/stylesheets/developer-bar.less
@@ -9,7 +9,7 @@
order:1000;
display:flex;
flex-direction:column;
-
+
.resizable {
display: flex;
width:100%;
@@ -49,12 +49,12 @@
font-size: 13px;
line-height: 15px;
height: 25px;
- background-color: #999;
+ background: rgba(60,60,60,1);
color: white;
}
.btn:hover {
- background-color: #AAA;
+ background: rgba(40,40,40,1);
}
.fa-caret-square-o-down,
@@ -146,7 +146,7 @@
color: white;
}
}
-
+
&.long-polling {
.item {
@@ -214,5 +214,3 @@
}
}
-
-
diff --git a/internal_packages/thread-list/lib/thread-list-icon.cjsx b/internal_packages/thread-list/lib/thread-list-icon.cjsx
index e06f1f70d..bcf4f07d4 100644
--- a/internal_packages/thread-list/lib/thread-list-icon.cjsx
+++ b/internal_packages/thread-list/lib/thread-list-icon.cjsx
@@ -12,7 +12,8 @@ class ThreadListIcon extends React.Component
thread: React.PropTypes.object
_iconType: =>
- myEmail = NamespaceStore.current()?.emailAddress
+ if !@props.thread
+ return 'thread-icon-star-on-hover'
if @props.thread.hasTagId('starred')
return 'thread-icon-star'
@@ -23,6 +24,7 @@ class ThreadListIcon extends React.Component
msgs = @_nonDraftMessages()
last = msgs[msgs.length - 1]
+ myEmail = NamespaceStore.current()?.emailAddress
if msgs.length > 1 and last.from[0]?.email is myEmail
if Utils.isForwardedMessage(last)
return 'thread-icon-forwarded thread-icon-star-on-hover'
diff --git a/src/flux/models/query.coffee b/src/flux/models/query.coffee
index deeb363fa..59f46f52b 100644
--- a/src/flux/models/query.coffee
+++ b/src/flux/models/query.coffee
@@ -159,13 +159,19 @@ class ModelQuery
Query Execution
###
- # Public: Starts query execution and returns a Promise.
+ # Public: Short-hand syntax that calls run().then(fn) with the provided function.
#
# Returns a {Promise} that resolves with the Models returned by the
# query, or rejects with an error from the Database layer.
#
then: (next) ->
- @_database.run(@).then(next)
+ @run(@).then(next)
+
+ # Public: Returns a {Promise} that resolves with the Models returned by the
+ # query, or rejects with an error from the Database layer.
+ #
+ run: ->
+ @_database.run(@)
formatResult: (result) ->
return null unless result
diff --git a/src/flux/stores/database-view.coffee b/src/flux/stores/database-view.coffee
index fcfa7223e..5222b2dfc 100644
--- a/src/flux/stores/database-view.coffee
+++ b/src/flux/stores/database-view.coffee
@@ -6,6 +6,36 @@ EventEmitter = require('events').EventEmitter
verbose = true
+# A small helper class that prevents the DatabaseView from making too many
+# queries. It tracks the number of jobs in flight via `increment` and allows
+# a callback to run "when there are fewer then N ongoing queries".
+# Sort of like _.throttle, but with a work threshold rather than a time threshold.
+class TaskThrottler
+ constructor: (@_maxConcurrent) ->
+ @_inflight = 0
+ @_whenReady = null
+
+ whenReady: (fn) ->
+ if @_inflight < @_maxConcurrent
+ fn()
+ else
+ @_whenReady = fn
+
+ increment: ->
+ decremented = false
+ @_inflight += 1
+
+ # Returns a function that can be called once and only once to
+ # decrement the counter.
+ return =>
+ if not decremented
+ @_inflight -= 1
+ if @_whenReady and @_inflight < @_maxConcurrent
+ @_whenReady()
+ @_whenReady = null
+ decremented = true
+
+
# Public: DatabaseView abstracts away the process of paginating a query
# and loading ranges of data. It's very smart about deciding when
# results need to be refreshed. There are a few core concepts that
@@ -34,13 +64,15 @@ class DatabaseView extends ModelView
constructor: (@klass, config = {}, @_metadataProvider) ->
super
@_pageSize = 100
+ @_throttler = new TaskThrottler(2)
+
@_matchers = config.matchers ? []
@_includes = config.includes ? []
@_orders = config.orders ? []
@_count = -1
@invalidateCount()
- @invalidateRetainedRangeImmediate()
+ @invalidateRetainedRange()
@
log: ->
@@ -251,18 +283,16 @@ class DatabaseView extends ModelView
@_count = count
@_emitter.emit('trigger')
- invalidateRetainedRange: _.debounce ->
- @invalidateRetainedRangeImmediate()
- ,10
-
- invalidateRetainedRangeImmediate: ->
- for idx in @pagesRetained()
- @retrievePage(idx)
+ invalidateRetainedRange: ->
+ @_throttler.whenReady =>
+ for idx in @pagesRetained()
+ @retrievePage(idx)
retrieveDirtyInRetainedRange: ->
- for idx in @pagesRetained()
- if not @_pages[idx] or @_pages[idx].lastTouchTime > @_pages[idx].lastLoadTime
- @retrievePage(idx)
+ @_throttler.whenReady =>
+ for idx in @pagesRetained()
+ if not @_pages[idx] or @_pages[idx].lastTouchTime > @_pages[idx].lastLoadTime
+ @retrievePage(idx)
retrievePage: (idx) ->
page = @_pages[idx] ? {
@@ -284,7 +314,8 @@ class DatabaseView extends ModelView
query.include(attr) for attr in @_includes
query.order(@_orders) if @_orders.length > 0
- query.then (items) =>
+ decrement = @_throttler.increment()
+ query.run().finally(decrement).then (items) =>
# If the page is no longer in the cache at all, it may have fallen out of the
# retained range and been cleaned up.
return unless @_pages[idx]
@@ -323,7 +354,8 @@ class DatabaseView extends ModelView
if idsMissingMetadata.length > 0 and @_metadataProvider
metadataPromise = @_metadataProvider(idsMissingMetadata)
- metadataPromise.then (results) =>
+ decrement = @_throttler.increment()
+ metadataPromise.finally(decrement).then (results) =>
# If we've started reloading since we made our query, don't do any more work
if page.lastTouchTime >= touchTime
@log("Metadata version #{touchTime} fetched, but out of date (current is #{page.lastTouchTime})")
diff --git a/static/images/attachments/icon-attachment-download-@1x.png b/static/images/attachments/icon-attachment-download@1x.png
similarity index 100%
rename from static/images/attachments/icon-attachment-download-@1x.png
rename to static/images/attachments/icon-attachment-download@1x.png
diff --git a/static/images/attachments/icon-attachment-download-@2x.png b/static/images/attachments/icon-attachment-download@2x.png
similarity index 100%
rename from static/images/attachments/icon-attachment-download-@2x.png
rename to static/images/attachments/icon-attachment-download@2x.png
diff --git a/static/images/attachments/transparency-background.png b/static/images/attachments/transparency-background.png
new file mode 100644
index 000000000..7aa8018aa
Binary files /dev/null and b/static/images/attachments/transparency-background.png differ