mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
feat(selection): add selection of read, unread, starred, etc
Summary: Can select all, deselect-all, read, unread, starred, unstarred. Yes, it's not REALLY select "all", but it uses the items in the current `retainedRange`. This is actually similar to what gmail does (only selects on the first page of a 100). Test Plan: new test Reviewers: juan, bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D2241
This commit is contained in:
parent
32ffc7123a
commit
119da453e1
5 changed files with 47 additions and 2 deletions
|
@ -23,6 +23,12 @@ class ThreadListStore extends NylasStore
|
||||||
@listenTo AccountStore, @_onAccountChanged
|
@listenTo AccountStore, @_onAccountChanged
|
||||||
@listenTo FocusedMailViewStore, @_onMailViewChanged
|
@listenTo FocusedMailViewStore, @_onMailViewChanged
|
||||||
|
|
||||||
|
atom.commands.add "body",
|
||||||
|
'thread-list:select-read' : @_onSelectRead
|
||||||
|
'thread-list:select-unread' : @_onSelectUnread
|
||||||
|
'thread-list:select-starred' : @_onSelectStarred
|
||||||
|
'thread-list:select-unstarred': @_onSelectUnstarred
|
||||||
|
|
||||||
# We can't create a @view on construction because the CategoryStore
|
# We can't create a @view on construction because the CategoryStore
|
||||||
# has hot yet been populated from the database with the list of
|
# has hot yet been populated from the database with the list of
|
||||||
# categories and their corresponding ids. Once that is ready, the
|
# categories and their corresponding ids. Once that is ready, the
|
||||||
|
@ -81,6 +87,22 @@ class ThreadListStore extends NylasStore
|
||||||
|
|
||||||
Actions.setFocus(collection: 'thread', item: null)
|
Actions.setFocus(collection: 'thread', item: null)
|
||||||
|
|
||||||
|
_onSelectRead: =>
|
||||||
|
items = @_view.itemsCurrentlyInViewMatching (item) -> not item.unread
|
||||||
|
@_view.selection.set(items)
|
||||||
|
|
||||||
|
_onSelectUnread: =>
|
||||||
|
items = @_view.itemsCurrentlyInViewMatching (item) -> item.unread
|
||||||
|
@_view.selection.set(items)
|
||||||
|
|
||||||
|
_onSelectStarred: =>
|
||||||
|
items = @_view.itemsCurrentlyInViewMatching (item) -> item.starred
|
||||||
|
@_view.selection.set(items)
|
||||||
|
|
||||||
|
_onSelectUnstarred: =>
|
||||||
|
items = @_view.itemsCurrentlyInViewMatching (item) -> not item.starred
|
||||||
|
@_view.selection.set(items)
|
||||||
|
|
||||||
# Inbound Events
|
# Inbound Events
|
||||||
|
|
||||||
_onMailViewChanged: ->
|
_onMailViewChanged: ->
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
'g l' : 'navigation:go-to-label'
|
'g l' : 'navigation:go-to-label'
|
||||||
|
|
||||||
### Threadlist selection ###
|
### Threadlist selection ###
|
||||||
'* a': 'thread-list:select-all'
|
'* a': 'multiselect-list:select-all'
|
||||||
'* n': 'thread-list:deselect-all'
|
'* n': 'multiselect-list:deselect-all'
|
||||||
'* r': 'thread-list:select-read'
|
'* r': 'thread-list:select-read'
|
||||||
'* u': 'thread-list:select-unread'
|
'* u': 'thread-list:select-unread'
|
||||||
'* s': 'thread-list:select-starred'
|
'* s': 'thread-list:select-starred'
|
||||||
|
|
|
@ -33,6 +33,14 @@ describe "ModelView", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@view = new TestModelView()
|
@view = new TestModelView()
|
||||||
|
|
||||||
|
describe "itemsCurrentlyInViewMatching", ->
|
||||||
|
it "returns matching items", ->
|
||||||
|
@view.stubFillPage(0)
|
||||||
|
items = @view.itemsCurrentlyInViewMatching (item) ->
|
||||||
|
item.id == "A55"
|
||||||
|
expect(items.length).toBe 1
|
||||||
|
expect(items[0].id).toBe "A55"
|
||||||
|
|
||||||
describe "setRetainedRange", ->
|
describe "setRetainedRange", ->
|
||||||
it "should perform basic bounds checks to avoid fetching non-existent pages", ->
|
it "should perform basic bounds checks to avoid fetching non-existent pages", ->
|
||||||
@view.setRetainedRange({start: -100, end: 15000})
|
@view.setRetainedRange({start: -100, end: 15000})
|
||||||
|
|
|
@ -83,6 +83,9 @@ class MultiselectList extends React.Component
|
||||||
'core:list-page-up': => @_onScrollByPage(-1)
|
'core:list-page-up': => @_onScrollByPage(-1)
|
||||||
'core:list-page-down': => @_onScrollByPage(1)
|
'core:list-page-down': => @_onScrollByPage(1)
|
||||||
'application:pop-sheet': => @_onDeselect()
|
'application:pop-sheet': => @_onDeselect()
|
||||||
|
'multiselect-list:select-all': @_onSelectAll
|
||||||
|
'core:select-all': @_onSelectAll
|
||||||
|
'multiselect-list:deselect-all': => @_onDeselect()
|
||||||
|
|
||||||
render: =>
|
render: =>
|
||||||
# IMPORTANT: DO NOT pass inline functions as props. _.isEqual thinks these
|
# IMPORTANT: DO NOT pass inline functions as props. _.isEqual thinks these
|
||||||
|
@ -152,6 +155,11 @@ class MultiselectList extends React.Component
|
||||||
return unless @state.handler
|
return unless @state.handler
|
||||||
@state.handler.onSelect()
|
@state.handler.onSelect()
|
||||||
|
|
||||||
|
_onSelectAll: =>
|
||||||
|
return unless @state.handler
|
||||||
|
items = @state.dataView.itemsCurrentlyInViewMatching -> true
|
||||||
|
@state.dataView.selection.set(items)
|
||||||
|
|
||||||
_onDeselect: =>
|
_onDeselect: =>
|
||||||
return unless @_visible() and @state.dataView
|
return unless @_visible() and @state.dataView
|
||||||
@state.dataView.selection.clear()
|
@state.dataView.selection.clear()
|
||||||
|
|
|
@ -73,6 +73,13 @@ class ModelView
|
||||||
pagesRetained: ->
|
pagesRetained: ->
|
||||||
[Math.floor(@_retainedRange.start / @_pageSize)..Math.floor(@_retainedRange.end / @_pageSize)]
|
[Math.floor(@_retainedRange.start / @_pageSize)..Math.floor(@_retainedRange.end / @_pageSize)]
|
||||||
|
|
||||||
|
itemsCurrentlyInViewMatching: (matchFn) ->
|
||||||
|
matchedItems = []
|
||||||
|
for index, page of @_pages
|
||||||
|
for item in (page.items ? [])
|
||||||
|
matchedItems.push item if matchFn(item)
|
||||||
|
return matchedItems
|
||||||
|
|
||||||
setRetainedRange: ({start, end}) ->
|
setRetainedRange: ({start, end}) ->
|
||||||
{start, end} = @padRetainedRange({start, end})
|
{start, end} = @padRetainedRange({start, end})
|
||||||
start = Math.max(0, Math.min(@count(), start))
|
start = Math.max(0, Math.min(@count(), start))
|
||||||
|
|
Loading…
Reference in a new issue