From 1c675935a7570459f8cede2a957d7d0f81e55ba8 Mon Sep 17 00:00:00 2001 From: Halla Moore Date: Wed, 12 Oct 2016 13:55:04 -0700 Subject: [PATCH] feat(popout-threads) Add functionality to open threads in popout windows Summary: Threads can now be opened in separate windows. This can be done via the popout icon next to the print icon, or by double-clicking the thread when in double- pane mode. Note that the single-click action is still fired, which is why double-clicking does not work in single-pane mode. The popout icon changes to a pop-in icon while in the popout window, to allow users to collapse it back into the main window. Test Plan: Tested locally Reviewers: evan, juan Reviewed By: juan Subscribers: sdw Differential Revision: https://phab.nylas.com/D3332 --- internal_packages/attachments/package.json | 3 +- internal_packages/composer-emoji/package.json | 3 +- .../composer-markdown/package.json | 3 +- .../composer-signature/package.json | 3 +- .../composer-spellcheck/package.json | 3 +- .../composer-templates/package.json | 3 +- .../composer-translate/package.json | 3 +- internal_packages/composer/lib/main.es6 | 4 +++ internal_packages/composer/package.json | 3 +- internal_packages/keybase/package.json | 3 +- internal_packages/message-list/lib/main.cjsx | 32 ++++++++++++------ .../message-list/lib/message-list.cjsx | 24 +++++++++++++ internal_packages/message-list/package.json | 4 +++ .../phishing-detection/package.json | 3 +- .../send-and-archive/package.json | 3 +- .../thread-list/lib/thread-list.cjsx | 1 + src/flux/actions.coffee | 24 +++++++++++++ src/flux/stores/message-store.coffee | 16 +++++++++ src/nylas-env.coffee | 3 ++ src/pro | 2 +- .../images/message-list/thread-popin@2x.png | Bin 0 -> 678 bytes .../images/message-list/thread-popout@2x.png | Bin 0 -> 619 bytes 22 files changed, 121 insertions(+), 22 deletions(-) create mode 100644 static/images/message-list/thread-popin@2x.png create mode 100644 static/images/message-list/thread-popout@2x.png diff --git a/internal_packages/attachments/package.json b/internal_packages/attachments/package.json index 42c35c833..5dfb572ba 100644 --- a/internal_packages/attachments/package.json +++ b/internal_packages/attachments/package.json @@ -13,6 +13,7 @@ "windowTypes": { "default": true, "composer": true, - "composer-preload": true + "composer-preload": true, + "thread-popout": true } } diff --git a/internal_packages/composer-emoji/package.json b/internal_packages/composer-emoji/package.json index a17c322c9..4cdf994d9 100644 --- a/internal_packages/composer-emoji/package.json +++ b/internal_packages/composer-emoji/package.json @@ -21,7 +21,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "license": "GPL-3.0" } diff --git a/internal_packages/composer-markdown/package.json b/internal_packages/composer-markdown/package.json index d2471cd55..14a597e0f 100644 --- a/internal_packages/composer-markdown/package.json +++ b/internal_packages/composer-markdown/package.json @@ -16,7 +16,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "dependencies": { "marked": "^0.3", diff --git a/internal_packages/composer-signature/package.json b/internal_packages/composer-signature/package.json index be75f2ba1..045b0da8c 100755 --- a/internal_packages/composer-signature/package.json +++ b/internal_packages/composer-signature/package.json @@ -10,7 +10,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "dependencies": { } diff --git a/internal_packages/composer-spellcheck/package.json b/internal_packages/composer-spellcheck/package.json index 834087dc4..546255970 100755 --- a/internal_packages/composer-spellcheck/package.json +++ b/internal_packages/composer-spellcheck/package.json @@ -10,7 +10,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "dependencies": { } diff --git a/internal_packages/composer-templates/package.json b/internal_packages/composer-templates/package.json index 99bfb2a07..6e2067772 100755 --- a/internal_packages/composer-templates/package.json +++ b/internal_packages/composer-templates/package.json @@ -16,6 +16,7 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true } } diff --git a/internal_packages/composer-translate/package.json b/internal_packages/composer-translate/package.json index 716397418..b04221ed8 100755 --- a/internal_packages/composer-translate/package.json +++ b/internal_packages/composer-translate/package.json @@ -19,6 +19,7 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true } } diff --git a/internal_packages/composer/lib/main.es6 b/internal_packages/composer/lib/main.es6 index 72b2f8f0d..57ba29ccd 100644 --- a/internal_packages/composer/lib/main.es6 +++ b/internal_packages/composer/lib/main.es6 @@ -115,6 +115,10 @@ export function activate() { ComponentRegistry.register(ComposeButton, { location: WorkspaceStore.Location.RootSidebar.Toolbar, }); + } else if (NylasEnv.isThreadWindow()) { + ComponentRegistry.register(ComposerViewForDraftClientId, { + role: 'Composer', + }); } else { NylasEnv.getCurrentWindow().setMinimumSize(480, 250); ComponentRegistry.register(ComposerWithWindowProps, { diff --git a/internal_packages/composer/package.json b/internal_packages/composer/package.json index 9dfb64905..a0f2b67fe 100644 --- a/internal_packages/composer/package.json +++ b/internal_packages/composer/package.json @@ -15,6 +15,7 @@ "windowTypes": { "default": true, "composer": true, - "composer-preload": true + "composer-preload": true, + "thread-popout": true } } diff --git a/internal_packages/keybase/package.json b/internal_packages/keybase/package.json index 001b26605..fef5a5255 100755 --- a/internal_packages/keybase/package.json +++ b/internal_packages/keybase/package.json @@ -17,6 +17,7 @@ "license": "GPL-3.0", "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true } } diff --git a/internal_packages/message-list/lib/main.cjsx b/internal_packages/message-list/lib/main.cjsx index 66ec072c3..47a7aaf19 100644 --- a/internal_packages/message-list/lib/main.cjsx +++ b/internal_packages/message-list/lib/main.cjsx @@ -1,6 +1,9 @@ {ComponentRegistry, ExtensionRegistry, - WorkspaceStore} = require 'nylas-exports' + WorkspaceStore, + DatabaseStore, + Actions, + Thread} = require 'nylas-exports' MessageList = require("./message-list") MessageListHiddenMessagesToggle = require('./message-list-hidden-messages-toggle').default @@ -10,18 +13,27 @@ SidebarParticipantPicker = require('./sidebar-participant-picker').default module.exports = activate: -> - # Register Message List Actions we provide globally - ComponentRegistry.register MessageList, - location: WorkspaceStore.Location.MessageList + if NylasEnv.isMainWindow() + # Register Message List Actions we provide globally + ComponentRegistry.register MessageList, + location: WorkspaceStore.Location.MessageList - ComponentRegistry.register SidebarParticipantPicker, - location: WorkspaceStore.Location.MessageListSidebar + ComponentRegistry.register SidebarParticipantPicker, + location: WorkspaceStore.Location.MessageListSidebar - ComponentRegistry.register SidebarPluginContainer, - location: WorkspaceStore.Location.MessageListSidebar + ComponentRegistry.register SidebarPluginContainer, + location: WorkspaceStore.Location.MessageListSidebar - ComponentRegistry.register MessageListHiddenMessagesToggle, - role: 'MessageListHeaders' + ComponentRegistry.register MessageListHiddenMessagesToggle, + role: 'MessageListHeaders' + else + # This is for the thread-popout window. + ComponentRegistry.register(MessageList, {location: WorkspaceStore.Location.Center}) + threadId = NylasEnv.getWindowProps().threadId; + # We need to locate the thread and focus it so that the MessageList displays it + DatabaseStore.find(Thread, threadId).then((thread) => + Actions.setFocus({collection: 'thread', item: thread}) + ) deactivate: -> ComponentRegistry.unregister MessageList diff --git a/internal_packages/message-list/lib/message-list.cjsx b/internal_packages/message-list/lib/message-list.cjsx index 405a72cff..41048e59c 100644 --- a/internal_packages/message-list/lib/message-list.cjsx +++ b/internal_packages/message-list/lib/message-list.cjsx @@ -204,6 +204,7 @@ class MessageList extends React.Component
+ {@_renderPopoutToggle()} _renderExpandToggle: => @@ -218,6 +219,17 @@ class MessageList extends React.Component + _renderPopoutToggle: => + if NylasEnv.isThreadWindow() +
+ +
+ else +
+ +
+ + _renderReplyArea: =>
@@ -250,6 +262,18 @@ class MessageList extends React.Component node = ReactDOM.findDOMNode(@) Actions.printThread(@state.currentThread, node.innerHTML) + _onPopThreadIn: => + return unless @state.currentThread + Actions.focusThreadMainWindow(@state.currentThread) + NylasEnv.close() + + _onPopoutThread: => + return unless @state.currentThread + Actions.popoutThread(@state.currentThread) + # This returns the single-pane view to the inbox, and does nothing for + # double-pane view because we're at the root sheet. + Actions.popSheet() + _onClickReplyArea: => return unless @state.currentThread Actions.composeReply({ diff --git a/internal_packages/message-list/package.json b/internal_packages/message-list/package.json index 5c4a8d614..0524f4893 100755 --- a/internal_packages/message-list/package.json +++ b/internal_packages/message-list/package.json @@ -7,5 +7,9 @@ "private": true, "engines": { "nylas": "*" + }, + "windowTypes": { + "default": true, + "thread-popout": true } } diff --git a/internal_packages/phishing-detection/package.json b/internal_packages/phishing-detection/package.json index d5e00886c..baeb9e80e 100644 --- a/internal_packages/phishing-detection/package.json +++ b/internal_packages/phishing-detection/package.json @@ -14,7 +14,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "dependencies": { "tld": "^0.0.2" diff --git a/internal_packages/send-and-archive/package.json b/internal_packages/send-and-archive/package.json index 29370271d..be14076d3 100755 --- a/internal_packages/send-and-archive/package.json +++ b/internal_packages/send-and-archive/package.json @@ -10,7 +10,8 @@ }, "windowTypes": { "default": true, - "composer": true + "composer": true, + "thread-popout": true }, "dependencies": { } diff --git a/internal_packages/thread-list/lib/thread-list.cjsx b/internal_packages/thread-list/lib/thread-list.cjsx index f8fea53f8..058b6f59f 100644 --- a/internal_packages/thread-list/lib/thread-list.cjsx +++ b/internal_packages/thread-list/lib/thread-list.cjsx @@ -101,6 +101,7 @@ class ThreadList extends React.Component scrollTooltipComponent={ThreadListScrollTooltip} emptyComponent={EmptyListState} keymapHandlers={@_keymapHandlers()} + onDoubleClick={(thread) -> Actions.popoutThread(thread)} onDragStart={@_onDragStart} onDragEnd={@_onDragEnd} draggable="true" /> diff --git a/src/flux/actions.coffee b/src/flux/actions.coffee index 1dc960bfb..a332b3b98 100644 --- a/src/flux/actions.coffee +++ b/src/flux/actions.coffee @@ -305,6 +305,30 @@ class Actions ### @printThread: ActionScopeWindow + ### + Public: Display the thread in a new popout window + + *Scope: Window* + + ``` + thread = + Actions.popoutThread(thread) + ``` + ### + @popoutThread: ActionScopeWindow + + ### + Public: Display the thread in the main window + + *Scope: Global* + + ``` + thread = + Actions.focusThreadMainWindow(thread) + ``` + ### + @focusThreadMainWindow: ActionScopeGlobal + ### Public: Create a new reply to the provided threadId and messageId and populate it with the body provided. diff --git a/src/flux/stores/message-store.coffee b/src/flux/stores/message-store.coffee index f4c2c3afd..fc54b1a61 100644 --- a/src/flux/stores/message-store.coffee +++ b/src/flux/stores/message-store.coffee @@ -107,6 +107,8 @@ class MessageStore extends NylasStore @listenTo Actions.toggleAllMessagesExpanded, @_onToggleAllMessagesExpanded @listenTo Actions.toggleHiddenMessages, @_onToggleHiddenMessages @listenTo FocusedPerspectiveStore, @_onPerspectiveChanged + @listenTo Actions.popoutThread, @_onPopoutThread + @listenTo Actions.focusThreadMainWindow, @_onFocusThreadMainWindow _onPerspectiveChanged: => @trigger() @@ -313,6 +315,20 @@ class MessageStore extends NylasStore items + _onPopoutThread: (thread) -> + NylasEnv.newWindow + title: false, # MessageList already displays the thread subject + hidden: false, + windowKey: "thread-#{thread.id}", + windowType: 'thread-popout', + windowProps: threadId: thread.id, + + _onFocusThreadMainWindow: (thread) -> + if NylasEnv.isMainWindow() + Actions.setFocus({collection: 'thread', item: thread}) + NylasEnv.focus() + + store = new MessageStore() store.registerExtension = deprecate( 'MessageStore.registerExtension', diff --git a/src/nylas-env.coffee b/src/nylas-env.coffee index 51494d2b1..20d00284f 100644 --- a/src/nylas-env.coffee +++ b/src/nylas-env.coffee @@ -360,6 +360,9 @@ class NylasEnvConstructor isComposerWindow: -> @getWindowType() in ["composer", "composer-preload"] + isThreadWindow: -> + @getWindowType() is 'thread-popout' + getWindowType: -> @getLoadSettings().windowType diff --git a/src/pro b/src/pro index ddb1789ab..26431475c 160000 --- a/src/pro +++ b/src/pro @@ -1 +1 @@ -Subproject commit ddb1789abc9aa34ac9a3fb893e7ee71e7893af67 +Subproject commit 26431475c6590262a190e572d0717fd730827cff diff --git a/static/images/message-list/thread-popin@2x.png b/static/images/message-list/thread-popin@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8b16614beba95ebf5a4f6fff6474c36228ee9f80 GIT binary patch literal 678 zcmV;X0$KfuP)BFA1s{on8O{02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00It4L_t(o!|j*9OB_KI$3LqV zNd!X@Eo>49!L+dmq)WMBT?k&|`3IOv3rTHxl~szfut^y%DGUV0(P^|6u@J;U1%DJ0 zh!BJ{qB)zlvJA7{&fao`IL$0Ov+w);zIpTA8MtqEy@gR6Bz+jgee&F!jQ4br%P8`;5_Qp1YSY_xjW?mJfzHXXrd!zi9@6U!Ui z=>vA!1-K4W!YIaQ^~Irl9{A)7p4W&@q*xyS&mH4Sz?h65`GEWQz<+zoiu#|x2jB|W z@&)h6h*IDH#%T4nNSvM$z*mh{<3)v33V^Qw?^DXFo%pmbfhjSVD^Xs}NxGHgoOzcZ z`S3B&2W)n#yhsKT5*NTymND5rQ3ioC;E_dSK4b2s%dE=S1^=XPm3>%fn*pQMdKkqY zMfJBF^SzcJuqzc9D)Jk?Wd{rbe|&bqq$5Zthu6uxHLxdrBaGr{VVBCJyDlv$OyJ02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00GlUL_t(o!|j$YNCZI?#(%DV z5J5z)D2fPj2Ga?mL6Ng8EQ`US7p@2*h?m4@yl4^5q+neQnudWv&;yH#NyMNQix@=2 zVi7H)EXVHc-Z}>HUYVVpH}k&t%{Olb{<}4pTF7?XOoYn-(5a-@HV!}s-v?SkT$+J) zpcPp3gJ?2Dz$(xmGocN@EC3}%-gexu;Ypweuyp_2KWK^ploa`Zt?__f&CEN{Sm|T^ zmb$errgirjI1BP?s+cqj6oGWitgG$$AHr$0$zdPYQBC3 zioWjx;3+Ad5(Ga2`hYiJMM?1x7r+hccqeI zKF&NVKv+p=UjTCYn4KyqX5!AZ3<$0pSl4CtUP&<(a$>STS+_>(zzNU+yZ{ps$2MEF z4%C==BT)7!el<$*#`l*6{?KZ3f_`3jX$4wc28Kr7vAZIwqTwDM*x-Hp`yKI69+i{nHQpNhAbzsf+ zBr9nkCYe|Z`uY7#zPr7-8b}Xb0~!6EGih^5iaqKUV4nx_h_33{WPAVs002ovPDHLk FV1hg3{5Ajp literal 0 HcmV?d00001